From 7241443f67bb352183bcfd7e3b807b87f2777b5d Mon Sep 17 00:00:00 2001 From: Heiko Stübner Date: Sat, 23 Feb 2013 12:08:55 -0800 Subject: Input: auo_pixcir_ts - add devicetree support Add the necessary code to create the needed platformdata from devicetree informations. The interrupt mode of the chip is not set via devicetree, as it is not a property of the hardware but instead only a preferred type of operation. This should probably be made settable via configfs in the future. The option set as default is the mode the datasheet mentions as default. Signed-off-by: Heiko Stuebner Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/auo_pixcir_ts.txt | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/auo_pixcir_ts.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/touchscreen/auo_pixcir_ts.txt b/Documentation/devicetree/bindings/input/touchscreen/auo_pixcir_ts.txt new file mode 100644 index 000000000000..f40f21c642b9 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/auo_pixcir_ts.txt @@ -0,0 +1,30 @@ +* AUO in-cell touchscreen controller using Pixcir sensors + +Required properties: +- compatible: must be "auo,auo_pixcir_ts" +- reg: I2C address of the chip +- interrupts: interrupt to which the chip is connected +- gpios: gpios the chip is connected to + first one is the interrupt gpio and second one the reset gpio +- x-size: horizontal resolution of touchscreen +- y-size: vertical resolution of touchscreen + +Example: + + i2c@00000000 { + /* ... */ + + auo_pixcir_ts@5c { + compatible = "auo,auo_pixcir_ts"; + reg = <0x5c>; + interrupts = <2 0>; + + gpios = <&gpf 2 0 2>, /* INT */ + <&gpf 5 1 0>; /* RST */ + + x-size = <800>; + y-size = <600>; + }; + + /* ... */ + }; -- cgit v1.2.3 From 3f8055c3583640ed3e4c81864dd76e06a7faa505 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 3 Mar 2013 23:08:16 +0100 Subject: ACPI / hotplug: Introduce user space interface for hotplug profiles Introduce user space interface for manipulating hotplug profiles associated with ACPI scan handlers. The interface consists of sysfs directories under /sys/firmware/acpi/hotplug/, one for each hotplug profile, containing an attribute allowing user space to manipulate the enabled field of the corresponding profile. Namely, switching the enabled attribute from '0' to '1' will cause the common hotplug notify handler to be installed for all ACPI namespace objects representing devices matching the scan handler associated with the given hotplug profile (and analogously for the converse switch). Drivers willing to use the new user space interface should add their ACPI scan handlers with the help of new funtion acpi_scan_add_handler_with_hotplug(). Signed-off-by: Rafael J. Wysocki Acked-by: Toshi Kani Tested-by: Toshi Kani --- Documentation/ABI/testing/sysfs-firmware-acpi | 26 +++++++++++ drivers/acpi/internal.h | 6 +++ drivers/acpi/scan.c | 59 ++++++++++++++++++++++++ drivers/acpi/sysfs.c | 66 +++++++++++++++++++++++++++ include/acpi/acpi_bus.h | 7 +++ 5 files changed, 164 insertions(+) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-firmware-acpi b/Documentation/ABI/testing/sysfs-firmware-acpi index dd930c8db41f..ce9bee98b43b 100644 --- a/Documentation/ABI/testing/sysfs-firmware-acpi +++ b/Documentation/ABI/testing/sysfs-firmware-acpi @@ -18,6 +18,32 @@ Description: yoffset: The number of pixels between the top of the screen and the top edge of the image. +What: /sys/firmware/acpi/hotplug/ +Date: February 2013 +Contact: Rafael J. Wysocki +Description: + There are separate hotplug profiles for different classes of + devices supported by ACPI, such as containers, memory modules, + processors, PCI root bridges etc. A hotplug profile for a given + class of devices is a collection of settings defining the way + that class of devices will be handled by the ACPI core hotplug + code. Those profiles are represented in sysfs as subdirectories + of /sys/firmware/acpi/hotplug/. + + The following setting is available to user space for each + hotplug profile: + + enabled: If set, the ACPI core will handle notifications of + hotplug events associated with the given class of + devices and will allow those devices to be ejected with + the help of the _EJ0 control method. Unsetting it + effectively disables hotplug for the correspoinding + class of devices. + + The value of the above attribute is an integer number: 1 (set) + or 0 (unset). Attempts to write any other values to it will + cause -EINVAL to be returned. + What: /sys/firmware/acpi/interrupts/ Date: February 2008 Contact: Len Brown diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 3c94a732b4b3..c708e4bad967 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -42,6 +42,12 @@ void acpi_container_init(void); static inline void acpi_container_init(void) {} #endif +void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, + const char *name); +int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, + const char *hotplug_profile_name); +void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val); + #ifdef CONFIG_DEBUG_FS extern struct dentry *acpi_debugfs_dir; int acpi_debugfs_init(void); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 45fbe95ba1f3..5458403c8249 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -63,6 +63,19 @@ int acpi_scan_add_handler(struct acpi_scan_handler *handler) return 0; } +int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, + const char *hotplug_profile_name) +{ + int error; + + error = acpi_scan_add_handler(handler); + if (error) + return error; + + acpi_sysfs_add_hotplug_profile(&handler->hotplug, hotplug_profile_name); + return 0; +} + /* * Creates hid/cid(s) string needed for modalias and uevent * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: @@ -1690,6 +1703,52 @@ static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler, return false; } +static acpi_status acpi_scan_hotplug_modify(acpi_handle handle, + u32 lvl_not_used, void *data, + void **ret_not_used) +{ + struct acpi_scan_handler *handler = data; + struct acpi_device_info *info; + bool match = false; + + if (ACPI_FAILURE(acpi_get_object_info(handle, &info))) + return AE_OK; + + if (info->valid & ACPI_VALID_HID) { + char *idstr = info->hardware_id.string; + match = acpi_scan_handler_matching(handler, idstr, NULL); + } + kfree(info); + if (!match) + return AE_OK; + + if (handler->hotplug.enabled) + acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + acpi_hotplug_notify_cb, NULL); + else + acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + acpi_hotplug_notify_cb); + + return AE_OK; +} + +void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val) +{ + struct acpi_scan_handler *handler; + + if (!!hotplug->enabled == !!val) + return; + + mutex_lock(&acpi_scan_lock); + + hotplug->enabled = val; + handler = container_of(hotplug, struct acpi_scan_handler, hotplug); + acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, + acpi_scan_hotplug_modify, NULL, handler, NULL); + + mutex_unlock(&acpi_scan_lock); +} + static struct acpi_scan_handler *acpi_scan_match_handler(char *idstr, const struct acpi_device_id **matchid) { diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 41c0504470db..83db3a68a7ee 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -7,6 +7,8 @@ #include #include +#include "internal.h" + #define _COMPONENT ACPI_SYSTEM_COMPONENT ACPI_MODULE_NAME("sysfs"); @@ -249,6 +251,7 @@ module_param_call(acpica_version, NULL, param_get_acpica_version, NULL, 0444); static LIST_HEAD(acpi_table_attr_list); static struct kobject *tables_kobj; static struct kobject *dynamic_tables_kobj; +static struct kobject *hotplug_kobj; struct acpi_table_attr { struct bin_attribute attr; @@ -716,6 +719,67 @@ acpi_show_profile(struct device *dev, struct device_attribute *attr, static const struct device_attribute pm_profile_attr = __ATTR(pm_profile, S_IRUGO, acpi_show_profile, NULL); +static ssize_t hotplug_enabled_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj); + + return sprintf(buf, "%d\n", hotplug->enabled); +} + +static ssize_t hotplug_enabled_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t size) +{ + struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj); + unsigned int val; + + if (kstrtouint(buf, 10, &val) || val > 1) + return -EINVAL; + + acpi_scan_hotplug_enabled(hotplug, val); + return size; +} + +static struct kobj_attribute hotplug_enabled_attr = + __ATTR(enabled, S_IRUGO | S_IWUSR, hotplug_enabled_show, + hotplug_enabled_store); + +static struct attribute *hotplug_profile_attrs[] = { + &hotplug_enabled_attr.attr, + NULL +}; + +struct kobj_type acpi_hotplug_profile_ktype = { + .sysfs_ops = &kobj_sysfs_ops, + .default_attrs = hotplug_profile_attrs, +}; + +void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, + const char *name) +{ + int error; + + if (!hotplug_kobj) + goto err_out; + + kobject_init(&hotplug->kobj, &acpi_hotplug_profile_ktype); + error = kobject_set_name(&hotplug->kobj, "%s", name); + if (error) + goto err_out; + + hotplug->kobj.parent = hotplug_kobj; + error = kobject_add(&hotplug->kobj, hotplug_kobj, NULL); + if (error) + goto err_out; + + kobject_uevent(&hotplug->kobj, KOBJ_ADD); + return; + + err_out: + pr_err(PREFIX "Unable to add hotplug profile '%s'\n", name); +} + int __init acpi_sysfs_init(void) { int result; @@ -723,6 +787,8 @@ int __init acpi_sysfs_init(void) result = acpi_tables_sysfs_init(); if (result) return result; + + hotplug_kobj = kobject_create_and_add("hotplug", acpi_kobj); result = sysfs_create_file(acpi_kobj, &pm_profile_attr.attr); return result; } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index f2c1d08a4798..533ef039c5e0 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -95,10 +95,17 @@ enum acpi_hotplug_mode { }; struct acpi_hotplug_profile { + struct kobject kobj; bool enabled:1; enum acpi_hotplug_mode mode; }; +static inline struct acpi_hotplug_profile *to_acpi_hotplug_profile( + struct kobject *kobj) +{ + return container_of(kobj, struct acpi_hotplug_profile, kobj); +} + struct acpi_scan_handler { const struct acpi_device_id *ids; struct list_head list_node; -- cgit v1.2.3 From 53bf0f446bc387eabdd535dca080789cc74607f4 Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Fri, 25 Jan 2013 06:29:56 -0300 Subject: [media] v4l: Define video buffer flag for the COPY timestamp type Define video buffer flag for the COPY timestamp. In this case the timestamp value is copied from the OUTPUT to the corresponding CAPTURE buffer. Signed-off-by: Kamil Debski Signed-off-by: Kyungmin Park Reviewed-by: Sylwester Nawrocki Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/io.xml | 6 ++++++ include/uapi/linux/videodev2.h | 1 + 2 files changed, 7 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index e6c58559ca6b..2c4c068dde83 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -1145,6 +1145,12 @@ in which case caches have not been used. same clock outside V4L2, use clock_gettime(2) . + + V4L2_BUF_FLAG_TIMESTAMP_COPY + 0x4000 + The CAPTURE buffer timestamp has been taken from the + corresponding OUTPUT buffer. This flag applies only to mem2mem devices. + diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 234d1d870914..b5f5cddcf1c3 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -705,6 +705,7 @@ struct v4l2_buffer { #define V4L2_BUF_FLAG_TIMESTAMP_MASK 0xe000 #define V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN 0x0000 #define V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC 0x2000 +#define V4L2_BUF_FLAG_TIMESTAMP_COPY 0x4000 /** * struct v4l2_exportbuffer - export of video buffer as DMABUF file descriptor -- cgit v1.2.3 From 5986453b7fe495687e4eafad8a3dd1ffd106bc80 Mon Sep 17 00:00:00 2001 From: Stuart Yoder Date: Tue, 5 Mar 2013 16:39:08 -0600 Subject: powerpc/e6500: Add architecture categories for e6500 cores -also define a binding for fsl,eref-* properties Signed-off-by: Stuart Yoder Signed-off-by: Kumar Gala --- .../devicetree/bindings/powerpc/fsl/cpus.txt | 22 ++++++++ arch/powerpc/boot/dts/fsl/e6500_power_isa.dtsi | 65 ++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 Documentation/devicetree/bindings/powerpc/fsl/cpus.txt create mode 100644 arch/powerpc/boot/dts/fsl/e6500_power_isa.dtsi (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/powerpc/fsl/cpus.txt b/Documentation/devicetree/bindings/powerpc/fsl/cpus.txt new file mode 100644 index 000000000000..922c30ad90d1 --- /dev/null +++ b/Documentation/devicetree/bindings/powerpc/fsl/cpus.txt @@ -0,0 +1,22 @@ +=================================================================== +Power Architecture CPU Binding +Copyright 2013 Freescale Semiconductor Inc. + +Power Architecture CPUs in Freescale SOCs are represented in device trees as +per the definition in ePAPR. + +In addition to the ePAPR definitions, the properties defined below may be +present on CPU nodes. + +PROPERTIES + + - fsl,eref-* + Usage: optional + Value type: + Definition: The EREF (EREF: A Programmer.s Reference Manual for + Freescale Power Architecture) defines the architecture for Freescale + Power CPUs. The EREF defines some architecture categories not defined + by the Power ISA. For these EREF-specific categories, the existence of + a property named fsl,eref-[CAT], where [CAT] is the abbreviated category + name with all uppercase letters converted to lowercase, indicates that + the category is supported by the implementation. diff --git a/arch/powerpc/boot/dts/fsl/e6500_power_isa.dtsi b/arch/powerpc/boot/dts/fsl/e6500_power_isa.dtsi new file mode 100644 index 000000000000..a912dbeff359 --- /dev/null +++ b/arch/powerpc/boot/dts/fsl/e6500_power_isa.dtsi @@ -0,0 +1,65 @@ +/* + * e6500 Power ISA Device Tree Source (include) + * + * Copyright 2013 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/ { + cpus { + power-isa-version = "2.06"; + power-isa-b; // Base + power-isa-e; // Embedded + power-isa-atb; // Alternate Time Base + power-isa-cs; // Cache Specification + power-isa-ds; // Decorated Storage + power-isa-e.ed; // Embedded.Enhanced Debug + power-isa-e.pd; // Embedded.External PID + power-isa-e.hv; // Embedded.Hypervisor + power-isa-e.le; // Embedded.Little-Endian + power-isa-e.pm; // Embedded.Performance Monitor + power-isa-e.pc; // Embedded.Processor Control + power-isa-ecl; // Embedded Cache Locking + power-isa-exp; // External Proxy + power-isa-fp; // Floating Point + power-isa-fp.r; // Floating Point.Record + power-isa-mmc; // Memory Coherence + power-isa-scpm; // Store Conditional Page Mobility + power-isa-wt; // Wait + power-isa-64; // 64-bit + power-isa-e.pt; // Embedded.Page Table + power-isa-e.hv.lrat; // Embedded.Hypervisor.LRAT + power-isa-e.em; // Embedded Multi-Threading + power-isa-v; // Vector (AltiVec) + fsl,eref-er; // Enhanced Reservations (Load and Reserve and Store Cond.) + fsl,eref-deo; // Data Cache Extended Operations + mmu-type = "power-embedded"; + }; +}; -- cgit v1.2.3 From 86853c83e33738397564e9377ceeff94d4bc041c Mon Sep 17 00:00:00 2001 From: Haojian Zhuang Date: Sun, 17 Feb 2013 19:42:47 +0800 Subject: gpio: add gpio offset in gpio range cells property Add gpio offset into "gpio-range-cells" property. It's used to support sparse pinctrl range in gpio chip. Signed-off-by: Haojian Zhuang Acked-by: Viresh Kumar Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/gpio/gpio.txt | 6 +++--- arch/arm/boot/dts/spear1310.dtsi | 4 ++-- arch/arm/boot/dts/spear1340.dtsi | 4 ++-- arch/arm/boot/dts/spear310.dtsi | 4 ++-- arch/arm/boot/dts/spear320.dtsi | 4 ++-- drivers/gpio/gpiolib-of.c | 15 ++------------- 6 files changed, 13 insertions(+), 24 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt index a33628759d36..d933af370697 100644 --- a/Documentation/devicetree/bindings/gpio/gpio.txt +++ b/Documentation/devicetree/bindings/gpio/gpio.txt @@ -98,7 +98,7 @@ announce the pinrange to the pin ctrl subsystem. For example, compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank"; reg = <0x1460 0x18>; gpio-controller; - gpio-ranges = <&pinctrl1 20 10>, <&pinctrl2 50 20>; + gpio-ranges = <&pinctrl1 0 20 10>, <&pinctrl2 10 50 20>; } @@ -107,8 +107,8 @@ where, Next values specify the base pin and number of pins for the range handled by 'qe_pio_e' gpio. In the given example from base pin 20 to - pin 29 under pinctrl1 and pin 50 to pin 69 under pinctrl2 is handled - by this gpio controller. + pin 29 under pinctrl1 with gpio offset 0 and pin 50 to pin 69 under + pinctrl2 with gpio offset 10 is handled by this gpio controller. The pinctrl node must have "#gpio-range-cells" property to show number of arguments to pass with phandle from gpio controllers node. diff --git a/arch/arm/boot/dts/spear1310.dtsi b/arch/arm/boot/dts/spear1310.dtsi index 1513c1927cc8..122ae94076c8 100644 --- a/arch/arm/boot/dts/spear1310.dtsi +++ b/arch/arm/boot/dts/spear1310.dtsi @@ -89,7 +89,7 @@ pinmux: pinmux@e0700000 { compatible = "st,spear1310-pinmux"; reg = <0xe0700000 0x1000>; - #gpio-range-cells = <2>; + #gpio-range-cells = <3>; }; apb { @@ -212,7 +212,7 @@ interrupt-controller; gpio-controller; #gpio-cells = <2>; - gpio-ranges = <&pinmux 0 246>; + gpio-ranges = <&pinmux 0 0 246>; status = "disabled"; st-plgpio,ngpio = <246>; diff --git a/arch/arm/boot/dts/spear1340.dtsi b/arch/arm/boot/dts/spear1340.dtsi index 34da11aa6795..c511c4772efd 100644 --- a/arch/arm/boot/dts/spear1340.dtsi +++ b/arch/arm/boot/dts/spear1340.dtsi @@ -63,7 +63,7 @@ pinmux: pinmux@e0700000 { compatible = "st,spear1340-pinmux"; reg = <0xe0700000 0x1000>; - #gpio-range-cells = <2>; + #gpio-range-cells = <3>; }; pwm: pwm@e0180000 { @@ -127,7 +127,7 @@ interrupt-controller; gpio-controller; #gpio-cells = <2>; - gpio-ranges = <&pinmux 0 252>; + gpio-ranges = <&pinmux 0 0 252>; status = "disabled"; st-plgpio,ngpio = <250>; diff --git a/arch/arm/boot/dts/spear310.dtsi b/arch/arm/boot/dts/spear310.dtsi index ab45b8c81982..95372080eea6 100644 --- a/arch/arm/boot/dts/spear310.dtsi +++ b/arch/arm/boot/dts/spear310.dtsi @@ -25,7 +25,7 @@ pinmux: pinmux@b4000000 { compatible = "st,spear310-pinmux"; reg = <0xb4000000 0x1000>; - #gpio-range-cells = <2>; + #gpio-range-cells = <3>; }; fsmc: flash@44000000 { @@ -102,7 +102,7 @@ interrupt-controller; gpio-controller; #gpio-cells = <2>; - gpio-ranges = <&pinmux 0 102>; + gpio-ranges = <&pinmux 0 0 102>; status = "disabled"; st-plgpio,ngpio = <102>; diff --git a/arch/arm/boot/dts/spear320.dtsi b/arch/arm/boot/dts/spear320.dtsi index caa5520b1fd4..ffea342aeec9 100644 --- a/arch/arm/boot/dts/spear320.dtsi +++ b/arch/arm/boot/dts/spear320.dtsi @@ -24,7 +24,7 @@ pinmux: pinmux@b3000000 { compatible = "st,spear320-pinmux"; reg = <0xb3000000 0x1000>; - #gpio-range-cells = <2>; + #gpio-range-cells = <3>; }; clcd@90000000 { @@ -130,7 +130,7 @@ interrupt-controller; gpio-controller; #gpio-cells = <2>; - gpio-ranges = <&pinmux 0 102>; + gpio-ranges = <&pinmux 0 0 102>; status = "disabled"; st-plgpio,ngpio = <102>; diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index a71a54a3e3f7..892040ad0095 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -203,22 +203,11 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip) if (!pctldev) break; - /* - * This assumes that the n GPIO pins are consecutive in the - * GPIO number space, and that the pins are also consecutive - * in their local number space. Currently it is not possible - * to add different ranges for one and the same GPIO chip, - * as the code assumes that we have one consecutive range - * on both, mapping 1-to-1. - * - * TODO: make the OF bindings handle multiple sparse ranges - * on the same GPIO chip. - */ ret = gpiochip_add_pin_range(chip, pinctrl_dev_get_devname(pctldev), - 0, /* offset in gpiochip */ pinspec.args[0], - pinspec.args[1]); + pinspec.args[1], + pinspec.args[2]); if (ret) break; -- cgit v1.2.3 From 32378ab781e3e5da6e25c51e452a43e21fbabb3a Mon Sep 17 00:00:00 2001 From: Haojian Zhuang Date: Sun, 17 Feb 2013 19:42:56 +0800 Subject: document: devicetree: bind pinconf with pin single Add comments with pinconf & gpio range in the document of pinctrl-single. Signed-off-by: Haojian Zhuang Acked-by: Tony Lindgren Signed-off-by: Linus Walleij --- .../devicetree/bindings/pinctrl/pinctrl-single.txt | 107 ++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt index 2c81e45f1374..fa1746b639b9 100644 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt @@ -1,7 +1,9 @@ One-register-per-pin type device tree based pinctrl driver Required properties: -- compatible : "pinctrl-single" +- compatible : "pinctrl-single" or "pinconf-single". + "pinctrl-single" means that pinconf isn't supported. + "pinconf-single" means that generic pinconf is supported. - reg : offset and length of the register set for the mux registers @@ -14,9 +16,61 @@ Optional properties: - pinctrl-single,function-off : function off mode for disabled state if available and same for all registers; if not specified, disabling of pin functions is ignored + - pinctrl-single,bit-per-mux : boolean to indicate that one register controls more than one pin +- pinctrl-single,drive-strength : array of value that are used to configure + drive strength in the pinmux register. They're value of drive strength + current and drive strength mask. + + /* drive strength current, mask */ + pinctrl-single,power-source = <0x30 0xf0>; + +- pinctrl-single,bias-pullup : array of value that are used to configure the + input bias pullup in the pinmux register. + + /* input, enabled pullup bits, disabled pullup bits, mask */ + pinctrl-single,bias-pullup = <0 1 0 1>; + +- pinctrl-single,bias-pulldown : array of value that are used to configure the + input bias pulldown in the pinmux register. + + /* input, enabled pulldown bits, disabled pulldown bits, mask */ + pinctrl-single,bias-pulldown = <2 2 0 2>; + + * Two bits to control input bias pullup and pulldown: User should use + pinctrl-single,bias-pullup & pinctrl-single,bias-pulldown. One bit means + pullup, and the other one bit means pulldown. + * Three bits to control input bias enable, pullup and pulldown. User should + use pinctrl-single,bias-pullup & pinctrl-single,bias-pulldown. Input bias + enable bit should be included in pullup or pulldown bits. + * Although driver could set PIN_CONFIG_BIAS_DISABLE, there's no property as + pinctrl-single,bias-disable. Because pinctrl single driver could implement + it by calling pulldown, pullup disabled. + +- pinctrl-single,input-schmitt : array of value that are used to configure + input schmitt in the pinmux register. In some silicons, there're two input + schmitt value (rising-edge & falling-edge) in the pinmux register. + + /* input schmitt value, mask */ + pinctrl-single,input-schmitt = <0x30 0x70>; + +- pinctrl-single,input-schmitt-enable : array of value that are used to + configure input schmitt enable or disable in the pinmux register. + + /* input, enable bits, disable bits, mask */ + pinctrl-single,input-schmitt-enable = <0x30 0x40 0 0x70>; + +- pinctrl-single,gpio-range : list of value that are used to configure a GPIO + range. They're value of subnode phandle, pin base in pinctrl device, pin + number in this range, GPIO function value of this GPIO range. + The number of parameters is depend on #pinctrl-single,gpio-range-cells + property. + + /* pin base, nr pins & gpio function */ + pinctrl-single,gpio-range = <&range 0 3 0 &range 3 9 1>; + This driver assumes that there is only one register for each pin (unless the pinctrl-single,bit-per-mux is set), and uses the common pinctrl bindings as specified in the pinctrl-bindings.txt document in this directory. @@ -42,6 +96,20 @@ Where 0xdc is the offset from the pinctrl register base address for the device pinctrl register, 0x18 is the desired value, and 0xff is the sub mask to be used when applying this change to the register. + +Optional sub-node: In case some pins could be configured as GPIO in the pinmux +register, those pins could be defined as a GPIO range. This sub-node is required +by pinctrl-single,gpio-range property. + +Required properties in sub-node: +- #pinctrl-single,gpio-range-cells : the number of parameters after phandle in + pinctrl-single,gpio-range property. + + range: gpio-range { + #pinctrl-single,gpio-range-cells = <3>; + }; + + Example: /* SoC common file */ @@ -76,6 +144,29 @@ control_devconf0: pinmux@48002274 { pinctrl-single,function-mask = <0x5F>; }; +/* third controller instance for pins in gpio domain */ +pmx_gpio: pinmux@d401e000 { + compatible = "pinconf-single"; + reg = <0xd401e000 0x0330>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <7>; + + /* sparse GPIO range could be supported */ + pinctrl-single,gpio-range = <&range 0 3 0 &range 3 9 1 + &range 12 1 0 &range 13 29 1 + &range 43 1 0 &range 44 49 1 + &range 94 1 1 &range 96 2 1>; + + range: gpio-range { + #pinctrl-single,gpio-range-cells = <3>; + }; +}; + + /* board specific .dts file */ &pmx_core { @@ -96,6 +187,15 @@ control_devconf0: pinmux@48002274 { >; }; + uart0_pins: pinmux_uart0_pins { + pinctrl-single,pins = < + 0x208 0 /* UART0_RXD (IOCFG138) */ + 0x20c 0 /* UART0_TXD (IOCFG139) */ + >; + pinctrl-single,bias-pulldown = <0 2 2>; + pinctrl-single,bias-pullup = <0 1 1>; + }; + /* map uart2 pins */ uart2_pins: pinmux_uart2_pins { pinctrl-single,pins = < @@ -122,6 +222,11 @@ control_devconf0: pinmux@48002274 { }; +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins>; +}; + &uart2 { pinctrl-names = "default"; pinctrl-0 = <&uart2_pins>; -- cgit v1.2.3 From e943789edbb1f9de71b129d9992489eb79ed341f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 15 Feb 2013 21:38:08 +0100 Subject: mac80211: provide ieee80211_sta_eosp() The irqsafe version ieee80211_sta_eosp_irqsafe() exists, but drivers must not mix calls to any irqsafe/non-irqsafe function. Both ath9k and iwlwifi, the likely first users of this interface, use non-irqsafe RX/TX/TX status so must also use a non-irqsafe version of this function. Since no driver uses the _irqsafe() version, remove that. Signed-off-by: Johannes Berg --- Documentation/DocBook/80211.tmpl | 2 +- include/net/mac80211.h | 25 ++++++++++++++----------- net/mac80211/ieee80211_i.h | 5 ----- net/mac80211/main.c | 14 -------------- net/mac80211/sta_info.c | 20 +++----------------- 5 files changed, 18 insertions(+), 48 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl index 284ced7a228f..0f6a3edcd44b 100644 --- a/Documentation/DocBook/80211.tmpl +++ b/Documentation/DocBook/80211.tmpl @@ -437,7 +437,7 @@ !Finclude/net/mac80211.h ieee80211_get_buffered_bc !Finclude/net/mac80211.h ieee80211_beacon_get -!Finclude/net/mac80211.h ieee80211_sta_eosp_irqsafe +!Finclude/net/mac80211.h ieee80211_sta_eosp !Finclude/net/mac80211.h ieee80211_frame_release_type !Finclude/net/mac80211.h ieee80211_sta_ps_transition !Finclude/net/mac80211.h ieee80211_sta_ps_transition_ni diff --git a/include/net/mac80211.h b/include/net/mac80211.h index cdd7cea1fd4c..8c0ca11a39c4 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1946,14 +1946,14 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb); * filter those response frames except in the case of frames that * are buffered in the driver -- those must remain buffered to avoid * reordering. Because it is possible that no frames are released - * in this case, the driver must call ieee80211_sta_eosp_irqsafe() + * in this case, the driver must call ieee80211_sta_eosp() * to indicate to mac80211 that the service period ended anyway. * * Finally, if frames from multiple TIDs are released from mac80211 * but the driver might reorder them, it must clear & set the flags * appropriately (only the last frame may have %IEEE80211_TX_STATUS_EOSP) * and also take care of the EOSP and MORE_DATA bits in the frame. - * The driver may also use ieee80211_sta_eosp_irqsafe() in this case. + * The driver may also use ieee80211_sta_eosp() in this case. */ /** @@ -2506,7 +2506,7 @@ enum ieee80211_roc_type { * setting the EOSP flag in the QoS header of the frames. Also, when the * service period ends, the driver must set %IEEE80211_TX_STATUS_EOSP * on the last frame in the SP. Alternatively, it may call the function - * ieee80211_sta_eosp_irqsafe() to inform mac80211 of the end of the SP. + * ieee80211_sta_eosp() to inform mac80211 of the end of the SP. * This callback must be atomic. * @allow_buffered_frames: Prepare device to allow the given number of frames * to go out to the given station. The frames will be sent by mac80211 @@ -2517,7 +2517,7 @@ enum ieee80211_roc_type { * them between the TIDs, it must set the %IEEE80211_TX_STATUS_EOSP flag * on the last frame and clear it on all others and also handle the EOSP * bit in the QoS header correctly. Alternatively, it can also call the - * ieee80211_sta_eosp_irqsafe() function. + * ieee80211_sta_eosp() function. * The @tids parameter is a bitmap and tells the driver which TIDs the * frames will be on; it will at most have two bits set. * This callback must be atomic. @@ -3857,14 +3857,17 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, * %IEEE80211_TX_STATUS_EOSP bit and call this function instead. * This applies for PS-Poll as well as uAPSD. * - * Note that there is no non-_irqsafe version right now as - * it wasn't needed, but just like _tx_status() and _rx() - * must not be mixed in irqsafe/non-irqsafe versions, this - * function must not be mixed with those either. Use the - * all irqsafe, or all non-irqsafe, don't mix! If you need - * the non-irqsafe version of this, you need to add it. + * Note that just like with _tx_status() and _rx() drivers must + * not mix calls to irqsafe/non-irqsafe versions, this function + * must not be mixed with those either. Use the all irqsafe, or + * all non-irqsafe, don't mix! + * + * NB: the _irqsafe version of this function doesn't exist, no + * driver needs it right now. Don't call this function if + * you'd need the _irqsafe version, look at the git history + * and restore the _irqsafe version! */ -void ieee80211_sta_eosp_irqsafe(struct ieee80211_sta *pubsta); +void ieee80211_sta_eosp(struct ieee80211_sta *pubsta); /** * ieee80211_iter_keys - iterate keys programmed into the device diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index f4433f081e77..95beb18588f6 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -800,11 +800,6 @@ enum sdata_queue_type { enum { IEEE80211_RX_MSG = 1, IEEE80211_TX_STATUS_MSG = 2, - IEEE80211_EOSP_MSG = 3, -}; - -struct skb_eosp_msg_data { - u8 sta[ETH_ALEN], iface[ETH_ALEN]; }; enum queue_stop_reason { diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 5a53aa5ede80..5531c89909d8 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -226,8 +226,6 @@ u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata) static void ieee80211_tasklet_handler(unsigned long data) { struct ieee80211_local *local = (struct ieee80211_local *) data; - struct sta_info *sta, *tmp; - struct skb_eosp_msg_data *eosp_data; struct sk_buff *skb; while ((skb = skb_dequeue(&local->skb_queue)) || @@ -243,18 +241,6 @@ static void ieee80211_tasklet_handler(unsigned long data) skb->pkt_type = 0; ieee80211_tx_status(&local->hw, skb); break; - case IEEE80211_EOSP_MSG: - eosp_data = (void *)skb->cb; - for_each_sta_info(local, eosp_data->sta, sta, tmp) { - /* skip wrong virtual interface */ - if (memcmp(eosp_data->iface, - sta->sdata->vif.addr, ETH_ALEN)) - continue; - clear_sta_flag(sta, WLAN_STA_SP); - break; - } - dev_kfree_skb(skb); - break; default: WARN(1, "mac80211: Packet is of unknown type %d\n", skb->pkt_type); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 3644ad79688a..852bf45fcfa3 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1390,30 +1390,16 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_sta_block_awake); -void ieee80211_sta_eosp_irqsafe(struct ieee80211_sta *pubsta) +void ieee80211_sta_eosp(struct ieee80211_sta *pubsta) { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); struct ieee80211_local *local = sta->local; - struct sk_buff *skb; - struct skb_eosp_msg_data *data; trace_api_eosp(local, pubsta); - skb = alloc_skb(0, GFP_ATOMIC); - if (!skb) { - /* too bad ... but race is better than loss */ - clear_sta_flag(sta, WLAN_STA_SP); - return; - } - - data = (void *)skb->cb; - memcpy(data->sta, pubsta->addr, ETH_ALEN); - memcpy(data->iface, sta->sdata->vif.addr, ETH_ALEN); - skb->pkt_type = IEEE80211_EOSP_MSG; - skb_queue_tail(&local->skb_queue, skb); - tasklet_schedule(&local->tasklet); + clear_sta_flag(sta, WLAN_STA_SP); } -EXPORT_SYMBOL(ieee80211_sta_eosp_irqsafe); +EXPORT_SYMBOL(ieee80211_sta_eosp); void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta, u8 tid, bool buffered) -- cgit v1.2.3 From d097ddaf529f69b9fea1efec2c9dd5e82ce388c1 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Fri, 8 Mar 2013 10:56:31 +0100 Subject: doc: virtual: Fix typos in virtio-spec.txt Correct spelling typo in documentation/virtual/virtio-spec.txt Signed-off-by: Masanari Iida Acked-by: Rob Landley Signed-off-by: Jiri Kosina --- Documentation/virtual/virtio-spec.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/virtual/virtio-spec.txt b/Documentation/virtual/virtio-spec.txt index 0d6ec85481cb..eb094039b50d 100644 --- a/Documentation/virtual/virtio-spec.txt +++ b/Documentation/virtual/virtio-spec.txt @@ -1389,7 +1389,7 @@ segmentation, if both guests are amenable. Packets are transmitted by placing them in the transmitq, and buffers for incoming packets are placed in the receiveq. In each -case, the packet itself is preceeded by a header: +case, the packet itself is preceded by a header: struct virtio_net_hdr { @@ -1631,7 +1631,7 @@ struct virtio_net_ctrl_mac { The device can filter incoming packets by any number of destination MAC addresses.[footnote: -Since there are no guarentees, it can use a hash filter +Since there are no guarantees, it can use a hash filter orsilently switch to allmulti or promiscuous mode if it is given too many addresses. ] This table is set using the class VIRTIO_NET_CTRL_MAC and the @@ -1822,7 +1822,7 @@ the FLUSH and FLUSH_OUT types are equivalent, the device does not distinguish between them ]). If the device has VIRTIO_BLK_F_BARRIER feature the high bit (VIRTIO_BLK_T_BARRIER) indicates that this request acts as a -barrier and that all preceeding requests must be complete before +barrier and that all preceding requests must be complete before this one, and all following requests must not be started until this is complete. Note that a barrier does not flush caches in the underlying backend device in host, and thus does not serve as -- cgit v1.2.3 From 6375bcf7867fb18241fda47bfaeec85b925221f3 Mon Sep 17 00:00:00 2001 From: Tang Chen Date: Thu, 7 Mar 2013 18:38:17 +0800 Subject: hwrng: Fix a wrong comment in Documentation/hw_random.txt Seeing from the comment, there should be three reasons for removing request_mem_region. Change the comment "two" to "three". Signed-off-by: Tang Chen Acked-by: Rob Landley Signed-off-by: Herbert Xu --- Documentation/hw_random.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/hw_random.txt b/Documentation/hw_random.txt index 690f52550c80..026e237bbc87 100644 --- a/Documentation/hw_random.txt +++ b/Documentation/hw_random.txt @@ -63,7 +63,7 @@ Intel RNG Driver notes: * FIXME: support poll(2) - NOTE: request_mem_region was removed, for two reasons: + NOTE: request_mem_region was removed, for three reasons: 1) Only one RNG is supported by this driver, 2) The location used by the RNG is a fixed location in MMIO-addressable memory, 3) users with properly working BIOS e820 handling will always -- cgit v1.2.3 From 29266e2e29f1f87b93321e56812f9fb16f91cb6d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 12 Mar 2013 15:37:33 +1030 Subject: Remove Documentation/virtual/virtio-spec.txt We haven't been keeping it in sync, so just remove it. Signed-off-by: Rusty Russell --- Documentation/virtual/00-INDEX | 3 - Documentation/virtual/virtio-spec.txt | 3210 --------------------------------- 2 files changed, 3213 deletions(-) delete mode 100644 Documentation/virtual/virtio-spec.txt (limited to 'Documentation') diff --git a/Documentation/virtual/00-INDEX b/Documentation/virtual/00-INDEX index 924bd462675e..e952d30bbf0f 100644 --- a/Documentation/virtual/00-INDEX +++ b/Documentation/virtual/00-INDEX @@ -6,6 +6,3 @@ kvm/ - Kernel Virtual Machine. See also http://linux-kvm.org uml/ - User Mode Linux, builds/runs Linux kernel as a userspace program. -virtio.txt - - Text version of draft virtio spec. - See http://ozlabs.org/~rusty/virtio-spec diff --git a/Documentation/virtual/virtio-spec.txt b/Documentation/virtual/virtio-spec.txt deleted file mode 100644 index 0d6ec85481cb..000000000000 --- a/Documentation/virtual/virtio-spec.txt +++ /dev/null @@ -1,3210 +0,0 @@ -[Generated file: see http://ozlabs.org/~rusty/virtio-spec/] -Virtio PCI Card Specification -v0.9.5 DRAFT -- - -Rusty Russell IBM Corporation (Editor) - -2012 May 7. - -Purpose and Description - -This document describes the specifications of the “virtio” family -of PCI[LaTeX Command: nomenclature] devices. These are devices -are found in virtual environments[LaTeX Command: nomenclature], -yet by design they are not all that different from physical PCI -devices, and this document treats them as such. This allows the -guest to use standard PCI drivers and discovery mechanisms. - -The purpose of virtio and this specification is that virtual -environments and guests should have a straightforward, efficient, -standard and extensible mechanism for virtual devices, rather -than boutique per-environment or per-OS mechanisms. - - Straightforward: Virtio PCI devices use normal PCI mechanisms - of interrupts and DMA which should be familiar to any device - driver author. There is no exotic page-flipping or COW - mechanism: it's just a PCI device.[footnote: -This lack of page-sharing implies that the implementation of the -device (e.g. the hypervisor or host) needs full access to the -guest memory. Communication with untrusted parties (i.e. -inter-guest communication) requires copying. -] - - Efficient: Virtio PCI devices consist of rings of descriptors - for input and output, which are neatly separated to avoid cache - effects from both guest and device writing to the same cache - lines. - - Standard: Virtio PCI makes no assumptions about the environment - in which it operates, beyond supporting PCI. In fact the virtio - devices specified in the appendices do not require PCI at all: - they have been implemented on non-PCI buses.[footnote: -The Linux implementation further separates the PCI virtio code -from the specific virtio drivers: these drivers are shared with -the non-PCI implementations (currently lguest and S/390). -] - - Extensible: Virtio PCI devices contain feature bits which are - acknowledged by the guest operating system during device setup. - This allows forwards and backwards compatibility: the device - offers all the features it knows about, and the driver - acknowledges those it understands and wishes to use. - - Virtqueues - -The mechanism for bulk data transport on virtio PCI devices is -pretentiously called a virtqueue. Each device can have zero or -more virtqueues: for example, the network device has one for -transmit and one for receive. - -Each virtqueue occupies two or more physically-contiguous pages -(defined, for the purposes of this specification, as 4096 bytes), -and consists of three parts: - - -+-------------------+-----------------------------------+-----------+ -| Descriptor Table | Available Ring (padding) | Used Ring | -+-------------------+-----------------------------------+-----------+ - - -When the driver wants to send a buffer to the device, it fills in -a slot in the descriptor table (or chains several together), and -writes the descriptor index into the available ring. It then -notifies the device. When the device has finished a buffer, it -writes the descriptor into the used ring, and sends an interrupt. - -Specification - - PCI Discovery - -Any PCI device with Vendor ID 0x1AF4, and Device ID 0x1000 -through 0x103F inclusive is a virtio device[footnote: -The actual value within this range is ignored -]. The device must also have a Revision ID of 0 to match this -specification. - -The Subsystem Device ID indicates which virtio device is -supported by the device. The Subsystem Vendor ID should reflect -the PCI Vendor ID of the environment (it's currently only used -for informational purposes by the guest). - - -+----------------------+--------------------+---------------+ -| Subsystem Device ID | Virtio Device | Specification | -+----------------------+--------------------+---------------+ -+----------------------+--------------------+---------------+ -| 1 | network card | Appendix C | -+----------------------+--------------------+---------------+ -| 2 | block device | Appendix D | -+----------------------+--------------------+---------------+ -| 3 | console | Appendix E | -+----------------------+--------------------+---------------+ -| 4 | entropy source | Appendix F | -+----------------------+--------------------+---------------+ -| 5 | memory ballooning | Appendix G | -+----------------------+--------------------+---------------+ -| 6 | ioMemory | - | -+----------------------+--------------------+---------------+ -| 7 | rpmsg | Appendix H | -+----------------------+--------------------+---------------+ -| 8 | SCSI host | Appendix I | -+----------------------+--------------------+---------------+ -| 9 | 9P transport | - | -+----------------------+--------------------+---------------+ -| 10 | mac80211 wlan | - | -+----------------------+--------------------+---------------+ - - - Device Configuration - -To configure the device, we use the first I/O region of the PCI -device. This contains a virtio header followed by a -device-specific region. - -There may be different widths of accesses to the I/O region; the “ -natural” access method for each field in the virtio header must -be used (i.e. 32-bit accesses for 32-bit fields, etc), but the -device-specific region can be accessed using any width accesses, -and should obtain the same results. - -Note that this is possible because while the virtio header is PCI -(i.e. little) endian, the device-specific region is encoded in -the native endian of the guest (where such distinction is -applicable). - - Device Initialization Sequence - -We start with an overview of device initialization, then expand -on the details of the device and how each step is preformed. - - Reset the device. This is not required on initial start up. - - The ACKNOWLEDGE status bit is set: we have noticed the device. - - The DRIVER status bit is set: we know how to drive the device. - - Device-specific setup, including reading the Device Feature - Bits, discovery of virtqueues for the device, optional MSI-X - setup, and reading and possibly writing the virtio - configuration space. - - The subset of Device Feature Bits understood by the driver is - written to the device. - - The DRIVER_OK status bit is set. - - The device can now be used (ie. buffers added to the - virtqueues)[footnote: -Historically, drivers have used the device before steps 5 and 6. -This is only allowed if the driver does not use any features -which would alter this early use of the device. -] - -If any of these steps go irrecoverably wrong, the guest should -set the FAILED status bit to indicate that it has given up on the -device (it can reset the device later to restart if desired). - -We now cover the fields required for general setup in detail. - - Virtio Header - -The virtio header looks as follows: - - -+------------++---------------------+---------------------+----------+--------+---------+---------+---------+--------+ -| Bits || 32 | 32 | 32 | 16 | 16 | 16 | 8 | 8 | -+------------++---------------------+---------------------+----------+--------+---------+---------+---------+--------+ -| Read/Write || R | R+W | R+W | R | R+W | R+W | R+W | R | -+------------++---------------------+---------------------+----------+--------+---------+---------+---------+--------+ -| Purpose || Device | Guest | Queue | Queue | Queue | Queue | Device | ISR | -| || Features bits 0:31 | Features bits 0:31 | Address | Size | Select | Notify | Status | Status | -+------------++---------------------+---------------------+----------+--------+---------+---------+---------+--------+ - - -If MSI-X is enabled for the device, two additional fields -immediately follow this header:[footnote: -ie. once you enable MSI-X on the device, the other fields move. -If you turn it off again, they move back! -] - - -+------------++----------------+--------+ -| Bits || 16 | 16 | - +----------------+--------+ -+------------++----------------+--------+ -| Read/Write || R+W | R+W | -+------------++----------------+--------+ -| Purpose || Configuration | Queue | -| (MSI-X) || Vector | Vector | -+------------++----------------+--------+ - - -Immediately following these general headers, there may be -device-specific headers: - - -+------------++--------------------+ -| Bits || Device Specific | - +--------------------+ -+------------++--------------------+ -| Read/Write || Device Specific | -+------------++--------------------+ -| Purpose || Device Specific... | -| || | -+------------++--------------------+ - - - Device Status - -The Device Status field is updated by the guest to indicate its -progress. This provides a simple low-level diagnostic: it's most -useful to imagine them hooked up to traffic lights on the console -indicating the status of each device. - -The device can be reset by writing a 0 to this field, otherwise -at least one bit should be set: - - ACKNOWLEDGE (1) Indicates that the guest OS has found the - device and recognized it as a valid virtio device. - - DRIVER (2) Indicates that the guest OS knows how to drive the - device. Under Linux, drivers can be loadable modules so there - may be a significant (or infinite) delay before setting this - bit. - - DRIVER_OK (4) Indicates that the driver is set up and ready to - drive the device. - - FAILED (128) Indicates that something went wrong in the guest, - and it has given up on the device. This could be an internal - error, or the driver didn't like the device for some reason, or - even a fatal error during device operation. The device must be - reset before attempting to re-initialize. - - Feature Bits - -Thefirst configuration field indicates the features that the -device supports. The bits are allocated as follows: - - 0 to 23 Feature bits for the specific device type - - 24 to 32 Feature bits reserved for extensions to the queue and - feature negotiation mechanisms - -For example, feature bit 0 for a network device (i.e. Subsystem -Device ID 1) indicates that the device supports checksumming of -packets. - -The feature bits are negotiated: the device lists all the -features it understands in the Device Features field, and the -guest writes the subset that it understands into the Guest -Features field. The only way to renegotiate is to reset the -device. - -In particular, new fields in the device configuration header are -indicated by offering a feature bit, so the guest can check -before accessing that part of the configuration space. - -This allows for forwards and backwards compatibility: if the -device is enhanced with a new feature bit, older guests will not -write that feature bit back to the Guest Features field and it -can go into backwards compatibility mode. Similarly, if a guest -is enhanced with a feature that the device doesn't support, it -will not see that feature bit in the Device Features field and -can go into backwards compatibility mode (or, for poor -implementations, set the FAILED Device Status bit). - - Configuration/Queue Vectors - -When MSI-X capability is present and enabled in the device -(through standard PCI configuration space) 4 bytes at byte offset -20 are used to map configuration change and queue interrupts to -MSI-X vectors. In this case, the ISR Status field is unused, and -device specific configuration starts at byte offset 24 in virtio -header structure. When MSI-X capability is not enabled, device -specific configuration starts at byte offset 20 in virtio header. - -Writing a valid MSI-X Table entry number, 0 to 0x7FF, to one of -Configuration/Queue Vector registers, maps interrupts triggered -by the configuration change/selected queue events respectively to -the corresponding MSI-X vector. To disable interrupts for a -specific event type, unmap it by writing a special NO_VECTOR -value: - -/* Vector value used to disable MSI for queue */ - -#define VIRTIO_MSI_NO_VECTOR 0xffff - -Reading these registers returns vector mapped to a given event, -or NO_VECTOR if unmapped. All queue and configuration change -events are unmapped by default. - -Note that mapping an event to vector might require allocating -internal device resources, and might fail. Devices report such -failures by returning the NO_VECTOR value when the relevant -Vector field is read. After mapping an event to vector, the -driver must verify success by reading the Vector field value: on -success, the previously written value is returned, and on -failure, NO_VECTOR is returned. If a mapping failure is detected, -the driver can retry mapping with fewervectors, or disable MSI-X. - - Virtqueue Configuration - -As a device can have zero or more virtqueues for bulk data -transport (for example, the network driver has two), the driver -needs to configure them as part of the device-specific -configuration. - -This is done as follows, for each virtqueue a device has: - - Write the virtqueue index (first queue is 0) to the Queue - Select field. - - Read the virtqueue size from the Queue Size field, which is - always a power of 2. This controls how big the virtqueue is - (see below). If this field is 0, the virtqueue does not exist. - - Allocate and zero virtqueue in contiguous physical memory, on a - 4096 byte alignment. Write the physical address, divided by - 4096 to the Queue Address field.[footnote: -The 4096 is based on the x86 page size, but it's also large -enough to ensure that the separate parts of the virtqueue are on -separate cache lines. -] - - Optionally, if MSI-X capability is present and enabled on the - device, select a vector to use to request interrupts triggered - by virtqueue events. Write the MSI-X Table entry number - corresponding to this vector in Queue Vector field. Read the - Queue Vector field: on success, previously written value is - returned; on failure, NO_VECTOR value is returned. - -The Queue Size field controls the total number of bytes required -for the virtqueue according to the following formula: - -#define ALIGN(x) (((x) + 4095) & ~4095) - -static inline unsigned vring_size(unsigned int qsz) - -{ - - return ALIGN(sizeof(struct vring_desc)*qsz + sizeof(u16)*(2 -+ qsz)) - - + ALIGN(sizeof(struct vring_used_elem)*qsz); - -} - -This currently wastes some space with padding, but also allows -future extensions. The virtqueue layout structure looks like this -(qsz is the Queue Size field, which is a variable, so this code -won't compile): - -struct vring { - - /* The actual descriptors (16 bytes each) */ - - struct vring_desc desc[qsz]; - - - - /* A ring of available descriptor heads with free-running -index. */ - - struct vring_avail avail; - - - - // Padding to the next 4096 boundary. - - char pad[]; - - - - // A ring of used descriptor heads with free-running index. - - struct vring_used used; - -}; - - A Note on Virtqueue Endianness - -Note that the endian of these fields and everything else in the -virtqueue is the native endian of the guest, not little-endian as -PCI normally is. This makes for simpler guest code, and it is -assumed that the host already has to be deeply aware of the guest -endian so such an “endian-aware” device is not a significant -issue. - - Descriptor Table - -The descriptor table refers to the buffers the guest is using for -the device. The addresses are physical addresses, and the buffers -can be chained via the next field. Each descriptor describes a -buffer which is read-only or write-only, but a chain of -descriptors can contain both read-only and write-only buffers. - -No descriptor chain may be more than 2^32 bytes long in total.struct vring_desc { - - /* Address (guest-physical). */ - - u64 addr; - - /* Length. */ - - u32 len; - -/* This marks a buffer as continuing via the next field. */ - -#define VRING_DESC_F_NEXT 1 - -/* This marks a buffer as write-only (otherwise read-only). */ - -#define VRING_DESC_F_WRITE 2 - -/* This means the buffer contains a list of buffer descriptors. -*/ - -#define VRING_DESC_F_INDIRECT 4 - - /* The flags as indicated above. */ - - u16 flags; - - /* Next field if flags & NEXT */ - - u16 next; - -}; - -The number of descriptors in the table is specified by the Queue -Size field for this virtqueue. - - Indirect Descriptors - -Some devices benefit by concurrently dispatching a large number -of large requests. The VIRTIO_RING_F_INDIRECT_DESC feature can be -used to allow this (see [cha:Reserved-Feature-Bits]). To increase -ring capacity it is possible to store a table of indirect -descriptors anywhere in memory, and insert a descriptor in main -virtqueue (with flags&INDIRECT on) that refers to memory buffer -containing this indirect descriptor table; fields addr and len -refer to the indirect table address and length in bytes, -respectively. The indirect table layout structure looks like this -(len is the length of the descriptor that refers to this table, -which is a variable, so this code won't compile): - -struct indirect_descriptor_table { - - /* The actual descriptors (16 bytes each) */ - - struct vring_desc desc[len / 16]; - -}; - -The first indirect descriptor is located at start of the indirect -descriptor table (index 0), additional indirect descriptors are -chained by next field. An indirect descriptor without next field -(with flags&NEXT off) signals the end of the indirect descriptor -table, and transfers control back to the main virtqueue. An -indirect descriptor can not refer to another indirect descriptor -table (flags&INDIRECT must be off). A single indirect descriptor -table can include both read-only and write-only descriptors; -write-only flag (flags&WRITE) in the descriptor that refers to it -is ignored. - - Available Ring - -The available ring refers to what descriptors we are offering the -device: it refers to the head of a descriptor chain. The “flags” -field is currently 0 or 1: 1 indicating that we do not need an -interrupt when the device consumes a descriptor from the -available ring. Alternatively, the guest can ask the device to -delay interrupts until an entry with an index specified by the “ -used_event” field is written in the used ring (equivalently, -until the idx field in the used ring will reach the value -used_event + 1). The method employed by the device is controlled -by the VIRTIO_RING_F_EVENT_IDX feature bit (see [cha:Reserved-Feature-Bits] -). This interrupt suppression is merely an optimization; it may -not suppress interrupts entirely. - -The “idx” field indicates where we would put the next descriptor -entry (modulo the ring size). This starts at 0, and increases. - -struct vring_avail { - -#define VRING_AVAIL_F_NO_INTERRUPT 1 - - u16 flags; - - u16 idx; - - u16 ring[qsz]; /* qsz is the Queue Size field read from device -*/ - - u16 used_event; - -}; - - Used Ring - -The used ring is where the device returns buffers once it is done -with them. The flags field can be used by the device to hint that -no notification is necessary when the guest adds to the available -ring. Alternatively, the “avail_event” field can be used by the -device to hint that no notification is necessary until an entry -with an index specified by the “avail_event” is written in the -available ring (equivalently, until the idx field in the -available ring will reach the value avail_event + 1). The method -employed by the device is controlled by the guest through the -VIRTIO_RING_F_EVENT_IDX feature bit (see [cha:Reserved-Feature-Bits] -). [footnote: -These fields are kept here because this is the only part of the -virtqueue written by the device -]. - -Each entry in the ring is a pair: the head entry of the -descriptor chain describing the buffer (this matches an entry -placed in the available ring by the guest earlier), and the total -of bytes written into the buffer. The latter is extremely useful -for guests using untrusted buffers: if you do not know exactly -how much has been written by the device, you usually have to zero -the buffer to ensure no data leakage occurs. - -/* u32 is used here for ids for padding reasons. */ - -struct vring_used_elem { - - /* Index of start of used descriptor chain. */ - - u32 id; - - /* Total length of the descriptor chain which was used -(written to) */ - - u32 len; - -}; - - - -struct vring_used { - -#define VRING_USED_F_NO_NOTIFY 1 - - u16 flags; - - u16 idx; - - struct vring_used_elem ring[qsz]; - - u16 avail_event; - -}; - - Helpers for Managing Virtqueues - -The Linux Kernel Source code contains the definitions above and -helper routines in a more usable form, in -include/linux/virtio_ring.h. This was explicitly licensed by IBM -and Red Hat under the (3-clause) BSD license so that it can be -freely used by all other projects, and is reproduced (with slight -variation to remove Linux assumptions) in Appendix A. - - Device Operation - -There are two parts to device operation: supplying new buffers to -the device, and processing used buffers from the device. As an -example, the virtio network device has two virtqueues: the -transmit virtqueue and the receive virtqueue. The driver adds -outgoing (read-only) packets to the transmit virtqueue, and then -frees them after they are used. Similarly, incoming (write-only) -buffers are added to the receive virtqueue, and processed after -they are used. - - Supplying Buffers to The Device - -Actual transfer of buffers from the guest OS to the device -operates as follows: - - Place the buffer(s) into free descriptor(s). - - If there are no free descriptors, the guest may choose to - notify the device even if notifications are suppressed (to - reduce latency).[footnote: -The Linux drivers do this only for read-only buffers: for -write-only buffers, it is assumed that the driver is merely -trying to keep the receive buffer ring full, and no notification -of this expected condition is necessary. -] - - Place the id of the buffer in the next ring entry of the - available ring. - - The steps (1) and (2) may be performed repeatedly if batching - is possible. - - A memory barrier should be executed to ensure the device sees - the updated descriptor table and available ring before the next - step. - - The available “idx” field should be increased by the number of - entries added to the available ring. - - A memory barrier should be executed to ensure that we update - the idx field before checking for notification suppression. - - If notifications are not suppressed, the device should be - notified of the new buffers. - -Note that the above code does not take precautions against the -available ring buffer wrapping around: this is not possible since -the ring buffer is the same size as the descriptor table, so step -(1) will prevent such a condition. - -In addition, the maximum queue size is 32768 (it must be a power -of 2 which fits in 16 bits), so the 16-bit “idx” value can always -distinguish between a full and empty buffer. - -Here is a description of each stage in more detail. - - Placing Buffers Into The Descriptor Table - -A buffer consists of zero or more read-only physically-contiguous -elements followed by zero or more physically-contiguous -write-only elements (it must have at least one element). This -algorithm maps it into the descriptor table: - - for each buffer element, b: - - Get the next free descriptor table entry, d - - Set d.addr to the physical address of the start of b - - Set d.len to the length of b. - - If b is write-only, set d.flags to VRING_DESC_F_WRITE, - otherwise 0. - - If there is a buffer element after this: - - Set d.next to the index of the next free descriptor element. - - Set the VRING_DESC_F_NEXT bit in d.flags. - -In practice, the d.next fields are usually used to chain free -descriptors, and a separate count kept to check there are enough -free descriptors before beginning the mappings. - - Updating The Available Ring - -The head of the buffer we mapped is the first d in the algorithm -above. A naive implementation would do the following: - -avail->ring[avail->idx % qsz] = head; - -However, in general we can add many descriptors before we update -the “idx” field (at which point they become visible to the -device), so we keep a counter of how many we've added: - -avail->ring[(avail->idx + added++) % qsz] = head; - - Updating The Index Field - -Once the idx field of the virtqueue is updated, the device will -be able to access the descriptor entries we've created and the -memory they refer to. This is why a memory barrier is generally -used before the idx update, to ensure it sees the most up-to-date -copy. - -The idx field always increments, and we let it wrap naturally at -65536: - -avail->idx += added; - - Notifying The Device - -Device notification occurs by writing the 16-bit virtqueue index -of this virtqueue to the Queue Notify field of the virtio header -in the first I/O region of the PCI device. This can be expensive, -however, so the device can suppress such notifications if it -doesn't need them. We have to be careful to expose the new idx -value before checking the suppression flag: it's OK to notify -gratuitously, but not to omit a required notification. So again, -we use a memory barrier here before reading the flags or the -avail_event field. - -If the VIRTIO_F_RING_EVENT_IDX feature is not negotiated, and if -the VRING_USED_F_NOTIFY flag is not set, we go ahead and write to -the PCI configuration space. - -If the VIRTIO_F_RING_EVENT_IDX feature is negotiated, we read the -avail_event field in the available ring structure. If the -available index crossed_the avail_event field value since the -last notification, we go ahead and write to the PCI configuration -space. The avail_event field wraps naturally at 65536 as well: - -(u16)(new_idx - avail_event - 1) < (u16)(new_idx - old_idx) - - Receiving Used Buffers From The - Device - -Once the device has used a buffer (read from or written to it, or -parts of both, depending on the nature of the virtqueue and the -device), it sends an interrupt, following an algorithm very -similar to the algorithm used for the driver to send the device a -buffer: - - Write the head descriptor number to the next field in the used - ring. - - Update the used ring idx. - - Determine whether an interrupt is necessary: - - If the VIRTIO_F_RING_EVENT_IDX feature is not negotiated: check - if f the VRING_AVAIL_F_NO_INTERRUPT flag is not set in avail- - >flags - - If the VIRTIO_F_RING_EVENT_IDX feature is negotiated: check - whether the used index crossed the used_event field value - since the last update. The used_event field wraps naturally - at 65536 as well:(u16)(new_idx - used_event - 1) < (u16)(new_idx - old_idx) - - If an interrupt is necessary: - - If MSI-X capability is disabled: - - Set the lower bit of the ISR Status field for the device. - - Send the appropriate PCI interrupt for the device. - - If MSI-X capability is enabled: - - Request the appropriate MSI-X interrupt message for the - device, Queue Vector field sets the MSI-X Table entry - number. - - If Queue Vector field value is NO_VECTOR, no interrupt - message is requested for this event. - -The guest interrupt handler should: - - If MSI-X capability is disabled: read the ISR Status field, - which will reset it to zero. If the lower bit is zero, the - interrupt was not for this device. Otherwise, the guest driver - should look through the used rings of each virtqueue for the - device, to see if any progress has been made by the device - which requires servicing. - - If MSI-X capability is enabled: look through the used rings of - each virtqueue mapped to the specific MSI-X vector for the - device, to see if any progress has been made by the device - which requires servicing. - -For each ring, guest should then disable interrupts by writing -VRING_AVAIL_F_NO_INTERRUPT flag in avail structure, if required. -It can then process used ring entries finally enabling interrupts -by clearing the VRING_AVAIL_F_NO_INTERRUPT flag or updating the -EVENT_IDX field in the available structure, Guest should then -execute a memory barrier, and then recheck the ring empty -condition. This is necessary to handle the case where, after the -last check and before enabling interrupts, an interrupt has been -suppressed by the device: - -vring_disable_interrupts(vq); - -for (;;) { - - if (vq->last_seen_used != vring->used.idx) { - - vring_enable_interrupts(vq); - - mb(); - - if (vq->last_seen_used != vring->used.idx) - - break; - - } - - struct vring_used_elem *e = -vring.used->ring[vq->last_seen_used%vsz]; - - process_buffer(e); - - vq->last_seen_used++; - -} - - Dealing With Configuration Changes - -Some virtio PCI devices can change the device configuration -state, as reflected in the virtio header in the PCI configuration -space. In this case: - - If MSI-X capability is disabled: an interrupt is delivered and - the second highest bit is set in the ISR Status field to - indicate that the driver should re-examine the configuration - space.Note that a single interrupt can indicate both that one - or more virtqueue has been used and that the configuration - space has changed: even if the config bit is set, virtqueues - must be scanned. - - If MSI-X capability is enabled: an interrupt message is - requested. The Configuration Vector field sets the MSI-X Table - entry number to use. If Configuration Vector field value is - NO_VECTOR, no interrupt message is requested for this event. - -Creating New Device Types - -Various considerations are necessary when creating a new device -type: - - How Many Virtqueues? - -It is possible that a very simple device will operate entirely -through its configuration space, but most will need at least one -virtqueue in which it will place requests. A device with both -input and output (eg. console and network devices described here) -need two queues: one which the driver fills with buffers to -receive input, and one which the driver places buffers to -transmit output. - - What Configuration Space Layout? - -Configuration space is generally used for rarely-changing or -initialization-time parameters. But it is a limited resource, so -it might be better to use a virtqueue to update configuration -information (the network device does this for filtering, -otherwise the table in the config space could potentially be very -large). - -Note that this space is generally the guest's native endian, -rather than PCI's little-endian. - - What Device Number? - -Currently device numbers are assigned quite freely: a simple -request mail to the author of this document or the Linux -virtualization mailing list[footnote: - -https://lists.linux-foundation.org/mailman/listinfo/virtualization -] will be sufficient to secure a unique one. - -Meanwhile for experimental drivers, use 65535 and work backwards. - - How many MSI-X vectors? - -Using the optional MSI-X capability devices can speed up -interrupt processing by removing the need to read ISR Status -register by guest driver (which might be an expensive operation), -reducing interrupt sharing between devices and queues within the -device, and handling interrupts from multiple CPUs. However, some -systems impose a limit (which might be as low as 256) on the -total number of MSI-X vectors that can be allocated to all -devices. Devices and/or device drivers should take this into -account, limiting the number of vectors used unless the device is -expected to cause a high volume of interrupts. Devices can -control the number of vectors used by limiting the MSI-X Table -Size or not presenting MSI-X capability in PCI configuration -space. Drivers can control this by mapping events to as small -number of vectors as possible, or disabling MSI-X capability -altogether. - - Message Framing - -The descriptors used for a buffer should not effect the semantics -of the message, except for the total length of the buffer. For -example, a network buffer consists of a 10 byte header followed -by the network packet. Whether this is presented in the ring -descriptor chain as (say) a 10 byte buffer and a 1514 byte -buffer, or a single 1524 byte buffer, or even three buffers, -should have no effect. - -In particular, no implementation should use the descriptor -boundaries to determine the size of any header in a request.[footnote: -The current qemu device implementations mistakenly insist that -the first descriptor cover the header in these cases exactly, so -a cautious driver should arrange it so. -] - - Device Improvements - -Any change to configuration space, or new virtqueues, or -behavioural changes, should be indicated by negotiation of a new -feature bit. This establishes clarity[footnote: -Even if it does mean documenting design or implementation -mistakes! -] and avoids future expansion problems. - -Clusters of functionality which are always implemented together -can use a single bit, but if one feature makes sense without the -others they should not be gratuitously grouped together to -conserve feature bits. We can always extend the spec when the -first person needs more than 24 feature bits for their device. - -[LaTeX Command: printnomenclature] - -Appendix A: virtio_ring.h - -#ifndef VIRTIO_RING_H - -#define VIRTIO_RING_H - -/* An interface for efficient virtio implementation. - - * - - * This header is BSD licensed so anyone can use the definitions - - * to implement compatible drivers/servers. - - * - - * Copyright 2007, 2009, IBM Corporation - - * Copyright 2011, Red Hat, Inc - - * All rights reserved. - - * - - * Redistribution and use in source and binary forms, with or -without - - * modification, are permitted provided that the following -conditions - - * are met: - - * 1. Redistributions of source code must retain the above -copyright - - * notice, this list of conditions and the following -disclaimer. - - * 2. Redistributions in binary form must reproduce the above -copyright - - * notice, this list of conditions and the following -disclaimer in the - - * documentation and/or other materials provided with the -distribution. - - * 3. Neither the name of IBM nor the names of its contributors - - * may be used to endorse or promote products derived from -this software - - * without specific prior written permission. - - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND -CONTRIBUTORS ``AS IS'' AND - - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE - - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE - - * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE -LIABLE - - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL - - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS - - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) - - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT - - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING -IN ANY WAY - - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF - - * SUCH DAMAGE. - - */ - - - -/* This marks a buffer as continuing via the next field. */ - -#define VRING_DESC_F_NEXT 1 - -/* This marks a buffer as write-only (otherwise read-only). */ - -#define VRING_DESC_F_WRITE 2 - - - -/* The Host uses this in used->flags to advise the Guest: don't -kick me - - * when you add a buffer. It's unreliable, so it's simply an - - * optimization. Guest will still kick if it's out of buffers. -*/ - -#define VRING_USED_F_NO_NOTIFY 1 - -/* The Guest uses this in avail->flags to advise the Host: don't - - * interrupt me when you consume a buffer. It's unreliable, so -it's - - * simply an optimization. */ - -#define VRING_AVAIL_F_NO_INTERRUPT 1 - - - -/* Virtio ring descriptors: 16 bytes. - - * These can chain together via "next". */ - -struct vring_desc { - - /* Address (guest-physical). */ - - uint64_t addr; - - /* Length. */ - - uint32_t len; - - /* The flags as indicated above. */ - - uint16_t flags; - - /* We chain unused descriptors via this, too */ - - uint16_t next; - -}; - - - -struct vring_avail { - - uint16_t flags; - - uint16_t idx; - - uint16_t ring[]; - - uint16_t used_event; - -}; - - - -/* u32 is used here for ids for padding reasons. */ - -struct vring_used_elem { - - /* Index of start of used descriptor chain. */ - - uint32_t id; - - /* Total length of the descriptor chain which was written -to. */ - - uint32_t len; - -}; - - - -struct vring_used { - - uint16_t flags; - - uint16_t idx; - - struct vring_used_elem ring[]; - - uint16_t avail_event; - -}; - - - -struct vring { - - unsigned int num; - - - - struct vring_desc *desc; - - struct vring_avail *avail; - - struct vring_used *used; - -}; - - - -/* The standard layout for the ring is a continuous chunk of -memory which - - * looks like this. We assume num is a power of 2. - - * - - * struct vring { - - * // The actual descriptors (16 bytes each) - - * struct vring_desc desc[num]; - - * - - * // A ring of available descriptor heads with free-running -index. - - * __u16 avail_flags; - - * __u16 avail_idx; - - * __u16 available[num]; - - * - - * // Padding to the next align boundary. - - * char pad[]; - - * - - * // A ring of used descriptor heads with free-running -index. - - * __u16 used_flags; - - * __u16 EVENT_IDX; - - * struct vring_used_elem used[num]; - - * }; - - * Note: for virtio PCI, align is 4096. - - */ - -static inline void vring_init(struct vring *vr, unsigned int num, -void *p, - - unsigned long align) - -{ - - vr->num = num; - - vr->desc = p; - - vr->avail = p + num*sizeof(struct vring_desc); - - vr->used = (void *)(((unsigned long)&vr->avail->ring[num] - - + align-1) - - & ~(align - 1)); - -} - - - -static inline unsigned vring_size(unsigned int num, unsigned long -align) - -{ - - return ((sizeof(struct vring_desc)*num + -sizeof(uint16_t)*(2+num) - - + align - 1) & ~(align - 1)) - - + sizeof(uint16_t)*3 + sizeof(struct -vring_used_elem)*num; - -} - - - -static inline int vring_need_event(uint16_t event_idx, uint16_t -new_idx, uint16_t old_idx) - -{ - - return (uint16_t)(new_idx - event_idx - 1) < -(uint16_t)(new_idx - old_idx); - -} - -#endif /* VIRTIO_RING_H */ - -Appendix B: Reserved Feature Bits - -Currently there are five device-independent feature bits defined: - - VIRTIO_F_NOTIFY_ON_EMPTY (24) Negotiating this feature - indicates that the driver wants an interrupt if the device runs - out of available descriptors on a virtqueue, even though - interrupts are suppressed using the VRING_AVAIL_F_NO_INTERRUPT - flag or the used_event field. An example of this is the - networking driver: it doesn't need to know every time a packet - is transmitted, but it does need to free the transmitted - packets a finite time after they are transmitted. It can avoid - using a timer if the device interrupts it when all the packets - are transmitted. - - VIRTIO_F_RING_INDIRECT_DESC (28) Negotiating this feature - indicates that the driver can use descriptors with the - VRING_DESC_F_INDIRECT flag set, as described in [sub:Indirect-Descriptors] - . - - VIRTIO_F_RING_EVENT_IDX(29) This feature enables the used_event - and the avail_event fields. If set, it indicates that the - device should ignore the flags field in the available ring - structure. Instead, the used_event field in this structure is - used by guest to suppress device interrupts. Further, the - driver should ignore the flags field in the used ring - structure. Instead, the avail_event field in this structure is - used by the device to suppress notifications. If unset, the - driver should ignore the used_event field; the device should - ignore the avail_event field; the flags field is used - -Appendix C: Network Device - -The virtio network device is a virtual ethernet card, and is the -most complex of the devices supported so far by virtio. It has -enhanced rapidly and demonstrates clearly how support for new -features should be added to an existing device. Empty buffers are -placed in one virtqueue for receiving packets, and outgoing -packets are enqueued into another for transmission in that order. -A third command queue is used to control advanced filtering -features. - - Configuration - - Subsystem Device ID 1 - - Virtqueues 0:receiveq. 1:transmitq. 2:controlq[footnote: -Only if VIRTIO_NET_F_CTRL_VQ set -] - - Feature bits - - VIRTIO_NET_F_CSUM (0) Device handles packets with partial - checksum - - VIRTIO_NET_F_GUEST_CSUM (1) Guest handles packets with partial - checksum - - VIRTIO_NET_F_MAC (5) Device has given MAC address. - - VIRTIO_NET_F_GSO (6) (Deprecated) device handles packets with - any GSO type.[footnote: -It was supposed to indicate segmentation offload support, but -upon further investigation it became clear that multiple bits -were required. -] - - VIRTIO_NET_F_GUEST_TSO4 (7) Guest can receive TSOv4. - - VIRTIO_NET_F_GUEST_TSO6 (8) Guest can receive TSOv6. - - VIRTIO_NET_F_GUEST_ECN (9) Guest can receive TSO with ECN. - - VIRTIO_NET_F_GUEST_UFO (10) Guest can receive UFO. - - VIRTIO_NET_F_HOST_TSO4 (11) Device can receive TSOv4. - - VIRTIO_NET_F_HOST_TSO6 (12) Device can receive TSOv6. - - VIRTIO_NET_F_HOST_ECN (13) Device can receive TSO with ECN. - - VIRTIO_NET_F_HOST_UFO (14) Device can receive UFO. - - VIRTIO_NET_F_MRG_RXBUF (15) Guest can merge receive buffers. - - VIRTIO_NET_F_STATUS (16) Configuration status field is - available. - - VIRTIO_NET_F_CTRL_VQ (17) Control channel is available. - - VIRTIO_NET_F_CTRL_RX (18) Control channel RX mode support. - - VIRTIO_NET_F_CTRL_VLAN (19) Control channel VLAN filtering. - - VIRTIO_NET_F_GUEST_ANNOUNCE(21) Guest can send gratuitous - packets. - - Device configuration layout Two configuration fields are - currently defined. The mac address field always exists (though - is only valid if VIRTIO_NET_F_MAC is set), and the status field - only exists if VIRTIO_NET_F_STATUS is set. Two read-only bits - are currently defined for the status field: - VIRTIO_NET_S_LINK_UP and VIRTIO_NET_S_ANNOUNCE. #define VIRTIO_NET_S_LINK_UP 1 - -#define VIRTIO_NET_S_ANNOUNCE 2 - - - -struct virtio_net_config { - - u8 mac[6]; - - u16 status; - -}; - - Device Initialization - - The initialization routine should identify the receive and - transmission virtqueues. - - If the VIRTIO_NET_F_MAC feature bit is set, the configuration - space “mac” entry indicates the “physical” address of the the - network card, otherwise a private MAC address should be - assigned. All guests are expected to negotiate this feature if - it is set. - - If the VIRTIO_NET_F_CTRL_VQ feature bit is negotiated, identify - the control virtqueue. - - If the VIRTIO_NET_F_STATUS feature bit is negotiated, the link - status can be read from the bottom bit of the “status” config - field. Otherwise, the link should be assumed active. - - The receive virtqueue should be filled with receive buffers. - This is described in detail below in “Setting Up Receive - Buffers”. - - A driver can indicate that it will generate checksumless - packets by negotating the VIRTIO_NET_F_CSUM feature. This “ - checksum offload” is a common feature on modern network cards. - - If that feature is negotiated[footnote: -ie. VIRTIO_NET_F_HOST_TSO* and VIRTIO_NET_F_HOST_UFO are -dependent on VIRTIO_NET_F_CSUM; a dvice which offers the offload -features must offer the checksum feature, and a driver which -accepts the offload features must accept the checksum feature. -Similar logic applies to the VIRTIO_NET_F_GUEST_TSO4 features -depending on VIRTIO_NET_F_GUEST_CSUM. -], a driver can use TCP or UDP segmentation offload by - negotiating the VIRTIO_NET_F_HOST_TSO4 (IPv4 TCP), - VIRTIO_NET_F_HOST_TSO6 (IPv6 TCP) and VIRTIO_NET_F_HOST_UFO - (UDP fragmentation) features. It should not send TCP packets - requiring segmentation offload which have the Explicit - Congestion Notification bit set, unless the - VIRTIO_NET_F_HOST_ECN feature is negotiated.[footnote: -This is a common restriction in real, older network cards. -] - - The converse features are also available: a driver can save the - virtual device some work by negotiating these features.[footnote: -For example, a network packet transported between two guests on -the same system may not require checksumming at all, nor -segmentation, if both guests are amenable. -] The VIRTIO_NET_F_GUEST_CSUM feature indicates that partially - checksummed packets can be received, and if it can do that then - the VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, - VIRTIO_NET_F_GUEST_UFO and VIRTIO_NET_F_GUEST_ECN are the input - equivalents of the features described above. See “Receiving - Packets” below. - - Device Operation - -Packets are transmitted by placing them in the transmitq, and -buffers for incoming packets are placed in the receiveq. In each -case, the packet itself is preceeded by a header: - -struct virtio_net_hdr { - -#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 - - u8 flags; - -#define VIRTIO_NET_HDR_GSO_NONE 0 - -#define VIRTIO_NET_HDR_GSO_TCPV4 1 - -#define VIRTIO_NET_HDR_GSO_UDP 3 - -#define VIRTIO_NET_HDR_GSO_TCPV6 4 - -#define VIRTIO_NET_HDR_GSO_ECN 0x80 - - u8 gso_type; - - u16 hdr_len; - - u16 gso_size; - - u16 csum_start; - - u16 csum_offset; - -/* Only if VIRTIO_NET_F_MRG_RXBUF: */ - - u16 num_buffers - -}; - -The controlq is used to control device features such as -filtering. - - Packet Transmission - -Transmitting a single packet is simple, but varies depending on -the different features the driver negotiated. - - If the driver negotiated VIRTIO_NET_F_CSUM, and the packet has - not been fully checksummed, then the virtio_net_hdr's fields - are set as follows. Otherwise, the packet must be fully - checksummed, and flags is zero. - - flags has the VIRTIO_NET_HDR_F_NEEDS_CSUM set, - - csum_start is set to the offset within - the packet to begin checksumming, and - - csum_offset indicates how many bytes after the csum_start the - new (16 bit ones' complement) checksum should be placed.[footnote: -For example, consider a partially checksummed TCP (IPv4) packet. -It will have a 14 byte ethernet header and 20 byte IP header -followed by the TCP header (with the TCP checksum field 16 bytes -into that header). csum_start will be 14+20 = 34 (the TCP -checksum includes the header), and csum_offset will be 16. The -value in the TCP checksum field should be initialized to the sum -of the TCP pseudo header, so that replacing it by the ones' -complement checksum of the TCP header and body will give the -correct result. -] - - If the driver negotiated - VIRTIO_NET_F_HOST_TSO4, TSO6 or UFO, and the packet requires - TCP segmentation or UDP fragmentation, then the “gso_type” - field is set to VIRTIO_NET_HDR_GSO_TCPV4, TCPV6 or UDP. - (Otherwise, it is set to VIRTIO_NET_HDR_GSO_NONE). In this - case, packets larger than 1514 bytes can be transmitted: the - metadata indicates how to replicate the packet header to cut it - into smaller packets. The other gso fields are set: - - hdr_len is a hint to the device as to how much of the header - needs to be kept to copy into each packet, usually set to the - length of the headers, including the transport header.[footnote: -Due to various bugs in implementations, this field is not useful -as a guarantee of the transport header size. -] - - gso_size is the maximum size of each packet beyond that header - (ie. MSS). - - If the driver negotiated the VIRTIO_NET_F_HOST_ECN feature, the - VIRTIO_NET_HDR_GSO_ECN bit may be set in “gso_type” as well, - indicating that the TCP packet has the ECN bit set.[footnote: -This case is not handled by some older hardware, so is called out -specifically in the protocol. -] - - If the driver negotiated the VIRTIO_NET_F_MRG_RXBUF feature, - the num_buffers field is set to zero. - - The header and packet are added as one output buffer to the - transmitq, and the device is notified of the new entry (see [sub:Notifying-The-Device] - ).[footnote: -Note that the header will be two bytes longer for the -VIRTIO_NET_F_MRG_RXBUF case. -] - - Packet Transmission Interrupt - -Often a driver will suppress transmission interrupts using the -VRING_AVAIL_F_NO_INTERRUPT flag (see [sub:Receiving-Used-Buffers] -) and check for used packets in the transmit path of following -packets. However, it will still receive interrupts if the -VIRTIO_F_NOTIFY_ON_EMPTY feature is negotiated, indicating that -the transmission queue is completely emptied. - -The normal behavior in this interrupt handler is to retrieve and -new descriptors from the used ring and free the corresponding -headers and packets. - - Setting Up Receive Buffers - -It is generally a good idea to keep the receive virtqueue as -fully populated as possible: if it runs out, network performance -will suffer. - -If the VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6 or -VIRTIO_NET_F_GUEST_UFO features are used, the Guest will need to -accept packets of up to 65550 bytes long (the maximum size of a -TCP or UDP packet, plus the 14 byte ethernet header), otherwise -1514 bytes. So unless VIRTIO_NET_F_MRG_RXBUF is negotiated, every -buffer in the receive queue needs to be at least this length [footnote: -Obviously each one can be split across multiple descriptor -elements. -]. - -If VIRTIO_NET_F_MRG_RXBUF is negotiated, each buffer must be at -least the size of the struct virtio_net_hdr. - - Packet Receive Interrupt - -When a packet is copied into a buffer in the receiveq, the -optimal path is to disable further interrupts for the receiveq -(see [sub:Receiving-Used-Buffers]) and process packets until no -more are found, then re-enable them. - -Processing packet involves: - - If the driver negotiated the VIRTIO_NET_F_MRG_RXBUF feature, - then the “num_buffers” field indicates how many descriptors - this packet is spread over (including this one). This allows - receipt of large packets without having to allocate large - buffers. In this case, there will be at least “num_buffers” in - the used ring, and they should be chained together to form a - single packet. The other buffers will not begin with a struct - virtio_net_hdr. - - If the VIRTIO_NET_F_MRG_RXBUF feature was not negotiated, or - the “num_buffers” field is one, then the entire packet will be - contained within this buffer, immediately following the struct - virtio_net_hdr. - - If the VIRTIO_NET_F_GUEST_CSUM feature was negotiated, the - VIRTIO_NET_HDR_F_NEEDS_CSUM bit in the “flags” field may be - set: if so, the checksum on the packet is incomplete and the “ - csum_start” and “csum_offset” fields indicate how to calculate - it (see [ite:csum_start-is-set]). - - If the VIRTIO_NET_F_GUEST_TSO4, TSO6 or UFO options were - negotiated, then the “gso_type” may be something other than - VIRTIO_NET_HDR_GSO_NONE, and the “gso_size” field indicates the - desired MSS (see [enu:If-the-driver]). - - Control Virtqueue - -The driver uses the control virtqueue (if VIRTIO_NET_F_VTRL_VQ is -negotiated) to send commands to manipulate various features of -the device which would not easily map into the configuration -space. - -All commands are of the following form: - -struct virtio_net_ctrl { - - u8 class; - - u8 command; - - u8 command-specific-data[]; - - u8 ack; - -}; - - - -/* ack values */ - -#define VIRTIO_NET_OK 0 - -#define VIRTIO_NET_ERR 1 - -The class, command and command-specific-data are set by the -driver, and the device sets the ack byte. There is little it can -do except issue a diagnostic if the ack byte is not -VIRTIO_NET_OK. - - Packet Receive Filtering - -If the VIRTIO_NET_F_CTRL_RX feature is negotiated, the driver can -send control commands for promiscuous mode, multicast receiving, -and filtering of MAC addresses. - -Note that in general, these commands are best-effort: unwanted -packets may still arrive. - - Setting Promiscuous Mode - -#define VIRTIO_NET_CTRL_RX 0 - - #define VIRTIO_NET_CTRL_RX_PROMISC 0 - - #define VIRTIO_NET_CTRL_RX_ALLMULTI 1 - -The class VIRTIO_NET_CTRL_RX has two commands: -VIRTIO_NET_CTRL_RX_PROMISC turns promiscuous mode on and off, and -VIRTIO_NET_CTRL_RX_ALLMULTI turns all-multicast receive on and -off. The command-specific-data is one byte containing 0 (off) or -1 (on). - - Setting MAC Address Filtering - -struct virtio_net_ctrl_mac { - - u32 entries; - - u8 macs[entries][ETH_ALEN]; - -}; - - - -#define VIRTIO_NET_CTRL_MAC 1 - - #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0 - -The device can filter incoming packets by any number of -destination MAC addresses.[footnote: -Since there are no guarentees, it can use a hash filter -orsilently switch to allmulti or promiscuous mode if it is given -too many addresses. -] This table is set using the class VIRTIO_NET_CTRL_MAC and the -command VIRTIO_NET_CTRL_MAC_TABLE_SET. The command-specific-data -is two variable length tables of 6-byte MAC addresses. The first -table contains unicast addresses, and the second contains -multicast addresses. - - VLAN Filtering - -If the driver negotiates the VIRTION_NET_F_CTRL_VLAN feature, it -can control a VLAN filter table in the device. - -#define VIRTIO_NET_CTRL_VLAN 2 - - #define VIRTIO_NET_CTRL_VLAN_ADD 0 - - #define VIRTIO_NET_CTRL_VLAN_DEL 1 - -Both the VIRTIO_NET_CTRL_VLAN_ADD and VIRTIO_NET_CTRL_VLAN_DEL -command take a 16-bit VLAN id as the command-specific-data. - - Gratuitous Packet Sending - -If the driver negotiates the VIRTIO_NET_F_GUEST_ANNOUNCE (depends -on VIRTIO_NET_F_CTRL_VQ), it can ask the guest to send gratuitous -packets; this is usually done after the guest has been physically -migrated, and needs to announce its presence on the new network -links. (As hypervisor does not have the knowledge of guest -network configuration (eg. tagged vlan) it is simplest to prod -the guest in this way). - -#define VIRTIO_NET_CTRL_ANNOUNCE 3 - - #define VIRTIO_NET_CTRL_ANNOUNCE_ACK 0 - -The Guest needs to check VIRTIO_NET_S_ANNOUNCE bit in status -field when it notices the changes of device configuration. The -command VIRTIO_NET_CTRL_ANNOUNCE_ACK is used to indicate that -driver has recevied the notification and device would clear the -VIRTIO_NET_S_ANNOUNCE bit in the status filed after it received -this command. - -Processing this notification involves: - - Sending the gratuitous packets or marking there are pending - gratuitous packets to be sent and letting deferred routine to - send them. - - Sending VIRTIO_NET_CTRL_ANNOUNCE_ACK command through control - vq. - - . - -Appendix D: Block Device - -The virtio block device is a simple virtual block device (ie. -disk). Read and write requests (and other exotic requests) are -placed in the queue, and serviced (probably out of order) by the -device except where noted. - - Configuration - - Subsystem Device ID 2 - - Virtqueues 0:requestq. - - Feature bits - - VIRTIO_BLK_F_BARRIER (0) Host supports request barriers. - - VIRTIO_BLK_F_SIZE_MAX (1) Maximum size of any single segment is - in “size_max”. - - VIRTIO_BLK_F_SEG_MAX (2) Maximum number of segments in a - request is in “seg_max”. - - VIRTIO_BLK_F_GEOMETRY (4) Disk-style geometry specified in “ - geometry”. - - VIRTIO_BLK_F_RO (5) Device is read-only. - - VIRTIO_BLK_F_BLK_SIZE (6) Block size of disk is in “blk_size”. - - VIRTIO_BLK_F_SCSI (7) Device supports scsi packet commands. - - VIRTIO_BLK_F_FLUSH (9) Cache flush command support. - - Device configuration layout The capacity of the device - (expressed in 512-byte sectors) is always present. The - availability of the others all depend on various feature bits - as indicated above. struct virtio_blk_config { - - u64 capacity; - - u32 size_max; - - u32 seg_max; - - struct virtio_blk_geometry { - - u16 cylinders; - - u8 heads; - - u8 sectors; - - } geometry; - - u32 blk_size; - - - -}; - - Device Initialization - - The device size should be read from the “capacity” - configuration field. No requests should be submitted which goes - beyond this limit. - - If the VIRTIO_BLK_F_BLK_SIZE feature is negotiated, the - blk_size field can be read to determine the optimal sector size - for the driver to use. This does not effect the units used in - the protocol (always 512 bytes), but awareness of the correct - value can effect performance. - - If the VIRTIO_BLK_F_RO feature is set by the device, any write - requests will fail. - - Device Operation - -The driver queues requests to the virtqueue, and they are used by -the device (not necessarily in order). Each request is of form: - -struct virtio_blk_req { - - - - u32 type; - - u32 ioprio; - - u64 sector; - - char data[][512]; - - u8 status; - -}; - -If the device has VIRTIO_BLK_F_SCSI feature, it can also support -scsi packet command requests, each of these requests is of form:struct virtio_scsi_pc_req { - - u32 type; - - u32 ioprio; - - u64 sector; - - char cmd[]; - - char data[][512]; - -#define SCSI_SENSE_BUFFERSIZE 96 - - u8 sense[SCSI_SENSE_BUFFERSIZE]; - - u32 errors; - - u32 data_len; - - u32 sense_len; - - u32 residual; - - u8 status; - -}; - -The type of the request is either a read (VIRTIO_BLK_T_IN), a -write (VIRTIO_BLK_T_OUT), a scsi packet command -(VIRTIO_BLK_T_SCSI_CMD or VIRTIO_BLK_T_SCSI_CMD_OUT[footnote: -the SCSI_CMD and SCSI_CMD_OUT types are equivalent, the device -does not distinguish between them -]) or a flush (VIRTIO_BLK_T_FLUSH or VIRTIO_BLK_T_FLUSH_OUT[footnote: -the FLUSH and FLUSH_OUT types are equivalent, the device does not -distinguish between them -]). If the device has VIRTIO_BLK_F_BARRIER feature the high bit -(VIRTIO_BLK_T_BARRIER) indicates that this request acts as a -barrier and that all preceeding requests must be complete before -this one, and all following requests must not be started until -this is complete. Note that a barrier does not flush caches in -the underlying backend device in host, and thus does not serve as -data consistency guarantee. Driver must use FLUSH request to -flush the host cache. - -#define VIRTIO_BLK_T_IN 0 - -#define VIRTIO_BLK_T_OUT 1 - -#define VIRTIO_BLK_T_SCSI_CMD 2 - -#define VIRTIO_BLK_T_SCSI_CMD_OUT 3 - -#define VIRTIO_BLK_T_FLUSH 4 - -#define VIRTIO_BLK_T_FLUSH_OUT 5 - -#define VIRTIO_BLK_T_BARRIER 0x80000000 - -The ioprio field is a hint about the relative priorities of -requests to the device: higher numbers indicate more important -requests. - -The sector number indicates the offset (multiplied by 512) where -the read or write is to occur. This field is unused and set to 0 -for scsi packet commands and for flush commands. - -The cmd field is only present for scsi packet command requests, -and indicates the command to perform. This field must reside in a -single, separate read-only buffer; command length can be derived -from the length of this buffer. - -Note that these first three (four for scsi packet commands) -fields are always read-only: the data field is either read-only -or write-only, depending on the request. The size of the read or -write can be derived from the total size of the request buffers. - -The sense field is only present for scsi packet command requests, -and indicates the buffer for scsi sense data. - -The data_len field is only present for scsi packet command -requests, this field is deprecated, and should be ignored by the -driver. Historically, devices copied data length there. - -The sense_len field is only present for scsi packet command -requests and indicates the number of bytes actually written to -the sense buffer. - -The residual field is only present for scsi packet command -requests and indicates the residual size, calculated as data -length - number of bytes actually transferred. - -The final status byte is written by the device: either -VIRTIO_BLK_S_OK for success, VIRTIO_BLK_S_IOERR for host or guest -error or VIRTIO_BLK_S_UNSUPP for a request unsupported by host:#define VIRTIO_BLK_S_OK 0 - -#define VIRTIO_BLK_S_IOERR 1 - -#define VIRTIO_BLK_S_UNSUPP 2 - -Historically, devices assumed that the fields type, ioprio and -sector reside in a single, separate read-only buffer; the fields -errors, data_len, sense_len and residual reside in a single, -separate write-only buffer; the sense field in a separate -write-only buffer of size 96 bytes, by itself; the fields errors, -data_len, sense_len and residual in a single write-only buffer; -and the status field is a separate read-only buffer of size 1 -byte, by itself. - -Appendix E: Console Device - -The virtio console device is a simple device for data input and -output. A device may have one or more ports. Each port has a pair -of input and output virtqueues. Moreover, a device has a pair of -control IO virtqueues. The control virtqueues are used to -communicate information between the device and the driver about -ports being opened and closed on either side of the connection, -indication from the host about whether a particular port is a -console port, adding new ports, port hot-plug/unplug, etc., and -indication from the guest about whether a port or a device was -successfully added, port open/close, etc.. For data IO, one or -more empty buffers are placed in the receive queue for incoming -data and outgoing characters are placed in the transmit queue. - - Configuration - - Subsystem Device ID 3 - - Virtqueues 0:receiveq(port0). 1:transmitq(port0), 2:control - receiveq[footnote: -Ports 2 onwards only if VIRTIO_CONSOLE_F_MULTIPORT is set -], 3:control transmitq, 4:receiveq(port1), 5:transmitq(port1), - ... - - Feature bits - - VIRTIO_CONSOLE_F_SIZE (0) Configuration cols and rows fields - are valid. - - VIRTIO_CONSOLE_F_MULTIPORT(1) Device has support for multiple - ports; configuration fields nr_ports and max_nr_ports are - valid and control virtqueues will be used. - - Device configuration layout The size of the console is supplied - in the configuration space if the VIRTIO_CONSOLE_F_SIZE feature - is set. Furthermore, if the VIRTIO_CONSOLE_F_MULTIPORT feature - is set, the maximum number of ports supported by the device can - be fetched.struct virtio_console_config { - - u16 cols; - - u16 rows; - - - - u32 max_nr_ports; - -}; - - Device Initialization - - If the VIRTIO_CONSOLE_F_SIZE feature is negotiated, the driver - can read the console dimensions from the configuration fields. - - If the VIRTIO_CONSOLE_F_MULTIPORT feature is negotiated, the - driver can spawn multiple ports, not all of which may be - attached to a console. Some could be generic ports. In this - case, the control virtqueues are enabled and according to the - max_nr_ports configuration-space value, the appropriate number - of virtqueues are created. A control message indicating the - driver is ready is sent to the host. The host can then send - control messages for adding new ports to the device. After - creating and initializing each port, a - VIRTIO_CONSOLE_PORT_READY control message is sent to the host - for that port so the host can let us know of any additional - configuration options set for that port. - - The receiveq for each port is populated with one or more - receive buffers. - - Device Operation - - For output, a buffer containing the characters is placed in the - port's transmitq.[footnote: -Because this is high importance and low bandwidth, the current -Linux implementation polls for the buffer to be used, rather than -waiting for an interrupt, simplifying the implementation -significantly. However, for generic serial ports with the -O_NONBLOCK flag set, the polling limitation is relaxed and the -consumed buffers are freed upon the next write or poll call or -when a port is closed or hot-unplugged. -] - - When a buffer is used in the receiveq (signalled by an - interrupt), the contents is the input to the port associated - with the virtqueue for which the notification was received. - - If the driver negotiated the VIRTIO_CONSOLE_F_SIZE feature, a - configuration change interrupt may occur. The updated size can - be read from the configuration fields. - - If the driver negotiated the VIRTIO_CONSOLE_F_MULTIPORT - feature, active ports are announced by the host using the - VIRTIO_CONSOLE_PORT_ADD control message. The same message is - used for port hot-plug as well. - - If the host specified a port `name', a sysfs attribute is - created with the name filled in, so that udev rules can be - written that can create a symlink from the port's name to the - char device for port discovery by applications in the guest. - - Changes to ports' state are effected by control messages. - Appropriate action is taken on the port indicated in the - control message. The layout of the structure of the control - buffer and the events associated are:struct virtio_console_control { - - uint32_t id; /* Port number */ - - uint16_t event; /* The kind of control event */ - - uint16_t value; /* Extra information for the event */ - -}; - - - -/* Some events for the internal messages (control packets) */ - - - -#define VIRTIO_CONSOLE_DEVICE_READY 0 - -#define VIRTIO_CONSOLE_PORT_ADD 1 - -#define VIRTIO_CONSOLE_PORT_REMOVE 2 - -#define VIRTIO_CONSOLE_PORT_READY 3 - -#define VIRTIO_CONSOLE_CONSOLE_PORT 4 - -#define VIRTIO_CONSOLE_RESIZE 5 - -#define VIRTIO_CONSOLE_PORT_OPEN 6 - -#define VIRTIO_CONSOLE_PORT_NAME 7 - -Appendix F: Entropy Device - -The virtio entropy device supplies high-quality randomness for -guest use. - - Configuration - - Subsystem Device ID 4 - - Virtqueues 0:requestq. - - Feature bits None currently defined - - Device configuration layout None currently defined. - - Device Initialization - - The virtqueue is initialized - - Device Operation - -When the driver requires random bytes, it places the descriptor -of one or more buffers in the queue. It will be completely filled -by random data by the device. - -Appendix G: Memory Balloon Device - -The virtio memory balloon device is a primitive device for -managing guest memory: the device asks for a certain amount of -memory, and the guest supplies it (or withdraws it, if the device -has more than it asks for). This allows the guest to adapt to -changes in allowance of underlying physical memory. If the -feature is negotiated, the device can also be used to communicate -guest memory statistics to the host. - - Configuration - - Subsystem Device ID 5 - - Virtqueues 0:inflateq. 1:deflateq. 2:statsq.[footnote: -Only if VIRTIO_BALLON_F_STATS_VQ set -] - - Feature bits - - VIRTIO_BALLOON_F_MUST_TELL_HOST (0) Host must be told before - pages from the balloon are used. - - VIRTIO_BALLOON_F_STATS_VQ (1) A virtqueue for reporting guest - memory statistics is present. - - Device configuration layout Both fields of this configuration - are always available. Note that they are little endian, despite - convention that device fields are guest endian:struct virtio_balloon_config { - - u32 num_pages; - - u32 actual; - -}; - - Device Initialization - - The inflate and deflate virtqueues are identified. - - If the VIRTIO_BALLOON_F_STATS_VQ feature bit is negotiated: - - Identify the stats virtqueue. - - Add one empty buffer to the stats virtqueue and notify the - host. - -Device operation begins immediately. - - Device Operation - - Memory Ballooning The device is driven by the receipt of a - configuration change interrupt. - - The “num_pages” configuration field is examined. If this is - greater than the “actual” number of pages, memory must be given - to the balloon. If it is less than the “actual” number of - pages, memory may be taken back from the balloon for general - use. - - To supply memory to the balloon (aka. inflate): - - The driver constructs an array of addresses of unused memory - pages. These addresses are divided by 4096[footnote: -This is historical, and independent of the guest page size -] and the descriptor describing the resulting 32-bit array is - added to the inflateq. - - To remove memory from the balloon (aka. deflate): - - The driver constructs an array of addresses of memory pages it - has previously given to the balloon, as described above. This - descriptor is added to the deflateq. - - If the VIRTIO_BALLOON_F_MUST_TELL_HOST feature is set, the - guest may not use these requested pages until that descriptor - in the deflateq has been used by the device. - - Otherwise, the guest may begin to re-use pages previously given - to the balloon before the device has acknowledged their - withdrawl. [footnote: -In this case, deflation advice is merely a courtesy -] - - In either case, once the device has completed the inflation or - deflation, the “actual” field of the configuration should be - updated to reflect the new number of pages in the balloon.[footnote: -As updates to configuration space are not atomic, this field -isn't particularly reliable, but can be used to diagnose buggy -guests. -] - - Memory Statistics - -The stats virtqueue is atypical because communication is driven -by the device (not the driver). The channel becomes active at -driver initialization time when the driver adds an empty buffer -and notifies the device. A request for memory statistics proceeds -as follows: - - The device pushes the buffer onto the used ring and sends an - interrupt. - - The driver pops the used buffer and discards it. - - The driver collects memory statistics and writes them into a - new buffer. - - The driver adds the buffer to the virtqueue and notifies the - device. - - The device pops the buffer (retaining it to initiate a - subsequent request) and consumes the statistics. - - Memory Statistics Format Each statistic consists of a 16 bit - tag and a 64 bit value. Both quantities are represented in the - native endian of the guest. All statistics are optional and the - driver may choose which ones to supply. To guarantee backwards - compatibility, unsupported statistics should be omitted. - - struct virtio_balloon_stat { - -#define VIRTIO_BALLOON_S_SWAP_IN 0 - -#define VIRTIO_BALLOON_S_SWAP_OUT 1 - -#define VIRTIO_BALLOON_S_MAJFLT 2 - -#define VIRTIO_BALLOON_S_MINFLT 3 - -#define VIRTIO_BALLOON_S_MEMFREE 4 - -#define VIRTIO_BALLOON_S_MEMTOT 5 - - u16 tag; - - u64 val; - -} __attribute__((packed)); - - Tags - - VIRTIO_BALLOON_S_SWAP_IN The amount of memory that has been - swapped in (in bytes). - - VIRTIO_BALLOON_S_SWAP_OUT The amount of memory that has been - swapped out to disk (in bytes). - - VIRTIO_BALLOON_S_MAJFLT The number of major page faults that - have occurred. - - VIRTIO_BALLOON_S_MINFLT The number of minor page faults that - have occurred. - - VIRTIO_BALLOON_S_MEMFREE The amount of memory not being used - for any purpose (in bytes). - - VIRTIO_BALLOON_S_MEMTOT The total amount of memory available - (in bytes). - -Appendix H: Rpmsg: Remote Processor Messaging - -Virtio rpmsg devices represent remote processors on the system -which run in asymmetric multi-processing (AMP) configuration, and -which are usually used to offload cpu-intensive tasks from the -main application processor (a typical SoC methodology). - -Virtio is being used to communicate with those remote processors; -empty buffers are placed in one virtqueue for receiving messages, -and non-empty buffers, containing outbound messages, are enqueued -in a second virtqueue for transmission. - -Numerous communication channels can be multiplexed over those two -virtqueues, so different entities, running on the application and -remote processor, can directly communicate in a point-to-point -fashion. - - Configuration - - Subsystem Device ID 7 - - Virtqueues 0:receiveq. 1:transmitq. - - Feature bits - - VIRTIO_RPMSG_F_NS (0) Device sends (and capable of receiving) - name service messages announcing the creation (or - destruction) of a channel:/** - - * struct rpmsg_ns_msg - dynamic name service announcement -message - - * @name: name of remote service that is published - - * @addr: address of remote service that is published - - * @flags: indicates whether service is created or destroyed - - * - - * This message is sent across to publish a new service (or -announce - - * about its removal). When we receives these messages, an -appropriate - - * rpmsg channel (i.e device) is created/destroyed. - - */ - -struct rpmsg_ns_msgoon_config { - - char name[RPMSG_NAME_SIZE]; - - u32 addr; - - u32 flags; - -} __packed; - - - -/** - - * enum rpmsg_ns_flags - dynamic name service announcement flags - - * - - * @RPMSG_NS_CREATE: a new remote service was just created - - * @RPMSG_NS_DESTROY: a remote service was just destroyed - - */ - -enum rpmsg_ns_flags { - - RPMSG_NS_CREATE = 0, - - RPMSG_NS_DESTROY = 1, - -}; - - Device configuration layout - -At his point none currently defined. - - Device Initialization - - The initialization routine should identify the receive and - transmission virtqueues. - - The receive virtqueue should be filled with receive buffers. - - Device Operation - -Messages are transmitted by placing them in the transmitq, and -buffers for inbound messages are placed in the receiveq. In any -case, messages are always preceded by the following header: /** - - * struct rpmsg_hdr - common header for all rpmsg messages - - * @src: source address - - * @dst: destination address - - * @reserved: reserved for future use - - * @len: length of payload (in bytes) - - * @flags: message flags - - * @data: @len bytes of message payload data - - * - - * Every message sent(/received) on the rpmsg bus begins with -this header. - - */ - -struct rpmsg_hdr { - - u32 src; - - u32 dst; - - u32 reserved; - - u16 len; - - u16 flags; - - u8 data[0]; - -} __packed; - -Appendix I: SCSI Host Device - -The virtio SCSI host device groups together one or more virtual -logical units (such as disks), and allows communicating to them -using the SCSI protocol. An instance of the device represents a -SCSI host to which many targets and LUNs are attached. - -The virtio SCSI device services two kinds of requests: - - command requests for a logical unit; - - task management functions related to a logical unit, target or - command. - -The device is also able to send out notifications about added and -removed logical units. Together, these capabilities provide a -SCSI transport protocol that uses virtqueues as the transfer -medium. In the transport protocol, the virtio driver acts as the -initiator, while the virtio SCSI host provides one or more -targets that receive and process the requests. - - Configuration - - Subsystem Device ID 8 - - Virtqueues 0:controlq; 1:eventq; 2..n:request queues. - - Feature bits - - VIRTIO_SCSI_F_INOUT (0) A single request can include both - read-only and write-only data buffers. - - VIRTIO_SCSI_F_HOTPLUG (1) The host should enable - hot-plug/hot-unplug of new LUNs and targets on the SCSI bus. - - Device configuration layout All fields of this configuration - are always available. sense_size and cdb_size are writable by - the guest.struct virtio_scsi_config { - - u32 num_queues; - - u32 seg_max; - - u32 max_sectors; - - u32 cmd_per_lun; - - u32 event_info_size; - - u32 sense_size; - - u32 cdb_size; - - u16 max_channel; - - u16 max_target; - - u32 max_lun; - -}; - - num_queues is the total number of request virtqueues exposed by - the device. The driver is free to use only one request queue, - or it can use more to achieve better performance. - - seg_max is the maximum number of segments that can be in a - command. A bidirectional command can include seg_max input - segments and seg_max output segments. - - max_sectors is a hint to the guest about the maximum transfer - size it should use. - - cmd_per_lun is a hint to the guest about the maximum number of - linked commands it should send to one LUN. The actual value - to be used is the minimum of cmd_per_lun and the virtqueue - size. - - event_info_size is the maximum size that the device will fill - for buffers that the driver places in the eventq. The driver - should always put buffers at least of this size. It is - written by the device depending on the set of negotated - features. - - sense_size is the maximum size of the sense data that the - device will write. The default value is written by the device - and will always be 96, but the driver can modify it. It is - restored to the default when the device is reset. - - cdb_size is the maximum size of the CDB that the driver will - write. The default value is written by the device and will - always be 32, but the driver can likewise modify it. It is - restored to the default when the device is reset. - - max_channel, max_target and max_lun can be used by the driver - as hints to constrain scanning the logical units on the - host.h - - Device Initialization - -The initialization routine should first of all discover the -device's virtqueues. - -If the driver uses the eventq, it should then place at least a -buffer in the eventq. - -The driver can immediately issue requests (for example, INQUIRY -or REPORT LUNS) or task management functions (for example, I_T -RESET). - - Device Operation: request queues - -The driver queues requests to an arbitrary request queue, and -they are used by the device on that same queue. It is the -responsibility of the driver to ensure strict request ordering -for commands placed on different queues, because they will be -consumed with no order constraints. - -Requests have the following format: - -struct virtio_scsi_req_cmd { - - // Read-only - - u8 lun[8]; - - u64 id; - - u8 task_attr; - - u8 prio; - - u8 crn; - - char cdb[cdb_size]; - - char dataout[]; - - // Write-only part - - u32 sense_len; - - u32 residual; - - u16 status_qualifier; - - u8 status; - - u8 response; - - u8 sense[sense_size]; - - char datain[]; - -}; - - - -/* command-specific response values */ - -#define VIRTIO_SCSI_S_OK 0 - -#define VIRTIO_SCSI_S_OVERRUN 1 - -#define VIRTIO_SCSI_S_ABORTED 2 - -#define VIRTIO_SCSI_S_BAD_TARGET 3 - -#define VIRTIO_SCSI_S_RESET 4 - -#define VIRTIO_SCSI_S_BUSY 5 - -#define VIRTIO_SCSI_S_TRANSPORT_FAILURE 6 - -#define VIRTIO_SCSI_S_TARGET_FAILURE 7 - -#define VIRTIO_SCSI_S_NEXUS_FAILURE 8 - -#define VIRTIO_SCSI_S_FAILURE 9 - - - -/* task_attr */ - -#define VIRTIO_SCSI_S_SIMPLE 0 - -#define VIRTIO_SCSI_S_ORDERED 1 - -#define VIRTIO_SCSI_S_HEAD 2 - -#define VIRTIO_SCSI_S_ACA 3 - -The lun field addresses a target and logical unit in the -virtio-scsi device's SCSI domain. The only supported format for -the LUN field is: first byte set to 1, second byte set to target, -third and fourth byte representing a single level LUN structure, -followed by four zero bytes. With this representation, a -virtio-scsi device can serve up to 256 targets and 16384 LUNs per -target. - -The id field is the command identifier (“tag”). - -task_attr, prio and crn should be left to zero. task_attr defines -the task attribute as in the table above, but all task attributes -may be mapped to SIMPLE by the device; crn may also be provided -by clients, but is generally expected to be 0. The maximum CRN -value defined by the protocol is 255, since CRN is stored in an -8-bit integer. - -All of these fields are defined in SAM. They are always -read-only, as are the cdb and dataout field. The cdb_size is -taken from the configuration space. - -sense and subsequent fields are always write-only. The sense_len -field indicates the number of bytes actually written to the sense -buffer. The residual field indicates the residual size, -calculated as “data_length - number_of_transferred_bytes”, for -read or write operations. For bidirectional commands, the -number_of_transferred_bytes includes both read and written bytes. -A residual field that is less than the size of datain means that -the dataout field was processed entirely. A residual field that -exceeds the size of datain means that the dataout field was -processed partially and the datain field was not processed at -all. - -The status byte is written by the device to be the status code as -defined in SAM. - -The response byte is written by the device to be one of the -following: - - VIRTIO_SCSI_S_OK when the request was completed and the status - byte is filled with a SCSI status code (not necessarily - "GOOD"). - - VIRTIO_SCSI_S_OVERRUN if the content of the CDB requires - transferring more data than is available in the data buffers. - - VIRTIO_SCSI_S_ABORTED if the request was cancelled due to an - ABORT TASK or ABORT TASK SET task management function. - - VIRTIO_SCSI_S_BAD_TARGET if the request was never processed - because the target indicated by the lun field does not exist. - - VIRTIO_SCSI_S_RESET if the request was cancelled due to a bus - or device reset (including a task management function). - - VIRTIO_SCSI_S_TRANSPORT_FAILURE if the request failed due to a - problem in the connection between the host and the target - (severed link). - - VIRTIO_SCSI_S_TARGET_FAILURE if the target is suffering a - failure and the guest should not retry on other paths. - - VIRTIO_SCSI_S_NEXUS_FAILURE if the nexus is suffering a failure - but retrying on other paths might yield a different result. - - VIRTIO_SCSI_S_BUSY if the request failed but retrying on the - same path should work. - - VIRTIO_SCSI_S_FAILURE for other host or guest error. In - particular, if neither dataout nor datain is empty, and the - VIRTIO_SCSI_F_INOUT feature has not been negotiated, the - request will be immediately returned with a response equal to - VIRTIO_SCSI_S_FAILURE. - - Device Operation: controlq - -The controlq is used for other SCSI transport operations. -Requests have the following format: - -struct virtio_scsi_ctrl { - - u32 type; - - ... - - u8 response; - -}; - - - -/* response values valid for all commands */ - -#define VIRTIO_SCSI_S_OK 0 - -#define VIRTIO_SCSI_S_BAD_TARGET 3 - -#define VIRTIO_SCSI_S_BUSY 5 - -#define VIRTIO_SCSI_S_TRANSPORT_FAILURE 6 - -#define VIRTIO_SCSI_S_TARGET_FAILURE 7 - -#define VIRTIO_SCSI_S_NEXUS_FAILURE 8 - -#define VIRTIO_SCSI_S_FAILURE 9 - -#define VIRTIO_SCSI_S_INCORRECT_LUN 12 - -The type identifies the remaining fields. - -The following commands are defined: - - Task management function -#define VIRTIO_SCSI_T_TMF 0 - - - -#define VIRTIO_SCSI_T_TMF_ABORT_TASK 0 - -#define VIRTIO_SCSI_T_TMF_ABORT_TASK_SET 1 - -#define VIRTIO_SCSI_T_TMF_CLEAR_ACA 2 - -#define VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET 3 - -#define VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET 4 - -#define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET 5 - -#define VIRTIO_SCSI_T_TMF_QUERY_TASK 6 - -#define VIRTIO_SCSI_T_TMF_QUERY_TASK_SET 7 - - - -struct virtio_scsi_ctrl_tmf - -{ - - // Read-only part - - u32 type; - - u32 subtype; - - u8 lun[8]; - - u64 id; - - // Write-only part - - u8 response; - -} - - - -/* command-specific response values */ - -#define VIRTIO_SCSI_S_FUNCTION_COMPLETE 0 - -#define VIRTIO_SCSI_S_FUNCTION_SUCCEEDED 10 - -#define VIRTIO_SCSI_S_FUNCTION_REJECTED 11 - - The type is VIRTIO_SCSI_T_TMF; the subtype field defines. All - fields except response are filled by the driver. The subtype - field must always be specified and identifies the requested - task management function. - - Other fields may be irrelevant for the requested TMF; if so, - they are ignored but they should still be present. The lun - field is in the same format specified for request queues; the - single level LUN is ignored when the task management function - addresses a whole I_T nexus. When relevant, the value of the id - field is matched against the id values passed on the requestq. - - The outcome of the task management function is written by the - device in the response field. The command-specific response - values map 1-to-1 with those defined in SAM. - - Asynchronous notification query -#define VIRTIO_SCSI_T_AN_QUERY 1 - - - -struct virtio_scsi_ctrl_an { - - // Read-only part - - u32 type; - - u8 lun[8]; - - u32 event_requested; - - // Write-only part - - u32 event_actual; - - u8 response; - -} - - - -#define VIRTIO_SCSI_EVT_ASYNC_OPERATIONAL_CHANGE 2 - -#define VIRTIO_SCSI_EVT_ASYNC_POWER_MGMT 4 - -#define VIRTIO_SCSI_EVT_ASYNC_EXTERNAL_REQUEST 8 - -#define VIRTIO_SCSI_EVT_ASYNC_MEDIA_CHANGE 16 - -#define VIRTIO_SCSI_EVT_ASYNC_MULTI_HOST 32 - -#define VIRTIO_SCSI_EVT_ASYNC_DEVICE_BUSY 64 - - By sending this command, the driver asks the device which - events the given LUN can report, as described in paragraphs 6.6 - and A.6 of the SCSI MMC specification. The driver writes the - events it is interested in into the event_requested; the device - responds by writing the events that it supports into - event_actual. - - The type is VIRTIO_SCSI_T_AN_QUERY. The lun and event_requested - fields are written by the driver. The event_actual and response - fields are written by the device. - - No command-specific values are defined for the response byte. - - Asynchronous notification subscription -#define VIRTIO_SCSI_T_AN_SUBSCRIBE 2 - - - -struct virtio_scsi_ctrl_an { - - // Read-only part - - u32 type; - - u8 lun[8]; - - u32 event_requested; - - // Write-only part - - u32 event_actual; - - u8 response; - -} - - By sending this command, the driver asks the specified LUN to - report events for its physical interface, again as described in - the SCSI MMC specification. The driver writes the events it is - interested in into the event_requested; the device responds by - writing the events that it supports into event_actual. - - Event types are the same as for the asynchronous notification - query message. - - The type is VIRTIO_SCSI_T_AN_SUBSCRIBE. The lun and - event_requested fields are written by the driver. The - event_actual and response fields are written by the device. - - No command-specific values are defined for the response byte. - - Device Operation: eventq - -The eventq is used by the device to report information on logical -units that are attached to it. The driver should always leave a -few buffers ready in the eventq. In general, the device will not -queue events to cope with an empty eventq, and will end up -dropping events if it finds no buffer ready. However, when -reporting events for many LUNs (e.g. when a whole target -disappears), the device can throttle events to avoid dropping -them. For this reason, placing 10-15 buffers on the event queue -should be enough. - -Buffers are placed in the eventq and filled by the device when -interesting events occur. The buffers should be strictly -write-only (device-filled) and the size of the buffers should be -at least the value given in the device's configuration -information. - -Buffers returned by the device on the eventq will be referred to -as "events" in the rest of this section. Events have the -following format: - -#define VIRTIO_SCSI_T_EVENTS_MISSED 0x80000000 - - - -struct virtio_scsi_event { - - // Write-only part - - u32 event; - - ... - -} - -If bit 31 is set in the event field, the device failed to report -an event due to missing buffers. In this case, the driver should -poll the logical units for unit attention conditions, and/or do -whatever form of bus scan is appropriate for the guest operating -system. - -Other data that the device writes to the buffer depends on the -contents of the event field. The following events are defined: - - No event -#define VIRTIO_SCSI_T_NO_EVENT 0 - - This event is fired in the following cases: - - When the device detects in the eventq a buffer that is shorter - than what is indicated in the configuration field, it might - use it immediately and put this dummy value in the event - field. A well-written driver will never observe this - situation. - - When events are dropped, the device may signal this event as - soon as the drivers makes a buffer available, in order to - request action from the driver. In this case, of course, this - event will be reported with the VIRTIO_SCSI_T_EVENTS_MISSED - flag. - - Transport reset -#define VIRTIO_SCSI_T_TRANSPORT_RESET 1 - - - -struct virtio_scsi_event_reset { - - // Write-only part - - u32 event; - - u8 lun[8]; - - u32 reason; - -} - - - -#define VIRTIO_SCSI_EVT_RESET_HARD 0 - -#define VIRTIO_SCSI_EVT_RESET_RESCAN 1 - -#define VIRTIO_SCSI_EVT_RESET_REMOVED 2 - - By sending this event, the device signals that a logical unit - on a target has been reset, including the case of a new device - appearing or disappearing on the bus.The device fills in all - fields. The event field is set to - VIRTIO_SCSI_T_TRANSPORT_RESET. The lun field addresses a - logical unit in the SCSI host. - - The reason value is one of the three #define values appearing - above: - - VIRTIO_SCSI_EVT_RESET_REMOVED (“LUN/target removed”) is used if - the target or logical unit is no longer able to receive - commands. - - VIRTIO_SCSI_EVT_RESET_HARD (“LUN hard reset”) is used if the - logical unit has been reset, but is still present. - - VIRTIO_SCSI_EVT_RESET_RESCAN (“rescan LUN/target”) is used if a - target or logical unit has just appeared on the device. - - The “removed” and “rescan” events, when sent for LUN 0, may - apply to the entire target. After receiving them the driver - should ask the initiator to rescan the target, in order to - detect the case when an entire target has appeared or - disappeared. These two events will never be reported unless the - VIRTIO_SCSI_F_HOTPLUG feature was negotiated between the host - and the guest. - - Events will also be reported via sense codes (this obviously - does not apply to newly appeared buses or targets, since the - application has never discovered them): - - “LUN/target removed” maps to sense key ILLEGAL REQUEST, asc - 0x25, ascq 0x00 (LOGICAL UNIT NOT SUPPORTED) - - “LUN hard reset” maps to sense key UNIT ATTENTION, asc 0x29 - (POWER ON, RESET OR BUS DEVICE RESET OCCURRED) - - “rescan LUN/target” maps to sense key UNIT ATTENTION, asc 0x3f, - ascq 0x0e (REPORTED LUNS DATA HAS CHANGED) - - The preferred way to detect transport reset is always to use - events, because sense codes are only seen by the driver when it - sends a SCSI command to the logical unit or target. However, in - case events are dropped, the initiator will still be able to - synchronize with the actual state of the controller if the - driver asks the initiator to rescan of the SCSI bus. During the - rescan, the initiator will be able to observe the above sense - codes, and it will process them as if it the driver had - received the equivalent event. - - Asynchronous notification -#define VIRTIO_SCSI_T_ASYNC_NOTIFY 2 - - - -struct virtio_scsi_event_an { - - // Write-only part - - u32 event; - - u8 lun[8]; - - u32 reason; - -} - - By sending this event, the device signals that an asynchronous - event was fired from a physical interface. - - All fields are written by the device. The event field is set to - VIRTIO_SCSI_T_ASYNC_NOTIFY. The lun field addresses a logical - unit in the SCSI host. The reason field is a subset of the - events that the driver has subscribed to via the "Asynchronous - notification subscription" command. - - When dropped events are reported, the driver should poll for - asynchronous events manually using SCSI commands. - -Appendix X: virtio-mmio - -Virtual environments without PCI support (a common situation in -embedded devices models) might use simple memory mapped device (“ -virtio-mmio”) instead of the PCI device. - -The memory mapped virtio device behaviour is based on the PCI -device specification. Therefore most of operations like device -initialization, queues configuration and buffer transfers are -nearly identical. Existing differences are described in the -following sections. - - Device Initialization - -Instead of using the PCI IO space for virtio header, the “ -virtio-mmio” device provides a set of memory mapped control -registers, all 32 bits wide, followed by device-specific -configuration space. The following list presents their layout: - - Offset from the device base address | Direction | Name - Description - - 0x000 | R | MagicValue - “virt” string. - - 0x004 | R | Version - Device version number. Currently must be 1. - - 0x008 | R | DeviceID - Virtio Subsystem Device ID (ie. 1 for network card). - - 0x00c | R | VendorID - Virtio Subsystem Vendor ID. - - 0x010 | R | HostFeatures - Flags representing features the device supports. - Reading from this register returns 32 consecutive flag bits, - first bit depending on the last value written to - HostFeaturesSel register. Access to this register returns bits HostFeaturesSel*32 - - to (HostFeaturesSel*32)+31 -, eg. feature bits 0 to 31 if - HostFeaturesSel is set to 0 and features bits 32 to 63 if - HostFeaturesSel is set to 1. Also see [sub:Feature-Bits] - - 0x014 | W | HostFeaturesSel - Device (Host) features word selection. - Writing to this register selects a set of 32 device feature bits - accessible by reading from HostFeatures register. Device driver - must write a value to the HostFeaturesSel register before - reading from the HostFeatures register. - - 0x020 | W | GuestFeatures - Flags representing device features understood and activated by - the driver. - Writing to this register sets 32 consecutive flag bits, first - bit depending on the last value written to GuestFeaturesSel - register. Access to this register sets bits GuestFeaturesSel*32 - - to (GuestFeaturesSel*32)+31 -, eg. feature bits 0 to 31 if - GuestFeaturesSel is set to 0 and features bits 32 to 63 if - GuestFeaturesSel is set to 1. Also see [sub:Feature-Bits] - - 0x024 | W | GuestFeaturesSel - Activated (Guest) features word selection. - Writing to this register selects a set of 32 activated feature - bits accessible by writing to the GuestFeatures register. - Device driver must write a value to the GuestFeaturesSel - register before writing to the GuestFeatures register. - - 0x028 | W | GuestPageSize - Guest page size. - Device driver must write the guest page size in bytes to the - register during initialization, before any queues are used. - This value must be a power of 2 and is used by the Host to - calculate Guest address of the first queue page (see QueuePFN). - - 0x030 | W | QueueSel - Virtual queue index (first queue is 0). - Writing to this register selects the virtual queue that the - following operations on QueueNum, QueueAlign and QueuePFN apply - to. - - 0x034 | R | QueueNumMax - Maximum virtual queue size. - Reading from the register returns the maximum size of the queue - the Host is ready to process or zero (0x0) if the queue is not - available. This applies to the queue selected by writing to - QueueSel and is allowed only when QueuePFN is set to zero - (0x0), so when the queue is not actively used. - - 0x038 | W | QueueNum - Virtual queue size. - Queue size is a number of elements in the queue, therefore size - of the descriptor table and both available and used rings. - Writing to this register notifies the Host what size of the - queue the Guest will use. This applies to the queue selected by - writing to QueueSel. - - 0x03c | W | QueueAlign - Used Ring alignment in the virtual queue. - Writing to this register notifies the Host about alignment - boundary of the Used Ring in bytes. This value must be a power - of 2 and applies to the queue selected by writing to QueueSel. - - 0x040 | RW | QueuePFN - Guest physical page number of the virtual queue. - Writing to this register notifies the host about location of the - virtual queue in the Guest's physical address space. This value - is the index number of a page starting with the queue - Descriptor Table. Value zero (0x0) means physical address zero - (0x00000000) and is illegal. When the Guest stops using the - queue it must write zero (0x0) to this register. - Reading from this register returns the currently used page - number of the queue, therefore a value other than zero (0x0) - means that the queue is in use. - Both read and write accesses apply to the queue selected by - writing to QueueSel. - - 0x050 | W | QueueNotify - Queue notifier. - Writing a queue index to this register notifies the Host that - there are new buffers to process in the queue. - - 0x60 | R | InterruptStatus -Interrupt status. -Reading from this register returns a bit mask of interrupts - asserted by the device. An interrupt is asserted if the - corresponding bit is set, ie. equals one (1). - - Bit 0 | Used Ring Update -This interrupt is asserted when the Host has updated the Used - Ring in at least one of the active virtual queues. - - Bit 1 | Configuration change -This interrupt is asserted when configuration of the device has - changed. - - 0x064 | W | InterruptACK - Interrupt acknowledge. - Writing to this register notifies the Host that the Guest - finished handling interrupts. Set bits in the value clear the - corresponding bits of the InterruptStatus register. - - 0x070 | RW | Status - Device status. - Reading from this register returns the current device status - flags. - Writing non-zero values to this register sets the status flags, - indicating the Guest progress. Writing zero (0x0) to this - register triggers a device reset. - Also see [sub:Device-Initialization-Sequence] - - 0x100+ | RW | Config - Device-specific configuration space starts at an offset 0x100 - and is accessed with byte alignment. Its meaning and size - depends on the device and the driver. - -Virtual queue size is a number of elements in the queue, -therefore size of the descriptor table and both available and -used rings. - -The endianness of the registers follows the native endianness of -the Guest. Writing to registers described as “R” and reading from -registers described as “W” is not permitted and can cause -undefined behavior. - -The device initialization is performed as described in [sub:Device-Initialization-Sequence] - with one exception: the Guest must notify the Host about its -page size, writing the size in bytes to GuestPageSize register -before the initialization is finished. - -The memory mapped virtio devices generate single interrupt only, -therefore no special configuration is required. - - Virtqueue Configuration - -The virtual queue configuration is performed in a similar way to -the one described in [sec:Virtqueue-Configuration] with a few -additional operations: - - Select the queue writing its index (first queue is 0) to the - QueueSel register. - - Check if the queue is not already in use: read QueuePFN - register, returned value should be zero (0x0). - - Read maximum queue size (number of elements) from the - QueueNumMax register. If the returned value is zero (0x0) the - queue is not available. - - Allocate and zero the queue pages in contiguous virtual memory, - aligning the Used Ring to an optimal boundary (usually page - size). Size of the allocated queue may be smaller than or equal - to the maximum size returned by the Host. - - Notify the Host about the queue size by writing the size to - QueueNum register. - - Notify the Host about the used alignment by writing its value - in bytes to QueueAlign register. - - Write the physical number of the first page of the queue to the - QueuePFN register. - -The queue and the device are ready to begin normal operations -now. - - Device Operation - -The memory mapped virtio device behaves in the same way as -described in [sec:Device-Operation], with the following -exceptions: - - The device is notified about new buffers available in a queue - by writing the queue index to register QueueNum instead of the - virtio header in PCI I/O space ([sub:Notifying-The-Device]). - - The memory mapped virtio device is using single, dedicated - interrupt signal, which is raised when at least one of the - interrupts described in the InterruptStatus register - description is asserted. After receiving an interrupt, the - driver must read the InterruptStatus register to check what - caused the interrupt (see the register description). After the - interrupt is handled, the driver must acknowledge it by writing - a bit mask corresponding to the serviced interrupt to the - InterruptACK register. - -- cgit v1.2.3 From 6ba8a3b19e764b6a65e4030ab0999be50c291e6c Mon Sep 17 00:00:00 2001 From: Nandita Dukkipati Date: Mon, 11 Mar 2013 10:00:43 +0000 Subject: tcp: Tail loss probe (TLP) This patch series implement the Tail loss probe (TLP) algorithm described in http://tools.ietf.org/html/draft-dukkipati-tcpm-tcp-loss-probe-01. The first patch implements the basic algorithm. TLP's goal is to reduce tail latency of short transactions. It achieves this by converting retransmission timeouts (RTOs) occuring due to tail losses (losses at end of transactions) into fast recovery. TLP transmits one packet in two round-trips when a connection is in Open state and isn't receiving any ACKs. The transmitted packet, aka loss probe, can be either new or a retransmission. When there is tail loss, the ACK from a loss probe triggers FACK/early-retransmit based fast recovery, thus avoiding a costly RTO. In the absence of loss, there is no change in the connection state. PTO stands for probe timeout. It is a timer event indicating that an ACK is overdue and triggers a loss probe packet. The PTO value is set to max(2*SRTT, 10ms) and is adjusted to account for delayed ACK timer when there is only one oustanding packet. TLP Algorithm On transmission of new data in Open state: -> packets_out > 1: schedule PTO in max(2*SRTT, 10ms). -> packets_out == 1: schedule PTO in max(2*RTT, 1.5*RTT + 200ms) -> PTO = min(PTO, RTO) Conditions for scheduling PTO: -> Connection is in Open state. -> Connection is either cwnd limited or no new data to send. -> Number of probes per tail loss episode is limited to one. -> Connection is SACK enabled. When PTO fires: new_segment_exists: -> transmit new segment. -> packets_out++. cwnd remains same. no_new_packet: -> retransmit the last segment. Its ACK triggers FACK or early retransmit based recovery. ACK path: -> rearm RTO at start of ACK processing. -> reschedule PTO if need be. In addition, the patch includes a small variation to the Early Retransmit (ER) algorithm, such that ER and TLP together can in principle recover any N-degree of tail loss through fast recovery. TLP is controlled by the same sysctl as ER, tcp_early_retrans sysctl. tcp_early_retrans==0; disables TLP and ER. ==1; enables RFC5827 ER. ==2; delayed ER. ==3; TLP and delayed ER. [DEFAULT] ==4; TLP only. The TLP patch series have been extensively tested on Google Web servers. It is most effective for short Web trasactions, where it reduced RTOs by 15% and improved HTTP response time (average by 6%, 99th percentile by 10%). The transmitted probes account for <0.5% of the overall transmissions. Signed-off-by: Nandita Dukkipati Acked-by: Neal Cardwell Acked-by: Yuchung Cheng Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 8 ++- include/linux/tcp.h | 1 - include/net/inet_connection_sock.h | 5 +- include/net/tcp.h | 6 +- include/uapi/linux/snmp.h | 1 + net/ipv4/inet_diag.c | 4 +- net/ipv4/proc.c | 1 + net/ipv4/sysctl_net_ipv4.c | 4 +- net/ipv4/tcp_input.c | 24 ++++--- net/ipv4/tcp_ipv4.c | 4 +- net/ipv4/tcp_output.c | 128 +++++++++++++++++++++++++++++++-- net/ipv4/tcp_timer.c | 13 ++-- 12 files changed, 171 insertions(+), 28 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index dc2dc87d2557..1cae6c383e1b 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -190,7 +190,9 @@ tcp_early_retrans - INTEGER Enable Early Retransmit (ER), per RFC 5827. ER lowers the threshold for triggering fast retransmit when the amount of outstanding data is small and when no previously unsent data can be transmitted (such - that limited transmit could be used). + that limited transmit could be used). Also controls the use of + Tail loss probe (TLP) that converts RTOs occuring due to tail + losses into fast recovery (draft-dukkipati-tcpm-tcp-loss-probe-01). Possible values: 0 disables ER 1 enables ER @@ -198,7 +200,9 @@ tcp_early_retrans - INTEGER by a fourth of RTT. This mitigates connection falsely recovers when network has a small degree of reordering (less than 3 packets). - Default: 2 + 3 enables delayed ER and TLP. + 4 enables TLP only. + Default: 3 tcp_ecn - INTEGER Control use of Explicit Congestion Notification (ECN) by TCP. diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 515c3746b675..01860d74555c 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -201,7 +201,6 @@ struct tcp_sock { unused : 1; u8 repair_queue; u8 do_early_retrans:1,/* Enable RFC5827 early-retransmit */ - early_retrans_delayed:1, /* Delayed ER timer installed */ syn_data:1, /* SYN includes data */ syn_fastopen:1, /* SYN includes Fast Open option */ syn_data_acked:1;/* data in SYN is acked by SYN-ACK */ diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index 183292722f6e..de2c78529afa 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -133,6 +133,8 @@ struct inet_connection_sock { #define ICSK_TIME_RETRANS 1 /* Retransmit timer */ #define ICSK_TIME_DACK 2 /* Delayed ack timer */ #define ICSK_TIME_PROBE0 3 /* Zero window probe timer */ +#define ICSK_TIME_EARLY_RETRANS 4 /* Early retransmit timer */ +#define ICSK_TIME_LOSS_PROBE 5 /* Tail loss probe timer */ static inline struct inet_connection_sock *inet_csk(const struct sock *sk) { @@ -222,7 +224,8 @@ static inline void inet_csk_reset_xmit_timer(struct sock *sk, const int what, when = max_when; } - if (what == ICSK_TIME_RETRANS || what == ICSK_TIME_PROBE0) { + if (what == ICSK_TIME_RETRANS || what == ICSK_TIME_PROBE0 || + what == ICSK_TIME_EARLY_RETRANS || what == ICSK_TIME_LOSS_PROBE) { icsk->icsk_pending = what; icsk->icsk_timeout = jiffies + when; sk_reset_timer(sk, &icsk->icsk_retransmit_timer, icsk->icsk_timeout); diff --git a/include/net/tcp.h b/include/net/tcp.h index a2baa5e4ba31..ab9f947b118b 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -543,6 +543,8 @@ extern bool tcp_syn_flood_action(struct sock *sk, extern void tcp_push_one(struct sock *, unsigned int mss_now); extern void tcp_send_ack(struct sock *sk); extern void tcp_send_delayed_ack(struct sock *sk); +extern void tcp_send_loss_probe(struct sock *sk); +extern bool tcp_schedule_loss_probe(struct sock *sk); /* tcp_input.c */ extern void tcp_cwnd_application_limited(struct sock *sk); @@ -873,8 +875,8 @@ static inline void tcp_enable_fack(struct tcp_sock *tp) static inline void tcp_enable_early_retrans(struct tcp_sock *tp) { tp->do_early_retrans = sysctl_tcp_early_retrans && - !sysctl_tcp_thin_dupack && sysctl_tcp_reordering == 3; - tp->early_retrans_delayed = 0; + sysctl_tcp_early_retrans < 4 && !sysctl_tcp_thin_dupack && + sysctl_tcp_reordering == 3; } static inline void tcp_disable_early_retrans(struct tcp_sock *tp) diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index b49eab89c9fd..290bed6b085f 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -202,6 +202,7 @@ enum LINUX_MIB_TCPFORWARDRETRANS, /* TCPForwardRetrans */ LINUX_MIB_TCPSLOWSTARTRETRANS, /* TCPSlowStartRetrans */ LINUX_MIB_TCPTIMEOUTS, /* TCPTimeouts */ + LINUX_MIB_TCPLOSSPROBES, /* TCPLossProbes */ LINUX_MIB_TCPRENORECOVERYFAIL, /* TCPRenoRecoveryFail */ LINUX_MIB_TCPSACKRECOVERYFAIL, /* TCPSackRecoveryFail */ LINUX_MIB_TCPSCHEDULERFAILED, /* TCPSchedulerFailed */ diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 7afa2c3c788f..8620408af574 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -158,7 +158,9 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, #define EXPIRES_IN_MS(tmo) DIV_ROUND_UP((tmo - jiffies) * 1000, HZ) - if (icsk->icsk_pending == ICSK_TIME_RETRANS) { + if (icsk->icsk_pending == ICSK_TIME_RETRANS || + icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS || + icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) { r->idiag_timer = 1; r->idiag_retrans = icsk->icsk_retransmits; r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout); diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 32030a24e776..4c35911d935f 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -224,6 +224,7 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPForwardRetrans", LINUX_MIB_TCPFORWARDRETRANS), SNMP_MIB_ITEM("TCPSlowStartRetrans", LINUX_MIB_TCPSLOWSTARTRETRANS), SNMP_MIB_ITEM("TCPTimeouts", LINUX_MIB_TCPTIMEOUTS), + SNMP_MIB_ITEM("TCPLossProbes", LINUX_MIB_TCPLOSSPROBES), SNMP_MIB_ITEM("TCPRenoRecoveryFail", LINUX_MIB_TCPRENORECOVERYFAIL), SNMP_MIB_ITEM("TCPSackRecoveryFail", LINUX_MIB_TCPSACKRECOVERYFAIL), SNMP_MIB_ITEM("TCPSchedulerFailed", LINUX_MIB_TCPSCHEDULERFAILED), diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 960fd29d9b8e..cca4550f4082 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -28,7 +28,7 @@ static int zero; static int one = 1; -static int two = 2; +static int four = 4; static int tcp_retr1_max = 255; static int ip_local_port_range_min[] = { 1, 1 }; static int ip_local_port_range_max[] = { 65535, 65535 }; @@ -760,7 +760,7 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec_minmax, .extra1 = &zero, - .extra2 = &two, + .extra2 = &four, }, { .procname = "udp_mem", diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 0d9bdacce99f..b794f89ac1f2 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -98,7 +98,7 @@ int sysctl_tcp_frto_response __read_mostly; int sysctl_tcp_thin_dupack __read_mostly; int sysctl_tcp_moderate_rcvbuf __read_mostly = 1; -int sysctl_tcp_early_retrans __read_mostly = 2; +int sysctl_tcp_early_retrans __read_mostly = 3; #define FLAG_DATA 0x01 /* Incoming frame contained data. */ #define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */ @@ -2150,15 +2150,16 @@ static bool tcp_pause_early_retransmit(struct sock *sk, int flag) * max(RTT/4, 2msec) unless ack has ECE mark, no RTT samples * available, or RTO is scheduled to fire first. */ - if (sysctl_tcp_early_retrans < 2 || (flag & FLAG_ECE) || !tp->srtt) + if (sysctl_tcp_early_retrans < 2 || sysctl_tcp_early_retrans > 3 || + (flag & FLAG_ECE) || !tp->srtt) return false; delay = max_t(unsigned long, (tp->srtt >> 5), msecs_to_jiffies(2)); if (!time_after(inet_csk(sk)->icsk_timeout, (jiffies + delay))) return false; - inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, delay, TCP_RTO_MAX); - tp->early_retrans_delayed = 1; + inet_csk_reset_xmit_timer(sk, ICSK_TIME_EARLY_RETRANS, delay, + TCP_RTO_MAX); return true; } @@ -2321,7 +2322,7 @@ static bool tcp_time_to_recover(struct sock *sk, int flag) * interval if appropriate. */ if (tp->do_early_retrans && !tp->retrans_out && tp->sacked_out && - (tp->packets_out == (tp->sacked_out + 1) && tp->packets_out < 4) && + (tp->packets_out >= (tp->sacked_out + 1) && tp->packets_out < 4) && !tcp_may_send_now(sk)) return !tcp_pause_early_retransmit(sk, flag); @@ -3081,6 +3082,7 @@ static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) */ void tcp_rearm_rto(struct sock *sk) { + const struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); /* If the retrans timer is currently being used by Fast Open @@ -3094,12 +3096,13 @@ void tcp_rearm_rto(struct sock *sk) } else { u32 rto = inet_csk(sk)->icsk_rto; /* Offset the time elapsed after installing regular RTO */ - if (tp->early_retrans_delayed) { + if (icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS || + icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) { struct sk_buff *skb = tcp_write_queue_head(sk); const u32 rto_time_stamp = TCP_SKB_CB(skb)->when + rto; s32 delta = (s32)(rto_time_stamp - tcp_time_stamp); /* delta may not be positive if the socket is locked - * when the delayed ER timer fires and is rescheduled. + * when the retrans timer fires and is rescheduled. */ if (delta > 0) rto = delta; @@ -3107,7 +3110,6 @@ void tcp_rearm_rto(struct sock *sk) inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, rto, TCP_RTO_MAX); } - tp->early_retrans_delayed = 0; } /* This function is called when the delayed ER timer fires. TCP enters @@ -3601,7 +3603,8 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) if (after(ack, tp->snd_nxt)) goto invalid_ack; - if (tp->early_retrans_delayed) + if (icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS || + icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) tcp_rearm_rto(sk); if (after(ack, prior_snd_una)) @@ -3678,6 +3681,9 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) if (dst) dst_confirm(dst); } + + if (icsk->icsk_pending == ICSK_TIME_RETRANS) + tcp_schedule_loss_probe(sk); return 1; no_queue: diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 8cdee120a50c..b7ab868c8284 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2703,7 +2703,9 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i, int *len) __u16 srcp = ntohs(inet->inet_sport); int rx_queue; - if (icsk->icsk_pending == ICSK_TIME_RETRANS) { + if (icsk->icsk_pending == ICSK_TIME_RETRANS || + icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS || + icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) { timer_active = 1; timer_expires = icsk->icsk_timeout; } else if (icsk->icsk_pending == ICSK_TIME_PROBE0) { diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index e2b4461074da..beb63dbc85f5 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -74,6 +74,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, /* Account for new data that has been sent to the network. */ static void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb) { + struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); unsigned int prior_packets = tp->packets_out; @@ -85,7 +86,8 @@ static void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb) tp->frto_counter = 3; tp->packets_out += tcp_skb_pcount(skb); - if (!prior_packets || tp->early_retrans_delayed) + if (!prior_packets || icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS || + icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) tcp_rearm_rto(sk); } @@ -1959,6 +1961,9 @@ static int tcp_mtu_probe(struct sock *sk) * snd_up-64k-mss .. snd_up cannot be large. However, taking into * account rare use of URG, this is not a big flaw. * + * Send at most one packet when push_one > 0. Temporarily ignore + * cwnd limit to force at most one packet out when push_one == 2. + * Returns true, if no segments are in flight and we have queued segments, * but cannot send anything now because of SWS or another problem. */ @@ -1994,8 +1999,13 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, goto repair; /* Skip network transmission */ cwnd_quota = tcp_cwnd_test(tp, skb); - if (!cwnd_quota) - break; + if (!cwnd_quota) { + if (push_one == 2) + /* Force out a loss probe pkt. */ + cwnd_quota = 1; + else + break; + } if (unlikely(!tcp_snd_wnd_test(tp, skb, mss_now))) break; @@ -2049,10 +2059,120 @@ repair: if (likely(sent_pkts)) { if (tcp_in_cwnd_reduction(sk)) tp->prr_out += sent_pkts; + + /* Send one loss probe per tail loss episode. */ + if (push_one != 2) + tcp_schedule_loss_probe(sk); tcp_cwnd_validate(sk); return false; } - return !tp->packets_out && tcp_send_head(sk); + return (push_one == 2) || (!tp->packets_out && tcp_send_head(sk)); +} + +bool tcp_schedule_loss_probe(struct sock *sk) +{ + struct inet_connection_sock *icsk = inet_csk(sk); + struct tcp_sock *tp = tcp_sk(sk); + u32 timeout, tlp_time_stamp, rto_time_stamp; + u32 rtt = tp->srtt >> 3; + + if (WARN_ON(icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS)) + return false; + /* No consecutive loss probes. */ + if (WARN_ON(icsk->icsk_pending == ICSK_TIME_LOSS_PROBE)) { + tcp_rearm_rto(sk); + return false; + } + /* Don't do any loss probe on a Fast Open connection before 3WHS + * finishes. + */ + if (sk->sk_state == TCP_SYN_RECV) + return false; + + /* TLP is only scheduled when next timer event is RTO. */ + if (icsk->icsk_pending != ICSK_TIME_RETRANS) + return false; + + /* Schedule a loss probe in 2*RTT for SACK capable connections + * in Open state, that are either limited by cwnd or application. + */ + if (sysctl_tcp_early_retrans < 3 || !rtt || !tp->packets_out || + !tcp_is_sack(tp) || inet_csk(sk)->icsk_ca_state != TCP_CA_Open) + return false; + + if ((tp->snd_cwnd > tcp_packets_in_flight(tp)) && + tcp_send_head(sk)) + return false; + + /* Probe timeout is at least 1.5*rtt + TCP_DELACK_MAX to account + * for delayed ack when there's one outstanding packet. + */ + timeout = rtt << 1; + if (tp->packets_out == 1) + timeout = max_t(u32, timeout, + (rtt + (rtt >> 1) + TCP_DELACK_MAX)); + timeout = max_t(u32, timeout, msecs_to_jiffies(10)); + + /* If RTO is shorter, just schedule TLP in its place. */ + tlp_time_stamp = tcp_time_stamp + timeout; + rto_time_stamp = (u32)inet_csk(sk)->icsk_timeout; + if ((s32)(tlp_time_stamp - rto_time_stamp) > 0) { + s32 delta = rto_time_stamp - tcp_time_stamp; + if (delta > 0) + timeout = delta; + } + + inet_csk_reset_xmit_timer(sk, ICSK_TIME_LOSS_PROBE, timeout, + TCP_RTO_MAX); + return true; +} + +/* When probe timeout (PTO) fires, send a new segment if one exists, else + * retransmit the last segment. + */ +void tcp_send_loss_probe(struct sock *sk) +{ + struct sk_buff *skb; + int pcount; + int mss = tcp_current_mss(sk); + int err = -1; + + if (tcp_send_head(sk) != NULL) { + err = tcp_write_xmit(sk, mss, TCP_NAGLE_OFF, 2, GFP_ATOMIC); + goto rearm_timer; + } + + /* Retransmit last segment. */ + skb = tcp_write_queue_tail(sk); + if (WARN_ON(!skb)) + goto rearm_timer; + + pcount = tcp_skb_pcount(skb); + if (WARN_ON(!pcount)) + goto rearm_timer; + + if ((pcount > 1) && (skb->len > (pcount - 1) * mss)) { + if (unlikely(tcp_fragment(sk, skb, (pcount - 1) * mss, mss))) + goto rearm_timer; + skb = tcp_write_queue_tail(sk); + } + + if (WARN_ON(!skb || !tcp_skb_pcount(skb))) + goto rearm_timer; + + /* Probe with zero data doesn't trigger fast recovery. */ + if (skb->len > 0) + err = __tcp_retransmit_skb(sk, skb); + +rearm_timer: + inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, + inet_csk(sk)->icsk_rto, + TCP_RTO_MAX); + + if (likely(!err)) + NET_INC_STATS_BH(sock_net(sk), + LINUX_MIB_TCPLOSSPROBES); + return; } /* Push out any pending frames which were held back due to diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index b78aac30c498..ecd61d54147f 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -342,10 +342,6 @@ void tcp_retransmit_timer(struct sock *sk) struct tcp_sock *tp = tcp_sk(sk); struct inet_connection_sock *icsk = inet_csk(sk); - if (tp->early_retrans_delayed) { - tcp_resume_early_retransmit(sk); - return; - } if (tp->fastopen_rsk) { WARN_ON_ONCE(sk->sk_state != TCP_SYN_RECV && sk->sk_state != TCP_FIN_WAIT1); @@ -495,13 +491,20 @@ void tcp_write_timer_handler(struct sock *sk) } event = icsk->icsk_pending; - icsk->icsk_pending = 0; switch (event) { + case ICSK_TIME_EARLY_RETRANS: + tcp_resume_early_retransmit(sk); + break; + case ICSK_TIME_LOSS_PROBE: + tcp_send_loss_probe(sk); + break; case ICSK_TIME_RETRANS: + icsk->icsk_pending = 0; tcp_retransmit_timer(sk); break; case ICSK_TIME_PROBE0: + icsk->icsk_pending = 0; tcp_probe_timer(sk); break; } -- cgit v1.2.3 From b4a034dab147776eab8eb8b2997ea16ef0e32c17 Mon Sep 17 00:00:00 2001 From: Daniel Hellstrom Date: Mon, 25 Feb 2013 22:51:37 -0800 Subject: Input: add support for GRLIB APBPS2 PS/2 Keyboard/Mouse APBPS2 is a PS/2 core part of GRLIB found in SPARC32/LEON products. Signed-off-by: Daniel Hellstrom Signed-off-by: Dmitry Torokhov --- .../bindings/input/ps2keyb-mouse-apbps2.txt | 16 ++ drivers/input/serio/Kconfig | 10 + drivers/input/serio/Makefile | 1 + drivers/input/serio/apbps2.c | 230 +++++++++++++++++++++ 4 files changed, 257 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt create mode 100644 drivers/input/serio/apbps2.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt b/Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt new file mode 100644 index 000000000000..3029c5694cf6 --- /dev/null +++ b/Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt @@ -0,0 +1,16 @@ +Aeroflex Gaisler APBPS2 PS/2 Core, supporting Keyboard or Mouse. + +The APBPS2 PS/2 core is available in the GRLIB VHDL IP core library. + +Note: In the ordinary environment for the APBPS2 core, a LEON SPARC system, +these properties are built from information in the AMBA plug&play and from +bootloader settings. + +Required properties: + +- name : Should be "GAISLER_APBPS2" or "01_060" +- reg : Address and length of the register set for the device +- interrupts : Interrupt numbers for this device + +For further information look in the documentation for the GLIB IP core library: +http://www.gaisler.com/products/grlib/grip.pdf diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 560c243bfcaf..dbb170916dd1 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -244,4 +244,14 @@ config SERIO_ARC_PS2 To compile this driver as a module, choose M here; the module will be called arc_ps2. +config SERIO_APBPS2 + tristate "GRLIB APBPS2 PS/2 keyboard/mouse controller" + depends on OF + help + Say Y here if you want support for GRLIB APBPS2 peripherals used + to connect to PS/2 keyboard and/or mouse. + + To compile this driver as a module, choose M here: the module will + be called apbps2. + endif diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index 4b0c8f84f1c1..8edb36c2cdb4 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -26,3 +26,4 @@ obj-$(CONFIG_SERIO_AMS_DELTA) += ams_delta_serio.o obj-$(CONFIG_SERIO_XILINX_XPS_PS2) += xilinx_ps2.o obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o obj-$(CONFIG_SERIO_ARC_PS2) += arc_ps2.o +obj-$(CONFIG_SERIO_APBPS2) += apbps2.o diff --git a/drivers/input/serio/apbps2.c b/drivers/input/serio/apbps2.c new file mode 100644 index 000000000000..2c14e6fa64c2 --- /dev/null +++ b/drivers/input/serio/apbps2.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2013 Aeroflex Gaisler + * + * This driver supports the APBPS2 PS/2 core available in the GRLIB + * VHDL IP core library. + * + * Full documentation of the APBPS2 core can be found here: + * http://www.gaisler.com/products/grlib/grip.pdf + * + * See "Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt" for + * information on open firmware properties. + * + * 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. + * + * Contributors: Daniel Hellstrom + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct apbps2_regs { + u32 __iomem data; /* 0x00 */ + u32 __iomem status; /* 0x04 */ + u32 __iomem ctrl; /* 0x08 */ + u32 __iomem reload; /* 0x0c */ +}; + +#define APBPS2_STATUS_DR (1<<0) +#define APBPS2_STATUS_PE (1<<1) +#define APBPS2_STATUS_FE (1<<2) +#define APBPS2_STATUS_KI (1<<3) +#define APBPS2_STATUS_RF (1<<4) +#define APBPS2_STATUS_TF (1<<5) +#define APBPS2_STATUS_TCNT (0x1f<<22) +#define APBPS2_STATUS_RCNT (0x1f<<27) + +#define APBPS2_CTRL_RE (1<<0) +#define APBPS2_CTRL_TE (1<<1) +#define APBPS2_CTRL_RI (1<<2) +#define APBPS2_CTRL_TI (1<<3) + +struct apbps2_priv { + struct serio *io; + struct apbps2_regs *regs; +}; + +static int apbps2_idx; + +static irqreturn_t apbps2_isr(int irq, void *dev_id) +{ + struct apbps2_priv *priv = dev_id; + unsigned long status, data, rxflags; + irqreturn_t ret = IRQ_NONE; + + while ((status = ioread32be(&priv->regs->status)) & APBPS2_STATUS_DR) { + data = ioread32be(&priv->regs->data); + rxflags = (status & APBPS2_STATUS_PE) ? SERIO_PARITY : 0; + rxflags |= (status & APBPS2_STATUS_FE) ? SERIO_FRAME : 0; + + /* clear error bits? */ + if (rxflags) + iowrite32be(0, &priv->regs->status); + + serio_interrupt(priv->io, data, rxflags); + + ret = IRQ_HANDLED; + } + + return ret; +} + +static int apbps2_write(struct serio *io, unsigned char val) +{ + struct apbps2_priv *priv = io->port_data; + unsigned int tleft = 10000; /* timeout in 100ms */ + + /* delay until PS/2 controller has room for more chars */ + while ((ioread32be(&priv->regs->status) & APBPS2_STATUS_TF) && tleft--) + udelay(10); + + if ((ioread32be(&priv->regs->status) & APBPS2_STATUS_TF) == 0) { + iowrite32be(val, &priv->regs->data); + + iowrite32be(APBPS2_CTRL_RE | APBPS2_CTRL_RI | APBPS2_CTRL_TE, + &priv->regs->ctrl); + return 0; + } + + return -ETIMEDOUT; +} + +static int apbps2_open(struct serio *io) +{ + struct apbps2_priv *priv = io->port_data; + int limit; + unsigned long tmp; + + /* clear error flags */ + iowrite32be(0, &priv->regs->status); + + /* Clear old data if available (unlikely) */ + limit = 1024; + while ((ioread32be(&priv->regs->status) & APBPS2_STATUS_DR) && --limit) + tmp = ioread32be(&priv->regs->data); + + /* Enable reciever and it's interrupt */ + iowrite32be(APBPS2_CTRL_RE | APBPS2_CTRL_RI, &priv->regs->ctrl); + + return 0; +} + +static void apbps2_close(struct serio *io) +{ + struct apbps2_priv *priv = io->port_data; + + /* stop interrupts at PS/2 HW level */ + iowrite32be(0, &priv->regs->ctrl); +} + +/* Initialize one APBPS2 PS/2 core */ +static int apbps2_of_probe(struct platform_device *ofdev) +{ + struct apbps2_priv *priv; + int irq, err; + u32 freq_hz; + struct resource *res; + + priv = devm_kzalloc(&ofdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&ofdev->dev, "memory allocation failed\n"); + return -ENOMEM; + } + + /* Find Device Address */ + res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); + priv->regs = devm_request_and_ioremap(&ofdev->dev, res); + if (!priv->regs) { + dev_err(&ofdev->dev, "io-regs mapping failed\n"); + return -EBUSY; + } + + /* Reset hardware, disable interrupt */ + iowrite32be(0, &priv->regs->ctrl); + + /* IRQ */ + irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); + err = devm_request_irq(&ofdev->dev, irq, apbps2_isr, + IRQF_SHARED, "apbps2", priv); + if (err) { + dev_err(&ofdev->dev, "request IRQ%d failed\n", irq); + return err; + } + + /* Get core frequency */ + if (of_property_read_u32(ofdev->dev.of_node, "freq", &freq_hz)) { + dev_err(&ofdev->dev, "unable to get core frequency\n"); + return -EINVAL; + } + + /* Set reload register to core freq in kHz/10 */ + iowrite32be(freq_hz / 10000, &priv->regs->reload); + + priv->io = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!priv->io) + return -ENOMEM; + + priv->io->id.type = SERIO_8042; + priv->io->open = apbps2_open; + priv->io->close = apbps2_close; + priv->io->write = apbps2_write; + priv->io->port_data = priv; + strlcpy(priv->io->name, "APBPS2 PS/2", sizeof(priv->io->name)); + snprintf(priv->io->phys, sizeof(priv->io->phys), + "apbps2_%d", apbps2_idx++); + + dev_info(&ofdev->dev, "irq = %d, base = 0x%p\n", irq, priv->regs); + + serio_register_port(priv->io); + + platform_set_drvdata(ofdev, priv); + + return 0; +} + +static int apbps2_of_remove(struct platform_device *of_dev) +{ + struct apbps2_priv *priv = platform_get_drvdata(of_dev); + + serio_unregister_port(priv->io); + + return 0; +} + +static struct of_device_id apbps2_of_match[] = { + { .name = "GAISLER_APBPS2", }, + { .name = "01_060", }, + {} +}; + +MODULE_DEVICE_TABLE(of, apbps2_of_match); + +static struct platform_driver apbps2_of_driver = { + .driver = { + .name = "grlib-apbps2", + .owner = THIS_MODULE, + .of_match_table = apbps2_of_match, + }, + .probe = apbps2_of_probe, + .remove = apbps2_of_remove, +}; + +module_platform_driver(apbps2_of_driver); + +MODULE_AUTHOR("Aeroflex Gaisler AB."); +MODULE_DESCRIPTION("GRLIB APBPS2 PS/2 serial I/O"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From f8043872e79614ae9c5aaf7804e0b0ccb1932ed0 Mon Sep 17 00:00:00 2001 From: Chris Boot Date: Mon, 11 Mar 2013 21:38:24 -0600 Subject: spi: add driver for BCM2835 The BCM2835 contains two forms of SPI master controller (one known simply as SPI0, and the other known as the "Universal SPI Master", in the auxilliary block) and one form of SPI slave controller. This patch adds support for the SPI0 controller. This driver is taken from Chris Boot's repository at git://github.com/bootc/linux.git rpi-linear as of commit 6de2905 "spi-bcm2708: fix printf with spurious %s". In the first SPI-related commit there, Chris wrote: Thanks to csoutreach / A Robinson for his driver which I used as an inspiration. You can find his version here: http://piface.openlx.org.uk/raspberry-pi-spi-kernel-driver-available-for Changes made during upstreaming: * Renamed bcm2708 to bcm2835 as per upstream naming for this SoC. * Removed support for brcm,realtime property. * Increased transfer timeout to 30 seconds. * Return IRQ_NONE from the IRQ handler if no interrupt was handled. * Disable TA (Transfer Active) and clear FIFOs on a transfer timeout. * Wrote device tree binding documentation. * Request unnamed clock rather than "sys_pclk"; the DT will provide the correct clock. * Assume that tfr->speed_hz and tfr->bits_per_word are always set in bcm2835_spi_start_transfer(), bcm2835_spi_transfer_one(), so no need to check spi->speed_hz or tft->bits_per_word. * Re-ordered probe() to remove the need for temporary variables. * Call clk_disable_unprepare() rather than just clk_unprepare() on probe() failure. * Don't use devm_request_irq(), to ensure that the IRQ doesn't fire after we've torn down the device, but not unhooked the IRQ. * Moved probe()'s call to clk_prepare_enable() so we can be sure the clock is enabled if the IRQ handler fires immediately. * Remove redundant checks from bcm2835_spi_check_transfer() and bcm2835_spi_setup(). * Re-ordered IRQ handler to check for RXR before DONE. Added comments to ISR. * Removed empty prepare/unprepare implementations. * Removed use of devinit/devexit. * Added BCM2835_ prefix to defines. Signed-off-by: Chris Boot Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/brcm,bcm2835-spi.txt | 22 + drivers/spi/Kconfig | 11 + drivers/spi/Makefile | 1 + drivers/spi/spi-bcm2835.c | 456 +++++++++++++++++++++ 4 files changed, 490 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/brcm,bcm2835-spi.txt create mode 100644 drivers/spi/spi-bcm2835.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/spi/brcm,bcm2835-spi.txt b/Documentation/devicetree/bindings/spi/brcm,bcm2835-spi.txt new file mode 100644 index 000000000000..8bf89c643640 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/brcm,bcm2835-spi.txt @@ -0,0 +1,22 @@ +Broadcom BCM2835 SPI0 controller + +The BCM2835 contains two forms of SPI master controller, one known simply as +SPI0, and the other known as the "Universal SPI Master"; part of the +auxilliary block. This binding applies to the SPI0 controller. + +Required properties: +- compatible: Should be "brcm,bcm2835-spi". +- reg: Should contain register location and length. +- interrupts: Should contain interrupt. +- clocks: The clock feeding the SPI controller. + +Example: + +spi@20204000 { + compatible = "brcm,bcm2835-spi"; + reg = <0x7e204000 0x1000>; + interrupts = <2 22>; + clocks = <&clk_spi>; + #address-cells = <1>; + #size-cells = <0>; +}; diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index f80eee74a311..32b85d43bbe2 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -74,6 +74,17 @@ config SPI_ATMEL This selects a driver for the Atmel SPI Controller, present on many AT32 (AVR32) and AT91 (ARM) chips. +config SPI_BCM2835 + tristate "BCM2835 SPI controller" + depends on ARCH_BCM2835 + help + This selects a driver for the Broadcom BCM2835 SPI master. + + The BCM2835 contains two types of SPI master controller; the + "universal SPI master", and the regular SPI controller. This driver + is for the regular SPI controller. Slave mode operation is not also + not supported. + config SPI_BFIN5XX tristate "SPI controller driver for ADI Blackfin5xx" depends on BLACKFIN diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index e53c30941340..3ce1d082ce79 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_SPI_ALTERA) += spi-altera.o obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o obj-$(CONFIG_SPI_ATH79) += spi-ath79.o obj-$(CONFIG_SPI_AU1550) += spi-au1550.o +obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c new file mode 100644 index 000000000000..346601e2461d --- /dev/null +++ b/drivers/spi/spi-bcm2835.c @@ -0,0 +1,456 @@ +/* + * Driver for Broadcom BCM2835 SPI Controllers + * + * Copyright (C) 2012 Chris Boot + * Copyright (C) 2013 Stephen Warren + * + * This driver is inspired by: + * spi-ath79.c, Copyright (C) 2009-2011 Gabor Juhos + * spi-atmel.c, Copyright (C) 2006 Atmel Corporation + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SPI register offsets */ +#define BCM2835_SPI_CS 0x00 +#define BCM2835_SPI_FIFO 0x04 +#define BCM2835_SPI_CLK 0x08 +#define BCM2835_SPI_DLEN 0x0c +#define BCM2835_SPI_LTOH 0x10 +#define BCM2835_SPI_DC 0x14 + +/* Bitfields in CS */ +#define BCM2835_SPI_CS_LEN_LONG 0x02000000 +#define BCM2835_SPI_CS_DMA_LEN 0x01000000 +#define BCM2835_SPI_CS_CSPOL2 0x00800000 +#define BCM2835_SPI_CS_CSPOL1 0x00400000 +#define BCM2835_SPI_CS_CSPOL0 0x00200000 +#define BCM2835_SPI_CS_RXF 0x00100000 +#define BCM2835_SPI_CS_RXR 0x00080000 +#define BCM2835_SPI_CS_TXD 0x00040000 +#define BCM2835_SPI_CS_RXD 0x00020000 +#define BCM2835_SPI_CS_DONE 0x00010000 +#define BCM2835_SPI_CS_LEN 0x00002000 +#define BCM2835_SPI_CS_REN 0x00001000 +#define BCM2835_SPI_CS_ADCS 0x00000800 +#define BCM2835_SPI_CS_INTR 0x00000400 +#define BCM2835_SPI_CS_INTD 0x00000200 +#define BCM2835_SPI_CS_DMAEN 0x00000100 +#define BCM2835_SPI_CS_TA 0x00000080 +#define BCM2835_SPI_CS_CSPOL 0x00000040 +#define BCM2835_SPI_CS_CLEAR_RX 0x00000020 +#define BCM2835_SPI_CS_CLEAR_TX 0x00000010 +#define BCM2835_SPI_CS_CPOL 0x00000008 +#define BCM2835_SPI_CS_CPHA 0x00000004 +#define BCM2835_SPI_CS_CS_10 0x00000002 +#define BCM2835_SPI_CS_CS_01 0x00000001 + +#define BCM2835_SPI_TIMEOUT_MS 30000 +#define BCM2835_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS) + +#define DRV_NAME "spi-bcm2835" + +struct bcm2835_spi { + void __iomem *regs; + struct clk *clk; + int irq; + struct completion done; + const u8 *tx_buf; + u8 *rx_buf; + int len; +}; + +static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg) +{ + return readl(bs->regs + reg); +} + +static inline void bcm2835_wr(struct bcm2835_spi *bs, unsigned reg, u32 val) +{ + writel(val, bs->regs + reg); +} + +static inline void bcm2835_rd_fifo(struct bcm2835_spi *bs, int len) +{ + u8 byte; + + while (len--) { + byte = bcm2835_rd(bs, BCM2835_SPI_FIFO); + if (bs->rx_buf) + *bs->rx_buf++ = byte; + } +} + +static inline void bcm2835_wr_fifo(struct bcm2835_spi *bs, int len) +{ + u8 byte; + + if (len > bs->len) + len = bs->len; + + while (len--) { + byte = bs->tx_buf ? *bs->tx_buf++ : 0; + bcm2835_wr(bs, BCM2835_SPI_FIFO, byte); + bs->len--; + } +} + +static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id) +{ + struct spi_master *master = dev_id; + struct bcm2835_spi *bs = spi_master_get_devdata(master); + u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); + + /* + * RXR - RX needs Reading. This means 12 (or more) bytes have been + * transmitted and hence 12 (or more) bytes have been received. + * + * The FIFO is 16-bytes deep. We check for this interrupt to keep the + * FIFO full; we have a 4-byte-time buffer for IRQ latency. We check + * this before DONE (TX empty) just in case we delayed processing this + * interrupt for some reason. + * + * We only check for this case if we have more bytes to TX; at the end + * of the transfer, we ignore this pipelining optimization, and let + * bcm2835_spi_finish_transfer() drain the RX FIFO. + */ + if (bs->len && (cs & BCM2835_SPI_CS_RXR)) { + /* Read 12 bytes of data */ + bcm2835_rd_fifo(bs, 12); + + /* Write up to 12 bytes */ + bcm2835_wr_fifo(bs, 12); + + /* + * We must have written something to the TX FIFO due to the + * bs->len check above, so cannot be DONE. Hence, return + * early. Note that DONE could also be set if we serviced an + * RXR interrupt really late. + */ + return IRQ_HANDLED; + } + + /* + * DONE - TX empty. This occurs when we first enable the transfer + * since we do not pre-fill the TX FIFO. At any other time, given that + * we refill the TX FIFO above based on RXR, and hence ignore DONE if + * RXR is set, DONE really does mean end-of-transfer. + */ + if (cs & BCM2835_SPI_CS_DONE) { + if (bs->len) { /* First interrupt in a transfer */ + bcm2835_wr_fifo(bs, 16); + } else { /* Transfer complete */ + /* Disable SPI interrupts */ + cs &= ~(BCM2835_SPI_CS_INTR | BCM2835_SPI_CS_INTD); + bcm2835_wr(bs, BCM2835_SPI_CS, cs); + + /* + * Wake up bcm2835_spi_transfer_one(), which will call + * bcm2835_spi_finish_transfer(), to drain the RX FIFO. + */ + complete(&bs->done); + } + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int bcm2835_spi_check_transfer(struct spi_device *spi, + struct spi_transfer *tfr) +{ + /* tfr==NULL when called from bcm2835_spi_setup() */ + u32 bpw = tfr ? tfr->bits_per_word : spi->bits_per_word; + + switch (bpw) { + case 8: + break; + default: + dev_err(&spi->dev, "unsupported bits_per_word=%d\n", bpw); + return -EINVAL; + } + + return 0; +} + +static int bcm2835_spi_start_transfer(struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct bcm2835_spi *bs = spi_master_get_devdata(spi->master); + unsigned long spi_hz, clk_hz, cdiv; + u32 cs = BCM2835_SPI_CS_INTR | BCM2835_SPI_CS_INTD | BCM2835_SPI_CS_TA; + + spi_hz = tfr->speed_hz; + clk_hz = clk_get_rate(bs->clk); + + if (spi_hz >= clk_hz / 2) { + cdiv = 2; /* clk_hz/2 is the fastest we can go */ + } else if (spi_hz) { + /* CDIV must be a power of two */ + cdiv = roundup_pow_of_two(DIV_ROUND_UP(clk_hz, spi_hz)); + + if (cdiv >= 65536) + cdiv = 0; /* 0 is the slowest we can go */ + } else + cdiv = 0; /* 0 is the slowest we can go */ + + if (spi->mode & SPI_CPOL) + cs |= BCM2835_SPI_CS_CPOL; + if (spi->mode & SPI_CPHA) + cs |= BCM2835_SPI_CS_CPHA; + + if (!(spi->mode & SPI_NO_CS)) { + if (spi->mode & SPI_CS_HIGH) { + cs |= BCM2835_SPI_CS_CSPOL; + cs |= BCM2835_SPI_CS_CSPOL0 << spi->chip_select; + } + + cs |= spi->chip_select; + } + + INIT_COMPLETION(bs->done); + bs->tx_buf = tfr->tx_buf; + bs->rx_buf = tfr->rx_buf; + bs->len = tfr->len; + + bcm2835_wr(bs, BCM2835_SPI_CLK, cdiv); + /* + * Enable the HW block. This will immediately trigger a DONE (TX + * empty) interrupt, upon which we will fill the TX FIFO with the + * first TX bytes. Pre-filling the TX FIFO here to avoid the + * interrupt doesn't work:-( + */ + bcm2835_wr(bs, BCM2835_SPI_CS, cs); + + return 0; +} + +static int bcm2835_spi_finish_transfer(struct spi_device *spi, + struct spi_transfer *tfr, bool cs_change) +{ + struct bcm2835_spi *bs = spi_master_get_devdata(spi->master); + u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); + + /* Drain RX FIFO */ + while (cs & BCM2835_SPI_CS_RXD) { + bcm2835_rd_fifo(bs, 1); + cs = bcm2835_rd(bs, BCM2835_SPI_CS); + } + + if (tfr->delay_usecs) + udelay(tfr->delay_usecs); + + if (cs_change) + /* Clear TA flag */ + bcm2835_wr(bs, BCM2835_SPI_CS, cs & ~BCM2835_SPI_CS_TA); + + return 0; +} + +static int bcm2835_spi_setup(struct spi_device *spi) +{ + int ret; + + ret = bcm2835_spi_check_transfer(spi, NULL); + if (ret) { + dev_err(&spi->dev, "setup: invalid message\n"); + return ret; + } + + return 0; +} + +static int bcm2835_spi_transfer_one(struct spi_master *master, + struct spi_message *mesg) +{ + struct bcm2835_spi *bs = spi_master_get_devdata(master); + struct spi_transfer *tfr; + struct spi_device *spi = mesg->spi; + int err = 0; + unsigned int timeout; + bool cs_change; + + list_for_each_entry(tfr, &mesg->transfers, transfer_list) { + err = bcm2835_spi_check_transfer(spi, tfr); + if (err) + goto out; + + err = bcm2835_spi_start_transfer(spi, tfr); + if (err) + goto out; + + timeout = wait_for_completion_timeout(&bs->done, + msecs_to_jiffies(BCM2835_SPI_TIMEOUT_MS)); + if (!timeout) { + err = -ETIMEDOUT; + goto out; + } + + cs_change = tfr->cs_change || + list_is_last(&tfr->transfer_list, &mesg->transfers); + + err = bcm2835_spi_finish_transfer(spi, tfr, cs_change); + if (err) + goto out; + + mesg->actual_length += (tfr->len - bs->len); + } + +out: + /* Clear FIFOs, and disable the HW block */ + bcm2835_wr(bs, BCM2835_SPI_CS, + BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); + mesg->status = err; + spi_finalize_current_message(master); + + return 0; +} + +static int bcm2835_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct bcm2835_spi *bs; + struct resource *res; + int err; + + master = spi_alloc_master(&pdev->dev, sizeof(*bs)); + if (!master) { + dev_err(&pdev->dev, "spi_alloc_master() failed\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, master); + + master->mode_bits = BCM2835_SPI_MODE_BITS; + master->bus_num = -1; + master->num_chipselect = 3; + master->setup = bcm2835_spi_setup; + master->transfer_one_message = bcm2835_spi_transfer_one; + master->dev.of_node = pdev->dev.of_node; + + bs = spi_master_get_devdata(master); + + init_completion(&bs->done); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "could not get memory resource\n"); + err = -ENODEV; + goto out_master_put; + } + + bs->regs = devm_request_and_ioremap(&pdev->dev, res); + if (!bs->regs) { + dev_err(&pdev->dev, "could not request/map memory region\n"); + err = -ENODEV; + goto out_master_put; + } + + bs->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(bs->clk)) { + err = PTR_ERR(bs->clk); + dev_err(&pdev->dev, "could not get clk: %d\n", err); + goto out_master_put; + } + + bs->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + if (bs->irq <= 0) { + dev_err(&pdev->dev, "could not get IRQ: %d\n", bs->irq); + err = bs->irq ? bs->irq : -ENODEV; + goto out_master_put; + } + + clk_prepare_enable(bs->clk); + + err = request_irq(bs->irq, bcm2835_spi_interrupt, 0, + dev_name(&pdev->dev), master); + if (err) { + dev_err(&pdev->dev, "could not request IRQ: %d\n", err); + goto out_clk_disable; + } + + /* initialise the hardware */ + bcm2835_wr(bs, BCM2835_SPI_CS, + BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); + + err = spi_register_master(master); + if (err) { + dev_err(&pdev->dev, "could not register SPI master: %d\n", err); + goto out_free_irq; + } + + return 0; + +out_free_irq: + free_irq(bs->irq, master); +out_clk_disable: + clk_disable_unprepare(bs->clk); +out_master_put: + spi_master_put(master); + return err; +} + +static int bcm2835_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct bcm2835_spi *bs = spi_master_get_devdata(master); + + free_irq(bs->irq, master); + spi_unregister_master(master); + + /* Clear FIFOs, and disable the HW block */ + bcm2835_wr(bs, BCM2835_SPI_CS, + BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); + + clk_disable_unprepare(bs->clk); + spi_master_put(master); + + return 0; +} + +static const struct of_device_id bcm2835_spi_match[] = { + { .compatible = "brcm,bcm2835-spi", }, + {} +}; +MODULE_DEVICE_TABLE(of, bcm2835_spi_match); + +static struct platform_driver bcm2835_spi_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = bcm2835_spi_match, + }, + .probe = bcm2835_spi_probe, + .remove = bcm2835_spi_remove, +}; +module_platform_driver(bcm2835_spi_driver); + +MODULE_DESCRIPTION("SPI controller driver for Broadcom BCM2835"); +MODULE_AUTHOR("Chris Boot "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From d7eeac1913ff86a17f891cb4b73f03d4b94907d0 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Tue, 12 Mar 2013 15:35:59 -0700 Subject: cgroup: hold cgroup_mutex before calling css_offline() cpuset no longer nests cgroup_mutex inside cpu_hotplug lock, so we don't have to release cgroup_mutex before calling css_offline(). Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- Documentation/cgroups/cgroups.txt | 1 + kernel/cgroup.c | 11 +---------- 2 files changed, 2 insertions(+), 10 deletions(-) (limited to 'Documentation') diff --git a/Documentation/cgroups/cgroups.txt b/Documentation/cgroups/cgroups.txt index bcf1a00b06a1..0028e888828c 100644 --- a/Documentation/cgroups/cgroups.txt +++ b/Documentation/cgroups/cgroups.txt @@ -580,6 +580,7 @@ propagation along the hierarchy. See the comment on cgroup_for_each_descendant_pre() for details. void css_offline(struct cgroup *cgrp); +(cgroup_mutex held by caller) This is the counterpart of css_online() and called iff css_online() has succeeded on @cgrp. This signifies the beginning of the end of diff --git a/kernel/cgroup.c b/kernel/cgroup.c index fd0b056d8da5..49297cbc134d 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -4169,17 +4169,8 @@ static void offline_css(struct cgroup_subsys *ss, struct cgroup *cgrp) if (!(css->flags & CSS_ONLINE)) return; - /* - * css_offline() should be called with cgroup_mutex unlocked. See - * 3fa59dfbc3 ("cgroup: fix potential deadlock in pre_destroy") for - * details. This temporary unlocking should go away once - * cgroup_mutex is unexported from controllers. - */ - if (ss->css_offline) { - mutex_unlock(&cgroup_mutex); + if (ss->css_offline) ss->css_offline(cgrp); - mutex_lock(&cgroup_mutex); - } cgrp->subsys[ss->subsys_id]->flags &= ~CSS_ONLINE; } -- cgit v1.2.3 From 470d147428563aba9c2eb7c019383335249c6110 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Mon, 11 Mar 2013 23:16:34 +0000 Subject: documentation: dt: bindings: cpsw: cleanup documentation Move all the slave note properties to separate section to reduce the confusion between slave note properties and cpsw node properties Signed-off-by: Mugunthan V N Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/cpsw.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt index ecfdf756d10f..8e49c4200928 100644 --- a/Documentation/devicetree/bindings/net/cpsw.txt +++ b/Documentation/devicetree/bindings/net/cpsw.txt @@ -18,13 +18,18 @@ Required properties: - cpts_active_slave : Specifies the slave to use for time stamping - cpts_clock_mult : Numerator to convert input clock ticks into nanoseconds - cpts_clock_shift : Denominator to convert input clock ticks into nanoseconds -- phy_id : Specifies slave phy id -- mac-address : Specifies slave MAC address Optional properties: - ti,hwmods : Must be "cpgmac0" - no_bd_ram : Must be 0 or 1 - dual_emac : Specifies Switch to act as Dual EMAC + +Slave Properties: +Required properties: +- phy_id : Specifies slave phy id +- mac-address : Specifies slave MAC address + +Optional properties: - dual_emac_res_vlan : Specifies VID to be used to segregate the ports Note: "ti,hwmods" field is used to fetch the base address and irq -- cgit v1.2.3 From e86ac13b031cf71d8f40ff513e627aac80e6b765 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Mon, 11 Mar 2013 23:16:35 +0000 Subject: drivers: net: ethernet: cpsw: change cpts_active_slave to active_slave Change cpts_active_slave to active_slave so that the same DT property can be used to ethtool and SIOCGMIIPHY. CC: Richard Cochran Signed-off-by: Mugunthan V N Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/cpsw.txt | 7 ++++--- arch/arm/boot/dts/am33xx.dtsi | 2 +- drivers/net/ethernet/ti/cpsw.c | 10 +++++----- include/linux/platform_data/cpsw.h | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt index 8e49c4200928..4f2ca6b4a182 100644 --- a/Documentation/devicetree/bindings/net/cpsw.txt +++ b/Documentation/devicetree/bindings/net/cpsw.txt @@ -15,7 +15,8 @@ Required properties: - mac_control : Specifies Default MAC control register content for the specific platform - slaves : Specifies number for slaves -- cpts_active_slave : Specifies the slave to use for time stamping +- active_slave : Specifies the slave to use for time stamping, + ethtool and SIOCGMIIPHY - cpts_clock_mult : Numerator to convert input clock ticks into nanoseconds - cpts_clock_shift : Denominator to convert input clock ticks into nanoseconds @@ -52,7 +53,7 @@ Examples: rx_descs = <64>; mac_control = <0x20>; slaves = <2>; - cpts_active_slave = <0>; + active_slave = <0>; cpts_clock_mult = <0x80000000>; cpts_clock_shift = <29>; cpsw_emac0: slave@0 { @@ -78,7 +79,7 @@ Examples: rx_descs = <64>; mac_control = <0x20>; slaves = <2>; - cpts_active_slave = <0>; + active_slave = <0>; cpts_clock_mult = <0x80000000>; cpts_clock_shift = <29>; cpsw_emac0: slave@0 { diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index 0957645b73af..91fe4f148f80 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -349,7 +349,7 @@ rx_descs = <64>; mac_control = <0x20>; slaves = <2>; - cpts_active_slave = <0>; + active_slave = <0>; cpts_clock_mult = <0x80000000>; cpts_clock_shift = <29>; reg = <0x4a100000 0x800 diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 01ffbc486982..98aa17a9516a 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -942,7 +942,7 @@ static void cpsw_ndo_change_rx_flags(struct net_device *ndev, int flags) static void cpsw_hwtstamp_v1(struct cpsw_priv *priv) { - struct cpsw_slave *slave = &priv->slaves[priv->data.cpts_active_slave]; + struct cpsw_slave *slave = &priv->slaves[priv->data.active_slave]; u32 ts_en, seq_id; if (!priv->cpts->tx_enable && !priv->cpts->rx_enable) { @@ -971,7 +971,7 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv) if (priv->data.dual_emac) slave = &priv->slaves[priv->emac_port]; else - slave = &priv->slaves[priv->data.cpts_active_slave]; + slave = &priv->slaves[priv->data.active_slave]; ctrl = slave_read(slave, CPSW2_CONTROL); ctrl &= ~CTRL_ALL_TS_MASK; @@ -1282,12 +1282,12 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->slaves = prop; - if (of_property_read_u32(node, "cpts_active_slave", &prop)) { - pr_err("Missing cpts_active_slave property in the DT.\n"); + if (of_property_read_u32(node, "active_slave", &prop)) { + pr_err("Missing active_slave property in the DT.\n"); ret = -EINVAL; goto error_ret; } - data->cpts_active_slave = prop; + data->active_slave = prop; if (of_property_read_u32(node, "cpts_clock_mult", &prop)) { pr_err("Missing cpts_clock_mult property in the DT.\n"); diff --git a/include/linux/platform_data/cpsw.h b/include/linux/platform_data/cpsw.h index 798fb80b024b..bb3cd58d71e3 100644 --- a/include/linux/platform_data/cpsw.h +++ b/include/linux/platform_data/cpsw.h @@ -30,7 +30,7 @@ struct cpsw_platform_data { u32 channels; /* number of cpdma channels (symmetric) */ u32 slaves; /* number of slave cpgmac ports */ struct cpsw_slave_data *slave_data; - u32 cpts_active_slave; /* time stamping slave */ + u32 active_slave; /* time stamping, ethtool and SIOCGMIIPHY slave */ u32 cpts_clock_mult; /* convert input clock ticks to nanoseconds */ u32 cpts_clock_shift; /* convert input clock ticks to nanoseconds */ u32 ale_entries; /* ale table size */ -- cgit v1.2.3 From d353d8d4d9f0184ac43a90c6e04b593c33bd28ea Mon Sep 17 00:00:00 2001 From: Martin Hundebøll Date: Fri, 25 Jan 2013 11:12:38 +0100 Subject: batman-adv: network coding - add the initial infrastructure code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Network coding exploits the 802.11 shared medium to allow multiple packets to be sent in a single transmission. In brief, a relay can XOR two packets, and send the coded packet to two destinations. The receivers can decode one of the original packets by XOR'ing the coded packet with the other original packet. This will lead to increased throughput in topologies where two packets cross one relay. In a simple topology with three nodes, it takes four transmissions without network coding to get one packet from Node A to Node B and one from Node B to Node A: 1. Node A ---- p1 ---> Node R Node B 2. Node A Node R <--- p2 ---- Node B 3. Node A <--- p2 ---- Node R Node B 4. Node A Node R ---- p1 ---> Node B With network coding, the relay only needs one transmission, which saves us one slot of valuable airtime: 1. Node A ---- p1 ---> Node R Node B 2. Node A Node R <--- p2 ---- Node B 3. Node A <- p1 x p2 - Node R - p1 x p2 -> Node B The same principle holds for a topology including five nodes. Here the packets from Node A and Node B are overheard by Node C and Node D, respectively. This allows Node R to send a network coded packet to save one transmission: Node A Node B | \ / | | p1 p2 | | \ / | p1 > Node R < p2 | | | / \ | | p1 x p2 p1 x p2 | v / \ v / \ Node C < > Node D More information is available on the open-mesh.org wiki[1]. This patch adds the initial code to support network coding in batman-adv. It sets up a worker thread to do house keeping and adds a sysfs file to enable/disable network coding. The feature is disabled by default, as it requires a wifi-driver with working promiscuous mode, and also because it adds a small delay at each hop. [1] http://www.open-mesh.org/projects/batman-adv/wiki/Catwoman Signed-off-by: Martin Hundebøll Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- Documentation/ABI/testing/sysfs-class-net-mesh | 8 +++ net/batman-adv/Kconfig | 14 +++++ net/batman-adv/Makefile | 1 + net/batman-adv/main.c | 6 ++ net/batman-adv/main.h | 4 +- net/batman-adv/network-coding.c | 81 ++++++++++++++++++++++++++ net/batman-adv/network-coding.h | 48 +++++++++++++++ net/batman-adv/soft-interface.c | 3 + net/batman-adv/sysfs.c | 6 ++ net/batman-adv/types.h | 14 +++++ 10 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 net/batman-adv/network-coding.c create mode 100644 net/batman-adv/network-coding.h (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-class-net-mesh b/Documentation/ABI/testing/sysfs-class-net-mesh index bc41da61608d..bdcd8b4e38f2 100644 --- a/Documentation/ABI/testing/sysfs-class-net-mesh +++ b/Documentation/ABI/testing/sysfs-class-net-mesh @@ -67,6 +67,14 @@ Description: Defines the penalty which will be applied to an originator message's tq-field on every hop. +What: /sys/class/net//mesh/network_coding +Date: Nov 2012 +Contact: Martin Hundeboll +Description: + Controls whether Network Coding (using some magic + to send fewer wifi packets but still the same + content) is enabled or not. + What: /sys/class/net//mesh/orig_interval Date: May 2010 Contact: Marek Lindner diff --git a/net/batman-adv/Kconfig b/net/batman-adv/Kconfig index 8d8afb134b3a..fa780b76630e 100644 --- a/net/batman-adv/Kconfig +++ b/net/batman-adv/Kconfig @@ -36,6 +36,20 @@ config BATMAN_ADV_DAT mesh networks. If you think that your network does not need this option you can safely remove it and save some space. +config BATMAN_ADV_NC + bool "Network Coding" + depends on BATMAN_ADV + default n + help + This option enables network coding, a mechanism that aims to + increase the overall network throughput by fusing multiple + packets in one transmission. + Note that interfaces controlled by batman-adv must be manually + configured to have promiscuous mode enabled in order to make + network coding work. + If you think that your network does not need this feature you + can safely disable it and save some space. + config BATMAN_ADV_DEBUG bool "B.A.T.M.A.N. debugging" depends on BATMAN_ADV diff --git a/net/batman-adv/Makefile b/net/batman-adv/Makefile index e45e3b4e32e3..4b8f192a9e43 100644 --- a/net/batman-adv/Makefile +++ b/net/batman-adv/Makefile @@ -30,6 +30,7 @@ batman-adv-y += hard-interface.o batman-adv-y += hash.o batman-adv-y += icmp_socket.o batman-adv-y += main.o +batman-adv-$(CONFIG_BATMAN_ADV_NC) += network-coding.o batman-adv-y += originator.o batman-adv-y += ring_buffer.o batman-adv-y += routing.o diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 0488d70c8c35..0495a7dc7505 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -35,6 +35,7 @@ #include "vis.h" #include "hash.h" #include "bat_algo.h" +#include "network-coding.h" /* List manipulations on hardif_list have to be rtnl_lock()'ed, @@ -135,6 +136,10 @@ int batadv_mesh_init(struct net_device *soft_iface) if (ret < 0) goto err; + ret = batadv_nc_init(bat_priv); + if (ret < 0) + goto err; + atomic_set(&bat_priv->gw.reselect, 0); atomic_set(&bat_priv->mesh_state, BATADV_MESH_ACTIVE); @@ -157,6 +162,7 @@ void batadv_mesh_free(struct net_device *soft_iface) batadv_gw_node_purge(bat_priv); batadv_originator_free(bat_priv); + batadv_nc_free(bat_priv); batadv_tt_free(bat_priv); diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index ced08b936a96..59ba2ff8e252 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -185,6 +185,7 @@ __be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr); * @BATADV_DBG_TT: translation table messages * @BATADV_DBG_BLA: bridge loop avoidance messages * @BATADV_DBG_DAT: ARP snooping and DAT related messages + * @BATADV_DBG_NC: network coding related messages * @BATADV_DBG_ALL: the union of all the above log levels */ enum batadv_dbg_level { @@ -193,7 +194,8 @@ enum batadv_dbg_level { BATADV_DBG_TT = BIT(2), BATADV_DBG_BLA = BIT(3), BATADV_DBG_DAT = BIT(4), - BATADV_DBG_ALL = 31, + BATADV_DBG_NC = BIT(5), + BATADV_DBG_ALL = 63, }; #ifdef CONFIG_BATMAN_ADV_DEBUG diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c new file mode 100644 index 000000000000..9c2d54bdd2ce --- /dev/null +++ b/net/batman-adv/network-coding.c @@ -0,0 +1,81 @@ +/* Copyright (C) 2012-2013 B.A.T.M.A.N. contributors: + * + * Martin Hundebøll, Jeppe Ledet-Pedersen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "main.h" +#include "network-coding.h" + +static void batadv_nc_worker(struct work_struct *work); + +/** + * batadv_nc_start_timer - initialise the nc periodic worker + * @bat_priv: the bat priv with all the soft interface information + */ +static void batadv_nc_start_timer(struct batadv_priv *bat_priv) +{ + queue_delayed_work(batadv_event_workqueue, &bat_priv->nc.work, + msecs_to_jiffies(10)); +} + +/** + * batadv_nc_init - initialise coding hash table and start house keeping + * @bat_priv: the bat priv with all the soft interface information + */ +int batadv_nc_init(struct batadv_priv *bat_priv) +{ + INIT_DELAYED_WORK(&bat_priv->nc.work, batadv_nc_worker); + batadv_nc_start_timer(bat_priv); + + return 0; +} + +/** + * batadv_nc_init_bat_priv - initialise the nc specific bat_priv variables + * @bat_priv: the bat priv with all the soft interface information + */ +void batadv_nc_init_bat_priv(struct batadv_priv *bat_priv) +{ + atomic_set(&bat_priv->network_coding, 1); +} + +/** + * batadv_nc_worker - periodic task for house keeping related to network coding + * @work: kernel work struct + */ +static void batadv_nc_worker(struct work_struct *work) +{ + struct delayed_work *delayed_work; + struct batadv_priv_nc *priv_nc; + struct batadv_priv *bat_priv; + + delayed_work = container_of(work, struct delayed_work, work); + priv_nc = container_of(delayed_work, struct batadv_priv_nc, work); + bat_priv = container_of(priv_nc, struct batadv_priv, nc); + + /* Schedule a new check */ + batadv_nc_start_timer(bat_priv); +} + +/** + * batadv_nc_free - clean up network coding memory + * @bat_priv: the bat priv with all the soft interface information + */ +void batadv_nc_free(struct batadv_priv *bat_priv) +{ + cancel_delayed_work_sync(&bat_priv->nc.work); +} diff --git a/net/batman-adv/network-coding.h b/net/batman-adv/network-coding.h new file mode 100644 index 000000000000..7483cba4b5d4 --- /dev/null +++ b/net/batman-adv/network-coding.h @@ -0,0 +1,48 @@ +/* Copyright (C) 2012-2013 B.A.T.M.A.N. contributors: + * + * Martin Hundebøll, Jeppe Ledet-Pedersen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _NET_BATMAN_ADV_NETWORK_CODING_H_ +#define _NET_BATMAN_ADV_NETWORK_CODING_H_ + +#ifdef CONFIG_BATMAN_ADV_NC + +int batadv_nc_init(struct batadv_priv *bat_priv); +void batadv_nc_free(struct batadv_priv *bat_priv); +void batadv_nc_init_bat_priv(struct batadv_priv *bat_priv); + +#else /* ifdef CONFIG_BATMAN_ADV_NC */ + +static inline int batadv_nc_init(struct batadv_priv *bat_priv) +{ + return 0; +} + +static inline void batadv_nc_free(struct batadv_priv *bat_priv) +{ + return; +} + +static inline void batadv_nc_init_bat_priv(struct batadv_priv *bat_priv) +{ + return; +} + +#endif /* ifdef CONFIG_BATMAN_ADV_NC */ + +#endif /* _NET_BATMAN_ADV_NETWORK_CODING_H_ */ diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 2711e870f557..7188e07dfc6f 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -37,6 +37,7 @@ #include #include "unicast.h" #include "bridge_loop_avoidance.h" +#include "network-coding.h" static int batadv_get_settings(struct net_device *dev, struct ethtool_cmd *cmd); @@ -544,6 +545,8 @@ struct net_device *batadv_softif_create(const char *name) if (ret < 0) goto unreg_soft_iface; + batadv_nc_init_bat_priv(bat_priv); + ret = batadv_sysfs_add_meshif(soft_iface); if (ret < 0) goto unreg_soft_iface; diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c index 6a44fed12837..ce39f62f751e 100644 --- a/net/batman-adv/sysfs.c +++ b/net/batman-adv/sysfs.c @@ -442,6 +442,9 @@ static BATADV_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, batadv_show_gw_bwidth, #ifdef CONFIG_BATMAN_ADV_DEBUG BATADV_ATTR_SIF_UINT(log_level, S_IRUGO | S_IWUSR, 0, BATADV_DBG_ALL, NULL); #endif +#ifdef CONFIG_BATMAN_ADV_NC +BATADV_ATTR_SIF_BOOL(network_coding, S_IRUGO | S_IWUSR, NULL); +#endif static struct batadv_attribute *batadv_mesh_attrs[] = { &batadv_attr_aggregated_ogms, @@ -463,6 +466,9 @@ static struct batadv_attribute *batadv_mesh_attrs[] = { &batadv_attr_gw_bandwidth, #ifdef CONFIG_BATMAN_ADV_DEBUG &batadv_attr_log_level, +#endif +#ifdef CONFIG_BATMAN_ADV_NC + &batadv_attr_network_coding, #endif NULL, }; diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 4cd87a0b5b80..83bfe7c38f81 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -427,6 +427,14 @@ struct batadv_priv_dat { }; #endif +/** + * struct batadv_priv_nc - per mesh interface network coding private data + * @work: work queue callback item for cleanup + */ +struct batadv_priv_nc { + struct delayed_work work; +}; + /** * struct batadv_priv - per mesh interface data * @mesh_state: current status of the mesh (inactive/active/deactivating) @@ -470,6 +478,8 @@ struct batadv_priv_dat { * @tt: translation table data * @vis: vis data * @dat: distributed arp table data + * @network_coding: bool indicating whether network coding is enabled + * @batadv_priv_nc: network coding data */ struct batadv_priv { atomic_t mesh_state; @@ -522,6 +532,10 @@ struct batadv_priv { #ifdef CONFIG_BATMAN_ADV_DAT struct batadv_priv_dat dat; #endif +#ifdef CONFIG_BATMAN_ADV_NC + atomic_t network_coding; + struct batadv_priv_nc nc; +#endif /* CONFIG_BATMAN_ADV_NC */ }; /** -- cgit v1.2.3 From 8936aa31cd5fd0bea828416a3c07efd303269a45 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 10 Mar 2013 12:32:44 +0100 Subject: HID: roccat: add support for Roccat Kone Pure gaming mouse Userland-tools can already be found at http://sourceforge.net/projects/roccat Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- .../ABI/testing/sysfs-driver-hid-roccat-konepure | 105 +++++++ drivers/hid/Makefile | 4 +- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-roccat-konepure.c | 304 +++++++++++++++++++++ drivers/hid/hid-roccat-konepure.h | 72 +++++ 6 files changed, 485 insertions(+), 2 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-driver-hid-roccat-konepure create mode 100644 drivers/hid/hid-roccat-konepure.c create mode 100644 drivers/hid/hid-roccat-konepure.h (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-konepure b/Documentation/ABI/testing/sysfs-driver-hid-roccat-konepure new file mode 100644 index 000000000000..41a9b7fbfc79 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-konepure @@ -0,0 +1,105 @@ +What: /sys/bus/usb/devices/-:./::./konepure/roccatkonepure/actual_profile +Date: December 2012 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. actual_profile holds number of actual profile. + This value is persistent, so its value determines the profile + that's active when the mouse is powered on next time. + When written, the mouse activates the set profile immediately. + The data has to be 3 bytes long. + The mouse will reject invalid data. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./konepure/roccatkonepure/control +Date: December 2012 +Contact: Stefan Achatz +Description: When written, this file lets one select which data from which + profile will be read next. The data has to be 3 bytes long. + This file is writeonly. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./konepure/roccatkonepure/info +Date: December 2012 +Contact: Stefan Achatz +Description: When read, this file returns general data like firmware version. + When written, the device can be reset. + The data is 6 bytes long. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./konepure/roccatkonepure/macro +Date: December 2012 +Contact: Stefan Achatz +Description: The mouse can store a macro with max 500 key/button strokes + internally. + When written, this file lets one set the sequence for a specific + button for a specific profile. Button and profile numbers are + included in written data. The data has to be 2082 bytes long. + This file is writeonly. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./konepure/roccatkonepure/profile_buttons +Date: December 2012 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_buttons holds information about button layout. + When written, this file lets one write the respective profile + buttons back to the mouse. The data has to be 59 bytes long. + The mouse will reject invalid data. + Which profile to write is determined by the profile number + contained in the data. + Before reading this file, control has to be written to select + which profile to read. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./konepure/roccatkonepure/profile_settings +Date: December 2012 +Contact: Stefan Achatz +Description: The mouse can store 5 profiles which can be switched by the + press of a button. A profile is split in settings and buttons. + profile_settings holds information like resolution, sensitivity + and light effects. + When written, this file lets one write the respective profile + settings back to the mouse. The data has to be 31 bytes long. + The mouse will reject invalid data. + Which profile to write is determined by the profile number + contained in the data. + Before reading this file, control has to be written to select + which profile to read. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./konepure/roccatkonepure/sensor +Date: December 2012 +Contact: Stefan Achatz +Description: The mouse has a tracking- and a distance-control-unit. These + can be activated/deactivated and the lift-off distance can be + set. The data has to be 6 bytes long. + This file is writeonly. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./konepure/roccatkonepure/talk +Date: December 2012 +Contact: Stefan Achatz +Description: Used to active some easy* functions of the mouse from outside. + The data has to be 16 bytes long. + This file is writeonly. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./konepure/roccatkonepure/tcu +Date: December 2012 +Contact: Stefan Achatz +Description: When written a calibration process for the tracking control unit + can be initiated/cancelled. Also lets one read/write sensor + registers. + The data has to be 4 bytes long. +Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./konepure/roccatkonepure/tcu_image +Date: December 2012 +Contact: Stefan Achatz +Description: When read the mouse returns a 30x30 pixel image of the + sampled underground. This works only in the course of a + calibration process initiated with tcu. + The returned data is 1028 bytes in size. + This file is readonly. +Users: http://roccat.sourceforge.net diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 72d1b0bc0a97..273515197c37 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -94,8 +94,8 @@ obj-$(CONFIG_HID_PRIMAX) += hid-primax.o obj-$(CONFIG_HID_PS3REMOTE) += hid-ps3remote.o obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \ hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \ - hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-lua.o \ - hid-roccat-pyra.o hid-roccat-savu.o + hid-roccat-koneplus.o hid-roccat-konepure.o hid-roccat-kovaplus.o \ + hid-roccat-lua.o hid-roccat-pyra.o hid-roccat-savu.o obj-$(CONFIG_HID_SAITEK) += hid-saitek.o obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 512b01c04ea7..733d7e059708 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1687,6 +1687,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_LUA) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 92e47e5c9564..007ee7441e34 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -689,6 +689,7 @@ #define USB_DEVICE_ID_ROCCAT_ISKU 0x319c #define USB_DEVICE_ID_ROCCAT_KONE 0x2ced #define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51 +#define USB_DEVICE_ID_ROCCAT_KONEPURE 0x2dbe #define USB_DEVICE_ID_ROCCAT_KONEXTD 0x2e22 #define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50 #define USB_DEVICE_ID_ROCCAT_LUA 0x2c2e diff --git a/drivers/hid/hid-roccat-konepure.c b/drivers/hid/hid-roccat-konepure.c new file mode 100644 index 000000000000..c79d0b06c143 --- /dev/null +++ b/drivers/hid/hid-roccat-konepure.c @@ -0,0 +1,304 @@ +/* + * Roccat KonePure driver for Linux + * + * Copyright (c) 2012 Stefan Achatz + */ + +/* + * 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. + */ + +/* + * Roccat KonePure is a smaller version of KoneXTD with less buttons and lights. + */ + +#include +#include +#include +#include +#include +#include +#include "hid-ids.h" +#include "hid-roccat-common.h" +#include "hid-roccat-konepure.h" + +static struct class *konepure_class; + +static ssize_t konepure_sysfs_read(struct file *fp, struct kobject *kobj, + char *buf, loff_t off, size_t count, + size_t real_size, uint command) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct konepure_device *konepure = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; + + if (off >= real_size) + return 0; + + if (off != 0 || count != real_size) + return -EINVAL; + + mutex_lock(&konepure->konepure_lock); + retval = roccat_common2_receive(usb_dev, command, buf, real_size); + mutex_unlock(&konepure->konepure_lock); + + return retval ? retval : real_size; +} + +static ssize_t konepure_sysfs_write(struct file *fp, struct kobject *kobj, + void const *buf, loff_t off, size_t count, + size_t real_size, uint command) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct konepure_device *konepure = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; + + if (off != 0 || count != real_size) + return -EINVAL; + + mutex_lock(&konepure->konepure_lock); + retval = roccat_common2_send_with_status(usb_dev, command, + (void *)buf, real_size); + mutex_unlock(&konepure->konepure_lock); + + return retval ? retval : real_size; +} + +#define KONEPURE_SYSFS_W(thingy, THINGY) \ +static ssize_t konepure_sysfs_write_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return konepure_sysfs_write(fp, kobj, buf, off, count, \ + KONEPURE_SIZE_ ## THINGY, KONEPURE_COMMAND_ ## THINGY); \ +} + +#define KONEPURE_SYSFS_R(thingy, THINGY) \ +static ssize_t konepure_sysfs_read_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return konepure_sysfs_read(fp, kobj, buf, off, count, \ + KONEPURE_SIZE_ ## THINGY, KONEPURE_COMMAND_ ## THINGY); \ +} + +#define KONEPURE_SYSFS_RW(thingy, THINGY) \ +KONEPURE_SYSFS_W(thingy, THINGY) \ +KONEPURE_SYSFS_R(thingy, THINGY) + +#define KONEPURE_BIN_ATTRIBUTE_RW(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0660 }, \ + .size = KONEPURE_SIZE_ ## THINGY, \ + .read = konepure_sysfs_read_ ## thingy, \ + .write = konepure_sysfs_write_ ## thingy \ +} + +#define KONEPURE_BIN_ATTRIBUTE_R(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0440 }, \ + .size = KONEPURE_SIZE_ ## THINGY, \ + .read = konepure_sysfs_read_ ## thingy, \ +} + +#define KONEPURE_BIN_ATTRIBUTE_W(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0220 }, \ + .size = KONEPURE_SIZE_ ## THINGY, \ + .write = konepure_sysfs_write_ ## thingy \ +} + +KONEPURE_SYSFS_RW(actual_profile, ACTUAL_PROFILE) +KONEPURE_SYSFS_W(control, CONTROL) +KONEPURE_SYSFS_RW(info, INFO) +KONEPURE_SYSFS_W(talk, TALK) +KONEPURE_SYSFS_W(macro, MACRO) +KONEPURE_SYSFS_RW(sensor, SENSOR) +KONEPURE_SYSFS_RW(tcu, TCU) +KONEPURE_SYSFS_R(tcu_image, TCU_IMAGE) +KONEPURE_SYSFS_RW(profile_settings, PROFILE_SETTINGS) +KONEPURE_SYSFS_RW(profile_buttons, PROFILE_BUTTONS) + +static struct bin_attribute konepure_bin_attributes[] = { + KONEPURE_BIN_ATTRIBUTE_RW(actual_profile, ACTUAL_PROFILE), + KONEPURE_BIN_ATTRIBUTE_W(control, CONTROL), + KONEPURE_BIN_ATTRIBUTE_RW(info, INFO), + KONEPURE_BIN_ATTRIBUTE_W(talk, TALK), + KONEPURE_BIN_ATTRIBUTE_W(macro, MACRO), + KONEPURE_BIN_ATTRIBUTE_RW(sensor, SENSOR), + KONEPURE_BIN_ATTRIBUTE_RW(tcu, TCU), + KONEPURE_BIN_ATTRIBUTE_R(tcu_image, TCU_IMAGE), + KONEPURE_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS), + KONEPURE_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS), + __ATTR_NULL +}; + +static int konepure_init_konepure_device_struct(struct usb_device *usb_dev, + struct konepure_device *konepure) +{ + mutex_init(&konepure->konepure_lock); + + return 0; +} + +static int konepure_init_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct usb_device *usb_dev = interface_to_usbdev(intf); + struct konepure_device *konepure; + int retval; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != USB_INTERFACE_PROTOCOL_MOUSE) { + hid_set_drvdata(hdev, NULL); + return 0; + } + + konepure = kzalloc(sizeof(*konepure), GFP_KERNEL); + if (!konepure) { + hid_err(hdev, "can't alloc device descriptor\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, konepure); + + retval = konepure_init_konepure_device_struct(usb_dev, konepure); + if (retval) { + hid_err(hdev, "couldn't init struct konepure_device\n"); + goto exit_free; + } + + retval = roccat_connect(konepure_class, hdev, + sizeof(struct konepure_mouse_report_button)); + if (retval < 0) { + hid_err(hdev, "couldn't init char dev\n"); + } else { + konepure->chrdev_minor = retval; + konepure->roccat_claimed = 1; + } + + return 0; +exit_free: + kfree(konepure); + return retval; +} + +static void konepure_remove_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct konepure_device *konepure; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != USB_INTERFACE_PROTOCOL_MOUSE) + return; + + konepure = hid_get_drvdata(hdev); + if (konepure->roccat_claimed) + roccat_disconnect(konepure->chrdev_minor); + kfree(konepure); +} + +static int konepure_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int retval; + + retval = hid_parse(hdev); + if (retval) { + hid_err(hdev, "parse failed\n"); + goto exit; + } + + retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (retval) { + hid_err(hdev, "hw start failed\n"); + goto exit; + } + + retval = konepure_init_specials(hdev); + if (retval) { + hid_err(hdev, "couldn't install mouse\n"); + goto exit_stop; + } + + return 0; + +exit_stop: + hid_hw_stop(hdev); +exit: + return retval; +} + +static void konepure_remove(struct hid_device *hdev) +{ + konepure_remove_specials(hdev); + hid_hw_stop(hdev); +} + +static int konepure_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct konepure_device *konepure = hid_get_drvdata(hdev); + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != USB_INTERFACE_PROTOCOL_MOUSE) + return 0; + + if (data[0] != KONEPURE_MOUSE_REPORT_NUMBER_BUTTON) + return 0; + + if (konepure != NULL && konepure->roccat_claimed) + roccat_report_event(konepure->chrdev_minor, data); + + return 0; +} + +static const struct hid_device_id konepure_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, konepure_devices); + +static struct hid_driver konepure_driver = { + .name = "konepure", + .id_table = konepure_devices, + .probe = konepure_probe, + .remove = konepure_remove, + .raw_event = konepure_raw_event +}; + +static int __init konepure_init(void) +{ + int retval; + + konepure_class = class_create(THIS_MODULE, "konepure"); + if (IS_ERR(konepure_class)) + return PTR_ERR(konepure_class); + konepure_class->dev_bin_attrs = konepure_bin_attributes; + + retval = hid_register_driver(&konepure_driver); + if (retval) + class_destroy(konepure_class); + return retval; +} + +static void __exit konepure_exit(void) +{ + hid_unregister_driver(&konepure_driver); + class_destroy(konepure_class); +} + +module_init(konepure_init); +module_exit(konepure_exit); + +MODULE_AUTHOR("Stefan Achatz"); +MODULE_DESCRIPTION("USB Roccat KonePure driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-konepure.h b/drivers/hid/hid-roccat-konepure.h new file mode 100644 index 000000000000..2cd24e93dfd6 --- /dev/null +++ b/drivers/hid/hid-roccat-konepure.h @@ -0,0 +1,72 @@ +#ifndef __HID_ROCCAT_KONEPURE_H +#define __HID_ROCCAT_KONEPURE_H + +/* + * Copyright (c) 2012 Stefan Achatz + */ + +/* + * 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. + */ + +#include + +enum { + KONEPURE_SIZE_ACTUAL_PROFILE = 0x03, + KONEPURE_SIZE_CONTROL = 0x03, + KONEPURE_SIZE_FIRMWARE_WRITE = 0x0402, + KONEPURE_SIZE_INFO = 0x06, + KONEPURE_SIZE_MACRO = 0x0822, + KONEPURE_SIZE_PROFILE_SETTINGS = 0x1f, + KONEPURE_SIZE_PROFILE_BUTTONS = 0x3b, + KONEPURE_SIZE_SENSOR = 0x06, + KONEPURE_SIZE_TALK = 0x10, + KONEPURE_SIZE_TCU = 0x04, + KONEPURE_SIZE_TCU_IMAGE = 0x0404, +}; + +enum konepure_control_requests { + KONEPURE_CONTROL_REQUEST_GENERAL = 0x80, + KONEPURE_CONTROL_REQUEST_BUTTONS = 0x90, +}; + +enum konepure_commands { + KONEPURE_COMMAND_CONTROL = 0x04, + KONEPURE_COMMAND_ACTUAL_PROFILE = 0x05, + KONEPURE_COMMAND_PROFILE_SETTINGS = 0x06, + KONEPURE_COMMAND_PROFILE_BUTTONS = 0x07, + KONEPURE_COMMAND_MACRO = 0x08, + KONEPURE_COMMAND_INFO = 0x09, + KONEPURE_COMMAND_TCU = 0x0c, + KONEPURE_COMMAND_TCU_IMAGE = 0x0c, + KONEPURE_COMMAND_E = 0x0e, + KONEPURE_COMMAND_SENSOR = 0x0f, + KONEPURE_COMMAND_TALK = 0x10, + KONEPURE_COMMAND_FIRMWARE_WRITE = 0x1b, + KONEPURE_COMMAND_FIRMWARE_WRITE_CONTROL = 0x1c, +}; + +enum { + KONEPURE_MOUSE_REPORT_NUMBER_BUTTON = 3, +}; + +struct konepure_mouse_report_button { + uint8_t report_number; /* always KONEPURE_MOUSE_REPORT_NUMBER_BUTTON */ + uint8_t zero; + uint8_t type; + uint8_t data1; + uint8_t data2; + uint8_t zero2; + uint8_t unknown[2]; +} __packed; + +struct konepure_device { + int roccat_claimed; + int chrdev_minor; + struct mutex konepure_lock; +}; + +#endif -- cgit v1.2.3 From ce7169652532a95bebbf2f02cd330a4e66f171ae Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 10 Mar 2013 12:33:02 +0100 Subject: HID: roccat: add support for IskuFX Extending isku module with one additional and one changed sysfs attr. IskuFX has larger light sysfs attr. Made the code size tolerant so both devices can be handled. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- Documentation/ABI/testing/sysfs-driver-hid-roccat-isku | 12 +++++++++++- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-roccat-isku.c | 17 ++++++++++------- drivers/hid/hid-roccat-isku.h | 4 +++- 4 files changed, 25 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku b/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku index 9eca5a182e64..c601d0f2ac46 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku @@ -101,7 +101,8 @@ Date: June 2011 Contact: Stefan Achatz Description: When written, this file lets one set the backlight intensity for a specific profile. Profile number is included in written data. - The data has to be 10 bytes long. + The data has to be 10 bytes long for Isku, IskuFX needs 16 bytes + of data. Before reading this file, control has to be written to select which profile to read. Users: http://roccat.sourceforge.net @@ -141,3 +142,12 @@ Description: When written, this file lets one trigger easyshift functionality The data has to be 16 bytes long. This file is writeonly. Users: http://roccat.sourceforge.net + +What: /sys/bus/usb/devices/-:./::./isku/roccatisku/talkfx +Date: February 2013 +Contact: Stefan Achatz +Description: When written, this file lets one trigger temporary color schemes + from the host. + The data has to be 16 bytes long. + This file is writeonly. +Users: http://roccat.sourceforge.net diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 007ee7441e34..a2e767b3d5d2 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -687,6 +687,7 @@ #define USB_VENDOR_ID_ROCCAT 0x1e7d #define USB_DEVICE_ID_ROCCAT_ARVO 0x30d4 #define USB_DEVICE_ID_ROCCAT_ISKU 0x319c +#define USB_DEVICE_ID_ROCCAT_ISKUFX 0x3264 #define USB_DEVICE_ID_ROCCAT_KONE 0x2ced #define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51 #define USB_DEVICE_ID_ROCCAT_KONEPURE 0x2dbe diff --git a/drivers/hid/hid-roccat-isku.c b/drivers/hid/hid-roccat-isku.c index 1219998a02d6..8023751d5257 100644 --- a/drivers/hid/hid-roccat-isku.c +++ b/drivers/hid/hid-roccat-isku.c @@ -130,14 +130,14 @@ static ssize_t isku_sysfs_read(struct file *fp, struct kobject *kobj, if (off >= real_size) return 0; - if (off != 0 || count != real_size) + if (off != 0 || count > real_size) return -EINVAL; mutex_lock(&isku->isku_lock); - retval = isku_receive(usb_dev, command, buf, real_size); + retval = isku_receive(usb_dev, command, buf, count); mutex_unlock(&isku->isku_lock); - return retval ? retval : real_size; + return retval ? retval : count; } static ssize_t isku_sysfs_write(struct file *fp, struct kobject *kobj, @@ -150,15 +150,15 @@ static ssize_t isku_sysfs_write(struct file *fp, struct kobject *kobj, struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); int retval; - if (off != 0 || count != real_size) + if (off != 0 || count > real_size) return -EINVAL; mutex_lock(&isku->isku_lock); retval = roccat_common2_send_with_status(usb_dev, command, - (void *)buf, real_size); + (void *)buf, count); mutex_unlock(&isku->isku_lock); - return retval ? retval : real_size; + return retval ? retval : count; } #define ISKU_SYSFS_W(thingy, THINGY) \ @@ -216,6 +216,7 @@ ISKU_SYSFS_RW(light, LIGHT) ISKU_SYSFS_RW(key_mask, KEY_MASK) ISKU_SYSFS_RW(last_set, LAST_SET) ISKU_SYSFS_W(talk, TALK) +ISKU_SYSFS_W(talkfx, TALKFX) ISKU_SYSFS_R(info, INFO) ISKU_SYSFS_W(control, CONTROL) ISKU_SYSFS_W(reset, RESET) @@ -232,6 +233,7 @@ static struct bin_attribute isku_bin_attributes[] = { ISKU_BIN_ATTR_RW(key_mask, KEY_MASK), ISKU_BIN_ATTR_RW(last_set, LAST_SET), ISKU_BIN_ATTR_W(talk, TALK), + ISKU_BIN_ATTR_W(talkfx, TALKFX), ISKU_BIN_ATTR_R(info, INFO), ISKU_BIN_ATTR_W(control, CONTROL), ISKU_BIN_ATTR_W(reset, RESET), @@ -405,6 +407,7 @@ static int isku_raw_event(struct hid_device *hdev, static const struct hid_device_id isku_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKUFX) }, { } }; @@ -443,5 +446,5 @@ module_init(isku_init); module_exit(isku_exit); MODULE_AUTHOR("Stefan Achatz"); -MODULE_DESCRIPTION("USB Roccat Isku driver"); +MODULE_DESCRIPTION("USB Roccat Isku/FX driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-isku.h b/drivers/hid/hid-roccat-isku.h index cf6896c83867..53056860d4d8 100644 --- a/drivers/hid/hid-roccat-isku.h +++ b/drivers/hid/hid-roccat-isku.h @@ -25,10 +25,11 @@ enum { ISKU_SIZE_KEYS_MACRO = 0x23, ISKU_SIZE_KEYS_CAPSLOCK = 0x06, ISKU_SIZE_LAST_SET = 0x14, - ISKU_SIZE_LIGHT = 0x0a, + ISKU_SIZE_LIGHT = 0x10, ISKU_SIZE_MACRO = 0x823, ISKU_SIZE_RESET = 0x03, ISKU_SIZE_TALK = 0x10, + ISKU_SIZE_TALKFX = 0x10, }; enum { @@ -59,6 +60,7 @@ enum isku_commands { ISKU_COMMAND_LAST_SET = 0x14, ISKU_COMMAND_15 = 0x15, ISKU_COMMAND_TALK = 0x16, + ISKU_COMMAND_TALKFX = 0x17, ISKU_COMMAND_FIRMWARE_WRITE = 0x1b, ISKU_COMMAND_FIRMWARE_WRITE_CONTROL = 0x1c, }; -- cgit v1.2.3 From 55034cd6e648155393b0d665eef76b38d49ad6bf Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Thu, 7 Mar 2013 22:48:09 -0500 Subject: tracing: Add alloc_snapshot kernel command line parameter If debugging the kernel, and the developer wants to use tracing_snapshot() in places where tracing_snapshot_alloc() may be difficult (or more likely, the developer is lazy and doesn't want to bother with tracing_snapshot_alloc() at all), then adding alloc_snapshot to the kernel command line parameter will tell ftrace to allocate the snapshot buffer (if configured) when it allocates the main tracing buffer. I also noticed that ring_buffer_expanded and tracing_selftest_disabled had inconsistent use of boolean "true" and "false" with "0" and "1". I cleaned that up too. Signed-off-by: Steven Rostedt --- Documentation/kernel-parameters.txt | 7 ++++ kernel/trace/trace.c | 81 ++++++++++++++++++++++--------------- kernel/trace/trace.h | 2 +- kernel/trace/trace_events.c | 4 +- 4 files changed, 58 insertions(+), 36 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 6c723811c0a0..0edc409f9ede 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -320,6 +320,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted. on: enable for both 32- and 64-bit processes off: disable for both 32- and 64-bit processes + alloc_snapshot [FTRACE] + Allocate the ftrace snapshot buffer on boot up when the + main buffer is allocated. This is handy if debugging + and you need to use tracing_snapshot() on boot up, and + do not want to use tracing_snapshot_alloc() as it needs + to be done where GFP_KERNEL allocations are allowed. + amd_iommu= [HW,X86-64] Pass parameters to the AMD IOMMU driver in the system. Possible values are: diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 57b4220d96a9..4021a5e66412 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -47,7 +47,7 @@ * On boot up, the ring buffer is set to the minimum size, so that * we do not waste memory on systems that are not using tracing. */ -int ring_buffer_expanded; +bool ring_buffer_expanded; /* * We need to change this state when a selftest is running. @@ -121,12 +121,14 @@ static int tracing_set_tracer(const char *buf); static char bootup_tracer_buf[MAX_TRACER_SIZE] __initdata; static char *default_bootup_tracer; +static bool allocate_snapshot; + static int __init set_cmdline_ftrace(char *str) { strncpy(bootup_tracer_buf, str, MAX_TRACER_SIZE); default_bootup_tracer = bootup_tracer_buf; /* We are using ftrace early, expand it */ - ring_buffer_expanded = 1; + ring_buffer_expanded = true; return 1; } __setup("ftrace=", set_cmdline_ftrace); @@ -147,6 +149,15 @@ static int __init set_ftrace_dump_on_oops(char *str) } __setup("ftrace_dump_on_oops", set_ftrace_dump_on_oops); +static int __init alloc_snapshot(char *str) +{ + allocate_snapshot = true; + /* We also need the main ring buffer expanded */ + ring_buffer_expanded = true; + return 1; +} +__setup("alloc_snapshot", alloc_snapshot); + static char trace_boot_options_buf[MAX_TRACER_SIZE] __initdata; static char *trace_boot_options __initdata; @@ -951,7 +962,7 @@ int register_tracer(struct tracer *type) tracing_set_tracer(type->name); default_bootup_tracer = NULL; /* disable other selftests, since this will break it. */ - tracing_selftest_disabled = 1; + tracing_selftest_disabled = true; #ifdef CONFIG_FTRACE_STARTUP_TEST printk(KERN_INFO "Disabling FTRACE selftests due to running tracer '%s'\n", type->name); @@ -3318,7 +3329,7 @@ static int __tracing_resize_ring_buffer(struct trace_array *tr, * we use the size that was given, and we can forget about * expanding it later. */ - ring_buffer_expanded = 1; + ring_buffer_expanded = true; /* May be called before buffers are initialized */ if (!tr->trace_buffer.buffer) @@ -5396,53 +5407,57 @@ static void init_trace_buffers(struct trace_array *tr, struct trace_buffer *buf) } } -static int allocate_trace_buffers(struct trace_array *tr, int size) +static int +allocate_trace_buffer(struct trace_array *tr, struct trace_buffer *buf, int size) { enum ring_buffer_flags rb_flags; rb_flags = trace_flags & TRACE_ITER_OVERWRITE ? RB_FL_OVERWRITE : 0; - tr->trace_buffer.buffer = ring_buffer_alloc(size, rb_flags); - if (!tr->trace_buffer.buffer) - goto out_free; + buf->buffer = ring_buffer_alloc(size, rb_flags); + if (!buf->buffer) + return -ENOMEM; - tr->trace_buffer.data = alloc_percpu(struct trace_array_cpu); - if (!tr->trace_buffer.data) - goto out_free; + buf->data = alloc_percpu(struct trace_array_cpu); + if (!buf->data) { + ring_buffer_free(buf->buffer); + return -ENOMEM; + } - init_trace_buffers(tr, &tr->trace_buffer); + init_trace_buffers(tr, buf); /* Allocate the first page for all buffers */ set_buffer_entries(&tr->trace_buffer, ring_buffer_size(tr->trace_buffer.buffer, 0)); -#ifdef CONFIG_TRACER_MAX_TRACE - - tr->max_buffer.buffer = ring_buffer_alloc(1, rb_flags); - if (!tr->max_buffer.buffer) - goto out_free; - - tr->max_buffer.data = alloc_percpu(struct trace_array_cpu); - if (!tr->max_buffer.data) - goto out_free; + return 0; +} - init_trace_buffers(tr, &tr->max_buffer); +static int allocate_trace_buffers(struct trace_array *tr, int size) +{ + int ret; - set_buffer_entries(&tr->max_buffer, 1); -#endif - return 0; + ret = allocate_trace_buffer(tr, &tr->trace_buffer, size); + if (ret) + return ret; - out_free: - if (tr->trace_buffer.buffer) +#ifdef CONFIG_TRACER_MAX_TRACE + ret = allocate_trace_buffer(tr, &tr->max_buffer, + allocate_snapshot ? size : 1); + if (WARN_ON(ret)) { ring_buffer_free(tr->trace_buffer.buffer); - free_percpu(tr->trace_buffer.data); + free_percpu(tr->trace_buffer.data); + return -ENOMEM; + } + tr->allocated_snapshot = allocate_snapshot; -#ifdef CONFIG_TRACER_MAX_TRACE - if (tr->max_buffer.buffer) - ring_buffer_free(tr->max_buffer.buffer); - free_percpu(tr->max_buffer.data); + /* + * Only the top level trace array gets its snapshot allocated + * from the kernel command line. + */ + allocate_snapshot = false; #endif - return -ENOMEM; + return 0; } static int new_instance_create(const char *name) diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index f4931821a966..26bc71834041 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -660,7 +660,7 @@ extern int DYN_FTRACE_TEST_NAME(void); #define DYN_FTRACE_TEST_NAME2 trace_selftest_dynamic_test_func2 extern int DYN_FTRACE_TEST_NAME2(void); -extern int ring_buffer_expanded; +extern bool ring_buffer_expanded; extern bool tracing_selftest_disabled; DECLARE_PER_CPU(int, ftrace_cpu_disabled); diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index a376ab5eec5c..38b54c5edeb9 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -1844,8 +1844,8 @@ static char bootup_event_buf[COMMAND_LINE_SIZE] __initdata; static __init int setup_trace_event(char *str) { strlcpy(bootup_event_buf, str, COMMAND_LINE_SIZE); - ring_buffer_expanded = 1; - tracing_selftest_disabled = 1; + ring_buffer_expanded = true; + tracing_selftest_disabled = true; return 1; } -- cgit v1.2.3 From 8d016091d10953e00f9d2c0125cc0ddd46c23a6a Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 13 Mar 2013 11:05:11 -0400 Subject: tracing: Bring Documentation/trace/ftrace.txt up to date The ftrace.txt document has been suffering from some serious bit rot. Updated the current content to how things are as of v3.10. Remove things that no longer exist. Add documentation about new features: per_cpu stats instances stack trace etc. Signed-off-by: Steven Rostedt --- Documentation/trace/ftrace.txt | 2097 ++++++++++++++++++++++++++++------------ 1 file changed, 1480 insertions(+), 617 deletions(-) (limited to 'Documentation') diff --git a/Documentation/trace/ftrace.txt b/Documentation/trace/ftrace.txt index a372304aef10..bfe8c29b1f1d 100644 --- a/Documentation/trace/ftrace.txt +++ b/Documentation/trace/ftrace.txt @@ -8,6 +8,7 @@ Copyright 2008 Red Hat Inc. Reviewers: Elias Oltmanns, Randy Dunlap, Andrew Morton, John Kacur, and David Teigland. Written for: 2.6.28-rc2 +Updated for: 3.10 Introduction ------------ @@ -17,13 +18,16 @@ designers of systems to find what is going on inside the kernel. It can be used for debugging or analyzing latencies and performance issues that take place outside of user-space. -Although ftrace is the function tracer, it also includes an -infrastructure that allows for other types of tracing. Some of -the tracers that are currently in ftrace include a tracer to -trace context switches, the time it takes for a high priority -task to run after it was woken up, the time interrupts are -disabled, and more (ftrace allows for tracer plugins, which -means that the list of tracers can always grow). +Although ftrace is typically considered the function tracer, it +is really a frame work of several assorted tracing utilities. +There's latency tracing to examine what occurs between interrupts +disabled and enabled, as well as for preemption and from a time +a task is woken to the task is actually scheduled in. + +One of the most common uses of ftrace is the event tracing. +Through out the kernel is hundreds of static event points that +can be enabled via the debugfs file system to see what is +going on in certain parts of the kernel. Implementation Details @@ -61,7 +65,7 @@ the extended "/sys/kernel/debug/tracing" path name. That's it! (assuming that you have ftrace configured into your kernel) -After mounting the debugfs, you can see a directory called +After mounting debugfs, you can see a directory called "tracing". This directory contains the control and output files of ftrace. Here is a list of some of the key files: @@ -84,7 +88,9 @@ of ftrace. Here is a list of some of the key files: This sets or displays whether writing to the trace ring buffer is enabled. Echo 0 into this file to disable - the tracer or 1 to enable it. + the tracer or 1 to enable it. Note, this only disables + writing to the ring buffer, the tracing overhead may + still be occurring. trace: @@ -109,7 +115,15 @@ of ftrace. Here is a list of some of the key files: This file lets the user control the amount of data that is displayed in one of the above output - files. + files. Options also exist to modify how a tracer + or events work (stack traces, timestamps, etc). + + options: + + This is a directory that has a file for every available + trace option (also in trace_options). Options may also be set + or cleared by writing a "1" or "0" respectively into the + corresponding file with the option name. tracing_max_latency: @@ -121,10 +135,17 @@ of ftrace. Here is a list of some of the key files: latency is greater than the value in this file. (in microseconds) + tracing_thresh: + + Some latency tracers will record a trace whenever the + latency is greater than the number in this file. + Only active when the file contains a number greater than 0. + (in microseconds) + buffer_size_kb: This sets or displays the number of kilobytes each CPU - buffer can hold. The tracer buffers are the same size + buffer holds. By default, the trace buffers are the same size for each CPU. The displayed number is the size of the CPU buffer and not total size of all buffers. The trace buffers are allocated in pages (blocks of memory @@ -133,16 +154,30 @@ of ftrace. Here is a list of some of the key files: than requested, the rest of the page will be used, making the actual allocation bigger than requested. ( Note, the size may not be a multiple of the page size - due to buffer management overhead. ) + due to buffer management meta-data. ) - This can only be updated when the current_tracer - is set to "nop". + buffer_total_size_kb: + + This displays the total combined size of all the trace buffers. + + free_buffer: + + If a process is performing the tracing, and the ring buffer + should be shrunk "freed" when the process is finished, even + if it were to be killed by a signal, this file can be used + for that purpose. On close of this file, the ring buffer will + be resized to its minimum size. Having a process that is tracing + also open this file, when the process exits its file descriptor + for this file will be closed, and in doing so, the ring buffer + will be "freed". + + It may also stop tracing if disable_on_free option is set. tracing_cpumask: This is a mask that lets the user only trace - on specified CPUS. The format is a hex string - representing the CPUS. + on specified CPUs. The format is a hex string + representing the CPUs. set_ftrace_filter: @@ -183,6 +218,261 @@ of ftrace. Here is a list of some of the key files: "set_ftrace_notrace". (See the section "dynamic ftrace" below for more details.) + enabled_functions: + + This file is more for debugging ftrace, but can also be useful + in seeing if any function has a callback attached to it. + Not only does the trace infrastructure use ftrace function + trace utility, but other subsystems might too. This file + displays all functions that have a callback attached to them + as well as the number of callbacks that have been attached. + Note, a callback may also call multiple functions which will + not be listed in this count. + + If the callback registered to be traced by a function with + the "save regs" attribute (thus even more overhead), a 'R' + will be displayed on the same line as the function that + is returning registers. + + function_profile_enabled: + + When set it will enable all functions with either the function + tracer, or if enabled, the function graph tracer. It will + keep a histogram of the number of functions that were called + and if run with the function graph tracer, it will also keep + track of the time spent in those functions. The histogram + content can be displayed in the files: + + trace_stats/function ( function0, function1, etc). + + trace_stats: + + A directory that holds different tracing stats. + + kprobe_events: + + Enable dynamic trace points. See kprobetrace.txt. + + kprobe_profile: + + Dynamic trace points stats. See kprobetrace.txt. + + max_graph_depth: + + Used with the function graph tracer. This is the max depth + it will trace into a function. Setting this to a value of + one will show only the first kernel function that is called + from user space. + + printk_formats: + + This is for tools that read the raw format files. If an event in + the ring buffer references a string (currently only trace_printk() + does this), only a pointer to the string is recorded into the buffer + and not the string itself. This prevents tools from knowing what + that string was. This file displays the string and address for + the string allowing tools to map the pointers to what the + strings were. + + saved_cmdlines: + + Only the pid of the task is recorded in a trace event unless + the event specifically saves the task comm as well. Ftrace + makes a cache of pid mappings to comms to try to display + comms for events. If a pid for a comm is not listed, then + "<...>" is displayed in the output. + + snapshot: + + This displays the "snapshot" buffer and also lets the user + take a snapshot of the current running trace. + See the "Snapshot" section below for more details. + + stack_max_size: + + When the stack tracer is activated, this will display the + maximum stack size it has encountered. + See the "Stack Trace" section below. + + stack_trace: + + This displays the stack back trace of the largest stack + that was encountered when the stack tracer is activated. + See the "Stack Trace" section below. + + stack_trace_filter: + + This is similar to "set_ftrace_filter" but it limits what + functions the stack tracer will check. + + trace_clock: + + Whenever an event is recorded into the ring buffer, a + "timestamp" is added. This stamp comes from a specified + clock. By default, ftrace uses the "local" clock. This + clock is very fast and strictly per cpu, but on some + systems it may not be monotonic with respect to other + CPUs. In other words, the local clocks may not be in sync + with local clocks on other CPUs. + + Usual clocks for tracing: + + # cat trace_clock + [local] global counter x86-tsc + + local: Default clock, but may not be in sync across CPUs + + global: This clock is in sync with all CPUs but may + be a bit slower than the local clock. + + counter: This is not a clock at all, but literally an atomic + counter. It counts up one by one, but is in sync + with all CPUs. This is useful when you need to + know exactly the order events occurred with respect to + each other on different CPUs. + + uptime: This uses the jiffies counter and the time stamp + is relative to the time since boot up. + + perf: This makes ftrace use the same clock that perf uses. + Eventually perf will be able to read ftrace buffers + and this will help out in interleaving the data. + + x86-tsc: Architectures may define their own clocks. For + example, x86 uses its own TSC cycle clock here. + + To set a clock, simply echo the clock name into this file. + + echo global > trace_clock + + trace_marker: + + This is a very useful file for synchronizing user space + with events happening in the kernel. Writing strings into + this file will be written into the ftrace buffer. + + It is useful in applications to open this file at the start + of the application and just reference the file descriptor + for the file. + + void trace_write(const char *fmt, ...) + { + va_list ap; + char buf[256]; + int n; + + if (trace_fd < 0) + return; + + va_start(ap, fmt); + n = vsnprintf(buf, 256, fmt, ap); + va_end(ap); + + write(trace_fd, buf, n); + } + + start: + + trace_fd = open("trace_marker", WR_ONLY); + + uprobe_events: + + Add dynamic tracepoints in programs. + See uprobetracer.txt + + uprobe_profile: + + Uprobe statistics. See uprobetrace.txt + + instances: + + This is a way to make multiple trace buffers where different + events can be recorded in different buffers. + See "Instances" section below. + + events: + + This is the trace event directory. It holds event tracepoints + (also known as static tracepoints) that have been compiled + into the kernel. It shows what event tracepoints exist + and how they are grouped by system. There are "enable" + files at various levels that can enable the tracepoints + when a "1" is written to them. + + See events.txt for more information. + + per_cpu: + + This is a directory that contains the trace per_cpu information. + + per_cpu/cpu0/buffer_size_kb: + + The ftrace buffer is defined per_cpu. That is, there's a separate + buffer for each CPU to allow writes to be done atomically, + and free from cache bouncing. These buffers may have different + size buffers. This file is similar to the buffer_size_kb + file, but it only displays or sets the buffer size for the + specific CPU. (here cpu0). + + per_cpu/cpu0/trace: + + This is similar to the "trace" file, but it will only display + the data specific for the CPU. If written to, it only clears + the specific CPU buffer. + + per_cpu/cpu0/trace_pipe + + This is similar to the "trace_pipe" file, and is a consuming + read, but it will only display (and consume) the data specific + for the CPU. + + per_cpu/cpu0/trace_pipe_raw + + For tools that can parse the ftrace ring buffer binary format, + the trace_pipe_raw file can be used to extract the data + from the ring buffer directly. With the use of the splice() + system call, the buffer data can be quickly transferred to + a file or to the network where a server is collecting the + data. + + Like trace_pipe, this is a consuming reader, where multiple + reads will always produce different data. + + per_cpu/cpu0/snapshot: + + This is similar to the main "snapshot" file, but will only + snapshot the current CPU (if supported). It only displays + the content of the snapshot for a given CPU, and if + written to, only clears this CPU buffer. + + per_cpu/cpu0/snapshot_raw: + + Similar to the trace_pipe_raw, but will read the binary format + from the snapshot buffer for the given CPU. + + per_cpu/cpu0/stats: + + This displays certain stats about the ring buffer: + + entries: The number of events that are still in the buffer. + + overrun: The number of lost events due to overwriting when + the buffer was full. + + commit overrun: Should always be zero. + This gets set if so many events happened within a nested + event (ring buffer is re-entrant), that it fills the + buffer and starts dropping events. + + bytes: Bytes actually read (not overwritten). + + oldest event ts: The oldest timestamp in the buffer + + now ts: The current timestamp + + dropped events: Events lost due to overwrite option being off. + + read events: The number of events read. The Tracers ----------- @@ -234,11 +524,6 @@ Here is the list of current tracers that may be configured. RT tasks (as the current "wakeup" does). This is useful for those interested in wake up timings of RT tasks. - "hw-branch-tracer" - - Uses the BTS CPU feature on x86 CPUs to traces all - branches executed. - "nop" This is the "trace nothing" tracer. To remove all @@ -261,70 +546,100 @@ Here is an example of the output format of the file "trace" -------- # tracer: function # -# TASK-PID CPU# TIMESTAMP FUNCTION -# | | | | | - bash-4251 [01] 10152.583854: path_put <-path_walk - bash-4251 [01] 10152.583855: dput <-path_put - bash-4251 [01] 10152.583855: _atomic_dec_and_lock <-dput +# entries-in-buffer/entries-written: 140080/250280 #P:4 +# +# _-----=> irqs-off +# / _----=> need-resched +# | / _---=> hardirq/softirq +# || / _--=> preempt-depth +# ||| / delay +# TASK-PID CPU# |||| TIMESTAMP FUNCTION +# | | | |||| | | + bash-1977 [000] .... 17284.993652: sys_close <-system_call_fastpath + bash-1977 [000] .... 17284.993653: __close_fd <-sys_close + bash-1977 [000] .... 17284.993653: _raw_spin_lock <-__close_fd + sshd-1974 [003] .... 17284.993653: __srcu_read_unlock <-fsnotify + bash-1977 [000] .... 17284.993654: add_preempt_count <-_raw_spin_lock + bash-1977 [000] ...1 17284.993655: _raw_spin_unlock <-__close_fd + bash-1977 [000] ...1 17284.993656: sub_preempt_count <-_raw_spin_unlock + bash-1977 [000] .... 17284.993657: filp_close <-__close_fd + bash-1977 [000] .... 17284.993657: dnotify_flush <-filp_close + sshd-1974 [003] .... 17284.993658: sys_select <-system_call_fastpath -------- A header is printed with the tracer name that is represented by -the trace. In this case the tracer is "function". Then a header -showing the format. Task name "bash", the task PID "4251", the -CPU that it was running on "01", the timestamp in . -format, the function name that was traced "path_put" and the -parent function that called this function "path_walk". The -timestamp is the time at which the function was entered. +the trace. In this case the tracer is "function". Then it shows the +number of events in the buffer as well as the total number of entries +that were written. The difference is the number of entries that were +lost due to the buffer filling up (250280 - 140080 = 110200 events +lost). + +The header explains the content of the events. Task name "bash", the task +PID "1977", the CPU that it was running on "000", the latency format +(explained below), the timestamp in . format, the +function name that was traced "sys_close" and the parent function that +called this function "system_call_fastpath". The timestamp is the time +at which the function was entered. Latency trace format -------------------- -When the latency-format option is enabled, the trace file gives -somewhat more information to see why a latency happened. -Here is a typical trace. +When the latency-format option is enabled or when one of the latency +tracers is set, the trace file gives somewhat more information to see +why a latency happened. Here is a typical trace. # tracer: irqsoff # -irqsoff latency trace v1.1.5 on 2.6.26-rc8 --------------------------------------------------------------------- - latency: 97 us, #3/3, CPU#0 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:2) - ----------------- - | task: swapper-0 (uid:0 nice:0 policy:0 rt_prio:0) - ----------------- - => started at: apic_timer_interrupt - => ended at: do_softirq - -# _------=> CPU# -# / _-----=> irqs-off -# | / _----=> need-resched -# || / _---=> hardirq/softirq -# ||| / _--=> preempt-depth -# |||| / -# ||||| delay -# cmd pid ||||| time | caller -# \ / ||||| \ | / - -0 0d..1 0us+: trace_hardirqs_off_thunk (apic_timer_interrupt) - -0 0d.s. 97us : __do_softirq (do_softirq) - -0 0d.s1 98us : trace_hardirqs_on (do_softirq) +# irqsoff latency trace v1.1.5 on 3.8.0-test+ +# -------------------------------------------------------------------- +# latency: 259 us, #4/4, CPU#2 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:4) +# ----------------- +# | task: ps-6143 (uid:0 nice:0 policy:0 rt_prio:0) +# ----------------- +# => started at: __lock_task_sighand +# => ended at: _raw_spin_unlock_irqrestore +# +# +# _------=> CPU# +# / _-----=> irqs-off +# | / _----=> need-resched +# || / _---=> hardirq/softirq +# ||| / _--=> preempt-depth +# |||| / delay +# cmd pid ||||| time | caller +# \ / ||||| \ | / + ps-6143 2d... 0us!: trace_hardirqs_off <-__lock_task_sighand + ps-6143 2d..1 259us+: trace_hardirqs_on <-_raw_spin_unlock_irqrestore + ps-6143 2d..1 263us+: time_hardirqs_on <-_raw_spin_unlock_irqrestore + ps-6143 2d..1 306us : + => trace_hardirqs_on_caller + => trace_hardirqs_on + => _raw_spin_unlock_irqrestore + => do_task_stat + => proc_tgid_stat + => proc_single_show + => seq_read + => vfs_read + => sys_read + => system_call_fastpath This shows that the current tracer is "irqsoff" tracing the time -for which interrupts were disabled. It gives the trace version -and the version of the kernel upon which this was executed on -(2.6.26-rc8). Then it displays the max latency in microsecs (97 -us). The number of trace entries displayed and the total number -recorded (both are three: #3/3). The type of preemption that was -used (PREEMPT). VP, KP, SP, and HP are always zero and are -reserved for later use. #P is the number of online CPUS (#P:2). +for which interrupts were disabled. It gives the trace version (which +never changes) and the version of the kernel upon which this was executed on +(3.10). Then it displays the max latency in microseconds (259 us). The number +of trace entries displayed and the total number (both are four: #4/4). +VP, KP, SP, and HP are always zero and are reserved for later use. +#P is the number of online CPUs (#P:4). The task is the process that was running when the latency -occurred. (swapper pid: 0). +occurred. (ps pid: 6143). The start and stop (the functions in which the interrupts were disabled and enabled respectively) that caused the latencies: - apic_timer_interrupt is where the interrupts were disabled. - do_softirq is where they were enabled again. + __lock_task_sighand is where the interrupts were disabled. + _raw_spin_unlock_irqrestore is where they were enabled again. The next lines after the header are the trace itself. The header explains which is which. @@ -367,16 +682,43 @@ The above is mostly meaningful for kernel developers. The rest is the same as the 'trace' file. + Note, the latency tracers will usually end with a back trace + to easily find where the latency occurred. trace_options ------------- -The trace_options file is used to control what gets printed in -the trace output. To see what is available, simply cat the file: +The trace_options file (or the options directory) is used to control +what gets printed in the trace output, or manipulate the tracers. +To see what is available, simply cat the file: cat trace_options - print-parent nosym-offset nosym-addr noverbose noraw nohex nobin \ - noblock nostacktrace nosched-tree nouserstacktrace nosym-userobj +print-parent +nosym-offset +nosym-addr +noverbose +noraw +nohex +nobin +noblock +nostacktrace +trace_printk +noftrace_preempt +nobranch +annotate +nouserstacktrace +nosym-userobj +noprintk-msg-only +context-info +latency-format +sleep-time +graph-time +record-cmd +overwrite +nodisable_on_free +irq-info +markers +function-trace To disable one of the options, echo in the option prepended with "no". @@ -428,13 +770,34 @@ Here are the available options: bin - This will print out the formats in raw binary. - block - TBD (needs update) + block - When set, reading trace_pipe will not block when polled. stacktrace - This is one of the options that changes the trace itself. When a trace is recorded, so is the stack of functions. This allows for back traces of trace sites. + trace_printk - Can disable trace_printk() from writing into the buffer. + + branch - Enable branch tracing with the tracer. + + annotate - It is sometimes confusing when the CPU buffers are full + and one CPU buffer had a lot of events recently, thus + a shorter time frame, were another CPU may have only had + a few events, which lets it have older events. When + the trace is reported, it shows the oldest events first, + and it may look like only one CPU ran (the one with the + oldest events). When the annotate option is set, it will + display when a new CPU buffer started: + + -0 [001] dNs4 21169.031481: wake_up_idle_cpu <-add_timer_on + -0 [001] dNs4 21169.031482: _raw_spin_unlock_irqrestore <-add_timer_on + -0 [001] .Ns4 21169.031484: sub_preempt_count <-_raw_spin_unlock_irqrestore +##### CPU 2 buffer started #### + -0 [002] .N.1 21169.031484: rcu_idle_exit <-cpu_idle + -0 [001] .Ns3 21169.031484: _raw_spin_unlock <-clocksource_watchdog + -0 [001] .Ns3 21169.031485: sub_preempt_count <-_raw_spin_unlock + userstacktrace - This option changes the trace. It records a stacktrace of the current userspace thread. @@ -451,9 +814,13 @@ Here are the available options: a.out-1623 [000] 40874.465068: /root/a.out[+0x480] <-/root/a.out[+0 x494] <- /root/a.out[+0x4a8] <- /lib/libc-2.7.so[+0x1e1a6] - sched-tree - trace all tasks that are on the runqueue, at - every scheduling event. Will add overhead if - there's a lot of tasks running at once. + + printk-msg-only - When set, trace_printk()s will only show the format + and not their parameters (if trace_bprintk() or + trace_bputs() was used to save the trace_printk()). + + context-info - Show only the event data. Hides the comm, PID, + timestamp, CPU, and other useful data. latency-format - This option changes the trace. When it is enabled, the trace displays @@ -461,31 +828,61 @@ x494] <- /root/a.out[+0x4a8] <- /lib/libc-2.7.so[+0x1e1a6] latencies, as described in "Latency trace format". + sleep-time - When running function graph tracer, to include + the time a task schedules out in its function. + When enabled, it will account time the task has been + scheduled out as part of the function call. + + graph-time - When running function graph tracer, to include the + time to call nested functions. When this is not set, + the time reported for the function will only include + the time the function itself executed for, not the time + for functions that it called. + + record-cmd - When any event or tracer is enabled, a hook is enabled + in the sched_switch trace point to fill comm cache + with mapped pids and comms. But this may cause some + overhead, and if you only care about pids, and not the + name of the task, disabling this option can lower the + impact of tracing. + overwrite - This controls what happens when the trace buffer is full. If "1" (default), the oldest events are discarded and overwritten. If "0", then the newest events are discarded. + (see per_cpu/cpu0/stats for overrun and dropped) -ftrace_enabled --------------- + disable_on_free - When the free_buffer is closed, tracing will + stop (tracing_on set to 0). -The following tracers (listed below) give different output -depending on whether or not the sysctl ftrace_enabled is set. To -set ftrace_enabled, one can either use the sysctl function or -set it via the proc file system interface. + irq-info - Shows the interrupt, preempt count, need resched data. + When disabled, the trace looks like: - sysctl kernel.ftrace_enabled=1 +# tracer: function +# +# entries-in-buffer/entries-written: 144405/9452052 #P:4 +# +# TASK-PID CPU# TIMESTAMP FUNCTION +# | | | | | + -0 [002] 23636.756054: ttwu_do_activate.constprop.89 <-try_to_wake_up + -0 [002] 23636.756054: activate_task <-ttwu_do_activate.constprop.89 + -0 [002] 23636.756055: enqueue_task <-activate_task - or - echo 1 > /proc/sys/kernel/ftrace_enabled + markers - When set, the trace_marker is writable (only by root). + When disabled, the trace_marker will error with EINVAL + on write. + + + function-trace - The latency tracers will enable function tracing + if this option is enabled (default it is). When + it is disabled, the latency tracers do not trace + functions. This keeps the overhead of the tracer down + when performing latency tests. -To disable ftrace_enabled simply replace the '1' with '0' in the -above commands. + Note: Some tracers have their own options. They only appear + when the tracer is active. -When ftrace_enabled is set the tracers will also record the -functions that are within the trace. The descriptions of the -tracers will also show an example with ftrace enabled. irqsoff @@ -506,95 +903,133 @@ new trace is saved. To reset the maximum, echo 0 into tracing_max_latency. Here is an example: + # echo 0 > options/function-trace # echo irqsoff > current_tracer - # echo latency-format > trace_options - # echo 0 > tracing_max_latency # echo 1 > tracing_on + # echo 0 > tracing_max_latency # ls -ltr [...] # echo 0 > tracing_on # cat trace # tracer: irqsoff # -irqsoff latency trace v1.1.5 on 2.6.26 --------------------------------------------------------------------- - latency: 12 us, #3/3, CPU#1 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:2) - ----------------- - | task: bash-3730 (uid:0 nice:0 policy:0 rt_prio:0) - ----------------- - => started at: sys_setpgid - => ended at: sys_setpgid - -# _------=> CPU# -# / _-----=> irqs-off -# | / _----=> need-resched -# || / _---=> hardirq/softirq -# ||| / _--=> preempt-depth -# |||| / -# ||||| delay -# cmd pid ||||| time | caller -# \ / ||||| \ | / - bash-3730 1d... 0us : _write_lock_irq (sys_setpgid) - bash-3730 1d..1 1us+: _write_unlock_irq (sys_setpgid) - bash-3730 1d..2 14us : trace_hardirqs_on (sys_setpgid) - - -Here we see that that we had a latency of 12 microsecs (which is -very good). The _write_lock_irq in sys_setpgid disabled -interrupts. The difference between the 12 and the displayed -timestamp 14us occurred because the clock was incremented +# irqsoff latency trace v1.1.5 on 3.8.0-test+ +# -------------------------------------------------------------------- +# latency: 16 us, #4/4, CPU#0 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:4) +# ----------------- +# | task: swapper/0-0 (uid:0 nice:0 policy:0 rt_prio:0) +# ----------------- +# => started at: run_timer_softirq +# => ended at: run_timer_softirq +# +# +# _------=> CPU# +# / _-----=> irqs-off +# | / _----=> need-resched +# || / _---=> hardirq/softirq +# ||| / _--=> preempt-depth +# |||| / delay +# cmd pid ||||| time | caller +# \ / ||||| \ | / + -0 0d.s2 0us+: _raw_spin_lock_irq <-run_timer_softirq + -0 0dNs3 17us : _raw_spin_unlock_irq <-run_timer_softirq + -0 0dNs3 17us+: trace_hardirqs_on <-run_timer_softirq + -0 0dNs3 25us : + => _raw_spin_unlock_irq + => run_timer_softirq + => __do_softirq + => call_softirq + => do_softirq + => irq_exit + => smp_apic_timer_interrupt + => apic_timer_interrupt + => rcu_idle_exit + => cpu_idle + => rest_init + => start_kernel + => x86_64_start_reservations + => x86_64_start_kernel + +Here we see that that we had a latency of 16 microseconds (which is +very good). The _raw_spin_lock_irq in run_timer_softirq disabled +interrupts. The difference between the 16 and the displayed +timestamp 25us occurred because the clock was incremented between the time of recording the max latency and the time of recording the function that had that latency. -Note the above example had ftrace_enabled not set. If we set the -ftrace_enabled, we get a much larger output: +Note the above example had function-trace not set. If we set +function-trace, we get a much larger output: + + with echo 1 > options/function-trace # tracer: irqsoff # -irqsoff latency trace v1.1.5 on 2.6.26-rc8 --------------------------------------------------------------------- - latency: 50 us, #101/101, CPU#0 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:2) - ----------------- - | task: ls-4339 (uid:0 nice:0 policy:0 rt_prio:0) - ----------------- - => started at: __alloc_pages_internal - => ended at: __alloc_pages_internal - -# _------=> CPU# -# / _-----=> irqs-off -# | / _----=> need-resched -# || / _---=> hardirq/softirq -# ||| / _--=> preempt-depth -# |||| / -# ||||| delay -# cmd pid ||||| time | caller -# \ / ||||| \ | / - ls-4339 0...1 0us+: get_page_from_freelist (__alloc_pages_internal) - ls-4339 0d..1 3us : rmqueue_bulk (get_page_from_freelist) - ls-4339 0d..1 3us : _spin_lock (rmqueue_bulk) - ls-4339 0d..1 4us : add_preempt_count (_spin_lock) - ls-4339 0d..2 4us : __rmqueue (rmqueue_bulk) - ls-4339 0d..2 5us : __rmqueue_smallest (__rmqueue) - ls-4339 0d..2 5us : __mod_zone_page_state (__rmqueue_smallest) - ls-4339 0d..2 6us : __rmqueue (rmqueue_bulk) - ls-4339 0d..2 6us : __rmqueue_smallest (__rmqueue) - ls-4339 0d..2 7us : __mod_zone_page_state (__rmqueue_smallest) - ls-4339 0d..2 7us : __rmqueue (rmqueue_bulk) - ls-4339 0d..2 8us : __rmqueue_smallest (__rmqueue) +# irqsoff latency trace v1.1.5 on 3.8.0-test+ +# -------------------------------------------------------------------- +# latency: 71 us, #168/168, CPU#3 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:4) +# ----------------- +# | task: bash-2042 (uid:0 nice:0 policy:0 rt_prio:0) +# ----------------- +# => started at: ata_scsi_queuecmd +# => ended at: ata_scsi_queuecmd +# +# +# _------=> CPU# +# / _-----=> irqs-off +# | / _----=> need-resched +# || / _---=> hardirq/softirq +# ||| / _--=> preempt-depth +# |||| / delay +# cmd pid ||||| time | caller +# \ / ||||| \ | / + bash-2042 3d... 0us : _raw_spin_lock_irqsave <-ata_scsi_queuecmd + bash-2042 3d... 0us : add_preempt_count <-_raw_spin_lock_irqsave + bash-2042 3d..1 1us : ata_scsi_find_dev <-ata_scsi_queuecmd + bash-2042 3d..1 1us : __ata_scsi_find_dev <-ata_scsi_find_dev + bash-2042 3d..1 2us : ata_find_dev.part.14 <-__ata_scsi_find_dev + bash-2042 3d..1 2us : ata_qc_new_init <-__ata_scsi_queuecmd + bash-2042 3d..1 3us : ata_sg_init <-__ata_scsi_queuecmd + bash-2042 3d..1 4us : ata_scsi_rw_xlat <-__ata_scsi_queuecmd + bash-2042 3d..1 4us : ata_build_rw_tf <-ata_scsi_rw_xlat [...] - ls-4339 0d..2 46us : __rmqueue_smallest (__rmqueue) - ls-4339 0d..2 47us : __mod_zone_page_state (__rmqueue_smallest) - ls-4339 0d..2 47us : __rmqueue (rmqueue_bulk) - ls-4339 0d..2 48us : __rmqueue_smallest (__rmqueue) - ls-4339 0d..2 48us : __mod_zone_page_state (__rmqueue_smallest) - ls-4339 0d..2 49us : _spin_unlock (rmqueue_bulk) - ls-4339 0d..2 49us : sub_preempt_count (_spin_unlock) - ls-4339 0d..1 50us : get_page_from_freelist (__alloc_pages_internal) - ls-4339 0d..2 51us : trace_hardirqs_on (__alloc_pages_internal) - - - -Here we traced a 50 microsecond latency. But we also see all the + bash-2042 3d..1 67us : delay_tsc <-__delay + bash-2042 3d..1 67us : add_preempt_count <-delay_tsc + bash-2042 3d..2 67us : sub_preempt_count <-delay_tsc + bash-2042 3d..1 67us : add_preempt_count <-delay_tsc + bash-2042 3d..2 68us : sub_preempt_count <-delay_tsc + bash-2042 3d..1 68us+: ata_bmdma_start <-ata_bmdma_qc_issue + bash-2042 3d..1 71us : _raw_spin_unlock_irqrestore <-ata_scsi_queuecmd + bash-2042 3d..1 71us : _raw_spin_unlock_irqrestore <-ata_scsi_queuecmd + bash-2042 3d..1 72us+: trace_hardirqs_on <-ata_scsi_queuecmd + bash-2042 3d..1 120us : + => _raw_spin_unlock_irqrestore + => ata_scsi_queuecmd + => scsi_dispatch_cmd + => scsi_request_fn + => __blk_run_queue_uncond + => __blk_run_queue + => blk_queue_bio + => generic_make_request + => submit_bio + => submit_bh + => __ext3_get_inode_loc + => ext3_iget + => ext3_lookup + => lookup_real + => __lookup_hash + => walk_component + => lookup_last + => path_lookupat + => filename_lookup + => user_path_at_empty + => user_path_at + => vfs_fstatat + => vfs_stat + => sys_newstat + => system_call_fastpath + + +Here we traced a 71 microsecond latency. But we also see all the functions that were called during that time. Note that by enabling function tracing, we incur an added overhead. This overhead may extend the latency times. But nevertheless, this @@ -614,120 +1049,122 @@ Like the irqsoff tracer, it records the maximum latency for which preemption was disabled. The control of preemptoff tracer is much like the irqsoff tracer. + # echo 0 > options/function-trace # echo preemptoff > current_tracer - # echo latency-format > trace_options - # echo 0 > tracing_max_latency # echo 1 > tracing_on + # echo 0 > tracing_max_latency # ls -ltr [...] # echo 0 > tracing_on # cat trace # tracer: preemptoff # -preemptoff latency trace v1.1.5 on 2.6.26-rc8 --------------------------------------------------------------------- - latency: 29 us, #3/3, CPU#0 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:2) - ----------------- - | task: sshd-4261 (uid:0 nice:0 policy:0 rt_prio:0) - ----------------- - => started at: do_IRQ - => ended at: __do_softirq - -# _------=> CPU# -# / _-----=> irqs-off -# | / _----=> need-resched -# || / _---=> hardirq/softirq -# ||| / _--=> preempt-depth -# |||| / -# ||||| delay -# cmd pid ||||| time | caller -# \ / ||||| \ | / - sshd-4261 0d.h. 0us+: irq_enter (do_IRQ) - sshd-4261 0d.s. 29us : _local_bh_enable (__do_softirq) - sshd-4261 0d.s1 30us : trace_preempt_on (__do_softirq) +# preemptoff latency trace v1.1.5 on 3.8.0-test+ +# -------------------------------------------------------------------- +# latency: 46 us, #4/4, CPU#1 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:4) +# ----------------- +# | task: sshd-1991 (uid:0 nice:0 policy:0 rt_prio:0) +# ----------------- +# => started at: do_IRQ +# => ended at: do_IRQ +# +# +# _------=> CPU# +# / _-----=> irqs-off +# | / _----=> need-resched +# || / _---=> hardirq/softirq +# ||| / _--=> preempt-depth +# |||| / delay +# cmd pid ||||| time | caller +# \ / ||||| \ | / + sshd-1991 1d.h. 0us+: irq_enter <-do_IRQ + sshd-1991 1d..1 46us : irq_exit <-do_IRQ + sshd-1991 1d..1 47us+: trace_preempt_on <-do_IRQ + sshd-1991 1d..1 52us : + => sub_preempt_count + => irq_exit + => do_IRQ + => ret_from_intr This has some more changes. Preemption was disabled when an -interrupt came in (notice the 'h'), and was enabled while doing -a softirq. (notice the 's'). But we also see that interrupts -have been disabled when entering the preempt off section and -leaving it (the 'd'). We do not know if interrupts were enabled -in the mean time. +interrupt came in (notice the 'h'), and was enabled on exit. +But we also see that interrupts have been disabled when entering +the preempt off section and leaving it (the 'd'). We do not know if +interrupts were enabled in the mean time or shortly after this +was over. # tracer: preemptoff # -preemptoff latency trace v1.1.5 on 2.6.26-rc8 --------------------------------------------------------------------- - latency: 63 us, #87/87, CPU#0 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:2) - ----------------- - | task: sshd-4261 (uid:0 nice:0 policy:0 rt_prio:0) - ----------------- - => started at: remove_wait_queue - => ended at: __do_softirq - -# _------=> CPU# -# / _-----=> irqs-off -# | / _----=> need-resched -# || / _---=> hardirq/softirq -# ||| / _--=> preempt-depth -# |||| / -# ||||| delay -# cmd pid ||||| time | caller -# \ / ||||| \ | / - sshd-4261 0d..1 0us : _spin_lock_irqsave (remove_wait_queue) - sshd-4261 0d..1 1us : _spin_unlock_irqrestore (remove_wait_queue) - sshd-4261 0d..1 2us : do_IRQ (common_interrupt) - sshd-4261 0d..1 2us : irq_enter (do_IRQ) - sshd-4261 0d..1 2us : idle_cpu (irq_enter) - sshd-4261 0d..1 3us : add_preempt_count (irq_enter) - sshd-4261 0d.h1 3us : idle_cpu (irq_enter) - sshd-4261 0d.h. 4us : handle_fasteoi_irq (do_IRQ) +# preemptoff latency trace v1.1.5 on 3.8.0-test+ +# -------------------------------------------------------------------- +# latency: 83 us, #241/241, CPU#1 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:4) +# ----------------- +# | task: bash-1994 (uid:0 nice:0 policy:0 rt_prio:0) +# ----------------- +# => started at: wake_up_new_task +# => ended at: task_rq_unlock +# +# +# _------=> CPU# +# / _-----=> irqs-off +# | / _----=> need-resched +# || / _---=> hardirq/softirq +# ||| / _--=> preempt-depth +# |||| / delay +# cmd pid ||||| time | caller +# \ / ||||| \ | / + bash-1994 1d..1 0us : _raw_spin_lock_irqsave <-wake_up_new_task + bash-1994 1d..1 0us : select_task_rq_fair <-select_task_rq + bash-1994 1d..1 1us : __rcu_read_lock <-select_task_rq_fair + bash-1994 1d..1 1us : source_load <-select_task_rq_fair + bash-1994 1d..1 1us : source_load <-select_task_rq_fair [...] - sshd-4261 0d.h. 12us : add_preempt_count (_spin_lock) - sshd-4261 0d.h1 12us : ack_ioapic_quirk_irq (handle_fasteoi_irq) - sshd-4261 0d.h1 13us : move_native_irq (ack_ioapic_quirk_irq) - sshd-4261 0d.h1 13us : _spin_unlock (handle_fasteoi_irq) - sshd-4261 0d.h1 14us : sub_preempt_count (_spin_unlock) - sshd-4261 0d.h1 14us : irq_exit (do_IRQ) - sshd-4261 0d.h1 15us : sub_preempt_count (irq_exit) - sshd-4261 0d..2 15us : do_softirq (irq_exit) - sshd-4261 0d... 15us : __do_softirq (do_softirq) - sshd-4261 0d... 16us : __local_bh_disable (__do_softirq) - sshd-4261 0d... 16us+: add_preempt_count (__local_bh_disable) - sshd-4261 0d.s4 20us : add_preempt_count (__local_bh_disable) - sshd-4261 0d.s4 21us : sub_preempt_count (local_bh_enable) - sshd-4261 0d.s5 21us : sub_preempt_count (local_bh_enable) + bash-1994 1d..1 12us : irq_enter <-smp_apic_timer_interrupt + bash-1994 1d..1 12us : rcu_irq_enter <-irq_enter + bash-1994 1d..1 13us : add_preempt_count <-irq_enter + bash-1994 1d.h1 13us : exit_idle <-smp_apic_timer_interrupt + bash-1994 1d.h1 13us : hrtimer_interrupt <-smp_apic_timer_interrupt + bash-1994 1d.h1 13us : _raw_spin_lock <-hrtimer_interrupt + bash-1994 1d.h1 14us : add_preempt_count <-_raw_spin_lock + bash-1994 1d.h2 14us : ktime_get_update_offsets <-hrtimer_interrupt [...] - sshd-4261 0d.s6 41us : add_preempt_count (__local_bh_disable) - sshd-4261 0d.s6 42us : sub_preempt_count (local_bh_enable) - sshd-4261 0d.s7 42us : sub_preempt_count (local_bh_enable) - sshd-4261 0d.s5 43us : add_preempt_count (__local_bh_disable) - sshd-4261 0d.s5 43us : sub_preempt_count (local_bh_enable_ip) - sshd-4261 0d.s6 44us : sub_preempt_count (local_bh_enable_ip) - sshd-4261 0d.s5 44us : add_preempt_count (__local_bh_disable) - sshd-4261 0d.s5 45us : sub_preempt_count (local_bh_enable) + bash-1994 1d.h1 35us : lapic_next_event <-clockevents_program_event + bash-1994 1d.h1 35us : irq_exit <-smp_apic_timer_interrupt + bash-1994 1d.h1 36us : sub_preempt_count <-irq_exit + bash-1994 1d..2 36us : do_softirq <-irq_exit + bash-1994 1d..2 36us : __do_softirq <-call_softirq + bash-1994 1d..2 36us : __local_bh_disable <-__do_softirq + bash-1994 1d.s2 37us : add_preempt_count <-_raw_spin_lock_irq + bash-1994 1d.s3 38us : _raw_spin_unlock <-run_timer_softirq + bash-1994 1d.s3 39us : sub_preempt_count <-_raw_spin_unlock + bash-1994 1d.s2 39us : call_timer_fn <-run_timer_softirq [...] - sshd-4261 0d.s. 63us : _local_bh_enable (__do_softirq) - sshd-4261 0d.s1 64us : trace_preempt_on (__do_softirq) + bash-1994 1dNs2 81us : cpu_needs_another_gp <-rcu_process_callbacks + bash-1994 1dNs2 82us : __local_bh_enable <-__do_softirq + bash-1994 1dNs2 82us : sub_preempt_count <-__local_bh_enable + bash-1994 1dN.2 82us : idle_cpu <-irq_exit + bash-1994 1dN.2 83us : rcu_irq_exit <-irq_exit + bash-1994 1dN.2 83us : sub_preempt_count <-irq_exit + bash-1994 1.N.1 84us : _raw_spin_unlock_irqrestore <-task_rq_unlock + bash-1994 1.N.1 84us+: trace_preempt_on <-task_rq_unlock + bash-1994 1.N.1 104us : + => sub_preempt_count + => _raw_spin_unlock_irqrestore + => task_rq_unlock + => wake_up_new_task + => do_fork + => sys_clone + => stub_clone The above is an example of the preemptoff trace with -ftrace_enabled set. Here we see that interrupts were disabled +function-trace set. Here we see that interrupts were not disabled the entire time. The irq_enter code lets us know that we entered an interrupt 'h'. Before that, the functions being traced still show that it is not in an interrupt, but we can see from the functions themselves that this is not the case. -Notice that __do_softirq when called does not have a -preempt_count. It may seem that we missed a preempt enabling. -What really happened is that the preempt count is held on the -thread's stack and we switched to the softirq stack (4K stacks -in effect). The code does not copy the preempt count, but -because interrupts are disabled, we do not need to worry about -it. Having a tracer like this is good for letting people know -what really happens inside the kernel. - - preemptirqsoff -------------- @@ -762,38 +1199,57 @@ tracer. Again, using this trace is much like the irqsoff and preemptoff tracers. + # echo 0 > options/function-trace # echo preemptirqsoff > current_tracer - # echo latency-format > trace_options - # echo 0 > tracing_max_latency # echo 1 > tracing_on + # echo 0 > tracing_max_latency # ls -ltr [...] # echo 0 > tracing_on # cat trace # tracer: preemptirqsoff # -preemptirqsoff latency trace v1.1.5 on 2.6.26-rc8 --------------------------------------------------------------------- - latency: 293 us, #3/3, CPU#0 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:2) - ----------------- - | task: ls-4860 (uid:0 nice:0 policy:0 rt_prio:0) - ----------------- - => started at: apic_timer_interrupt - => ended at: __do_softirq - -# _------=> CPU# -# / _-----=> irqs-off -# | / _----=> need-resched -# || / _---=> hardirq/softirq -# ||| / _--=> preempt-depth -# |||| / -# ||||| delay -# cmd pid ||||| time | caller -# \ / ||||| \ | / - ls-4860 0d... 0us!: trace_hardirqs_off_thunk (apic_timer_interrupt) - ls-4860 0d.s. 294us : _local_bh_enable (__do_softirq) - ls-4860 0d.s1 294us : trace_preempt_on (__do_softirq) - +# preemptirqsoff latency trace v1.1.5 on 3.8.0-test+ +# -------------------------------------------------------------------- +# latency: 100 us, #4/4, CPU#3 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:4) +# ----------------- +# | task: ls-2230 (uid:0 nice:0 policy:0 rt_prio:0) +# ----------------- +# => started at: ata_scsi_queuecmd +# => ended at: ata_scsi_queuecmd +# +# +# _------=> CPU# +# / _-----=> irqs-off +# | / _----=> need-resched +# || / _---=> hardirq/softirq +# ||| / _--=> preempt-depth +# |||| / delay +# cmd pid ||||| time | caller +# \ / ||||| \ | / + ls-2230 3d... 0us+: _raw_spin_lock_irqsave <-ata_scsi_queuecmd + ls-2230 3...1 100us : _raw_spin_unlock_irqrestore <-ata_scsi_queuecmd + ls-2230 3...1 101us+: trace_preempt_on <-ata_scsi_queuecmd + ls-2230 3...1 111us : + => sub_preempt_count + => _raw_spin_unlock_irqrestore + => ata_scsi_queuecmd + => scsi_dispatch_cmd + => scsi_request_fn + => __blk_run_queue_uncond + => __blk_run_queue + => blk_queue_bio + => generic_make_request + => submit_bio + => submit_bh + => ext3_bread + => ext3_dir_bread + => htree_dirblock_to_tree + => ext3_htree_fill_tree + => ext3_readdir + => vfs_readdir + => sys_getdents + => system_call_fastpath The trace_hardirqs_off_thunk is called from assembly on x86 when @@ -802,105 +1258,158 @@ function tracing, we do not know if interrupts were enabled within the preemption points. We do see that it started with preemption enabled. -Here is a trace with ftrace_enabled set: - +Here is a trace with function-trace set: # tracer: preemptirqsoff # -preemptirqsoff latency trace v1.1.5 on 2.6.26-rc8 --------------------------------------------------------------------- - latency: 105 us, #183/183, CPU#0 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:2) - ----------------- - | task: sshd-4261 (uid:0 nice:0 policy:0 rt_prio:0) - ----------------- - => started at: write_chan - => ended at: __do_softirq - -# _------=> CPU# -# / _-----=> irqs-off -# | / _----=> need-resched -# || / _---=> hardirq/softirq -# ||| / _--=> preempt-depth -# |||| / -# ||||| delay -# cmd pid ||||| time | caller -# \ / ||||| \ | / - ls-4473 0.N.. 0us : preempt_schedule (write_chan) - ls-4473 0dN.1 1us : _spin_lock (schedule) - ls-4473 0dN.1 2us : add_preempt_count (_spin_lock) - ls-4473 0d..2 2us : put_prev_task_fair (schedule) -[...] - ls-4473 0d..2 13us : set_normalized_timespec (ktime_get_ts) - ls-4473 0d..2 13us : __switch_to (schedule) - sshd-4261 0d..2 14us : finish_task_switch (schedule) - sshd-4261 0d..2 14us : _spin_unlock_irq (finish_task_switch) - sshd-4261 0d..1 15us : add_preempt_count (_spin_lock_irqsave) - sshd-4261 0d..2 16us : _spin_unlock_irqrestore (hrtick_set) - sshd-4261 0d..2 16us : do_IRQ (common_interrupt) - sshd-4261 0d..2 17us : irq_enter (do_IRQ) - sshd-4261 0d..2 17us : idle_cpu (irq_enter) - sshd-4261 0d..2 18us : add_preempt_count (irq_enter) - sshd-4261 0d.h2 18us : idle_cpu (irq_enter) - sshd-4261 0d.h. 18us : handle_fasteoi_irq (do_IRQ) - sshd-4261 0d.h. 19us : _spin_lock (handle_fasteoi_irq) - sshd-4261 0d.h. 19us : add_preempt_count (_spin_lock) - sshd-4261 0d.h1 20us : _spin_unlock (handle_fasteoi_irq) - sshd-4261 0d.h1 20us : sub_preempt_count (_spin_unlock) -[...] - sshd-4261 0d.h1 28us : _spin_unlock (handle_fasteoi_irq) - sshd-4261 0d.h1 29us : sub_preempt_count (_spin_unlock) - sshd-4261 0d.h2 29us : irq_exit (do_IRQ) - sshd-4261 0d.h2 29us : sub_preempt_count (irq_exit) - sshd-4261 0d..3 30us : do_softirq (irq_exit) - sshd-4261 0d... 30us : __do_softirq (do_softirq) - sshd-4261 0d... 31us : __local_bh_disable (__do_softirq) - sshd-4261 0d... 31us+: add_preempt_count (__local_bh_disable) - sshd-4261 0d.s4 34us : add_preempt_count (__local_bh_disable) +# preemptirqsoff latency trace v1.1.5 on 3.8.0-test+ +# -------------------------------------------------------------------- +# latency: 161 us, #339/339, CPU#3 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:4) +# ----------------- +# | task: ls-2269 (uid:0 nice:0 policy:0 rt_prio:0) +# ----------------- +# => started at: schedule +# => ended at: mutex_unlock +# +# +# _------=> CPU# +# / _-----=> irqs-off +# | / _----=> need-resched +# || / _---=> hardirq/softirq +# ||| / _--=> preempt-depth +# |||| / delay +# cmd pid ||||| time | caller +# \ / ||||| \ | / +kworker/-59 3...1 0us : __schedule <-schedule +kworker/-59 3d..1 0us : rcu_preempt_qs <-rcu_note_context_switch +kworker/-59 3d..1 1us : add_preempt_count <-_raw_spin_lock_irq +kworker/-59 3d..2 1us : deactivate_task <-__schedule +kworker/-59 3d..2 1us : dequeue_task <-deactivate_task +kworker/-59 3d..2 2us : update_rq_clock <-dequeue_task +kworker/-59 3d..2 2us : dequeue_task_fair <-dequeue_task +kworker/-59 3d..2 2us : update_curr <-dequeue_task_fair +kworker/-59 3d..2 2us : update_min_vruntime <-update_curr +kworker/-59 3d..2 3us : cpuacct_charge <-update_curr +kworker/-59 3d..2 3us : __rcu_read_lock <-cpuacct_charge +kworker/-59 3d..2 3us : __rcu_read_unlock <-cpuacct_charge +kworker/-59 3d..2 3us : update_cfs_rq_blocked_load <-dequeue_task_fair +kworker/-59 3d..2 4us : clear_buddies <-dequeue_task_fair +kworker/-59 3d..2 4us : account_entity_dequeue <-dequeue_task_fair +kworker/-59 3d..2 4us : update_min_vruntime <-dequeue_task_fair +kworker/-59 3d..2 4us : update_cfs_shares <-dequeue_task_fair +kworker/-59 3d..2 5us : hrtick_update <-dequeue_task_fair +kworker/-59 3d..2 5us : wq_worker_sleeping <-__schedule +kworker/-59 3d..2 5us : kthread_data <-wq_worker_sleeping +kworker/-59 3d..2 5us : put_prev_task_fair <-__schedule +kworker/-59 3d..2 6us : pick_next_task_fair <-pick_next_task +kworker/-59 3d..2 6us : clear_buddies <-pick_next_task_fair +kworker/-59 3d..2 6us : set_next_entity <-pick_next_task_fair +kworker/-59 3d..2 6us : update_stats_wait_end <-set_next_entity + ls-2269 3d..2 7us : finish_task_switch <-__schedule + ls-2269 3d..2 7us : _raw_spin_unlock_irq <-finish_task_switch + ls-2269 3d..2 8us : do_IRQ <-ret_from_intr + ls-2269 3d..2 8us : irq_enter <-do_IRQ + ls-2269 3d..2 8us : rcu_irq_enter <-irq_enter + ls-2269 3d..2 9us : add_preempt_count <-irq_enter + ls-2269 3d.h2 9us : exit_idle <-do_IRQ [...] - sshd-4261 0d.s3 43us : sub_preempt_count (local_bh_enable_ip) - sshd-4261 0d.s4 44us : sub_preempt_count (local_bh_enable_ip) - sshd-4261 0d.s3 44us : smp_apic_timer_interrupt (apic_timer_interrupt) - sshd-4261 0d.s3 45us : irq_enter (smp_apic_timer_interrupt) - sshd-4261 0d.s3 45us : idle_cpu (irq_enter) - sshd-4261 0d.s3 46us : add_preempt_count (irq_enter) - sshd-4261 0d.H3 46us : idle_cpu (irq_enter) - sshd-4261 0d.H3 47us : hrtimer_interrupt (smp_apic_timer_interrupt) - sshd-4261 0d.H3 47us : ktime_get (hrtimer_interrupt) + ls-2269 3d.h3 20us : sub_preempt_count <-_raw_spin_unlock + ls-2269 3d.h2 20us : irq_exit <-do_IRQ + ls-2269 3d.h2 21us : sub_preempt_count <-irq_exit + ls-2269 3d..3 21us : do_softirq <-irq_exit + ls-2269 3d..3 21us : __do_softirq <-call_softirq + ls-2269 3d..3 21us+: __local_bh_disable <-__do_softirq + ls-2269 3d.s4 29us : sub_preempt_count <-_local_bh_enable_ip + ls-2269 3d.s5 29us : sub_preempt_count <-_local_bh_enable_ip + ls-2269 3d.s5 31us : do_IRQ <-ret_from_intr + ls-2269 3d.s5 31us : irq_enter <-do_IRQ + ls-2269 3d.s5 31us : rcu_irq_enter <-irq_enter [...] - sshd-4261 0d.H3 81us : tick_program_event (hrtimer_interrupt) - sshd-4261 0d.H3 82us : ktime_get (tick_program_event) - sshd-4261 0d.H3 82us : ktime_get_ts (ktime_get) - sshd-4261 0d.H3 83us : getnstimeofday (ktime_get_ts) - sshd-4261 0d.H3 83us : set_normalized_timespec (ktime_get_ts) - sshd-4261 0d.H3 84us : clockevents_program_event (tick_program_event) - sshd-4261 0d.H3 84us : lapic_next_event (clockevents_program_event) - sshd-4261 0d.H3 85us : irq_exit (smp_apic_timer_interrupt) - sshd-4261 0d.H3 85us : sub_preempt_count (irq_exit) - sshd-4261 0d.s4 86us : sub_preempt_count (irq_exit) - sshd-4261 0d.s3 86us : add_preempt_count (__local_bh_disable) + ls-2269 3d.s5 31us : rcu_irq_enter <-irq_enter + ls-2269 3d.s5 32us : add_preempt_count <-irq_enter + ls-2269 3d.H5 32us : exit_idle <-do_IRQ + ls-2269 3d.H5 32us : handle_irq <-do_IRQ + ls-2269 3d.H5 32us : irq_to_desc <-handle_irq + ls-2269 3d.H5 33us : handle_fasteoi_irq <-handle_irq [...] - sshd-4261 0d.s1 98us : sub_preempt_count (net_rx_action) - sshd-4261 0d.s. 99us : add_preempt_count (_spin_lock_irq) - sshd-4261 0d.s1 99us+: _spin_unlock_irq (run_timer_softirq) - sshd-4261 0d.s. 104us : _local_bh_enable (__do_softirq) - sshd-4261 0d.s. 104us : sub_preempt_count (_local_bh_enable) - sshd-4261 0d.s. 105us : _local_bh_enable (__do_softirq) - sshd-4261 0d.s1 105us : trace_preempt_on (__do_softirq) - - -This is a very interesting trace. It started with the preemption -of the ls task. We see that the task had the "need_resched" bit -set via the 'N' in the trace. Interrupts were disabled before -the spin_lock at the beginning of the trace. We see that a -schedule took place to run sshd. When the interrupts were -enabled, we took an interrupt. On return from the interrupt -handler, the softirq ran. We took another interrupt while -running the softirq as we see from the capital 'H'. + ls-2269 3d.s5 158us : _raw_spin_unlock_irqrestore <-rtl8139_poll + ls-2269 3d.s3 158us : net_rps_action_and_irq_enable.isra.65 <-net_rx_action + ls-2269 3d.s3 159us : __local_bh_enable <-__do_softirq + ls-2269 3d.s3 159us : sub_preempt_count <-__local_bh_enable + ls-2269 3d..3 159us : idle_cpu <-irq_exit + ls-2269 3d..3 159us : rcu_irq_exit <-irq_exit + ls-2269 3d..3 160us : sub_preempt_count <-irq_exit + ls-2269 3d... 161us : __mutex_unlock_slowpath <-mutex_unlock + ls-2269 3d... 162us+: trace_hardirqs_on <-mutex_unlock + ls-2269 3d... 186us : + => __mutex_unlock_slowpath + => mutex_unlock + => process_output + => n_tty_write + => tty_write + => vfs_write + => sys_write + => system_call_fastpath + +This is an interesting trace. It started with kworker running and +scheduling out and ls taking over. But as soon as ls released the +rq lock and enabled interrupts (but not preemption) an interrupt +triggered. When the interrupt finished, it started running softirqs. +But while the softirq was running, another interrupt triggered. +When an interrupt is running inside a softirq, the annotation is 'H'. wakeup ------ +One common case that people are interested in tracing is the +time it takes for a task that is woken to actually wake up. +Now for non Real-Time tasks, this can be arbitrary. But tracing +it none the less can be interesting. + +Without function tracing: + + # echo 0 > options/function-trace + # echo wakeup > current_tracer + # echo 1 > tracing_on + # echo 0 > tracing_max_latency + # chrt -f 5 sleep 1 + # echo 0 > tracing_on + # cat trace +# tracer: wakeup +# +# wakeup latency trace v1.1.5 on 3.8.0-test+ +# -------------------------------------------------------------------- +# latency: 15 us, #4/4, CPU#3 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:4) +# ----------------- +# | task: kworker/3:1H-312 (uid:0 nice:-20 policy:0 rt_prio:0) +# ----------------- +# +# _------=> CPU# +# / _-----=> irqs-off +# | / _----=> need-resched +# || / _---=> hardirq/softirq +# ||| / _--=> preempt-depth +# |||| / delay +# cmd pid ||||| time | caller +# \ / ||||| \ | / + -0 3dNs7 0us : 0:120:R + [003] 312:100:R kworker/3:1H + -0 3dNs7 1us+: ttwu_do_activate.constprop.87 <-try_to_wake_up + -0 3d..3 15us : __schedule <-schedule + -0 3d..3 15us : 0:120:R ==> [003] 312:100:R kworker/3:1H + +The tracer only traces the highest priority task in the system +to avoid tracing the normal circumstances. Here we see that +the kworker with a nice priority of -20 (not very nice), took +just 15 microseconds from the time it woke up, to the time it +ran. + +Non Real-Time tasks are not that interesting. A more interesting +trace is to concentrate only on Real-Time tasks. + +wakeup_rt +--------- + In a Real-Time environment it is very important to know the wakeup time it takes for the highest priority task that is woken up to the time that it executes. This is also known as "schedule @@ -914,124 +1423,229 @@ Real-Time environments are interested in the worst case latency. That is the longest latency it takes for something to happen, and not the average. We can have a very fast scheduler that may only have a large latency once in a while, but that would not -work well with Real-Time tasks. The wakeup tracer was designed +work well with Real-Time tasks. The wakeup_rt tracer was designed to record the worst case wakeups of RT tasks. Non-RT tasks are not recorded because the tracer only records one worst case and tracing non-RT tasks that are unpredictable will overwrite the -worst case latency of RT tasks. +worst case latency of RT tasks (just run the normal wakeup +tracer for a while to see that effect). Since this tracer only deals with RT tasks, we will run this slightly differently than we did with the previous tracers. Instead of performing an 'ls', we will run 'sleep 1' under 'chrt' which changes the priority of the task. - # echo wakeup > current_tracer - # echo latency-format > trace_options - # echo 0 > tracing_max_latency + # echo 0 > options/function-trace + # echo wakeup_rt > current_tracer # echo 1 > tracing_on + # echo 0 > tracing_max_latency # chrt -f 5 sleep 1 # echo 0 > tracing_on # cat trace # tracer: wakeup # -wakeup latency trace v1.1.5 on 2.6.26-rc8 --------------------------------------------------------------------- - latency: 4 us, #2/2, CPU#1 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:2) - ----------------- - | task: sleep-4901 (uid:0 nice:0 policy:1 rt_prio:5) - ----------------- - -# _------=> CPU# -# / _-----=> irqs-off -# | / _----=> need-resched -# || / _---=> hardirq/softirq -# ||| / _--=> preempt-depth -# |||| / -# ||||| delay -# cmd pid ||||| time | caller -# \ / ||||| \ | / - -0 1d.h4 0us+: try_to_wake_up (wake_up_process) - -0 1d..4 4us : schedule (cpu_idle) - - -Running this on an idle system, we see that it only took 4 -microseconds to perform the task switch. Note, since the trace -marker in the schedule is before the actual "switch", we stop -the tracing when the recorded task is about to schedule in. This -may change if we add a new marker at the end of the scheduler. - -Notice that the recorded task is 'sleep' with the PID of 4901 +# tracer: wakeup_rt +# +# wakeup_rt latency trace v1.1.5 on 3.8.0-test+ +# -------------------------------------------------------------------- +# latency: 5 us, #4/4, CPU#3 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:4) +# ----------------- +# | task: sleep-2389 (uid:0 nice:0 policy:1 rt_prio:5) +# ----------------- +# +# _------=> CPU# +# / _-----=> irqs-off +# | / _----=> need-resched +# || / _---=> hardirq/softirq +# ||| / _--=> preempt-depth +# |||| / delay +# cmd pid ||||| time | caller +# \ / ||||| \ | / + -0 3d.h4 0us : 0:120:R + [003] 2389: 94:R sleep + -0 3d.h4 1us+: ttwu_do_activate.constprop.87 <-try_to_wake_up + -0 3d..3 5us : __schedule <-schedule + -0 3d..3 5us : 0:120:R ==> [003] 2389: 94:R sleep + + +Running this on an idle system, we see that it only took 5 microseconds +to perform the task switch. Note, since the trace point in the schedule +is before the actual "switch", we stop the tracing when the recorded task +is about to schedule in. This may change if we add a new marker at the +end of the scheduler. + +Notice that the recorded task is 'sleep' with the PID of 2389 and it has an rt_prio of 5. This priority is user-space priority and not the internal kernel priority. The policy is 1 for SCHED_FIFO and 2 for SCHED_RR. -Doing the same with chrt -r 5 and ftrace_enabled set. +Note, that the trace data shows the internal priority (99 - rtprio). -# tracer: wakeup + -0 3d..3 5us : 0:120:R ==> [003] 2389: 94:R sleep + +The 0:120:R means idle was running with a nice priority of 0 (120 - 20) +and in the running state 'R'. The sleep task was scheduled in with +2389: 94:R. That is the priority is the kernel rtprio (99 - 5 = 94) +and it too is in the running state. + +Doing the same with chrt -r 5 and function-trace set. + + echo 1 > options/function-trace + +# tracer: wakeup_rt # -wakeup latency trace v1.1.5 on 2.6.26-rc8 --------------------------------------------------------------------- - latency: 50 us, #60/60, CPU#1 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:2) - ----------------- - | task: sleep-4068 (uid:0 nice:0 policy:2 rt_prio:5) - ----------------- - -# _------=> CPU# -# / _-----=> irqs-off -# | / _----=> need-resched -# || / _---=> hardirq/softirq -# ||| / _--=> preempt-depth -# |||| / -# ||||| delay -# cmd pid ||||| time | caller -# \ / ||||| \ | / -ksoftirq-7 1d.H3 0us : try_to_wake_up (wake_up_process) -ksoftirq-7 1d.H4 1us : sub_preempt_count (marker_probe_cb) -ksoftirq-7 1d.H3 2us : check_preempt_wakeup (try_to_wake_up) -ksoftirq-7 1d.H3 3us : update_curr (check_preempt_wakeup) -ksoftirq-7 1d.H3 4us : calc_delta_mine (update_curr) -ksoftirq-7 1d.H3 5us : __resched_task (check_preempt_wakeup) -ksoftirq-7 1d.H3 6us : task_wake_up_rt (try_to_wake_up) -ksoftirq-7 1d.H3 7us : _spin_unlock_irqrestore (try_to_wake_up) -[...] -ksoftirq-7 1d.H2 17us : irq_exit (smp_apic_timer_interrupt) -ksoftirq-7 1d.H2 18us : sub_preempt_count (irq_exit) -ksoftirq-7 1d.s3 19us : sub_preempt_count (irq_exit) -ksoftirq-7 1..s2 20us : rcu_process_callbacks (__do_softirq) -[...] -ksoftirq-7 1..s2 26us : __rcu_process_callbacks (rcu_process_callbacks) -ksoftirq-7 1d.s2 27us : _local_bh_enable (__do_softirq) -ksoftirq-7 1d.s2 28us : sub_preempt_count (_local_bh_enable) -ksoftirq-7 1.N.3 29us : sub_preempt_count (ksoftirqd) -ksoftirq-7 1.N.2 30us : _cond_resched (ksoftirqd) -ksoftirq-7 1.N.2 31us : __cond_resched (_cond_resched) -ksoftirq-7 1.N.2 32us : add_preempt_count (__cond_resched) -ksoftirq-7 1.N.2 33us : schedule (__cond_resched) -ksoftirq-7 1.N.2 33us : add_preempt_count (schedule) -ksoftirq-7 1.N.3 34us : hrtick_clear (schedule) -ksoftirq-7 1dN.3 35us : _spin_lock (schedule) -ksoftirq-7 1dN.3 36us : add_preempt_count (_spin_lock) -ksoftirq-7 1d..4 37us : put_prev_task_fair (schedule) -ksoftirq-7 1d..4 38us : update_curr (put_prev_task_fair) -[...] -ksoftirq-7 1d..5 47us : _spin_trylock (tracing_record_cmdline) -ksoftirq-7 1d..5 48us : add_preempt_count (_spin_trylock) -ksoftirq-7 1d..6 49us : _spin_unlock (tracing_record_cmdline) -ksoftirq-7 1d..6 49us : sub_preempt_count (_spin_unlock) -ksoftirq-7 1d..4 50us : schedule (__cond_resched) - -The interrupt went off while running ksoftirqd. This task runs -at SCHED_OTHER. Why did not we see the 'N' set early? This may -be a harmless bug with x86_32 and 4K stacks. On x86_32 with 4K -stacks configured, the interrupt and softirq run with their own -stack. Some information is held on the top of the task's stack -(need_resched and preempt_count are both stored there). The -setting of the NEED_RESCHED bit is done directly to the task's -stack, but the reading of the NEED_RESCHED is done by looking at -the current stack, which in this case is the stack for the hard -interrupt. This hides the fact that NEED_RESCHED has been set. -We do not see the 'N' until we switch back to the task's -assigned stack. +# wakeup_rt latency trace v1.1.5 on 3.8.0-test+ +# -------------------------------------------------------------------- +# latency: 29 us, #85/85, CPU#3 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:4) +# ----------------- +# | task: sleep-2448 (uid:0 nice:0 policy:1 rt_prio:5) +# ----------------- +# +# _------=> CPU# +# / _-----=> irqs-off +# | / _----=> need-resched +# || / _---=> hardirq/softirq +# ||| / _--=> preempt-depth +# |||| / delay +# cmd pid ||||| time | caller +# \ / ||||| \ | / + -0 3d.h4 1us+: 0:120:R + [003] 2448: 94:R sleep + -0 3d.h4 2us : ttwu_do_activate.constprop.87 <-try_to_wake_up + -0 3d.h3 3us : check_preempt_curr <-ttwu_do_wakeup + -0 3d.h3 3us : resched_task <-check_preempt_curr + -0 3dNh3 4us : task_woken_rt <-ttwu_do_wakeup + -0 3dNh3 4us : _raw_spin_unlock <-try_to_wake_up + -0 3dNh3 4us : sub_preempt_count <-_raw_spin_unlock + -0 3dNh2 5us : ttwu_stat <-try_to_wake_up + -0 3dNh2 5us : _raw_spin_unlock_irqrestore <-try_to_wake_up + -0 3dNh2 6us : sub_preempt_count <-_raw_spin_unlock_irqrestore + -0 3dNh1 6us : _raw_spin_lock <-__run_hrtimer + -0 3dNh1 6us : add_preempt_count <-_raw_spin_lock + -0 3dNh2 7us : _raw_spin_unlock <-hrtimer_interrupt + -0 3dNh2 7us : sub_preempt_count <-_raw_spin_unlock + -0 3dNh1 7us : tick_program_event <-hrtimer_interrupt + -0 3dNh1 7us : clockevents_program_event <-tick_program_event + -0 3dNh1 8us : ktime_get <-clockevents_program_event + -0 3dNh1 8us : lapic_next_event <-clockevents_program_event + -0 3dNh1 8us : irq_exit <-smp_apic_timer_interrupt + -0 3dNh1 9us : sub_preempt_count <-irq_exit + -0 3dN.2 9us : idle_cpu <-irq_exit + -0 3dN.2 9us : rcu_irq_exit <-irq_exit + -0 3dN.2 10us : rcu_eqs_enter_common.isra.45 <-rcu_irq_exit + -0 3dN.2 10us : sub_preempt_count <-irq_exit + -0 3.N.1 11us : rcu_idle_exit <-cpu_idle + -0 3dN.1 11us : rcu_eqs_exit_common.isra.43 <-rcu_idle_exit + -0 3.N.1 11us : tick_nohz_idle_exit <-cpu_idle + -0 3dN.1 12us : menu_hrtimer_cancel <-tick_nohz_idle_exit + -0 3dN.1 12us : ktime_get <-tick_nohz_idle_exit + -0 3dN.1 12us : tick_do_update_jiffies64 <-tick_nohz_idle_exit + -0 3dN.1 13us : update_cpu_load_nohz <-tick_nohz_idle_exit + -0 3dN.1 13us : _raw_spin_lock <-update_cpu_load_nohz + -0 3dN.1 13us : add_preempt_count <-_raw_spin_lock + -0 3dN.2 13us : __update_cpu_load <-update_cpu_load_nohz + -0 3dN.2 14us : sched_avg_update <-__update_cpu_load + -0 3dN.2 14us : _raw_spin_unlock <-update_cpu_load_nohz + -0 3dN.2 14us : sub_preempt_count <-_raw_spin_unlock + -0 3dN.1 15us : calc_load_exit_idle <-tick_nohz_idle_exit + -0 3dN.1 15us : touch_softlockup_watchdog <-tick_nohz_idle_exit + -0 3dN.1 15us : hrtimer_cancel <-tick_nohz_idle_exit + -0 3dN.1 15us : hrtimer_try_to_cancel <-hrtimer_cancel + -0 3dN.1 16us : lock_hrtimer_base.isra.18 <-hrtimer_try_to_cancel + -0 3dN.1 16us : _raw_spin_lock_irqsave <-lock_hrtimer_base.isra.18 + -0 3dN.1 16us : add_preempt_count <-_raw_spin_lock_irqsave + -0 3dN.2 17us : __remove_hrtimer <-remove_hrtimer.part.16 + -0 3dN.2 17us : hrtimer_force_reprogram <-__remove_hrtimer + -0 3dN.2 17us : tick_program_event <-hrtimer_force_reprogram + -0 3dN.2 18us : clockevents_program_event <-tick_program_event + -0 3dN.2 18us : ktime_get <-clockevents_program_event + -0 3dN.2 18us : lapic_next_event <-clockevents_program_event + -0 3dN.2 19us : _raw_spin_unlock_irqrestore <-hrtimer_try_to_cancel + -0 3dN.2 19us : sub_preempt_count <-_raw_spin_unlock_irqrestore + -0 3dN.1 19us : hrtimer_forward <-tick_nohz_idle_exit + -0 3dN.1 20us : ktime_add_safe <-hrtimer_forward + -0 3dN.1 20us : ktime_add_safe <-hrtimer_forward + -0 3dN.1 20us : hrtimer_start_range_ns <-hrtimer_start_expires.constprop.11 + -0 3dN.1 20us : __hrtimer_start_range_ns <-hrtimer_start_range_ns + -0 3dN.1 21us : lock_hrtimer_base.isra.18 <-__hrtimer_start_range_ns + -0 3dN.1 21us : _raw_spin_lock_irqsave <-lock_hrtimer_base.isra.18 + -0 3dN.1 21us : add_preempt_count <-_raw_spin_lock_irqsave + -0 3dN.2 22us : ktime_add_safe <-__hrtimer_start_range_ns + -0 3dN.2 22us : enqueue_hrtimer <-__hrtimer_start_range_ns + -0 3dN.2 22us : tick_program_event <-__hrtimer_start_range_ns + -0 3dN.2 23us : clockevents_program_event <-tick_program_event + -0 3dN.2 23us : ktime_get <-clockevents_program_event + -0 3dN.2 23us : lapic_next_event <-clockevents_program_event + -0 3dN.2 24us : _raw_spin_unlock_irqrestore <-__hrtimer_start_range_ns + -0 3dN.2 24us : sub_preempt_count <-_raw_spin_unlock_irqrestore + -0 3dN.1 24us : account_idle_ticks <-tick_nohz_idle_exit + -0 3dN.1 24us : account_idle_time <-account_idle_ticks + -0 3.N.1 25us : sub_preempt_count <-cpu_idle + -0 3.N.. 25us : schedule <-cpu_idle + -0 3.N.. 25us : __schedule <-preempt_schedule + -0 3.N.. 26us : add_preempt_count <-__schedule + -0 3.N.1 26us : rcu_note_context_switch <-__schedule + -0 3.N.1 26us : rcu_sched_qs <-rcu_note_context_switch + -0 3dN.1 27us : rcu_preempt_qs <-rcu_note_context_switch + -0 3.N.1 27us : _raw_spin_lock_irq <-__schedule + -0 3dN.1 27us : add_preempt_count <-_raw_spin_lock_irq + -0 3dN.2 28us : put_prev_task_idle <-__schedule + -0 3dN.2 28us : pick_next_task_stop <-pick_next_task + -0 3dN.2 28us : pick_next_task_rt <-pick_next_task + -0 3dN.2 29us : dequeue_pushable_task <-pick_next_task_rt + -0 3d..3 29us : __schedule <-preempt_schedule + -0 3d..3 30us : 0:120:R ==> [003] 2448: 94:R sleep + +This isn't that big of a trace, even with function tracing enabled, +so I included the entire trace. + +The interrupt went off while when the system was idle. Somewhere +before task_woken_rt() was called, the NEED_RESCHED flag was set, +this is indicated by the first occurrence of the 'N' flag. + +Latency tracing and events +-------------------------- +As function tracing can induce a much larger latency, but without +seeing what happens within the latency it is hard to know what +caused it. There is a middle ground, and that is with enabling +events. + + # echo 0 > options/function-trace + # echo wakeup_rt > current_tracer + # echo 1 > events/enable + # echo 1 > tracing_on + # echo 0 > tracing_max_latency + # chrt -f 5 sleep 1 + # echo 0 > tracing_on + # cat trace +# tracer: wakeup_rt +# +# wakeup_rt latency trace v1.1.5 on 3.8.0-test+ +# -------------------------------------------------------------------- +# latency: 6 us, #12/12, CPU#2 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:4) +# ----------------- +# | task: sleep-5882 (uid:0 nice:0 policy:1 rt_prio:5) +# ----------------- +# +# _------=> CPU# +# / _-----=> irqs-off +# | / _----=> need-resched +# || / _---=> hardirq/softirq +# ||| / _--=> preempt-depth +# |||| / delay +# cmd pid ||||| time | caller +# \ / ||||| \ | / + -0 2d.h4 0us : 0:120:R + [002] 5882: 94:R sleep + -0 2d.h4 0us : ttwu_do_activate.constprop.87 <-try_to_wake_up + -0 2d.h4 1us : sched_wakeup: comm=sleep pid=5882 prio=94 success=1 target_cpu=002 + -0 2dNh2 1us : hrtimer_expire_exit: hrtimer=ffff88007796feb8 + -0 2.N.2 2us : power_end: cpu_id=2 + -0 2.N.2 3us : cpu_idle: state=4294967295 cpu_id=2 + -0 2dN.3 4us : hrtimer_cancel: hrtimer=ffff88007d50d5e0 + -0 2dN.3 4us : hrtimer_start: hrtimer=ffff88007d50d5e0 function=tick_sched_timer expires=34311211000000 softexpires=34311211000000 + -0 2.N.2 5us : rcu_utilization: Start context switch + -0 2.N.2 5us : rcu_utilization: End context switch + -0 2d..3 6us : __schedule <-schedule + -0 2d..3 6us : 0:120:R ==> [002] 5882: 94:R sleep + function -------- @@ -1039,6 +1653,7 @@ function This tracer is the function tracer. Enabling the function tracer can be done from the debug file system. Make sure the ftrace_enabled is set; otherwise this tracer is a nop. +See the "ftrace_enabled" section below. # sysctl kernel.ftrace_enabled=1 # echo function > current_tracer @@ -1048,23 +1663,23 @@ ftrace_enabled is set; otherwise this tracer is a nop. # cat trace # tracer: function # -# TASK-PID CPU# TIMESTAMP FUNCTION -# | | | | | - bash-4003 [00] 123.638713: finish_task_switch <-schedule - bash-4003 [00] 123.638714: _spin_unlock_irq <-finish_task_switch - bash-4003 [00] 123.638714: sub_preempt_count <-_spin_unlock_irq - bash-4003 [00] 123.638715: hrtick_set <-schedule - bash-4003 [00] 123.638715: _spin_lock_irqsave <-hrtick_set - bash-4003 [00] 123.638716: add_preempt_count <-_spin_lock_irqsave - bash-4003 [00] 123.638716: _spin_unlock_irqrestore <-hrtick_set - bash-4003 [00] 123.638717: sub_preempt_count <-_spin_unlock_irqrestore - bash-4003 [00] 123.638717: hrtick_clear <-hrtick_set - bash-4003 [00] 123.638718: sub_preempt_count <-schedule - bash-4003 [00] 123.638718: sub_preempt_count <-preempt_schedule - bash-4003 [00] 123.638719: wait_for_completion <-__stop_machine_run - bash-4003 [00] 123.638719: wait_for_common <-wait_for_completion - bash-4003 [00] 123.638720: _spin_lock_irq <-wait_for_common - bash-4003 [00] 123.638720: add_preempt_count <-_spin_lock_irq +# entries-in-buffer/entries-written: 24799/24799 #P:4 +# +# _-----=> irqs-off +# / _----=> need-resched +# | / _---=> hardirq/softirq +# || / _--=> preempt-depth +# ||| / delay +# TASK-PID CPU# |||| TIMESTAMP FUNCTION +# | | | |||| | | + bash-1994 [002] .... 3082.063030: mutex_unlock <-rb_simple_write + bash-1994 [002] .... 3082.063031: __mutex_unlock_slowpath <-mutex_unlock + bash-1994 [002] .... 3082.063031: __fsnotify_parent <-fsnotify_modify + bash-1994 [002] .... 3082.063032: fsnotify <-fsnotify_modify + bash-1994 [002] .... 3082.063032: __srcu_read_lock <-fsnotify + bash-1994 [002] .... 3082.063032: add_preempt_count <-__srcu_read_lock + bash-1994 [002] ...1 3082.063032: sub_preempt_count <-__srcu_read_lock + bash-1994 [002] .... 3082.063033: __srcu_read_unlock <-fsnotify [...] @@ -1214,79 +1829,19 @@ int main (int argc, char **argv) return 0; } +Or this simple script! -hw-branch-tracer (x86 only) ---------------------------- - -This tracer uses the x86 last branch tracing hardware feature to -collect a branch trace on all cpus with relatively low overhead. - -The tracer uses a fixed-size circular buffer per cpu and only -traces ring 0 branches. The trace file dumps that buffer in the -following format: - -# tracer: hw-branch-tracer -# -# CPU# TO <- FROM - 0 scheduler_tick+0xb5/0x1bf <- task_tick_idle+0x5/0x6 - 2 run_posix_cpu_timers+0x2b/0x72a <- run_posix_cpu_timers+0x25/0x72a - 0 scheduler_tick+0x139/0x1bf <- scheduler_tick+0xed/0x1bf - 0 scheduler_tick+0x17c/0x1bf <- scheduler_tick+0x148/0x1bf - 2 run_posix_cpu_timers+0x9e/0x72a <- run_posix_cpu_timers+0x5e/0x72a - 0 scheduler_tick+0x1b6/0x1bf <- scheduler_tick+0x1aa/0x1bf - - -The tracer may be used to dump the trace for the oops'ing cpu on -a kernel oops into the system log. To enable this, -ftrace_dump_on_oops must be set. To set ftrace_dump_on_oops, one -can either use the sysctl function or set it via the proc system -interface. - - sysctl kernel.ftrace_dump_on_oops=n - -or - - echo n > /proc/sys/kernel/ftrace_dump_on_oops - -If n = 1, ftrace will dump buffers of all CPUs, if n = 2 ftrace will -only dump the buffer of the CPU that triggered the oops. - -Here's an example of such a dump after a null pointer -dereference in a kernel module: - -[57848.105921] BUG: unable to handle kernel NULL pointer dereference at 0000000000000000 -[57848.106019] IP: [] open+0x6/0x14 [oops] -[57848.106019] PGD 2354e9067 PUD 2375e7067 PMD 0 -[57848.106019] Oops: 0002 [#1] SMP -[57848.106019] last sysfs file: /sys/devices/pci0000:00/0000:00:1e.0/0000:20:05.0/local_cpus -[57848.106019] Dumping ftrace buffer: -[57848.106019] --------------------------------- -[...] -[57848.106019] 0 chrdev_open+0xe6/0x165 <- cdev_put+0x23/0x24 -[57848.106019] 0 chrdev_open+0x117/0x165 <- chrdev_open+0xfa/0x165 -[57848.106019] 0 chrdev_open+0x120/0x165 <- chrdev_open+0x11c/0x165 -[57848.106019] 0 chrdev_open+0x134/0x165 <- chrdev_open+0x12b/0x165 -[57848.106019] 0 open+0x0/0x14 [oops] <- chrdev_open+0x144/0x165 -[57848.106019] 0 page_fault+0x0/0x30 <- open+0x6/0x14 [oops] -[57848.106019] 0 error_entry+0x0/0x5b <- page_fault+0x4/0x30 -[57848.106019] 0 error_kernelspace+0x0/0x31 <- error_entry+0x59/0x5b -[57848.106019] 0 error_sti+0x0/0x1 <- error_kernelspace+0x2d/0x31 -[57848.106019] 0 page_fault+0x9/0x30 <- error_sti+0x0/0x1 -[57848.106019] 0 do_page_fault+0x0/0x881 <- page_fault+0x1a/0x30 -[...] -[57848.106019] 0 do_page_fault+0x66b/0x881 <- is_prefetch+0x1ee/0x1f2 -[57848.106019] 0 do_page_fault+0x6e0/0x881 <- do_page_fault+0x67a/0x881 -[57848.106019] 0 oops_begin+0x0/0x96 <- do_page_fault+0x6e0/0x881 -[57848.106019] 0 trace_hw_branch_oops+0x0/0x2d <- oops_begin+0x9/0x96 -[...] -[57848.106019] 0 ds_suspend_bts+0x2a/0xe3 <- ds_suspend_bts+0x1a/0xe3 -[57848.106019] --------------------------------- -[57848.106019] CPU 0 -[57848.106019] Modules linked in: oops -[57848.106019] Pid: 5542, comm: cat Tainted: G W 2.6.28 #23 -[57848.106019] RIP: 0010:[] [] open+0x6/0x14 [oops] -[57848.106019] RSP: 0018:ffff880235457d48 EFLAGS: 00010246 -[...] +------ +#!/bin/bash + +debugfs=`sed -ne 's/^debugfs \(.*\) debugfs.*/\1/p' /proc/mounts` +echo nop > $debugfs/tracing/current_tracer +echo 0 > $debugfs/tracing/tracing_on +echo $$ > $debugfs/tracing/set_ftrace_pid +echo function > $debugfs/tracing/current_tracer +echo 1 > $debugfs/tracing/tracing_on +exec "$@" +------ function graph tracer @@ -1473,16 +2028,18 @@ starts of pointing to a simple return. (Enabling FTRACE will include the -pg switch in the compiling of the kernel.) At compile time every C file object is run through the -recordmcount.pl script (located in the scripts directory). This -script will process the C object using objdump to find all the -locations in the .text section that call mcount. (Note, only the -.text section is processed, since processing other sections like -.init.text may cause races due to those sections being freed). +recordmcount program (located in the scripts directory). This +program will parse the ELF headers in the C object to find all +the locations in the .text section that call mcount. (Note, only +white listed .text sections are processed, since processing other +sections like .init.text may cause races due to those sections +being freed unexpectedly). A new section called "__mcount_loc" is created that holds references to all the mcount call sites in the .text section. -This section is compiled back into the original object. The -final linker will add all these references into a single table. +The recordmcount program re-links this section back into the +original object. The final linking stage of the kernel will add all these +references into a single table. On boot up, before SMP is initialized, the dynamic ftrace code scans this table and updates all the locations into nops. It @@ -1493,13 +2050,25 @@ unloaded, it also removes its functions from the ftrace function list. This is automatic in the module unload code, and the module author does not need to worry about it. -When tracing is enabled, kstop_machine is called to prevent -races with the CPUS executing code being modified (which can -cause the CPU to do undesirable things), and the nops are +When tracing is enabled, the process of modifying the function +tracepoints is dependent on architecture. The old method is to use +kstop_machine to prevent races with the CPUs executing code being +modified (which can cause the CPU to do undesirable things, especially +if the modified code crosses cache (or page) boundaries), and the nops are patched back to calls. But this time, they do not call mcount (which is just a function stub). They now call into the ftrace infrastructure. +The new method of modifying the function tracepoints is to place +a breakpoint at the location to be modified, sync all CPUs, modify +the rest of the instruction not covered by the breakpoint. Sync +all CPUs again, and then remove the breakpoint with the finished +version to the ftrace call site. + +Some archs do not even need to monkey around with the synchronization, +and can just slap the new code on top of the old without any +problems with other CPUs executing it at the same time. + One special side-effect to the recording of the functions being traced is that we can now selectively choose which functions we wish to trace and which ones we want the mcount calls to remain @@ -1530,20 +2099,28 @@ mutex_lock If I am only interested in sys_nanosleep and hrtimer_interrupt: - # echo sys_nanosleep hrtimer_interrupt \ - > set_ftrace_filter + # echo sys_nanosleep hrtimer_interrupt > set_ftrace_filter # echo function > current_tracer # echo 1 > tracing_on # usleep 1 # echo 0 > tracing_on # cat trace -# tracer: ftrace +# tracer: function +# +# entries-in-buffer/entries-written: 5/5 #P:4 # -# TASK-PID CPU# TIMESTAMP FUNCTION -# | | | | | - usleep-4134 [00] 1317.070017: hrtimer_interrupt <-smp_apic_timer_interrupt - usleep-4134 [00] 1317.070111: sys_nanosleep <-syscall_call - -0 [00] 1317.070115: hrtimer_interrupt <-smp_apic_timer_interrupt +# _-----=> irqs-off +# / _----=> need-resched +# | / _---=> hardirq/softirq +# || / _--=> preempt-depth +# ||| / delay +# TASK-PID CPU# |||| TIMESTAMP FUNCTION +# | | | |||| | | + usleep-2665 [001] .... 4186.475355: sys_nanosleep <-system_call_fastpath + -0 [001] d.h1 4186.475409: hrtimer_interrupt <-smp_apic_timer_interrupt + usleep-2665 [001] d.h1 4186.475426: hrtimer_interrupt <-smp_apic_timer_interrupt + -0 [003] d.h1 4186.475426: hrtimer_interrupt <-smp_apic_timer_interrupt + -0 [002] d.h1 4186.475427: hrtimer_interrupt <-smp_apic_timer_interrupt To see which functions are being traced, you can cat the file: @@ -1571,20 +2148,25 @@ Note: It is better to use quotes to enclose the wild cards, Produces: -# tracer: ftrace +# tracer: function # -# TASK-PID CPU# TIMESTAMP FUNCTION -# | | | | | - bash-4003 [00] 1480.611794: hrtimer_init <-copy_process - bash-4003 [00] 1480.611941: hrtimer_start <-hrtick_set - bash-4003 [00] 1480.611956: hrtimer_cancel <-hrtick_clear - bash-4003 [00] 1480.611956: hrtimer_try_to_cancel <-hrtimer_cancel - -0 [00] 1480.612019: hrtimer_get_next_event <-get_next_timer_interrupt - -0 [00] 1480.612025: hrtimer_get_next_event <-get_next_timer_interrupt - -0 [00] 1480.612032: hrtimer_get_next_event <-get_next_timer_interrupt - -0 [00] 1480.612037: hrtimer_get_next_event <-get_next_timer_interrupt - -0 [00] 1480.612382: hrtimer_get_next_event <-get_next_timer_interrupt - +# entries-in-buffer/entries-written: 897/897 #P:4 +# +# _-----=> irqs-off +# / _----=> need-resched +# | / _---=> hardirq/softirq +# || / _--=> preempt-depth +# ||| / delay +# TASK-PID CPU# |||| TIMESTAMP FUNCTION +# | | | |||| | | + -0 [003] dN.1 4228.547803: hrtimer_cancel <-tick_nohz_idle_exit + -0 [003] dN.1 4228.547804: hrtimer_try_to_cancel <-hrtimer_cancel + -0 [003] dN.2 4228.547805: hrtimer_force_reprogram <-__remove_hrtimer + -0 [003] dN.1 4228.547805: hrtimer_forward <-tick_nohz_idle_exit + -0 [003] dN.1 4228.547805: hrtimer_start_range_ns <-hrtimer_start_expires.constprop.11 + -0 [003] d..1 4228.547858: hrtimer_get_next_event <-get_next_timer_interrupt + -0 [003] d..1 4228.547859: hrtimer_start <-__tick_nohz_idle_enter + -0 [003] d..2 4228.547860: hrtimer_force_reprogram <-__rem Notice that we lost the sys_nanosleep. @@ -1651,19 +2233,29 @@ traced. Produces: -# tracer: ftrace +# tracer: function +# +# entries-in-buffer/entries-written: 39608/39608 #P:4 # -# TASK-PID CPU# TIMESTAMP FUNCTION -# | | | | | - bash-4043 [01] 115.281644: finish_task_switch <-schedule - bash-4043 [01] 115.281645: hrtick_set <-schedule - bash-4043 [01] 115.281645: hrtick_clear <-hrtick_set - bash-4043 [01] 115.281646: wait_for_completion <-__stop_machine_run - bash-4043 [01] 115.281647: wait_for_common <-wait_for_completion - bash-4043 [01] 115.281647: kthread_stop <-stop_machine_run - bash-4043 [01] 115.281648: init_waitqueue_head <-kthread_stop - bash-4043 [01] 115.281648: wake_up_process <-kthread_stop - bash-4043 [01] 115.281649: try_to_wake_up <-wake_up_process +# _-----=> irqs-off +# / _----=> need-resched +# | / _---=> hardirq/softirq +# || / _--=> preempt-depth +# ||| / delay +# TASK-PID CPU# |||| TIMESTAMP FUNCTION +# | | | |||| | | + bash-1994 [000] .... 4342.324896: file_ra_state_init <-do_dentry_open + bash-1994 [000] .... 4342.324897: open_check_o_direct <-do_last + bash-1994 [000] .... 4342.324897: ima_file_check <-do_last + bash-1994 [000] .... 4342.324898: process_measurement <-ima_file_check + bash-1994 [000] .... 4342.324898: ima_get_action <-process_measurement + bash-1994 [000] .... 4342.324898: ima_match_policy <-ima_get_action + bash-1994 [000] .... 4342.324899: do_truncate <-do_last + bash-1994 [000] .... 4342.324899: should_remove_suid <-do_truncate + bash-1994 [000] .... 4342.324899: notify_change <-do_truncate + bash-1994 [000] .... 4342.324900: current_fs_time <-notify_change + bash-1994 [000] .... 4342.324900: current_kernel_time <-current_fs_time + bash-1994 [000] .... 4342.324900: timespec_trunc <-current_fs_time We can see that there's no more lock or preempt tracing. @@ -1729,6 +2321,28 @@ this special filter via: echo > set_graph_function +ftrace_enabled +-------------- + +Note, the proc sysctl ftrace_enable is a big on/off switch for the +function tracer. By default it is enabled (when function tracing is +enabled in the kernel). If it is disabled, all function tracing is +disabled. This includes not only the function tracers for ftrace, but +also for any other uses (perf, kprobes, stack tracing, profiling, etc). + +Please disable this with care. + +This can be disable (and enabled) with: + + sysctl kernel.ftrace_enabled=0 + sysctl kernel.ftrace_enabled=1 + + or + + echo 0 > /proc/sys/kernel/ftrace_enabled + echo 1 > /proc/sys/kernel/ftrace_enabled + + Filter commands --------------- @@ -1763,12 +2377,58 @@ The following commands are supported: echo '__schedule_bug:traceoff:5' > set_ftrace_filter + To always disable tracing when __schedule_bug is hit: + + echo '__schedule_bug:traceoff' > set_ftrace_filter + These commands are cumulative whether or not they are appended to set_ftrace_filter. To remove a command, prepend it by '!' and drop the parameter: + echo '!__schedule_bug:traceoff:0' > set_ftrace_filter + + The above removes the traceoff command for __schedule_bug + that have a counter. To remove commands without counters: + echo '!__schedule_bug:traceoff' > set_ftrace_filter +- snapshot + Will cause a snapshot to be triggered when the function is hit. + + echo 'native_flush_tlb_others:snapshot' > set_ftrace_filter + + To only snapshot once: + + echo 'native_flush_tlb_others:snapshot:1' > set_ftrace_filter + + To remove the above commands: + + echo '!native_flush_tlb_others:snapshot' > set_ftrace_filter + echo '!native_flush_tlb_others:snapshot:0' > set_ftrace_filter + +- enable_event/disable_event + These commands can enable or disable a trace event. Note, because + function tracing callbacks are very sensitive, when these commands + are registered, the trace point is activated, but disabled in + a "soft" mode. That is, the tracepoint will be called, but + just will not be traced. The event tracepoint stays in this mode + as long as there's a command that triggers it. + + echo 'try_to_wake_up:enable_event:sched:sched_switch:2' > \ + set_ftrace_filter + + The format is: + + :enable_event::[:count] + :disable_event::[:count] + + To remove the events commands: + + + echo '!try_to_wake_up:enable_event:sched:sched_switch:0' > \ + set_ftrace_filter + echo '!schedule:disable_event:sched:sched_switch' > \ + set_ftrace_filter trace_pipe ---------- @@ -1787,28 +2447,31 @@ different. The trace is live. # cat trace # tracer: function # -# TASK-PID CPU# TIMESTAMP FUNCTION -# | | | | | +# entries-in-buffer/entries-written: 0/0 #P:4 +# +# _-----=> irqs-off +# / _----=> need-resched +# | / _---=> hardirq/softirq +# || / _--=> preempt-depth +# ||| / delay +# TASK-PID CPU# |||| TIMESTAMP FUNCTION +# | | | |||| | | # # cat /tmp/trace.out - bash-4043 [00] 41.267106: finish_task_switch <-schedule - bash-4043 [00] 41.267106: hrtick_set <-schedule - bash-4043 [00] 41.267107: hrtick_clear <-hrtick_set - bash-4043 [00] 41.267108: wait_for_completion <-__stop_machine_run - bash-4043 [00] 41.267108: wait_for_common <-wait_for_completion - bash-4043 [00] 41.267109: kthread_stop <-stop_machine_run - bash-4043 [00] 41.267109: init_waitqueue_head <-kthread_stop - bash-4043 [00] 41.267110: wake_up_process <-kthread_stop - bash-4043 [00] 41.267110: try_to_wake_up <-wake_up_process - bash-4043 [00] 41.267111: select_task_rq_rt <-try_to_wake_up + bash-1994 [000] .... 5281.568961: mutex_unlock <-rb_simple_write + bash-1994 [000] .... 5281.568963: __mutex_unlock_slowpath <-mutex_unlock + bash-1994 [000] .... 5281.568963: __fsnotify_parent <-fsnotify_modify + bash-1994 [000] .... 5281.568964: fsnotify <-fsnotify_modify + bash-1994 [000] .... 5281.568964: __srcu_read_lock <-fsnotify + bash-1994 [000] .... 5281.568964: add_preempt_count <-__srcu_read_lock + bash-1994 [000] ...1 5281.568965: sub_preempt_count <-__srcu_read_lock + bash-1994 [000] .... 5281.568965: __srcu_read_unlock <-fsnotify + bash-1994 [000] .... 5281.568967: sys_dup2 <-system_call_fastpath Note, reading the trace_pipe file will block until more input is -added. By changing the tracer, trace_pipe will issue an EOF. We -needed to set the function tracer _before_ we "cat" the -trace_pipe file. - +added. trace entries ------------- @@ -1817,31 +2480,50 @@ Having too much or not enough data can be troublesome in diagnosing an issue in the kernel. The file buffer_size_kb is used to modify the size of the internal trace buffers. The number listed is the number of entries that can be recorded per -CPU. To know the full size, multiply the number of possible CPUS +CPU. To know the full size, multiply the number of possible CPUs with the number of entries. # cat buffer_size_kb 1408 (units kilobytes) -Note, to modify this, you must have tracing completely disabled. -To do that, echo "nop" into the current_tracer. If the -current_tracer is not set to "nop", an EINVAL error will be -returned. +Or simply read buffer_total_size_kb + + # cat buffer_total_size_kb +5632 + +To modify the buffer, simple echo in a number (in 1024 byte segments). - # echo nop > current_tracer # echo 10000 > buffer_size_kb # cat buffer_size_kb 10000 (units kilobytes) -The number of pages which will be allocated is limited to a -percentage of available memory. Allocating too much will produce -an error. +It will try to allocate as much as possible. If you allocate too +much, it can cause Out-Of-Memory to trigger. # echo 1000000000000 > buffer_size_kb -bash: echo: write error: Cannot allocate memory # cat buffer_size_kb 85 +The per_cpu buffers can be changed individually as well: + + # echo 10000 > per_cpu/cpu0/buffer_size_kb + # echo 100 > per_cpu/cpu1/buffer_size_kb + +When the per_cpu buffers are not the same, the buffer_size_kb +at the top level will just show an X + + # cat buffer_size_kb +X + +This is where the buffer_total_size_kb is useful: + + # cat buffer_total_size_kb +12916 + +Writing to the top level buffer_size_kb will reset all the buffers +to be the same again. + Snapshot -------- CONFIG_TRACER_SNAPSHOT makes a generic snapshot feature @@ -1925,7 +2607,188 @@ bash: echo: write error: Device or resource busy # cat snapshot cat: snapshot: Device or resource busy + +Instances +--------- +In the debugfs tracing directory is a directory called "instances". +This directory can have new directories created inside of it using +mkdir, and removing directories with rmdir. The directory created +with mkdir in this directory will already contain files and other +directories after it is created. + + # mkdir instances/foo + # ls instances/foo +buffer_size_kb buffer_total_size_kb events free_buffer per_cpu +set_event snapshot trace trace_clock trace_marker trace_options +trace_pipe tracing_on + +As you can see, the new directory looks similar to the tracing directory +itself. In fact, it is very similar, except that the buffer and +events are agnostic from the main director, or from any other +instances that are created. + +The files in the new directory work just like the files with the +same name in the tracing directory except the buffer that is used +is a separate and new buffer. The files affect that buffer but do not +affect the main buffer with the exception of trace_options. Currently, +the trace_options affect all instances and the top level buffer +the same, but this may change in future releases. That is, options +may become specific to the instance they reside in. + +Notice that none of the function tracer files are there, nor is +current_tracer and available_tracers. This is because the buffers +can currently only have events enabled for them. + + # mkdir instances/foo + # mkdir instances/bar + # mkdir instances/zoot + # echo 100000 > buffer_size_kb + # echo 1000 > instances/foo/buffer_size_kb + # echo 5000 > instances/bar/per_cpu/cpu1/buffer_size_kb + # echo function > current_trace + # echo 1 > instances/foo/events/sched/sched_wakeup/enable + # echo 1 > instances/foo/events/sched/sched_wakeup_new/enable + # echo 1 > instances/foo/events/sched/sched_switch/enable + # echo 1 > instances/bar/events/irq/enable + # echo 1 > instances/zoot/events/syscalls/enable + # cat trace_pipe +CPU:2 [LOST 11745 EVENTS] + bash-2044 [002] .... 10594.481032: _raw_spin_lock_irqsave <-get_page_from_freelist + bash-2044 [002] d... 10594.481032: add_preempt_count <-_raw_spin_lock_irqsave + bash-2044 [002] d..1 10594.481032: __rmqueue <-get_page_from_freelist + bash-2044 [002] d..1 10594.481033: _raw_spin_unlock <-get_page_from_freelist + bash-2044 [002] d..1 10594.481033: sub_preempt_count <-_raw_spin_unlock + bash-2044 [002] d... 10594.481033: get_pageblock_flags_group <-get_pageblock_migratetype + bash-2044 [002] d... 10594.481034: __mod_zone_page_state <-get_page_from_freelist + bash-2044 [002] d... 10594.481034: zone_statistics <-get_page_from_freelist + bash-2044 [002] d... 10594.481034: __inc_zone_state <-zone_statistics + bash-2044 [002] d... 10594.481034: __inc_zone_state <-zone_statistics + bash-2044 [002] .... 10594.481035: arch_dup_task_struct <-copy_process +[...] + + # cat instances/foo/trace_pipe + bash-1998 [000] d..4 136.676759: sched_wakeup: comm=kworker/0:1 pid=59 prio=120 success=1 target_cpu=000 + bash-1998 [000] dN.4 136.676760: sched_wakeup: comm=bash pid=1998 prio=120 success=1 target_cpu=000 + -0 [003] d.h3 136.676906: sched_wakeup: comm=rcu_preempt pid=9 prio=120 success=1 target_cpu=003 + -0 [003] d..3 136.676909: sched_switch: prev_comm=swapper/3 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=rcu_preempt next_pid=9 next_prio=120 + rcu_preempt-9 [003] d..3 136.676916: sched_switch: prev_comm=rcu_preempt prev_pid=9 prev_prio=120 prev_state=S ==> next_comm=swapper/3 next_pid=0 next_prio=120 + bash-1998 [000] d..4 136.677014: sched_wakeup: comm=kworker/0:1 pid=59 prio=120 success=1 target_cpu=000 + bash-1998 [000] dN.4 136.677016: sched_wakeup: comm=bash pid=1998 prio=120 success=1 target_cpu=000 + bash-1998 [000] d..3 136.677018: sched_switch: prev_comm=bash prev_pid=1998 prev_prio=120 prev_state=R+ ==> next_comm=kworker/0:1 next_pid=59 next_prio=120 + kworker/0:1-59 [000] d..4 136.677022: sched_wakeup: comm=sshd pid=1995 prio=120 success=1 target_cpu=001 + kworker/0:1-59 [000] d..3 136.677025: sched_switch: prev_comm=kworker/0:1 prev_pid=59 prev_prio=120 prev_state=S ==> next_comm=bash next_pid=1998 next_prio=120 +[...] + + # cat instances/bar/trace_pipe + migration/1-14 [001] d.h3 138.732674: softirq_raise: vec=3 [action=NET_RX] + -0 [001] dNh3 138.732725: softirq_raise: vec=3 [action=NET_RX] + bash-1998 [000] d.h1 138.733101: softirq_raise: vec=1 [action=TIMER] + bash-1998 [000] d.h1 138.733102: softirq_raise: vec=9 [action=RCU] + bash-1998 [000] ..s2 138.733105: softirq_entry: vec=1 [action=TIMER] + bash-1998 [000] ..s2 138.733106: softirq_exit: vec=1 [action=TIMER] + bash-1998 [000] ..s2 138.733106: softirq_entry: vec=9 [action=RCU] + bash-1998 [000] ..s2 138.733109: softirq_exit: vec=9 [action=RCU] + sshd-1995 [001] d.h1 138.733278: irq_handler_entry: irq=21 name=uhci_hcd:usb4 + sshd-1995 [001] d.h1 138.733280: irq_handler_exit: irq=21 ret=unhandled + sshd-1995 [001] d.h1 138.733281: irq_handler_entry: irq=21 name=eth0 + sshd-1995 [001] d.h1 138.733283: irq_handler_exit: irq=21 ret=handled +[...] + + # cat instances/zoot/trace +# tracer: nop +# +# entries-in-buffer/entries-written: 18996/18996 #P:4 +# +# _-----=> irqs-off +# / _----=> need-resched +# | / _---=> hardirq/softirq +# || / _--=> preempt-depth +# ||| / delay +# TASK-PID CPU# |||| TIMESTAMP FUNCTION +# | | | |||| | | + bash-1998 [000] d... 140.733501: sys_write -> 0x2 + bash-1998 [000] d... 140.733504: sys_dup2(oldfd: a, newfd: 1) + bash-1998 [000] d... 140.733506: sys_dup2 -> 0x1 + bash-1998 [000] d... 140.733508: sys_fcntl(fd: a, cmd: 1, arg: 0) + bash-1998 [000] d... 140.733509: sys_fcntl -> 0x1 + bash-1998 [000] d... 140.733510: sys_close(fd: a) + bash-1998 [000] d... 140.733510: sys_close -> 0x0 + bash-1998 [000] d... 140.733514: sys_rt_sigprocmask(how: 0, nset: 0, oset: 6e2768, sigsetsize: 8) + bash-1998 [000] d... 140.733515: sys_rt_sigprocmask -> 0x0 + bash-1998 [000] d... 140.733516: sys_rt_sigaction(sig: 2, act: 7fff718846f0, oact: 7fff71884650, sigsetsize: 8) + bash-1998 [000] d... 140.733516: sys_rt_sigaction -> 0x0 + +You can see that the trace of the top most trace buffer shows only +the function tracing. The foo instance displays wakeups and task +switches. + +To remove the instances, simply delete their directories: + + # rmdir instances/foo + # rmdir instances/bar + # rmdir instances/zoot + +Note, if a process has a trace file open in one of the instance +directories, the rmdir will fail with EBUSY. + + +Stack trace ----------- +Since the kernel has a fixed sized stack, it is important not to +waste it in functions. A kernel developer must be conscience of +what they allocate on the stack. If they add too much, the system +can be in danger of a stack overflow, and corruption will occur, +usually leading to a system panic. + +There are some tools that check this, usually with interrupts +periodically checking usage. But if you can perform a check +at every function call that will become very useful. As ftrace provides +a function tracer, it makes it convenient to check the stack size +at every function call. This is enabled via the stack tracer. + +CONFIG_STACK_TRACER enables the ftrace stack tracing functionality. +To enable it, write a '1' into /proc/sys/kernel/stack_tracer_enabled. + + # echo 1 > /proc/sys/kernel/stack_tracer_enabled + +You can also enable it from the kernel command line to trace +the stack size of the kernel during boot up, by adding "stacktrace" +to the kernel command line parameter. + +After running it for a few minutes, the output looks like: + + # cat stack_max_size +2928 + + # cat stack_trace + Depth Size Location (18 entries) + ----- ---- -------- + 0) 2928 224 update_sd_lb_stats+0xbc/0x4ac + 1) 2704 160 find_busiest_group+0x31/0x1f1 + 2) 2544 256 load_balance+0xd9/0x662 + 3) 2288 80 idle_balance+0xbb/0x130 + 4) 2208 128 __schedule+0x26e/0x5b9 + 5) 2080 16 schedule+0x64/0x66 + 6) 2064 128 schedule_timeout+0x34/0xe0 + 7) 1936 112 wait_for_common+0x97/0xf1 + 8) 1824 16 wait_for_completion+0x1d/0x1f + 9) 1808 128 flush_work+0xfe/0x119 + 10) 1680 16 tty_flush_to_ldisc+0x1e/0x20 + 11) 1664 48 input_available_p+0x1d/0x5c + 12) 1616 48 n_tty_poll+0x6d/0x134 + 13) 1568 64 tty_poll+0x64/0x7f + 14) 1504 880 do_select+0x31e/0x511 + 15) 624 400 core_sys_select+0x177/0x216 + 16) 224 96 sys_select+0x91/0xb9 + 17) 128 128 system_call_fastpath+0x16/0x1b + +Note, if -mfentry is being used by gcc, functions get traced before +they set up the stack frame. This means that leaf level functions +are not tested by the stack tracer when -mfentry is used. + +Currently, -mfentry is used by gcc 4.6.0 and above on x86 only. + +--------- More details can be found in the source code, in the kernel/trace/*.c files. -- cgit v1.2.3 From b66c66dc5cc8f8f8d68ea1177b9672f91e1e7a19 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Thu, 14 Mar 2013 22:49:47 +0000 Subject: Documentation: fix neigh/default/gc_thresh1 default value. The default value is 128, not 256 #grep gc_thresh1 net/ -rI net/decnet/dn_neigh.c: .gc_thresh1 = 128, net/ipv6/ndisc.c: .gc_thresh1 = 128, net/ipv4/arp.c: .gc_thresh1 = 128, Signed-off-by: Li RongQing Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 1cae6c383e1b..18a24c405ac0 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -29,7 +29,7 @@ route/max_size - INTEGER neigh/default/gc_thresh1 - INTEGER Minimum number of entries to keep. Garbage collector will not purge entries if there are fewer than this number. - Default: 256 + Default: 128 neigh/default/gc_thresh3 - INTEGER Maximum number of neighbor entries allowed. Increase this -- cgit v1.2.3 From 5867320dec346c2dc26f224f876d780111ca149d Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 12:44:47 +0200 Subject: USB: ohci-omap3: Add device tree support and binding information Allows the OHCI controller found in OMAP3 and later chips to be specified via device tree. Signed-off-by: Roger Quadros Reviewed-by: Mark Rutland Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/ohci-omap3.txt | 15 +++++++++++++++ drivers/usb/host/ohci-omap3.c | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/ohci-omap3.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/ohci-omap3.txt b/Documentation/devicetree/bindings/usb/ohci-omap3.txt new file mode 100644 index 000000000000..14ab42812a8e --- /dev/null +++ b/Documentation/devicetree/bindings/usb/ohci-omap3.txt @@ -0,0 +1,15 @@ +OMAP HS USB OHCI controller (OMAP3 and later) + +Required properties: + +- compatible: should be "ti,ohci-omap3" +- reg: should contain one register range i.e. start and length +- interrupts: description of the interrupt line + +Example for OMAP4: + +usbhsohci: ohci@4a064800 { + compatible = "ti,ohci-omap3", "usb-ohci"; + reg = <0x4a064800 0x400>; + interrupts = <0 76 0x4>; +}; diff --git a/drivers/usb/host/ohci-omap3.c b/drivers/usb/host/ohci-omap3.c index 5ed28c5af759..ddfc31427bc0 100644 --- a/drivers/usb/host/ohci-omap3.c +++ b/drivers/usb/host/ohci-omap3.c @@ -31,6 +31,8 @@ #include #include +#include +#include /*-------------------------------------------------------------------------*/ @@ -112,6 +114,8 @@ static const struct hc_driver ohci_omap3_hc_driver = { /*-------------------------------------------------------------------------*/ +static u64 omap_ohci_dma_mask = DMA_BIT_MASK(32); + /* * configure so an HC device and id are always provided * always called with process context; sleeping is OK @@ -159,6 +163,13 @@ static int ohci_hcd_omap3_probe(struct platform_device *pdev) return -ENOMEM; } + /* + * Right now device-tree probed devices don't get dma_mask set. + * Since shared usb code relies on it, set it here for now. + * Once we have dma capability bindings this can go away. + */ + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &omap_ohci_dma_mask; hcd = usb_create_hcd(&ohci_omap3_hc_driver, dev, dev_name(dev)); @@ -228,12 +239,20 @@ static void ohci_hcd_omap3_shutdown(struct platform_device *pdev) hcd->driver->shutdown(hcd); } +static const struct of_device_id omap_ohci_dt_ids[] = { + { .compatible = "ti,ohci-omap3" }, + { } +}; + +MODULE_DEVICE_TABLE(of, omap_ohci_dt_ids); + static struct platform_driver ohci_hcd_omap3_driver = { .probe = ohci_hcd_omap3_probe, .remove = ohci_hcd_omap3_remove, .shutdown = ohci_hcd_omap3_shutdown, .driver = { .name = "ohci-omap3", + .of_match_table = of_match_ptr(omap_ohci_dt_ids), }, }; -- cgit v1.2.3 From a1ae0affee119e6deb937d157aa8b43319c1d6f3 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 12:44:48 +0200 Subject: USB: ehci-omap: Add device tree support and binding information Allows the OMAP EHCI controller to be specified via device tree. Signed-off-by: Roger Quadros Reviewed-by: Mark Rutland Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/ehci-omap.txt | 32 +++++++++++++++++++ drivers/usb/host/ehci-omap.c | 37 +++++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/usb/ehci-omap.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/ehci-omap.txt b/Documentation/devicetree/bindings/usb/ehci-omap.txt new file mode 100644 index 000000000000..485a9a1efa7a --- /dev/null +++ b/Documentation/devicetree/bindings/usb/ehci-omap.txt @@ -0,0 +1,32 @@ +OMAP HS USB EHCI controller + +This device is usually the child of the omap-usb-host +Documentation/devicetree/bindings/mfd/omap-usb-host.txt + +Required properties: + +- compatible: should be "ti,ehci-omap" +- reg: should contain one register range i.e. start and length +- interrupts: description of the interrupt line + +Optional properties: + +- phys: list of phandles to PHY nodes. + This property is required if at least one of the ports are in + PHY mode i.e. OMAP_EHCI_PORT_MODE_PHY + +To specify the port mode, see +Documentation/devicetree/bindings/mfd/omap-usb-host.txt + +Example for OMAP4: + +usbhsehci: ehci@4a064c00 { + compatible = "ti,ehci-omap", "usb-ehci"; + reg = <0x4a064c00 0x400>; + interrupts = <0 77 0x4>; +}; + +&usbhsehci { + phys = <&hsusb1_phy 0 &hsusb3_phy>; +}; + diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 7d05cce62037..45cd01e29252 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -48,6 +48,8 @@ #include #include #include +#include +#include #include "ehci.h" @@ -121,6 +123,8 @@ static const struct ehci_driver_overrides ehci_omap_overrides __initdata = { .extra_priv_size = sizeof(struct omap_hcd), }; +static u64 omap_ehci_dma_mask = DMA_BIT_MASK(32); + /** * ehci_hcd_omap_probe - initialize TI-based HCDs * @@ -148,6 +152,17 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) return -ENODEV; } + /* For DT boot, get platform data from parent. i.e. usbhshost */ + if (dev->of_node) { + pdata = dev->parent->platform_data; + dev->platform_data = pdata; + } + + if (!pdata) { + dev_err(dev, "Missing platform data\n"); + return -ENODEV; + } + irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "EHCI irq failed\n"); @@ -159,6 +174,14 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) if (IS_ERR(regs)) return PTR_ERR(regs); + /* + * Right now device-tree probed devices don't get dma_mask set. + * Since shared usb code relies on it, set it here for now. + * Once we have dma capability bindings this can go away. + */ + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &omap_ehci_dma_mask; + hcd = usb_create_hcd(&ehci_omap_hc_driver, dev, dev_name(dev)); if (!hcd) { @@ -183,7 +206,10 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) continue; /* get the PHY device */ - phy = devm_usb_get_phy_dev(dev, i); + if (dev->of_node) + phy = devm_usb_get_phy_by_phandle(dev, "phys", i); + else + phy = devm_usb_get_phy_dev(dev, i); if (IS_ERR(phy) || !phy) { ret = IS_ERR(phy) ? PTR_ERR(phy) : -ENODEV; dev_err(dev, "Can't get PHY device for port %d: %d\n", @@ -273,6 +299,13 @@ static void ehci_hcd_omap_shutdown(struct platform_device *pdev) hcd->driver->shutdown(hcd); } +static const struct of_device_id omap_ehci_dt_ids[] = { + { .compatible = "ti,ehci-omap" }, + { } +}; + +MODULE_DEVICE_TABLE(of, omap_ehci_dt_ids); + static struct platform_driver ehci_hcd_omap_driver = { .probe = ehci_hcd_omap_probe, .remove = ehci_hcd_omap_remove, @@ -281,6 +314,7 @@ static struct platform_driver ehci_hcd_omap_driver = { /*.resume = ehci_hcd_omap_resume, */ .driver = { .name = hcd_name, + .of_match_table = of_match_ptr(omap_ehci_dt_ids), } }; @@ -307,6 +341,7 @@ module_exit(ehci_omap_cleanup); MODULE_ALIAS("platform:ehci-omap"); MODULE_AUTHOR("Texas Instruments, Inc."); MODULE_AUTHOR("Felipe Balbi "); +MODULE_AUTHOR("Roger Quadros "); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 17d82b47a215ded05ee3fb8d93b7c1269dbe0083 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 7 Feb 2013 17:09:00 +0000 Subject: iio: Add OF support Provide bindings and parse OF data during initialization. Signed-off-by: Guenter Roeck Reviewed-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/iio-bindings.txt | 97 ++++++++++++ drivers/iio/iio_core.h | 1 + drivers/iio/industrialio-core.c | 8 +- drivers/iio/inkern.c | 171 +++++++++++++++++++++ 4 files changed, 275 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/iio/iio-bindings.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/iio-bindings.txt b/Documentation/devicetree/bindings/iio/iio-bindings.txt new file mode 100644 index 000000000000..0b447d9ad196 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/iio-bindings.txt @@ -0,0 +1,97 @@ +This binding is derived from clock bindings, and based on suggestions +from Lars-Peter Clausen [1]. + +Sources of IIO channels can be represented by any node in the device +tree. Those nodes are designated as IIO providers. IIO consumer +nodes use a phandle and IIO specifier pair to connect IIO provider +outputs to IIO inputs. Similar to the gpio specifiers, an IIO +specifier is an array of one or more cells identifying the IIO +output on a device. The length of an IIO specifier is defined by the +value of a #io-channel-cells property in the IIO provider node. + +[1] http://marc.info/?l=linux-iio&m=135902119507483&w=2 + +==IIO providers== + +Required properties: +#io-channel-cells: Number of cells in an IIO specifier; Typically 0 for nodes + with a single IIO output and 1 for nodes with multiple + IIO outputs. + +Example for a simple configuration with no trigger: + + adc: voltage-sensor@35 { + compatible = "maxim,max1139"; + reg = <0x35>; + #io-channel-cells = <1>; + }; + +Example for a configuration with trigger: + + adc@35 { + compatible = "some-vendor,some-adc"; + reg = <0x35>; + + adc1: iio-device@0 { + #io-channel-cells = <1>; + /* other properties */ + }; + adc2: iio-device@1 { + #io-channel-cells = <1>; + /* other properties */ + }; + }; + +==IIO consumers== + +Required properties: +io-channels: List of phandle and IIO specifier pairs, one pair + for each IIO input to the device. Note: if the + IIO provider specifies '0' for #io-channel-cells, + then only the phandle portion of the pair will appear. + +Optional properties: +io-channel-names: + List of IIO input name strings sorted in the same + order as the io-channels property. Consumers drivers + will use io-channel-names to match IIO input names + with IIO specifiers. +io-channel-ranges: + Empty property indicating that child nodes can inherit named + IIO channels from this node. Useful for bus nodes to provide + and IIO channel to their children. + +For example: + + device { + io-channels = <&adc 1>, <&ref 0>; + io-channel-names = "vcc", "vdd"; + }; + +This represents a device with two IIO inputs, named "vcc" and "vdd". +The vcc channel is connected to output 1 of the &adc device, and the +vdd channel is connected to output 0 of the &ref device. + +==Example== + + adc: max1139@35 { + compatible = "maxim,max1139"; + reg = <0x35>; + #io-channel-cells = <1>; + }; + + ... + + iio_hwmon { + compatible = "iio-hwmon"; + io-channels = <&adc 0>, <&adc 1>, <&adc 2>, + <&adc 3>, <&adc 4>, <&adc 5>, + <&adc 6>, <&adc 7>, <&adc 8>, + <&adc 9>; + }; + + some_consumer { + compatible = "some-consumer"; + io-channels = <&adc 10>, <&adc 11>; + io-channel-names = "adc1", "adc2"; + }; diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index f652e6ae5a35..05c1b74502a3 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -18,6 +18,7 @@ struct iio_chan_spec; struct iio_dev; +extern struct device_type iio_device_type; int __iio_add_chan_devattr(const char *postfix, struct iio_chan_spec const *chan, diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 8848f16c547b..6d8b02785647 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -847,7 +847,7 @@ static void iio_dev_release(struct device *device) kfree(indio_dev); } -static struct device_type iio_dev_type = { +struct device_type iio_device_type = { .name = "iio_device", .release = iio_dev_release, }; @@ -869,7 +869,7 @@ struct iio_dev *iio_device_alloc(int sizeof_priv) if (dev) { dev->dev.groups = dev->groups; - dev->dev.type = &iio_dev_type; + dev->dev.type = &iio_device_type; dev->dev.bus = &iio_bus_type; device_initialize(&dev->dev); dev_set_drvdata(&dev->dev, (void *)dev); @@ -960,6 +960,10 @@ int iio_device_register(struct iio_dev *indio_dev) { int ret; + /* If the calling driver did not initialize of_node, do it here */ + if (!indio_dev->dev.of_node && indio_dev->dev.parent) + indio_dev->dev.of_node = indio_dev->dev.parent->of_node; + /* configure elements for the chrdev */ indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id); diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index b289915b8469..795d100b4c36 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "iio_core.h" @@ -92,6 +93,164 @@ static const struct iio_chan_spec return chan; } +#ifdef CONFIG_OF + +static int iio_dev_node_match(struct device *dev, void *data) +{ + return dev->of_node == data && dev->type == &iio_device_type; +} + +static int __of_iio_channel_get(struct iio_channel *channel, + struct device_node *np, int index) +{ + struct device *idev; + struct iio_dev *indio_dev; + int err; + struct of_phandle_args iiospec; + + err = of_parse_phandle_with_args(np, "io-channels", + "#io-channel-cells", + index, &iiospec); + if (err) + return err; + + idev = bus_find_device(&iio_bus_type, NULL, iiospec.np, + iio_dev_node_match); + of_node_put(iiospec.np); + if (idev == NULL) + return -EPROBE_DEFER; + + indio_dev = dev_to_iio_dev(idev); + channel->indio_dev = indio_dev; + index = iiospec.args_count ? iiospec.args[0] : 0; + if (index >= indio_dev->num_channels) { + return -EINVAL; + goto err_put; + } + channel->channel = &indio_dev->channels[index]; + + return 0; + +err_put: + iio_device_put(indio_dev); + return err; +} + +static struct iio_channel *of_iio_channel_get(struct device_node *np, int index) +{ + struct iio_channel *channel; + int err; + + if (index < 0) + return ERR_PTR(-EINVAL); + + channel = kzalloc(sizeof(*channel), GFP_KERNEL); + if (channel == NULL) + return ERR_PTR(-ENOMEM); + + err = __of_iio_channel_get(channel, np, index); + if (err) + goto err_free_channel; + + return channel; + +err_free_channel: + kfree(channel); + return ERR_PTR(err); +} + +static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np, + const char *name) +{ + struct iio_channel *chan = NULL; + + /* Walk up the tree of devices looking for a matching iio channel */ + while (np) { + int index = 0; + + /* + * For named iio channels, first look up the name in the + * "io-channel-names" property. If it cannot be found, the + * index will be an error code, and of_iio_channel_get() + * will fail. + */ + if (name) + index = of_property_match_string(np, "io-channel-names", + name); + chan = of_iio_channel_get(np, index); + if (!IS_ERR(chan)) + break; + else if (name && index >= 0) { + pr_err("ERROR: could not get IIO channel %s:%s(%i)\n", + np->full_name, name ? name : "", index); + return chan; + } + + /* + * No matching IIO channel found on this node. + * If the parent node has a "io-channel-ranges" property, + * then we can try one of its channels. + */ + np = np->parent; + if (np && !of_get_property(np, "io-channel-ranges", NULL)) + break; + } + return chan; +} + +static struct iio_channel *of_iio_channel_get_all(struct device *dev) +{ + struct iio_channel *chans; + int i, mapind, nummaps = 0; + int ret; + + do { + ret = of_parse_phandle_with_args(dev->of_node, + "io-channels", + "#io-channel-cells", + nummaps, NULL); + if (ret < 0) + break; + } while (++nummaps); + + if (nummaps == 0) /* no error, return NULL to search map table */ + return NULL; + + /* NULL terminated array to save passing size */ + chans = kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL); + if (chans == NULL) + return ERR_PTR(-ENOMEM); + + /* Search for OF matches */ + for (mapind = 0; mapind < nummaps; mapind++) { + ret = __of_iio_channel_get(&chans[mapind], dev->of_node, + mapind); + if (ret) + goto error_free_chans; + } + return chans; + +error_free_chans: + for (i = 0; i < mapind; i++) + iio_device_put(chans[i].indio_dev); + kfree(chans); + return ERR_PTR(ret); +} + +#else /* CONFIG_OF */ + +static inline struct iio_channel * +of_iio_channel_get_by_name(struct device_node *np, const char *name) +{ + return NULL; +} + +static inline struct iio_channel *of_iio_channel_get_all(struct device *dev) +{ + return NULL; +} + +#endif /* CONFIG_OF */ static struct iio_channel *iio_channel_get_sys(const char *name, const char *channel_name) @@ -150,7 +309,14 @@ struct iio_channel *iio_channel_get(struct device *dev, const char *channel_name) { const char *name = dev ? dev_name(dev) : NULL; + struct iio_channel *channel; + if (dev) { + channel = of_iio_channel_get_by_name(dev->of_node, + channel_name); + if (channel != NULL) + return channel; + } return iio_channel_get_sys(name, channel_name); } EXPORT_SYMBOL_GPL(iio_channel_get); @@ -173,6 +339,11 @@ struct iio_channel *iio_channel_get_all(struct device *dev) if (dev == NULL) return ERR_PTR(-EINVAL); + + chans = of_iio_channel_get_all(dev); + if (chans) + return chans; + name = dev_name(dev); mutex_lock(&iio_map_list_lock); -- cgit v1.2.3 From 10f5b14811023df0ba1a936b14880eabb6d9c199 Mon Sep 17 00:00:00 2001 From: Naveen Krishna Chatradhi Date: Fri, 15 Feb 2013 06:56:00 +0000 Subject: iio: adc: add exynos adc driver under iio framwork This patch adds New driver to support: 1. Supports ADC IF found on EXYNOS4412/EXYNOS5250 and future SoCs from Samsung 2. Add ADC driver under iio/adc framework 3. Also adds the Documentation for device tree bindings Signed-off-by: Naveen Krishna Chatradhi Reviewed-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/arm/samsung/exynos-adc.txt | 52 +++ drivers/iio/adc/Kconfig | 7 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/exynos_adc.c | 440 +++++++++++++++++++++ 4 files changed, 500 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt create mode 100644 drivers/iio/adc/exynos_adc.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt b/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt new file mode 100644 index 000000000000..f68637861b05 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt @@ -0,0 +1,52 @@ +Samsung Exynos Analog to Digital Converter bindings + +This devicetree binding are for the new adc driver written fori +Exynos4 and upward SoCs from Samsung. + +New driver handles the following +1. Supports ADC IF found on EXYNOS4412/EXYNOS5250 + and future SoCs from Samsung +2. Add ADC driver under iio/adc framework +3. Also adds the Documentation for device tree bindings + +Required properties: +- compatible: Must be "samsung,exynos-adc-v1" + for exynos4412/5250 controllers. + Must be "samsung,exynos-adc-v2" for + future controllers. +- reg: Contains ADC register address range (base address and + length). +- interrupts: Contains the interrupt information for the timer. The + format is being dependent on which interrupt controller + the Samsung device uses. +- #io-channel-cells = <1>; As ADC has multiple outputs + +Note: child nodes can be added for auto probing from device tree. + +Example: adding device info in dtsi file + +adc: adc@12D10000 { + compatible = "samsung,exynos-adc-v1"; + reg = <0x12D10000 0x100>; + interrupts = <0 106 0>; + #io-channel-cells = <1>; + io-channel-ranges; +}; + + +Example: Adding child nodes in dts file + +adc@12D10000 { + + /* NTC thermistor is a hwmon device */ + ncp15wb473@0 { + compatible = "ntc,ncp15wb473"; + pullup-uV = <1800000>; + pullup-ohm = <47000>; + pulldown-ohm = <0>; + io-channels = <&adc 4>; + }; +}; + +Note: Does not apply to ADC driver under arch/arm/plat-samsung/ +Note: The child node can be added under the adc node or seperately. diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 00213eaf8aa9..a40d3c29f0cb 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -103,6 +103,13 @@ config AT91_ADC help Say yes here to build support for Atmel AT91 ADC. +config EXYNOS_ADC + bool "Exynos ADC driver support" + help + Core support for the ADC block found in the Samsung EXYNOS series + of SoCs for drivers such as the touchscreen and hwmon to use to share + this resource. + config LP8788_ADC bool "LP8788 ADC driver" depends on MFD_LP8788 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index ab910ba4664c..0a825bed43f6 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_AD7791) += ad7791.o obj-$(CONFIG_AD7793) += ad7793.o obj-$(CONFIG_AD7887) += ad7887.o obj-$(CONFIG_AT91_ADC) += at91_adc.o +obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_MAX1363) += max1363.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c new file mode 100644 index 000000000000..ed6fdd7e5212 --- /dev/null +++ b/drivers/iio/adc/exynos_adc.c @@ -0,0 +1,440 @@ +/* + * exynos_adc.c - Support for ADC in EXYNOS SoCs + * + * 8 ~ 10 channel, 10/12-bit ADC + * + * Copyright (C) 2013 Naveen Krishna Chatradhi + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +enum adc_version { + ADC_V1, + ADC_V2 +}; + +/* EXYNOS4412/5250 ADC_V1 registers definitions */ +#define ADC_V1_CON(x) ((x) + 0x00) +#define ADC_V1_DLY(x) ((x) + 0x08) +#define ADC_V1_DATX(x) ((x) + 0x0C) +#define ADC_V1_INTCLR(x) ((x) + 0x18) +#define ADC_V1_MUX(x) ((x) + 0x1c) + +/* Future ADC_V2 registers definitions */ +#define ADC_V2_CON1(x) ((x) + 0x00) +#define ADC_V2_CON2(x) ((x) + 0x04) +#define ADC_V2_STAT(x) ((x) + 0x08) +#define ADC_V2_INT_EN(x) ((x) + 0x10) +#define ADC_V2_INT_ST(x) ((x) + 0x14) +#define ADC_V2_VER(x) ((x) + 0x20) + +/* Bit definitions for ADC_V1 */ +#define ADC_V1_CON_RES (1u << 16) +#define ADC_V1_CON_PRSCEN (1u << 14) +#define ADC_V1_CON_PRSCLV(x) (((x) & 0xFF) << 6) +#define ADC_V1_CON_STANDBY (1u << 2) + +/* Bit definitions for ADC_V2 */ +#define ADC_V2_CON1_SOFT_RESET (1u << 2) + +#define ADC_V2_CON2_OSEL (1u << 10) +#define ADC_V2_CON2_ESEL (1u << 9) +#define ADC_V2_CON2_HIGHF (1u << 8) +#define ADC_V2_CON2_C_TIME(x) (((x) & 7) << 4) +#define ADC_V2_CON2_ACH_SEL(x) (((x) & 0xF) << 0) +#define ADC_V2_CON2_ACH_MASK 0xF + +#define MAX_ADC_V2_CHANNELS 10 +#define MAX_ADC_V1_CHANNELS 8 + +/* Bit definitions common for ADC_V1 and ADC_V2 */ +#define ADC_CON_EN_START (1u << 0) +#define ADC_DATX_MASK 0xFFF + +#define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(1000)) + +struct exynos_adc { + void __iomem *regs; + struct clk *clk; + unsigned int irq; + struct regulator *vdd; + + struct completion completion; + + u32 value; + unsigned int version; +}; + +static const struct of_device_id exynos_adc_match[] = { + { .compatible = "samsung,exynos-adc-v1", .data = (void *)ADC_V1 }, + { .compatible = "samsung,exynos-adc-v2", .data = (void *)ADC_V2 }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_adc_match); + +static inline unsigned int exynos_adc_get_version(struct platform_device *pdev) +{ + const struct of_device_id *match; + + match = of_match_node(exynos_adc_match, pdev->dev.of_node); + return (unsigned int)match->data; +} + +static int exynos_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct exynos_adc *info = iio_priv(indio_dev); + unsigned long timeout; + u32 con1, con2; + + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + mutex_lock(&indio_dev->mlock); + + /* Select the channel to be used and Trigger conversion */ + if (info->version == ADC_V2) { + con2 = readl(ADC_V2_CON2(info->regs)); + con2 &= ~ADC_V2_CON2_ACH_MASK; + con2 |= ADC_V2_CON2_ACH_SEL(chan->address); + writel(con2, ADC_V2_CON2(info->regs)); + + con1 = readl(ADC_V2_CON1(info->regs)); + writel(con1 | ADC_CON_EN_START, + ADC_V2_CON1(info->regs)); + } else { + writel(chan->address, ADC_V1_MUX(info->regs)); + + con1 = readl(ADC_V1_CON(info->regs)); + writel(con1 | ADC_CON_EN_START, + ADC_V1_CON(info->regs)); + } + + timeout = wait_for_completion_interruptible_timeout + (&info->completion, EXYNOS_ADC_TIMEOUT); + *val = info->value; + + mutex_unlock(&indio_dev->mlock); + + if (timeout == 0) + return -ETIMEDOUT; + + return IIO_VAL_INT; +} + +static irqreturn_t exynos_adc_isr(int irq, void *dev_id) +{ + struct exynos_adc *info = (struct exynos_adc *)dev_id; + + /* Read value */ + info->value = readl(ADC_V1_DATX(info->regs)) & + ADC_DATX_MASK; + /* clear irq */ + if (info->version == ADC_V2) + writel(1, ADC_V2_INT_ST(info->regs)); + else + writel(1, ADC_V1_INTCLR(info->regs)); + + complete(&info->completion); + + return IRQ_HANDLED; +} + +static int exynos_adc_reg_access(struct iio_dev *indio_dev, + unsigned reg, unsigned writeval, + unsigned *readval) +{ + struct exynos_adc *info = iio_priv(indio_dev); + + if (readval == NULL) + return -EINVAL; + + *readval = readl(info->regs + reg); + + return 0; +} + +static const struct iio_info exynos_adc_iio_info = { + .read_raw = &exynos_read_raw, + .debugfs_reg_access = &exynos_adc_reg_access, + .driver_module = THIS_MODULE, +}; + +#define ADC_CHANNEL(_index, _id) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _index, \ + .address = _index, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, \ + .datasheet_name = _id, \ +} + +static const struct iio_chan_spec exynos_adc_iio_channels[] = { + ADC_CHANNEL(0, "adc0"), + ADC_CHANNEL(1, "adc1"), + ADC_CHANNEL(2, "adc2"), + ADC_CHANNEL(3, "adc3"), + ADC_CHANNEL(4, "adc4"), + ADC_CHANNEL(5, "adc5"), + ADC_CHANNEL(6, "adc6"), + ADC_CHANNEL(7, "adc7"), + ADC_CHANNEL(8, "adc8"), + ADC_CHANNEL(9, "adc9"), +}; + +static int exynos_adc_remove_devices(struct device *dev, void *c) +{ + struct platform_device *pdev = to_platform_device(dev); + + platform_device_unregister(pdev); + + return 0; +} + +static void exynos_adc_hw_init(struct exynos_adc *info) +{ + u32 con1, con2; + + if (info->version == ADC_V2) { + con1 = ADC_V2_CON1_SOFT_RESET; + writel(con1, ADC_V2_CON1(info->regs)); + + con2 = ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL | + ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(0); + writel(con2, ADC_V2_CON2(info->regs)); + + /* Enable interrupts */ + writel(1, ADC_V2_INT_EN(info->regs)); + } else { + /* set default prescaler values and Enable prescaler */ + con1 = ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN; + + /* Enable 12-bit ADC resolution */ + con1 |= ADC_V1_CON_RES; + writel(con1, ADC_V1_CON(info->regs)); + } +} + +static int exynos_adc_probe(struct platform_device *pdev) +{ + struct exynos_adc *info = NULL; + struct device_node *np = pdev->dev.of_node; + struct iio_dev *indio_dev = NULL; + struct resource *mem; + int ret = -ENODEV; + int irq; + + if (!np) + return ret; + + indio_dev = iio_device_alloc(sizeof(struct exynos_adc)); + if (!indio_dev) { + dev_err(&pdev->dev, "failed allocating iio device\n"); + return -ENOMEM; + } + + info = iio_priv(indio_dev); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + info->regs = devm_request_and_ioremap(&pdev->dev, mem); + if (!info->regs) { + ret = -ENOMEM; + goto err_iio; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq resource?\n"); + ret = irq; + goto err_iio; + } + + info->irq = irq; + + init_completion(&info->completion); + + ret = request_irq(info->irq, exynos_adc_isr, + 0, dev_name(&pdev->dev), info); + if (ret < 0) { + dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", + info->irq); + goto err_iio; + } + + info->clk = devm_clk_get(&pdev->dev, "adc"); + if (IS_ERR(info->clk)) { + dev_err(&pdev->dev, "failed getting clock, err = %ld\n", + PTR_ERR(info->clk)); + ret = PTR_ERR(info->clk); + goto err_irq; + } + + info->vdd = devm_regulator_get(&pdev->dev, "vdd"); + if (IS_ERR(info->vdd)) { + dev_err(&pdev->dev, "failed getting regulator, err = %ld\n", + PTR_ERR(info->vdd)); + ret = PTR_ERR(info->vdd); + goto err_irq; + } + + info->version = exynos_adc_get_version(pdev); + + platform_set_drvdata(pdev, indio_dev); + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->dev.parent = &pdev->dev; + indio_dev->dev.of_node = pdev->dev.of_node; + indio_dev->info = &exynos_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = exynos_adc_iio_channels; + + if (info->version == ADC_V1) + indio_dev->num_channels = MAX_ADC_V1_CHANNELS; + else + indio_dev->num_channels = MAX_ADC_V2_CHANNELS; + + ret = iio_device_register(indio_dev); + if (ret) + goto err_irq; + + ret = regulator_enable(info->vdd); + if (ret) + goto err_iio_dev; + + clk_prepare_enable(info->clk); + + exynos_adc_hw_init(info); + + ret = of_platform_populate(np, exynos_adc_match, NULL, &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed adding child nodes\n"); + goto err_of_populate; + } + + return 0; + +err_of_populate: + device_for_each_child(&pdev->dev, NULL, + exynos_adc_remove_devices); + regulator_disable(info->vdd); + clk_disable_unprepare(info->clk); +err_iio_dev: + iio_device_unregister(indio_dev); +err_irq: + free_irq(info->irq, info); +err_iio: + iio_device_free(indio_dev); + return ret; +} + +static int exynos_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct exynos_adc *info = iio_priv(indio_dev); + + device_for_each_child(&pdev->dev, NULL, + exynos_adc_remove_devices); + regulator_disable(info->vdd); + clk_disable_unprepare(info->clk); + iio_device_unregister(indio_dev); + free_irq(info->irq, info); + iio_device_free(indio_dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int exynos_adc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct exynos_adc *info = platform_get_drvdata(pdev); + u32 con; + + if (info->version == ADC_V2) { + con = readl(ADC_V2_CON1(info->regs)); + con &= ~ADC_CON_EN_START; + writel(con, ADC_V2_CON1(info->regs)); + } else { + con = readl(ADC_V1_CON(info->regs)); + con |= ADC_V1_CON_STANDBY; + writel(con, ADC_V1_CON(info->regs)); + } + + clk_disable_unprepare(info->clk); + regulator_disable(info->vdd); + + return 0; +} + +static int exynos_adc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct exynos_adc *info = platform_get_drvdata(pdev); + int ret; + + ret = regulator_enable(info->vdd); + if (ret) + return ret; + + clk_prepare_enable(info->clk); + + exynos_adc_hw_init(info); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(exynos_adc_pm_ops, + exynos_adc_suspend, + exynos_adc_resume); + +static struct platform_driver exynos_adc_driver = { + .probe = exynos_adc_probe, + .remove = exynos_adc_remove, + .driver = { + .name = "exynos-adc", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(exynos_adc_match), + .pm = &exynos_adc_pm_ops, + }, +}; + +module_platform_driver(exynos_adc_driver); + +MODULE_AUTHOR("Naveen Krishna Chatradhi "); +MODULE_DESCRIPTION("Samsung EXYNOS5 ADC driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 1a2c6181c4a1922021b4d7df373bba612c3e5f04 Mon Sep 17 00:00:00 2001 From: Christoph Paasch Date: Sun, 17 Mar 2013 08:23:34 +0000 Subject: tcp: Remove TCPCT TCPCT uses option-number 253, reserved for experimental use and should not be used in production environments. Further, TCPCT does not fully implement RFC 6013. As a nice side-effect, removing TCPCT increases TCP's performance for very short flows: Doing an apache-benchmark with -c 100 -n 100000, sending HTTP-requests for files of 1KB size. before this patch: average (among 7 runs) of 20845.5 Requests/Second after: average (among 7 runs) of 21403.6 Requests/Second Signed-off-by: Christoph Paasch Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 8 - drivers/infiniband/hw/cxgb4/cm.c | 2 +- include/linux/tcp.h | 10 -- include/net/request_sock.h | 8 +- include/net/tcp.h | 89 +---------- include/uapi/linux/tcp.h | 26 ---- net/dccp/ipv4.c | 5 +- net/dccp/ipv6.c | 5 +- net/ipv4/inet_connection_sock.c | 2 +- net/ipv4/syncookies.c | 3 +- net/ipv4/sysctl_net_ipv4.c | 7 - net/ipv4/tcp.c | 267 --------------------------------- net/ipv4/tcp_input.c | 69 +-------- net/ipv4/tcp_ipv4.c | 60 +------- net/ipv4/tcp_minisocks.c | 40 +---- net/ipv4/tcp_output.c | 219 +-------------------------- net/ipv6/syncookies.c | 3 +- net/ipv6/tcp_ipv6.c | 56 +------ 18 files changed, 38 insertions(+), 841 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 18a24c405ac0..17953e2bc3e9 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -175,14 +175,6 @@ tcp_congestion_control - STRING is inherited. [see setsockopt(listenfd, SOL_TCP, TCP_CONGESTION, "name" ...) ] -tcp_cookie_size - INTEGER - Default size of TCP Cookie Transactions (TCPCT) option, that may be - overridden on a per socket basis by the TCPCT socket option. - Values greater than the maximum (16) are interpreted as the maximum. - Values greater than zero and less than the minimum (8) are interpreted - as the minimum. Odd values are interpreted as the next even value. - Default: 0 (off). - tcp_dsack - BOOLEAN Allows TCP to send "duplicate" SACKs. diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 8dcc84fd9d30..54fd31fcc332 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -2915,7 +2915,7 @@ static void build_cpl_pass_accept_req(struct sk_buff *skb, int stid , u8 tos) */ memset(&tmp_opt, 0, sizeof(tmp_opt)); tcp_clear_options(&tmp_opt); - tcp_parse_options(skb, &tmp_opt, NULL, 0, NULL); + tcp_parse_options(skb, &tmp_opt, 0, NULL); req = (struct cpl_pass_accept_req *)__skb_push(skb, sizeof(*req)); memset(req, 0, sizeof(*req)); diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 763c108ee03d..ed6a7456eecd 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -90,9 +90,6 @@ struct tcp_options_received { sack_ok : 4, /* SACK seen on SYN packet */ snd_wscale : 4, /* Window scaling received from sender */ rcv_wscale : 4; /* Window scaling to send to receiver */ - u8 cookie_plus:6, /* bytes in authenticator/cookie option */ - cookie_out_never:1, - cookie_in_always:1; u8 num_sacks; /* Number of SACK blocks */ u16 user_mss; /* mss requested by user in ioctl */ u16 mss_clamp; /* Maximal mss, negotiated at connection setup */ @@ -102,7 +99,6 @@ static inline void tcp_clear_options(struct tcp_options_received *rx_opt) { rx_opt->tstamp_ok = rx_opt->sack_ok = 0; rx_opt->wscale_ok = rx_opt->snd_wscale = 0; - rx_opt->cookie_plus = 0; } /* This is the max number of SACKS that we'll generate and process. It's safe @@ -320,12 +316,6 @@ struct tcp_sock { struct tcp_md5sig_info __rcu *md5sig_info; #endif - /* When the cookie options are generated and exchanged, then this - * object holds a reference to them (cookie_values->kref). Also - * contains related tcp_cookie_transactions fields. - */ - struct tcp_cookie_values *cookie_values; - /* TCP fastopen related information */ struct tcp_fastopen_request *fastopen_req; /* fastopen_rsk points to request_sock that resulted in this big diff --git a/include/net/request_sock.h b/include/net/request_sock.h index a51dbd17c2de..9069e65c1c56 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -27,19 +27,13 @@ struct sk_buff; struct dst_entry; struct proto; -/* empty to "strongly type" an otherwise void parameter. - */ -struct request_values { -}; - struct request_sock_ops { int family; int obj_size; struct kmem_cache *slab; char *slab_name; int (*rtx_syn_ack)(struct sock *sk, - struct request_sock *req, - struct request_values *rvp); + struct request_sock *req); void (*send_ack)(struct sock *sk, struct sk_buff *skb, struct request_sock *req); void (*send_reset)(struct sock *sk, diff --git a/include/net/tcp.h b/include/net/tcp.h index ab9f947b118b..7f2f17198d75 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -179,7 +179,6 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo); #define TCPOPT_SACK 5 /* SACK Block */ #define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */ #define TCPOPT_MD5SIG 19 /* MD5 Signature (RFC2385) */ -#define TCPOPT_COOKIE 253 /* Cookie extension (experimental) */ #define TCPOPT_EXP 254 /* Experimental */ /* Magic number to be after the option value for sharing TCP * experimental options. See draft-ietf-tcpm-experimental-options-00.txt @@ -454,7 +453,7 @@ extern void tcp_syn_ack_timeout(struct sock *sk, struct request_sock *req); extern int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len, int nonblock, int flags, int *addr_len); extern void tcp_parse_options(const struct sk_buff *skb, - struct tcp_options_received *opt_rx, const u8 **hvpp, + struct tcp_options_received *opt_rx, int estab, struct tcp_fastopen_cookie *foc); extern const u8 *tcp_parse_md5sig_option(const struct tcphdr *th); @@ -476,7 +475,6 @@ extern int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, extern int tcp_connect(struct sock *sk); extern struct sk_buff * tcp_make_synack(struct sock *sk, struct dst_entry *dst, struct request_sock *req, - struct request_values *rvp, struct tcp_fastopen_cookie *foc); extern int tcp_disconnect(struct sock *sk, int flags); @@ -1589,91 +1587,6 @@ struct tcp_request_sock_ops { #endif }; -/* Using SHA1 for now, define some constants. - */ -#define COOKIE_DIGEST_WORDS (SHA_DIGEST_WORDS) -#define COOKIE_MESSAGE_WORDS (SHA_MESSAGE_BYTES / 4) -#define COOKIE_WORKSPACE_WORDS (COOKIE_DIGEST_WORDS + COOKIE_MESSAGE_WORDS) - -extern int tcp_cookie_generator(u32 *bakery); - -/** - * struct tcp_cookie_values - each socket needs extra space for the - * cookies, together with (optional) space for any SYN data. - * - * A tcp_sock contains a pointer to the current value, and this is - * cloned to the tcp_timewait_sock. - * - * @cookie_pair: variable data from the option exchange. - * - * @cookie_desired: user specified tcpct_cookie_desired. Zero - * indicates default (sysctl_tcp_cookie_size). - * After cookie sent, remembers size of cookie. - * Range 0, TCP_COOKIE_MIN to TCP_COOKIE_MAX. - * - * @s_data_desired: user specified tcpct_s_data_desired. When the - * constant payload is specified (@s_data_constant), - * holds its length instead. - * Range 0 to TCP_MSS_DESIRED. - * - * @s_data_payload: constant data that is to be included in the - * payload of SYN or SYNACK segments when the - * cookie option is present. - */ -struct tcp_cookie_values { - struct kref kref; - u8 cookie_pair[TCP_COOKIE_PAIR_SIZE]; - u8 cookie_pair_size; - u8 cookie_desired; - u16 s_data_desired:11, - s_data_constant:1, - s_data_in:1, - s_data_out:1, - s_data_unused:2; - u8 s_data_payload[0]; -}; - -static inline void tcp_cookie_values_release(struct kref *kref) -{ - kfree(container_of(kref, struct tcp_cookie_values, kref)); -} - -/* The length of constant payload data. Note that s_data_desired is - * overloaded, depending on s_data_constant: either the length of constant - * data (returned here) or the limit on variable data. - */ -static inline int tcp_s_data_size(const struct tcp_sock *tp) -{ - return (tp->cookie_values != NULL && tp->cookie_values->s_data_constant) - ? tp->cookie_values->s_data_desired - : 0; -} - -/** - * struct tcp_extend_values - tcp_ipv?.c to tcp_output.c workspace. - * - * As tcp_request_sock has already been extended in other places, the - * only remaining method is to pass stack values along as function - * parameters. These parameters are not needed after sending SYNACK. - * - * @cookie_bakery: cryptographic secret and message workspace. - * - * @cookie_plus: bytes in authenticator/cookie option, copied from - * struct tcp_options_received (above). - */ -struct tcp_extend_values { - struct request_values rv; - u32 cookie_bakery[COOKIE_WORKSPACE_WORDS]; - u8 cookie_plus:6, - cookie_out_never:1, - cookie_in_always:1; -}; - -static inline struct tcp_extend_values *tcp_xv(struct request_values *rvp) -{ - return (struct tcp_extend_values *)rvp; -} - extern void tcp_v4_init(void); extern void tcp_init(void); diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 6b1ead0b0c9d..8d776ebc4829 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -102,7 +102,6 @@ enum { #define TCP_QUICKACK 12 /* Block/reenable quick acks */ #define TCP_CONGESTION 13 /* Congestion control algorithm */ #define TCP_MD5SIG 14 /* TCP MD5 Signature (RFC2385) */ -#define TCP_COOKIE_TRANSACTIONS 15 /* TCP Cookie Transactions */ #define TCP_THIN_LINEAR_TIMEOUTS 16 /* Use linear timeouts for thin streams*/ #define TCP_THIN_DUPACK 17 /* Fast retrans. after 1 dupack */ #define TCP_USER_TIMEOUT 18 /* How long for loss retry before timeout */ @@ -199,29 +198,4 @@ struct tcp_md5sig { __u8 tcpm_key[TCP_MD5SIG_MAXKEYLEN]; /* key (binary) */ }; -/* for TCP_COOKIE_TRANSACTIONS (TCPCT) socket option */ -#define TCP_COOKIE_MIN 8 /* 64-bits */ -#define TCP_COOKIE_MAX 16 /* 128-bits */ -#define TCP_COOKIE_PAIR_SIZE (2*TCP_COOKIE_MAX) - -/* Flags for both getsockopt and setsockopt */ -#define TCP_COOKIE_IN_ALWAYS (1 << 0) /* Discard SYN without cookie */ -#define TCP_COOKIE_OUT_NEVER (1 << 1) /* Prohibit outgoing cookies, - * supercedes everything. */ - -/* Flags for getsockopt */ -#define TCP_S_DATA_IN (1 << 2) /* Was data received? */ -#define TCP_S_DATA_OUT (1 << 3) /* Was data sent? */ - -/* TCP_COOKIE_TRANSACTIONS data */ -struct tcp_cookie_transactions { - __u16 tcpct_flags; /* see above */ - __u8 __tcpct_pad1; /* zero */ - __u8 tcpct_cookie_desired; /* bytes */ - __u16 tcpct_s_data_desired; /* bytes of variable data */ - __u16 tcpct_used; /* bytes in value */ - __u8 tcpct_value[TCP_MSS_DEFAULT]; -}; - - #endif /* _UAPI_LINUX_TCP_H */ diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 4f9f5eb478f1..ebc54fef85a5 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -500,8 +500,7 @@ static struct dst_entry* dccp_v4_route_skb(struct net *net, struct sock *sk, return &rt->dst; } -static int dccp_v4_send_response(struct sock *sk, struct request_sock *req, - struct request_values *rv_unused) +static int dccp_v4_send_response(struct sock *sk, struct request_sock *req) { int err = -1; struct sk_buff *skb; @@ -658,7 +657,7 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) dreq->dreq_gss = dreq->dreq_iss; dreq->dreq_service = service; - if (dccp_v4_send_response(sk, req, NULL)) + if (dccp_v4_send_response(sk, req)) goto drop_and_free; inet_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT); diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 6e05981f271e..9c61f9c02fdb 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -213,8 +213,7 @@ out: } -static int dccp_v6_send_response(struct sock *sk, struct request_sock *req, - struct request_values *rv_unused) +static int dccp_v6_send_response(struct sock *sk, struct request_sock *req) { struct inet6_request_sock *ireq6 = inet6_rsk(req); struct ipv6_pinfo *np = inet6_sk(sk); @@ -428,7 +427,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb) dreq->dreq_gss = dreq->dreq_iss; dreq->dreq_service = service; - if (dccp_v6_send_response(sk, req, NULL)) + if (dccp_v6_send_response(sk, req)) goto drop_and_free; inet6_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT); diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 786d97aee751..6acb541c9091 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -559,7 +559,7 @@ static inline void syn_ack_recalc(struct request_sock *req, const int thresh, int inet_rtx_syn_ack(struct sock *parent, struct request_sock *req) { - int err = req->rsk_ops->rtx_syn_ack(parent, req, NULL); + int err = req->rsk_ops->rtx_syn_ack(parent, req); if (!err) req->num_retrans++; diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index ef54377fb11c..7f4a5cb8f8d0 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -267,7 +267,6 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, struct ip_options *opt) { struct tcp_options_received tcp_opt; - const u8 *hash_location; struct inet_request_sock *ireq; struct tcp_request_sock *treq; struct tcp_sock *tp = tcp_sk(sk); @@ -294,7 +293,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, /* check for timestamp cookie support */ memset(&tcp_opt, 0, sizeof(tcp_opt)); - tcp_parse_options(skb, &tcp_opt, &hash_location, 0, NULL); + tcp_parse_options(skb, &tcp_opt, 0, NULL); if (!cookie_check_timestamp(&tcp_opt, sock_net(sk), &ecn_ok)) goto out; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index cca4550f4082..cb45062c8be0 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -732,13 +732,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, - { - .procname = "tcp_cookie_size", - .data = &sysctl_tcp_cookie_size, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { .procname = "tcp_thin_linear_timeouts", .data = &sysctl_tcp_thin_linear_timeouts, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 8d14573ade77..17a6810af5c8 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -409,15 +409,6 @@ void tcp_init_sock(struct sock *sk) icsk->icsk_sync_mss = tcp_sync_mss; - /* TCP Cookie Transactions */ - if (sysctl_tcp_cookie_size > 0) { - /* Default, cookies without s_data_payload. */ - tp->cookie_values = - kzalloc(sizeof(*tp->cookie_values), - sk->sk_allocation); - if (tp->cookie_values != NULL) - kref_init(&tp->cookie_values->kref); - } /* Presumed zeroed, in order of appearance: * cookie_in_always, cookie_out_never, * s_data_constant, s_data_in, s_data_out @@ -2397,92 +2388,6 @@ static int do_tcp_setsockopt(struct sock *sk, int level, release_sock(sk); return err; } - case TCP_COOKIE_TRANSACTIONS: { - struct tcp_cookie_transactions ctd; - struct tcp_cookie_values *cvp = NULL; - - if (sizeof(ctd) > optlen) - return -EINVAL; - if (copy_from_user(&ctd, optval, sizeof(ctd))) - return -EFAULT; - - if (ctd.tcpct_used > sizeof(ctd.tcpct_value) || - ctd.tcpct_s_data_desired > TCP_MSS_DESIRED) - return -EINVAL; - - if (ctd.tcpct_cookie_desired == 0) { - /* default to global value */ - } else if ((0x1 & ctd.tcpct_cookie_desired) || - ctd.tcpct_cookie_desired > TCP_COOKIE_MAX || - ctd.tcpct_cookie_desired < TCP_COOKIE_MIN) { - return -EINVAL; - } - - if (TCP_COOKIE_OUT_NEVER & ctd.tcpct_flags) { - /* Supercedes all other values */ - lock_sock(sk); - if (tp->cookie_values != NULL) { - kref_put(&tp->cookie_values->kref, - tcp_cookie_values_release); - tp->cookie_values = NULL; - } - tp->rx_opt.cookie_in_always = 0; /* false */ - tp->rx_opt.cookie_out_never = 1; /* true */ - release_sock(sk); - return err; - } - - /* Allocate ancillary memory before locking. - */ - if (ctd.tcpct_used > 0 || - (tp->cookie_values == NULL && - (sysctl_tcp_cookie_size > 0 || - ctd.tcpct_cookie_desired > 0 || - ctd.tcpct_s_data_desired > 0))) { - cvp = kzalloc(sizeof(*cvp) + ctd.tcpct_used, - GFP_KERNEL); - if (cvp == NULL) - return -ENOMEM; - - kref_init(&cvp->kref); - } - lock_sock(sk); - tp->rx_opt.cookie_in_always = - (TCP_COOKIE_IN_ALWAYS & ctd.tcpct_flags); - tp->rx_opt.cookie_out_never = 0; /* false */ - - if (tp->cookie_values != NULL) { - if (cvp != NULL) { - /* Changed values are recorded by a changed - * pointer, ensuring the cookie will differ, - * without separately hashing each value later. - */ - kref_put(&tp->cookie_values->kref, - tcp_cookie_values_release); - } else { - cvp = tp->cookie_values; - } - } - - if (cvp != NULL) { - cvp->cookie_desired = ctd.tcpct_cookie_desired; - - if (ctd.tcpct_used > 0) { - memcpy(cvp->s_data_payload, ctd.tcpct_value, - ctd.tcpct_used); - cvp->s_data_desired = ctd.tcpct_used; - cvp->s_data_constant = 1; /* true */ - } else { - /* No constant payload data. */ - cvp->s_data_desired = ctd.tcpct_s_data_desired; - cvp->s_data_constant = 0; /* false */ - } - - tp->cookie_values = cvp; - } - release_sock(sk); - return err; - } default: /* fallthru */ break; @@ -2902,41 +2807,6 @@ static int do_tcp_getsockopt(struct sock *sk, int level, return -EFAULT; return 0; - case TCP_COOKIE_TRANSACTIONS: { - struct tcp_cookie_transactions ctd; - struct tcp_cookie_values *cvp = tp->cookie_values; - - if (get_user(len, optlen)) - return -EFAULT; - if (len < sizeof(ctd)) - return -EINVAL; - - memset(&ctd, 0, sizeof(ctd)); - ctd.tcpct_flags = (tp->rx_opt.cookie_in_always ? - TCP_COOKIE_IN_ALWAYS : 0) - | (tp->rx_opt.cookie_out_never ? - TCP_COOKIE_OUT_NEVER : 0); - - if (cvp != NULL) { - ctd.tcpct_flags |= (cvp->s_data_in ? - TCP_S_DATA_IN : 0) - | (cvp->s_data_out ? - TCP_S_DATA_OUT : 0); - - ctd.tcpct_cookie_desired = cvp->cookie_desired; - ctd.tcpct_s_data_desired = cvp->s_data_desired; - - memcpy(&ctd.tcpct_value[0], &cvp->cookie_pair[0], - cvp->cookie_pair_size); - ctd.tcpct_used = cvp->cookie_pair_size; - } - - if (put_user(sizeof(ctd), optlen)) - return -EFAULT; - if (copy_to_user(optval, &ctd, sizeof(ctd))) - return -EFAULT; - return 0; - } case TCP_THIN_LINEAR_TIMEOUTS: val = tp->thin_lto; break; @@ -3409,134 +3279,6 @@ EXPORT_SYMBOL(tcp_md5_hash_key); #endif -/* Each Responder maintains up to two secret values concurrently for - * efficient secret rollover. Each secret value has 4 states: - * - * Generating. (tcp_secret_generating != tcp_secret_primary) - * Generates new Responder-Cookies, but not yet used for primary - * verification. This is a short-term state, typically lasting only - * one round trip time (RTT). - * - * Primary. (tcp_secret_generating == tcp_secret_primary) - * Used both for generation and primary verification. - * - * Retiring. (tcp_secret_retiring != tcp_secret_secondary) - * Used for verification, until the first failure that can be - * verified by the newer Generating secret. At that time, this - * cookie's state is changed to Secondary, and the Generating - * cookie's state is changed to Primary. This is a short-term state, - * typically lasting only one round trip time (RTT). - * - * Secondary. (tcp_secret_retiring == tcp_secret_secondary) - * Used for secondary verification, after primary verification - * failures. This state lasts no more than twice the Maximum Segment - * Lifetime (2MSL). Then, the secret is discarded. - */ -struct tcp_cookie_secret { - /* The secret is divided into two parts. The digest part is the - * equivalent of previously hashing a secret and saving the state, - * and serves as an initialization vector (IV). The message part - * serves as the trailing secret. - */ - u32 secrets[COOKIE_WORKSPACE_WORDS]; - unsigned long expires; -}; - -#define TCP_SECRET_1MSL (HZ * TCP_PAWS_MSL) -#define TCP_SECRET_2MSL (HZ * TCP_PAWS_MSL * 2) -#define TCP_SECRET_LIFE (HZ * 600) - -static struct tcp_cookie_secret tcp_secret_one; -static struct tcp_cookie_secret tcp_secret_two; - -/* Essentially a circular list, without dynamic allocation. */ -static struct tcp_cookie_secret *tcp_secret_generating; -static struct tcp_cookie_secret *tcp_secret_primary; -static struct tcp_cookie_secret *tcp_secret_retiring; -static struct tcp_cookie_secret *tcp_secret_secondary; - -static DEFINE_SPINLOCK(tcp_secret_locker); - -/* Select a pseudo-random word in the cookie workspace. - */ -static inline u32 tcp_cookie_work(const u32 *ws, const int n) -{ - return ws[COOKIE_DIGEST_WORDS + ((COOKIE_MESSAGE_WORDS-1) & ws[n])]; -} - -/* Fill bakery[COOKIE_WORKSPACE_WORDS] with generator, updating as needed. - * Called in softirq context. - * Returns: 0 for success. - */ -int tcp_cookie_generator(u32 *bakery) -{ - unsigned long jiffy = jiffies; - - if (unlikely(time_after_eq(jiffy, tcp_secret_generating->expires))) { - spin_lock_bh(&tcp_secret_locker); - if (!time_after_eq(jiffy, tcp_secret_generating->expires)) { - /* refreshed by another */ - memcpy(bakery, - &tcp_secret_generating->secrets[0], - COOKIE_WORKSPACE_WORDS); - } else { - /* still needs refreshing */ - get_random_bytes(bakery, COOKIE_WORKSPACE_WORDS); - - /* The first time, paranoia assumes that the - * randomization function isn't as strong. But, - * this secret initialization is delayed until - * the last possible moment (packet arrival). - * Although that time is observable, it is - * unpredictably variable. Mash in the most - * volatile clock bits available, and expire the - * secret extra quickly. - */ - if (unlikely(tcp_secret_primary->expires == - tcp_secret_secondary->expires)) { - struct timespec tv; - - getnstimeofday(&tv); - bakery[COOKIE_DIGEST_WORDS+0] ^= - (u32)tv.tv_nsec; - - tcp_secret_secondary->expires = jiffy - + TCP_SECRET_1MSL - + (0x0f & tcp_cookie_work(bakery, 0)); - } else { - tcp_secret_secondary->expires = jiffy - + TCP_SECRET_LIFE - + (0xff & tcp_cookie_work(bakery, 1)); - tcp_secret_primary->expires = jiffy - + TCP_SECRET_2MSL - + (0x1f & tcp_cookie_work(bakery, 2)); - } - memcpy(&tcp_secret_secondary->secrets[0], - bakery, COOKIE_WORKSPACE_WORDS); - - rcu_assign_pointer(tcp_secret_generating, - tcp_secret_secondary); - rcu_assign_pointer(tcp_secret_retiring, - tcp_secret_primary); - /* - * Neither call_rcu() nor synchronize_rcu() needed. - * Retiring data is not freed. It is replaced after - * further (locked) pointer updates, and a quiet time - * (minimum 1MSL, maximum LIFE - 2MSL). - */ - } - spin_unlock_bh(&tcp_secret_locker); - } else { - rcu_read_lock_bh(); - memcpy(bakery, - &rcu_dereference(tcp_secret_generating)->secrets[0], - COOKIE_WORKSPACE_WORDS); - rcu_read_unlock_bh(); - } - return 0; -} -EXPORT_SYMBOL(tcp_cookie_generator); - void tcp_done(struct sock *sk) { struct request_sock *req = tcp_sk(sk)->fastopen_rsk; @@ -3591,7 +3333,6 @@ void __init tcp_init(void) unsigned long limit; int max_rshare, max_wshare, cnt; unsigned int i; - unsigned long jiffy = jiffies; BUILD_BUG_ON(sizeof(struct tcp_skb_cb) > sizeof(skb->cb)); @@ -3667,13 +3408,5 @@ void __init tcp_init(void) tcp_register_congestion_control(&tcp_reno); - memset(&tcp_secret_one.secrets[0], 0, sizeof(tcp_secret_one.secrets)); - memset(&tcp_secret_two.secrets[0], 0, sizeof(tcp_secret_two.secrets)); - tcp_secret_one.expires = jiffy; /* past due */ - tcp_secret_two.expires = jiffy; /* past due */ - tcp_secret_generating = &tcp_secret_one; - tcp_secret_primary = &tcp_secret_one; - tcp_secret_retiring = &tcp_secret_two; - tcp_secret_secondary = &tcp_secret_two; tcp_tasklet_init(); } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 836d74dd0187..19f0149fb6a2 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3760,8 +3760,8 @@ old_ack: * But, this can also be called on packets in the established flow when * the fast version below fails. */ -void tcp_parse_options(const struct sk_buff *skb, struct tcp_options_received *opt_rx, - const u8 **hvpp, int estab, +void tcp_parse_options(const struct sk_buff *skb, + struct tcp_options_received *opt_rx, int estab, struct tcp_fastopen_cookie *foc) { const unsigned char *ptr; @@ -3845,31 +3845,6 @@ void tcp_parse_options(const struct sk_buff *skb, struct tcp_options_received *o */ break; #endif - case TCPOPT_COOKIE: - /* This option is variable length. - */ - switch (opsize) { - case TCPOLEN_COOKIE_BASE: - /* not yet implemented */ - break; - case TCPOLEN_COOKIE_PAIR: - /* not yet implemented */ - break; - case TCPOLEN_COOKIE_MIN+0: - case TCPOLEN_COOKIE_MIN+2: - case TCPOLEN_COOKIE_MIN+4: - case TCPOLEN_COOKIE_MIN+6: - case TCPOLEN_COOKIE_MAX: - /* 16-bit multiple */ - opt_rx->cookie_plus = opsize; - *hvpp = ptr; - break; - default: - /* ignore option */ - break; - } - break; - case TCPOPT_EXP: /* Fast Open option shares code 254 using a * 16 bits magic number. It's valid only in @@ -3915,8 +3890,7 @@ static bool tcp_parse_aligned_timestamp(struct tcp_sock *tp, const struct tcphdr * If it is wrong it falls back on tcp_parse_options(). */ static bool tcp_fast_parse_options(const struct sk_buff *skb, - const struct tcphdr *th, - struct tcp_sock *tp, const u8 **hvpp) + const struct tcphdr *th, struct tcp_sock *tp) { /* In the spirit of fast parsing, compare doff directly to constant * values. Because equality is used, short doff can be ignored here. @@ -3930,7 +3904,7 @@ static bool tcp_fast_parse_options(const struct sk_buff *skb, return true; } - tcp_parse_options(skb, &tp->rx_opt, hvpp, 1, NULL); + tcp_parse_options(skb, &tp->rx_opt, 1, NULL); if (tp->rx_opt.saw_tstamp) tp->rx_opt.rcv_tsecr -= tp->tsoffset; @@ -5311,12 +5285,10 @@ out: static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb, const struct tcphdr *th, int syn_inerr) { - const u8 *hash_location; struct tcp_sock *tp = tcp_sk(sk); /* RFC1323: H1. Apply PAWS check first. */ - if (tcp_fast_parse_options(skb, th, tp, &hash_location) && - tp->rx_opt.saw_tstamp && + if (tcp_fast_parse_options(skb, th, tp) && tp->rx_opt.saw_tstamp && tcp_paws_discard(sk, skb)) { if (!th->rst) { NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED); @@ -5670,12 +5642,11 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack, if (mss == tp->rx_opt.user_mss) { struct tcp_options_received opt; - const u8 *hash_location; /* Get original SYNACK MSS value if user MSS sets mss_clamp */ tcp_clear_options(&opt); opt.user_mss = opt.mss_clamp = 0; - tcp_parse_options(synack, &opt, &hash_location, 0, NULL); + tcp_parse_options(synack, &opt, 0, NULL); mss = opt.mss_clamp; } @@ -5706,14 +5677,12 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack, static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, const struct tcphdr *th, unsigned int len) { - const u8 *hash_location; struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); - struct tcp_cookie_values *cvp = tp->cookie_values; struct tcp_fastopen_cookie foc = { .len = -1 }; int saved_clamp = tp->rx_opt.mss_clamp; - tcp_parse_options(skb, &tp->rx_opt, &hash_location, 0, &foc); + tcp_parse_options(skb, &tp->rx_opt, 0, &foc); if (tp->rx_opt.saw_tstamp) tp->rx_opt.rcv_tsecr -= tp->tsoffset; @@ -5810,30 +5779,6 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, * is initialized. */ tp->copied_seq = tp->rcv_nxt; - if (cvp != NULL && - cvp->cookie_pair_size > 0 && - tp->rx_opt.cookie_plus > 0) { - int cookie_size = tp->rx_opt.cookie_plus - - TCPOLEN_COOKIE_BASE; - int cookie_pair_size = cookie_size - + cvp->cookie_desired; - - /* A cookie extension option was sent and returned. - * Note that each incoming SYNACK replaces the - * Responder cookie. The initial exchange is most - * fragile, as protection against spoofing relies - * entirely upon the sequence and timestamp (above). - * This replacement strategy allows the correct pair to - * pass through, while any others will be filtered via - * Responder verification later. - */ - if (sizeof(cvp->cookie_pair) >= cookie_pair_size) { - memcpy(&cvp->cookie_pair[cvp->cookie_desired], - hash_location, cookie_size); - cvp->cookie_pair_size = cookie_pair_size; - } - } - smp_mb(); tcp_finish_connect(sk, skb); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index b7ab868c8284..b27c758ca23f 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -838,7 +838,6 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, */ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, struct request_sock *req, - struct request_values *rvp, u16 queue_mapping, bool nocache) { @@ -851,7 +850,7 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL) return -1; - skb = tcp_make_synack(sk, dst, req, rvp, NULL); + skb = tcp_make_synack(sk, dst, req, NULL); if (skb) { __tcp_v4_send_check(skb, ireq->loc_addr, ireq->rmt_addr); @@ -868,10 +867,9 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, return err; } -static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req, - struct request_values *rvp) +static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req) { - int res = tcp_v4_send_synack(sk, NULL, req, rvp, 0, false); + int res = tcp_v4_send_synack(sk, NULL, req, 0, false); if (!res) TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS); @@ -1371,8 +1369,7 @@ static bool tcp_fastopen_check(struct sock *sk, struct sk_buff *skb, static int tcp_v4_conn_req_fastopen(struct sock *sk, struct sk_buff *skb, struct sk_buff *skb_synack, - struct request_sock *req, - struct request_values *rvp) + struct request_sock *req) { struct tcp_sock *tp = tcp_sk(sk); struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue; @@ -1467,9 +1464,7 @@ static int tcp_v4_conn_req_fastopen(struct sock *sk, int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) { - struct tcp_extend_values tmp_ext; struct tcp_options_received tmp_opt; - const u8 *hash_location; struct request_sock *req; struct inet_request_sock *ireq; struct tcp_sock *tp = tcp_sk(sk); @@ -1519,42 +1514,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) tcp_clear_options(&tmp_opt); tmp_opt.mss_clamp = TCP_MSS_DEFAULT; tmp_opt.user_mss = tp->rx_opt.user_mss; - tcp_parse_options(skb, &tmp_opt, &hash_location, 0, - want_cookie ? NULL : &foc); - - if (tmp_opt.cookie_plus > 0 && - tmp_opt.saw_tstamp && - !tp->rx_opt.cookie_out_never && - (sysctl_tcp_cookie_size > 0 || - (tp->cookie_values != NULL && - tp->cookie_values->cookie_desired > 0))) { - u8 *c; - u32 *mess = &tmp_ext.cookie_bakery[COOKIE_DIGEST_WORDS]; - int l = tmp_opt.cookie_plus - TCPOLEN_COOKIE_BASE; - - if (tcp_cookie_generator(&tmp_ext.cookie_bakery[0]) != 0) - goto drop_and_release; - - /* Secret recipe starts with IP addresses */ - *mess++ ^= (__force u32)daddr; - *mess++ ^= (__force u32)saddr; - - /* plus variable length Initiator Cookie */ - c = (u8 *)mess; - while (l-- > 0) - *c++ ^= *hash_location++; - - want_cookie = false; /* not our kind of cookie */ - tmp_ext.cookie_out_never = 0; /* false */ - tmp_ext.cookie_plus = tmp_opt.cookie_plus; - } else if (!tp->rx_opt.cookie_in_always) { - /* redundant indications, but ensure initialization. */ - tmp_ext.cookie_out_never = 1; /* true */ - tmp_ext.cookie_plus = 0; - } else { - goto drop_and_release; - } - tmp_ext.cookie_in_always = tp->rx_opt.cookie_in_always; + tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc); if (want_cookie && !tmp_opt.saw_tstamp) tcp_clear_options(&tmp_opt); @@ -1636,7 +1596,6 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) * of tcp_v4_send_synack()->tcp_select_initial_window(). */ skb_synack = tcp_make_synack(sk, dst, req, - (struct request_values *)&tmp_ext, fastopen_cookie_present(&valid_foc) ? &valid_foc : NULL); if (skb_synack) { @@ -1660,8 +1619,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) if (fastopen_cookie_present(&foc) && foc.len != 0) NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVEFAIL); - } else if (tcp_v4_conn_req_fastopen(sk, skb, skb_synack, req, - (struct request_values *)&tmp_ext)) + } else if (tcp_v4_conn_req_fastopen(sk, skb, skb_synack, req)) goto drop_and_free; return 0; @@ -2241,12 +2199,6 @@ void tcp_v4_destroy_sock(struct sock *sk) if (inet_csk(sk)->icsk_bind_hash) inet_put_port(sk); - /* TCP Cookie Transactions */ - if (tp->cookie_values != NULL) { - kref_put(&tp->cookie_values->kref, - tcp_cookie_values_release); - tp->cookie_values = NULL; - } BUG_ON(tp->fastopen_rsk != NULL); /* If socket is aborted during connect operation */ diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 4bdb09fca401..8f0234f8bb95 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -93,13 +93,12 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb, const struct tcphdr *th) { struct tcp_options_received tmp_opt; - const u8 *hash_location; struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw); bool paws_reject = false; tmp_opt.saw_tstamp = 0; if (th->doff > (sizeof(*th) >> 2) && tcptw->tw_ts_recent_stamp) { - tcp_parse_options(skb, &tmp_opt, &hash_location, 0, NULL); + tcp_parse_options(skb, &tmp_opt, 0, NULL); if (tmp_opt.saw_tstamp) { tmp_opt.rcv_tsecr -= tcptw->tw_ts_offset; @@ -388,32 +387,6 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, struct tcp_request_sock *treq = tcp_rsk(req); struct inet_connection_sock *newicsk = inet_csk(newsk); struct tcp_sock *newtp = tcp_sk(newsk); - struct tcp_sock *oldtp = tcp_sk(sk); - struct tcp_cookie_values *oldcvp = oldtp->cookie_values; - - /* TCP Cookie Transactions require space for the cookie pair, - * as it differs for each connection. There is no need to - * copy any s_data_payload stored at the original socket. - * Failure will prevent resuming the connection. - * - * Presumed copied, in order of appearance: - * cookie_in_always, cookie_out_never - */ - if (oldcvp != NULL) { - struct tcp_cookie_values *newcvp = - kzalloc(sizeof(*newtp->cookie_values), - GFP_ATOMIC); - - if (newcvp != NULL) { - kref_init(&newcvp->kref); - newcvp->cookie_desired = - oldcvp->cookie_desired; - newtp->cookie_values = newcvp; - } else { - /* Not Yet Implemented */ - newtp->cookie_values = NULL; - } - } /* Now setup tcp_sock */ newtp->pred_flags = 0; @@ -422,8 +395,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, newtp->rcv_nxt = treq->rcv_isn + 1; newtp->snd_sml = newtp->snd_una = - newtp->snd_nxt = newtp->snd_up = - treq->snt_isn + 1 + tcp_s_data_size(oldtp); + newtp->snd_nxt = newtp->snd_up = treq->snt_isn + 1; tcp_prequeue_init(newtp); INIT_LIST_HEAD(&newtp->tsq_node); @@ -460,8 +432,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, tcp_set_ca_state(newsk, TCP_CA_Open); tcp_init_xmit_timers(newsk); skb_queue_head_init(&newtp->out_of_order_queue); - newtp->write_seq = newtp->pushed_seq = - treq->snt_isn + 1 + tcp_s_data_size(oldtp); + newtp->write_seq = newtp->pushed_seq = treq->snt_isn + 1; newtp->rx_opt.saw_tstamp = 0; @@ -538,7 +509,6 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, bool fastopen) { struct tcp_options_received tmp_opt; - const u8 *hash_location; struct sock *child; const struct tcphdr *th = tcp_hdr(skb); __be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK); @@ -548,7 +518,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, tmp_opt.saw_tstamp = 0; if (th->doff > (sizeof(struct tcphdr)>>2)) { - tcp_parse_options(skb, &tmp_opt, &hash_location, 0, NULL); + tcp_parse_options(skb, &tmp_opt, 0, NULL); if (tmp_opt.saw_tstamp) { tmp_opt.ts_recent = req->ts_recent; @@ -648,7 +618,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, */ if ((flg & TCP_FLAG_ACK) && !fastopen && (TCP_SKB_CB(skb)->ack_seq != - tcp_rsk(req)->snt_isn + 1 + tcp_s_data_size(tcp_sk(sk)))) + tcp_rsk(req)->snt_isn + 1)) return sk; /* Also, it would be not so bad idea to check rcv_tsecr, which diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 8e7742f0b5d2..ac5871ebe086 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -65,9 +65,6 @@ int sysctl_tcp_base_mss __read_mostly = TCP_BASE_MSS; /* By default, RFC2861 behavior. */ int sysctl_tcp_slow_start_after_idle __read_mostly = 1; -int sysctl_tcp_cookie_size __read_mostly = 0; /* TCP_COOKIE_MAX */ -EXPORT_SYMBOL_GPL(sysctl_tcp_cookie_size); - static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, int push_one, gfp_t gfp); @@ -386,7 +383,6 @@ static inline bool tcp_urg_mode(const struct tcp_sock *tp) #define OPTION_TS (1 << 1) #define OPTION_MD5 (1 << 2) #define OPTION_WSCALE (1 << 3) -#define OPTION_COOKIE_EXTENSION (1 << 4) #define OPTION_FAST_OPEN_COOKIE (1 << 8) struct tcp_out_options { @@ -400,36 +396,6 @@ struct tcp_out_options { struct tcp_fastopen_cookie *fastopen_cookie; /* Fast open cookie */ }; -/* The sysctl int routines are generic, so check consistency here. - */ -static u8 tcp_cookie_size_check(u8 desired) -{ - int cookie_size; - - if (desired > 0) - /* previously specified */ - return desired; - - cookie_size = ACCESS_ONCE(sysctl_tcp_cookie_size); - if (cookie_size <= 0) - /* no default specified */ - return 0; - - if (cookie_size <= TCP_COOKIE_MIN) - /* value too small, specify minimum */ - return TCP_COOKIE_MIN; - - if (cookie_size >= TCP_COOKIE_MAX) - /* value too large, specify maximum */ - return TCP_COOKIE_MAX; - - if (cookie_size & 1) - /* 8-bit multiple, illegal, fix it */ - cookie_size++; - - return (u8)cookie_size; -} - /* Write previously computed TCP options to the packet. * * Beware: Something in the Internet is very sensitive to the ordering of @@ -448,27 +414,9 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp, { u16 options = opts->options; /* mungable copy */ - /* Having both authentication and cookies for security is redundant, - * and there's certainly not enough room. Instead, the cookie-less - * extension variant is proposed. - * - * Consider the pessimal case with authentication. The options - * could look like: - * COOKIE|MD5(20) + MSS(4) + SACK|TS(12) + WSCALE(4) == 40 - */ if (unlikely(OPTION_MD5 & options)) { - if (unlikely(OPTION_COOKIE_EXTENSION & options)) { - *ptr++ = htonl((TCPOPT_COOKIE << 24) | - (TCPOLEN_COOKIE_BASE << 16) | - (TCPOPT_MD5SIG << 8) | - TCPOLEN_MD5SIG); - } else { - *ptr++ = htonl((TCPOPT_NOP << 24) | - (TCPOPT_NOP << 16) | - (TCPOPT_MD5SIG << 8) | - TCPOLEN_MD5SIG); - } - options &= ~OPTION_COOKIE_EXTENSION; + *ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | + (TCPOPT_MD5SIG << 8) | TCPOLEN_MD5SIG); /* overload cookie hash location */ opts->hash_location = (__u8 *)ptr; ptr += 4; @@ -497,44 +445,6 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp, *ptr++ = htonl(opts->tsecr); } - /* Specification requires after timestamp, so do it now. - * - * Consider the pessimal case without authentication. The options - * could look like: - * MSS(4) + SACK|TS(12) + COOKIE(20) + WSCALE(4) == 40 - */ - if (unlikely(OPTION_COOKIE_EXTENSION & options)) { - __u8 *cookie_copy = opts->hash_location; - u8 cookie_size = opts->hash_size; - - /* 8-bit multiple handled in tcp_cookie_size_check() above, - * and elsewhere. - */ - if (0x2 & cookie_size) { - __u8 *p = (__u8 *)ptr; - - /* 16-bit multiple */ - *p++ = TCPOPT_COOKIE; - *p++ = TCPOLEN_COOKIE_BASE + cookie_size; - *p++ = *cookie_copy++; - *p++ = *cookie_copy++; - ptr++; - cookie_size -= 2; - } else { - /* 32-bit multiple */ - *ptr++ = htonl(((TCPOPT_NOP << 24) | - (TCPOPT_NOP << 16) | - (TCPOPT_COOKIE << 8) | - TCPOLEN_COOKIE_BASE) + - cookie_size); - } - - if (cookie_size > 0) { - memcpy(ptr, cookie_copy, cookie_size); - ptr += (cookie_size / 4); - } - } - if (unlikely(OPTION_SACK_ADVERTISE & options)) { *ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) | @@ -593,11 +503,7 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb, struct tcp_md5sig_key **md5) { struct tcp_sock *tp = tcp_sk(sk); - struct tcp_cookie_values *cvp = tp->cookie_values; unsigned int remaining = MAX_TCP_OPTION_SPACE; - u8 cookie_size = (!tp->rx_opt.cookie_out_never && cvp != NULL) ? - tcp_cookie_size_check(cvp->cookie_desired) : - 0; struct tcp_fastopen_request *fastopen = tp->fastopen_req; #ifdef CONFIG_TCP_MD5SIG @@ -649,52 +555,7 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb, tp->syn_fastopen = 1; } } - /* Note that timestamps are required by the specification. - * - * Odd numbers of bytes are prohibited by the specification, ensuring - * that the cookie is 16-bit aligned, and the resulting cookie pair is - * 32-bit aligned. - */ - if (*md5 == NULL && - (OPTION_TS & opts->options) && - cookie_size > 0) { - int need = TCPOLEN_COOKIE_BASE + cookie_size; - - if (0x2 & need) { - /* 32-bit multiple */ - need += 2; /* NOPs */ - - if (need > remaining) { - /* try shrinking cookie to fit */ - cookie_size -= 2; - need -= 4; - } - } - while (need > remaining && TCP_COOKIE_MIN <= cookie_size) { - cookie_size -= 4; - need -= 4; - } - if (TCP_COOKIE_MIN <= cookie_size) { - opts->options |= OPTION_COOKIE_EXTENSION; - opts->hash_location = (__u8 *)&cvp->cookie_pair[0]; - opts->hash_size = cookie_size; - - /* Remember for future incarnations. */ - cvp->cookie_desired = cookie_size; - - if (cvp->cookie_desired != cvp->cookie_pair_size) { - /* Currently use random bytes as a nonce, - * assuming these are completely unpredictable - * by hostile users of the same system. - */ - get_random_bytes(&cvp->cookie_pair[0], - cookie_size); - cvp->cookie_pair_size = cookie_size; - } - remaining -= need; - } - } return MAX_TCP_OPTION_SPACE - remaining; } @@ -704,14 +565,10 @@ static unsigned int tcp_synack_options(struct sock *sk, unsigned int mss, struct sk_buff *skb, struct tcp_out_options *opts, struct tcp_md5sig_key **md5, - struct tcp_extend_values *xvp, struct tcp_fastopen_cookie *foc) { struct inet_request_sock *ireq = inet_rsk(req); unsigned int remaining = MAX_TCP_OPTION_SPACE; - u8 cookie_plus = (xvp != NULL && !xvp->cookie_out_never) ? - xvp->cookie_plus : - 0; #ifdef CONFIG_TCP_MD5SIG *md5 = tcp_rsk(req)->af_specific->md5_lookup(sk, req); @@ -759,28 +616,7 @@ static unsigned int tcp_synack_options(struct sock *sk, remaining -= need; } } - /* Similar rationale to tcp_syn_options() applies here, too. - * If the options fit, the same options should fit now! - */ - if (*md5 == NULL && - ireq->tstamp_ok && - cookie_plus > TCPOLEN_COOKIE_BASE) { - int need = cookie_plus; /* has TCPOLEN_COOKIE_BASE */ - - if (0x2 & need) { - /* 32-bit multiple */ - need += 2; /* NOPs */ - } - if (need <= remaining) { - opts->options |= OPTION_COOKIE_EXTENSION; - opts->hash_size = cookie_plus - TCPOLEN_COOKIE_BASE; - remaining -= need; - } else { - /* There's no error return, so flag it. */ - xvp->cookie_out_never = 1; /* true */ - opts->hash_size = 0; - } - } + return MAX_TCP_OPTION_SPACE - remaining; } @@ -2802,32 +2638,24 @@ int tcp_send_synack(struct sock *sk) * sk: listener socket * dst: dst entry attached to the SYNACK * req: request_sock pointer - * rvp: request_values pointer * * Allocate one skb and build a SYNACK packet. * @dst is consumed : Caller should not use it again. */ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, struct request_sock *req, - struct request_values *rvp, struct tcp_fastopen_cookie *foc) { struct tcp_out_options opts; - struct tcp_extend_values *xvp = tcp_xv(rvp); struct inet_request_sock *ireq = inet_rsk(req); struct tcp_sock *tp = tcp_sk(sk); - const struct tcp_cookie_values *cvp = tp->cookie_values; struct tcphdr *th; struct sk_buff *skb; struct tcp_md5sig_key *md5; int tcp_header_size; int mss; - int s_data_desired = 0; - if (cvp != NULL && cvp->s_data_constant && cvp->s_data_desired) - s_data_desired = cvp->s_data_desired; - skb = alloc_skb(MAX_TCP_HEADER + 15 + s_data_desired, - sk_gfp_atomic(sk, GFP_ATOMIC)); + skb = alloc_skb(MAX_TCP_HEADER + 15, sk_gfp_atomic(sk, GFP_ATOMIC)); if (unlikely(!skb)) { dst_release(dst); return NULL; @@ -2869,9 +2697,8 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, else #endif TCP_SKB_CB(skb)->when = tcp_time_stamp; - tcp_header_size = tcp_synack_options(sk, req, mss, - skb, &opts, &md5, xvp, foc) - + sizeof(*th); + tcp_header_size = tcp_synack_options(sk, req, mss, skb, &opts, &md5, + foc) + sizeof(*th); skb_push(skb, tcp_header_size); skb_reset_transport_header(skb); @@ -2889,40 +2716,6 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, tcp_init_nondata_skb(skb, tcp_rsk(req)->snt_isn, TCPHDR_SYN | TCPHDR_ACK); - if (OPTION_COOKIE_EXTENSION & opts.options) { - if (s_data_desired) { - u8 *buf = skb_put(skb, s_data_desired); - - /* copy data directly from the listening socket. */ - memcpy(buf, cvp->s_data_payload, s_data_desired); - TCP_SKB_CB(skb)->end_seq += s_data_desired; - } - - if (opts.hash_size > 0) { - __u32 workspace[SHA_WORKSPACE_WORDS]; - u32 *mess = &xvp->cookie_bakery[COOKIE_DIGEST_WORDS]; - u32 *tail = &mess[COOKIE_MESSAGE_WORDS-1]; - - /* Secret recipe depends on the Timestamp, (future) - * Sequence and Acknowledgment Numbers, Initiator - * Cookie, and others handled by IP variant caller. - */ - *tail-- ^= opts.tsval; - *tail-- ^= tcp_rsk(req)->rcv_isn + 1; - *tail-- ^= TCP_SKB_CB(skb)->seq + 1; - - /* recommended */ - *tail-- ^= (((__force u32)th->dest << 16) | (__force u32)th->source); - *tail-- ^= (u32)(unsigned long)cvp; /* per sockopt */ - - sha_transform((__u32 *)&xvp->cookie_bakery[0], - (char *)mess, - &workspace[0]); - opts.hash_location = - (__u8 *)&xvp->cookie_bakery[0]; - } - } - th->seq = htonl(TCP_SKB_CB(skb)->seq); /* XXX data is queued and acked as is. No buffer/window check */ th->ack_seq = htonl(tcp_rsk(req)->rcv_nxt); diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 8a0848b60b35..d5dda20bd717 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -149,7 +149,6 @@ static inline int cookie_check(const struct sk_buff *skb, __u32 cookie) struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) { struct tcp_options_received tcp_opt; - const u8 *hash_location; struct inet_request_sock *ireq; struct inet6_request_sock *ireq6; struct tcp_request_sock *treq; @@ -177,7 +176,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) /* check for timestamp cookie support */ memset(&tcp_opt, 0, sizeof(tcp_opt)); - tcp_parse_options(skb, &tcp_opt, &hash_location, 0, NULL); + tcp_parse_options(skb, &tcp_opt, 0, NULL); if (!cookie_check_timestamp(&tcp_opt, sock_net(sk), &ecn_ok)) goto out; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 9b6460055df5..0a97add2ab74 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -454,7 +454,6 @@ out: static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, struct flowi6 *fl6, struct request_sock *req, - struct request_values *rvp, u16 queue_mapping) { struct inet6_request_sock *treq = inet6_rsk(req); @@ -466,7 +465,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, if (!dst && (dst = inet6_csk_route_req(sk, fl6, req)) == NULL) goto done; - skb = tcp_make_synack(sk, dst, req, rvp, NULL); + skb = tcp_make_synack(sk, dst, req, NULL); if (skb) { __tcp_v6_send_check(skb, &treq->loc_addr, &treq->rmt_addr); @@ -481,13 +480,12 @@ done: return err; } -static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req, - struct request_values *rvp) +static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req) { struct flowi6 fl6; int res; - res = tcp_v6_send_synack(sk, NULL, &fl6, req, rvp, 0); + res = tcp_v6_send_synack(sk, NULL, &fl6, req, 0); if (!res) TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS); return res; @@ -940,9 +938,7 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb) */ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) { - struct tcp_extend_values tmp_ext; struct tcp_options_received tmp_opt; - const u8 *hash_location; struct request_sock *req; struct inet6_request_sock *treq; struct ipv6_pinfo *np = inet6_sk(sk); @@ -980,50 +976,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) tcp_clear_options(&tmp_opt); tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); tmp_opt.user_mss = tp->rx_opt.user_mss; - tcp_parse_options(skb, &tmp_opt, &hash_location, 0, NULL); - - if (tmp_opt.cookie_plus > 0 && - tmp_opt.saw_tstamp && - !tp->rx_opt.cookie_out_never && - (sysctl_tcp_cookie_size > 0 || - (tp->cookie_values != NULL && - tp->cookie_values->cookie_desired > 0))) { - u8 *c; - u32 *d; - u32 *mess = &tmp_ext.cookie_bakery[COOKIE_DIGEST_WORDS]; - int l = tmp_opt.cookie_plus - TCPOLEN_COOKIE_BASE; - - if (tcp_cookie_generator(&tmp_ext.cookie_bakery[0]) != 0) - goto drop_and_free; - - /* Secret recipe starts with IP addresses */ - d = (__force u32 *)&ipv6_hdr(skb)->daddr.s6_addr32[0]; - *mess++ ^= *d++; - *mess++ ^= *d++; - *mess++ ^= *d++; - *mess++ ^= *d++; - d = (__force u32 *)&ipv6_hdr(skb)->saddr.s6_addr32[0]; - *mess++ ^= *d++; - *mess++ ^= *d++; - *mess++ ^= *d++; - *mess++ ^= *d++; - - /* plus variable length Initiator Cookie */ - c = (u8 *)mess; - while (l-- > 0) - *c++ ^= *hash_location++; - - want_cookie = false; /* not our kind of cookie */ - tmp_ext.cookie_out_never = 0; /* false */ - tmp_ext.cookie_plus = tmp_opt.cookie_plus; - } else if (!tp->rx_opt.cookie_in_always) { - /* redundant indications, but ensure initialization. */ - tmp_ext.cookie_out_never = 1; /* true */ - tmp_ext.cookie_plus = 0; - } else { - goto drop_and_free; - } - tmp_ext.cookie_in_always = tp->rx_opt.cookie_in_always; + tcp_parse_options(skb, &tmp_opt, 0, NULL); if (want_cookie && !tmp_opt.saw_tstamp) tcp_clear_options(&tmp_opt); @@ -1101,7 +1054,6 @@ have_isn: goto drop_and_release; if (tcp_v6_send_synack(sk, dst, &fl6, req, - (struct request_values *)&tmp_ext, skb_get_queue_mapping(skb)) || want_cookie) goto drop_and_free; -- cgit v1.2.3 From bb916ebbeabd18f7dc3c661275d2c9d343f4fa85 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Wed, 13 Mar 2013 20:40:00 +0000 Subject: iio: adc: Add dt support for turning on the phy in exynos-adc Without this change the exynos adc controller needed to have its phy enabled in some out-of-driver C code. Add support for specifying the phy enable register by listing it in the reg list. Signed-off-by: Doug Anderson Tested-by: Naveen Krishna Chatradhi Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/arm/samsung/exynos-adc.txt | 4 ++-- drivers/iio/adc/exynos_adc.c | 14 +++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt b/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt index f68637861b05..05e9d95ede5c 100644 --- a/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt +++ b/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt @@ -15,7 +15,7 @@ Required properties: Must be "samsung,exynos-adc-v2" for future controllers. - reg: Contains ADC register address range (base address and - length). + length) and the address of the phy enable register. - interrupts: Contains the interrupt information for the timer. The format is being dependent on which interrupt controller the Samsung device uses. @@ -27,7 +27,7 @@ Example: adding device info in dtsi file adc: adc@12D10000 { compatible = "samsung,exynos-adc-v1"; - reg = <0x12D10000 0x100>; + reg = <0x12D10000 0x100>, <0x10040718 0x4>; interrupts = <0 106 0>; #io-channel-cells = <1>; io-channel-ranges; diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index 6e968ae48c8a..4a8a9a34228f 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c @@ -85,6 +85,7 @@ enum adc_version { struct exynos_adc { void __iomem *regs; + void __iomem *enable_reg; struct clk *clk; unsigned int irq; struct regulator *vdd; @@ -269,13 +270,19 @@ static int exynos_adc_probe(struct platform_device *pdev) info = iio_priv(indio_dev); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - info->regs = devm_request_and_ioremap(&pdev->dev, mem); if (!info->regs) { ret = -ENOMEM; goto err_iio; } + mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + info->enable_reg = devm_request_and_ioremap(&pdev->dev, mem); + if (!info->enable_reg) { + ret = -ENOMEM; + goto err_iio; + } + irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "no irq resource?\n"); @@ -295,6 +302,8 @@ static int exynos_adc_probe(struct platform_device *pdev) goto err_iio; } + writel(1, info->enable_reg); + info->clk = devm_clk_get(&pdev->dev, "adc"); if (IS_ERR(info->clk)) { dev_err(&pdev->dev, "failed getting clock, err = %ld\n", @@ -370,6 +379,7 @@ static int exynos_adc_remove(struct platform_device *pdev) exynos_adc_remove_devices); regulator_disable(info->vdd); clk_disable_unprepare(info->clk); + writel(0, info->enable_reg); iio_device_unregister(indio_dev); free_irq(info->irq, info); iio_device_free(indio_dev); @@ -395,6 +405,7 @@ static int exynos_adc_suspend(struct device *dev) } clk_disable_unprepare(info->clk); + writel(0, info->enable_reg); regulator_disable(info->vdd); return 0; @@ -410,6 +421,7 @@ static int exynos_adc_resume(struct device *dev) if (ret) return ret; + writel(1, info->enable_reg); clk_prepare_enable(info->clk); exynos_adc_hw_init(info); -- cgit v1.2.3 From 0eba387973f521e57f00584e5e840e5328a61dda Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 12 Mar 2013 13:24:25 +0200 Subject: usb: phy: nop: Add device tree support and binding information The PHY clock, clock rate, VCC regulator and RESET regulator can now be provided via device tree. Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/usb-nop-xceiv.txt | 34 +++++++++++++++++++++ drivers/usb/otg/nop-usb-xceiv.c | 35 +++++++++++++++++----- 2 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt b/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt new file mode 100644 index 000000000000..d7e272671c7e --- /dev/null +++ b/Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt @@ -0,0 +1,34 @@ +USB NOP PHY + +Required properties: +- compatible: should be usb-nop-xceiv + +Optional properties: +- clocks: phandle to the PHY clock. Use as per Documentation/devicetree + /bindings/clock/clock-bindings.txt + This property is required if clock-frequency is specified. + +- clock-names: Should be "main_clk" + +- clock-frequency: the clock frequency (in Hz) that the PHY clock must + be configured to. + +- vcc-supply: phandle to the regulator that provides RESET to the PHY. + +- reset-supply: phandle to the regulator that provides power to the PHY. + +Example: + + hsusb1_phy { + compatible = "usb-nop-xceiv"; + clock-frequency = <19200000>; + clocks = <&osc 0>; + clock-names = "main_clk"; + vcc-supply = <&hsusb1_vcc_regulator>; + reset-supply = <&hsusb1_reset_regulator>; + }; + +hsusb1_phy is a NOP USB PHY device that gets its clock from an oscillator +and expects that clock to be configured to 19.2MHz by the NOP PHY driver. +hsusb1_vcc_regulator provides power to the PHY and hsusb1_reset_regulator +controls RESET. diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index fe7a46001854..b26b1c29194e 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -34,13 +34,14 @@ #include #include #include +#include struct nop_usb_xceiv { - struct usb_phy phy; - struct device *dev; - struct clk *clk; - struct regulator *vcc; - struct regulator *reset; + struct usb_phy phy; + struct device *dev; + struct clk *clk; + struct regulator *vcc; + struct regulator *reset; }; static struct platform_device *pd; @@ -140,10 +141,12 @@ static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) static int nop_usb_xceiv_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct nop_usb_xceiv_platform_data *pdata = pdev->dev.platform_data; struct nop_usb_xceiv *nop; enum usb_phy_type type = USB_PHY_TYPE_USB2; int err; + u32 clk_rate = 0; nop = devm_kzalloc(&pdev->dev, sizeof(*nop), GFP_KERNEL); if (!nop) @@ -154,8 +157,16 @@ static int nop_usb_xceiv_probe(struct platform_device *pdev) if (!nop->phy.otg) return -ENOMEM; - if (pdata) + if (dev->of_node) { + struct device_node *node = dev->of_node; + + if (of_property_read_u32(node, "clock-frequency", &clk_rate)) + clk_rate = 0; + + } else if (pdata) { type = pdata->type; + clk_rate = pdata->clk_rate; + } nop->clk = devm_clk_get(&pdev->dev, "main_clk"); if (IS_ERR(nop->clk)) { @@ -163,8 +174,8 @@ static int nop_usb_xceiv_probe(struct platform_device *pdev) PTR_ERR(nop->clk)); } - if (!IS_ERR(nop->clk) && pdata && pdata->clk_rate) { - err = clk_set_rate(nop->clk, pdata->clk_rate); + if (!IS_ERR(nop->clk) && clk_rate) { + err = clk_set_rate(nop->clk, clk_rate); if (err) { dev_err(&pdev->dev, "Error setting clock rate\n"); return err; @@ -237,12 +248,20 @@ static int nop_usb_xceiv_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id nop_xceiv_dt_ids[] = { + { .compatible = "usb-nop-xceiv" }, + { } +}; + +MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids); + static struct platform_driver nop_usb_xceiv_driver = { .probe = nop_usb_xceiv_probe, .remove = nop_usb_xceiv_remove, .driver = { .name = "nop_usb_xceiv", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(nop_xceiv_dt_ids), }, }; -- cgit v1.2.3 From e36a0c870f7dbbfa7ed13cd83b79be00bcd00380 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 26 Feb 2013 20:03:27 +0530 Subject: usb: dwc3: omap: minor fixes to get dt working Includes few minor fixes in dwc3-omap like populating the compatible string in a correct way, extracting the utmi-mode property properly and changing the index of get_irq since irq of core is removed from hwmod entry. Also updated the documentation with dwc3-omap device tree binding information. Signed-off-by: Kishon Vijay Abraham I [ balbi@ti.com : fix a compile warning introduced by this commit ] Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/omap-usb.txt | 28 ++++++++++++++ drivers/usb/dwc3/dwc3-omap.c | 45 ++++++++++------------ 2 files changed, 49 insertions(+), 24 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/omap-usb.txt b/Documentation/devicetree/bindings/usb/omap-usb.txt index 1ef0ce71f8fa..1b9f55fd96c0 100644 --- a/Documentation/devicetree/bindings/usb/omap-usb.txt +++ b/Documentation/devicetree/bindings/usb/omap-usb.txt @@ -41,6 +41,34 @@ Board specific device node entry power = <50>; }; +OMAP DWC3 GLUE + - compatible : Should be "ti,dwc3" + - ti,hwmods : Should be "usb_otg_ss" + - reg : Address and length of the register set for the device. + - interrupts : The irq number of this device that is used to interrupt the + MPU + - #address-cells, #size-cells : Must be present if the device has sub-nodes + - utmi-mode : controls the source of UTMI/PIPE status for VBUS and OTG ID. + It should be set to "1" for HW mode and "2" for SW mode. + - ranges: the child address space are mapped 1:1 onto the parent address space + +Sub-nodes: +The dwc3 core should be added as subnode to omap dwc3 glue. +- dwc3 : + The binding details of dwc3 can be found in: + Documentation/devicetree/bindings/usb/dwc3.txt + +omap_dwc3 { + compatible = "ti,dwc3"; + ti,hwmods = "usb_otg_ss"; + reg = <0x4a020000 0x1ff>; + interrupts = <0 93 4>; + #address-cells = <1>; + #size-cells = <1>; + utmi-mode = <2>; + ranges; +}; + OMAP CONTROL USB Required properties: diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index afa05e3c9cf4..e1206b419932 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -316,11 +316,11 @@ static int dwc3_omap_probe(struct platform_device *pdev) struct resource *res; struct device *dev = &pdev->dev; - int size; int ret = -ENOMEM; int irq; - const u32 *utmi_mode; + int utmi_mode = 0; + u32 reg; void __iomem *base; @@ -334,13 +334,13 @@ static int dwc3_omap_probe(struct platform_device *pdev) platform_set_drvdata(pdev, omap); - irq = platform_get_irq(pdev, 1); + irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "missing IRQ resource\n"); return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "missing memory base resource\n"); return -EINVAL; @@ -387,25 +387,22 @@ static int dwc3_omap_probe(struct platform_device *pdev) reg = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS); - utmi_mode = of_get_property(node, "utmi-mode", &size); - if (utmi_mode && size == sizeof(*utmi_mode)) { - reg |= *utmi_mode; - } else { - if (!pdata) { - dev_dbg(dev, "missing platform data\n"); - } else { - switch (pdata->utmi_mode) { - case DWC3_OMAP_UTMI_MODE_SW: - reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE; - break; - case DWC3_OMAP_UTMI_MODE_HW: - reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE; - break; - default: - dev_dbg(dev, "UNKNOWN utmi mode %d\n", - pdata->utmi_mode); - } - } + if (node) + of_property_read_u32(node, "utmi-mode", &utmi_mode); + else if (pdata) + utmi_mode = pdata->utmi_mode; + else + dev_dbg(dev, "missing platform data\n"); + + switch (utmi_mode) { + case DWC3_OMAP_UTMI_MODE_SW: + reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE; + break; + case DWC3_OMAP_UTMI_MODE_HW: + reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE; + break; + default: + dev_dbg(dev, "UNKNOWN utmi mode %d\n", utmi_mode); } dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg); @@ -465,7 +462,7 @@ static int dwc3_omap_remove(struct platform_device *pdev) static const struct of_device_id of_dwc3_match[] = { { - "ti,dwc3", + .compatible = "ti,dwc3" }, { }, }; -- cgit v1.2.3 From dc2377d0b0a298ec9d7d232c0d757f462dedcca2 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Thu, 14 Mar 2013 15:59:10 +0530 Subject: usb: phy: samsung: Common out the generic stuff Moving register and structure definitions to header file, and keeping the generic functions to be used across multiple PHYs in common phy helper driver under SAMSUNG_USBPHY, and moving USB 2.0 PHY driver under SAMSUNG_USB2PHY. Also allowing samsung PHY drivers be built as modules. Signed-off-by: Vivek Gautam Acked-by: Kukjin Kim Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/samsung-usbphy.txt | 22 +- drivers/usb/phy/Kconfig | 15 +- drivers/usb/phy/Makefile | 1 + drivers/usb/phy/phy-samsung-usb.c | 726 +-------------------- drivers/usb/phy/phy-samsung-usb.h | 247 +++++++ drivers/usb/phy/phy-samsung-usb2.c | 509 +++++++++++++++ 6 files changed, 800 insertions(+), 720 deletions(-) create mode 100644 drivers/usb/phy/phy-samsung-usb.h create mode 100644 drivers/usb/phy/phy-samsung-usb2.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt index 033194934f64..96940abe9a57 100644 --- a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt +++ b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt @@ -1,20 +1,25 @@ -* Samsung's usb phy transceiver +SAMSUNG USB-PHY controllers -The Samsung's phy transceiver is used for controlling usb phy for -s3c-hsotg as well as ehci-s5p and ohci-exynos usb controllers -across Samsung SOCs. +** Samsung's usb 2.0 phy transceiver + +The Samsung's usb 2.0 phy transceiver is used for controlling +usb 2.0 phy for s3c-hsotg as well as ehci-s5p and ohci-exynos +usb controllers across Samsung SOCs. TODO: Adding the PHY binding with controller(s) according to the under developement generic PHY driver. Required properties: Exynos4210: -- compatible : should be "samsung,exynos4210-usbphy" +- compatible : should be "samsung,exynos4210-usb2phy" - reg : base physical address of the phy registers and length of memory mapped region. +- clocks: Clock IDs array as required by the controller. +- clock-names: names of clock correseponding IDs clock property as requested + by the controller driver. Exynos5250: -- compatible : should be "samsung,exynos5250-usbphy" +- compatible : should be "samsung,exynos5250-usb2phy" - reg : base physical address of the phy registers and length of memory mapped region. @@ -44,10 +49,13 @@ Example: usbphy@125B0000 { #address-cells = <1>; #size-cells = <1>; - compatible = "samsung,exynos4210-usbphy"; + compatible = "samsung,exynos4210-usb2phy"; reg = <0x125B0000 0x100>; ranges; + clocks = <&clock 2>, <&clock 305>; + clock-names = "xusbxti", "otg"; + usbphy-sys { /* USB device and host PHY_CONTROL registers */ reg = <0x10020704 0x8>; diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 97de6de9b4b9..e8cd52ac5c05 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -86,11 +86,18 @@ config OMAP_USB3 on/off the PHY. config SAMSUNG_USBPHY - bool "Samsung USB PHY controller Driver" - depends on USB_S3C_HSOTG || USB_EHCI_S5P || USB_OHCI_EXYNOS + tristate "Samsung USB PHY Driver" help - Enable this to support Samsung USB phy controller for samsung - SoCs. + Enable this to support Samsung USB phy helper driver for Samsung SoCs. + This driver provides common interface to interact, for Samsung USB 2.0 PHY + driver and later for Samsung USB 3.0 PHY driver. + +config SAMSUNG_USB2PHY + tristate "Samsung USB 2.0 PHY controller Driver" + select SAMSUNG_USBPHY + help + Enable this to support Samsung USB 2.0 (High Speed) PHY controller + driver for Samsung SoCs. config TWL4030_USB tristate "TWL4030 USB Transceiver Driver" diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 5fb4a5d55945..8cd355f051f6 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o +obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o obj-$(CONFIG_TWL6030_USB) += phy-twl6030-usb.o obj-$(CONFIG_USB_EHCI_TEGRA) += phy-tegra-usb.o diff --git a/drivers/usb/phy/phy-samsung-usb.c b/drivers/usb/phy/phy-samsung-usb.c index 967101ec15fd..7b118ee5f5e4 100644 --- a/drivers/usb/phy/phy-samsung-usb.c +++ b/drivers/usb/phy/phy-samsung-usb.c @@ -1,12 +1,13 @@ -/* linux/drivers/usb/phy/samsung-usbphy.c +/* linux/drivers/usb/phy/phy-samsung-usb.c * * Copyright (c) 2012 Samsung Electronics Co., Ltd. * http://www.samsung.com * * Author: Praveen Paneri * - * Samsung USB2.0 PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and - * OHCI-EXYNOS controllers. + * Samsung USB-PHY helper driver with common function calls; + * interacts with Samsung USB 2.0 PHY controller driver and later + * with Samsung USB 3.0 PHY driver. * * 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 @@ -21,233 +22,16 @@ #include #include #include -#include #include #include #include #include #include -#include #include -#include -/* Register definitions */ +#include "phy-samsung-usb.h" -#define SAMSUNG_PHYPWR (0x00) - -#define PHYPWR_NORMAL_MASK (0x19 << 0) -#define PHYPWR_OTG_DISABLE (0x1 << 4) -#define PHYPWR_ANALOG_POWERDOWN (0x1 << 3) -#define PHYPWR_FORCE_SUSPEND (0x1 << 1) -/* For Exynos4 */ -#define PHYPWR_NORMAL_MASK_PHY0 (0x39 << 0) -#define PHYPWR_SLEEP_PHY0 (0x1 << 5) - -#define SAMSUNG_PHYCLK (0x04) - -#define PHYCLK_MODE_USB11 (0x1 << 6) -#define PHYCLK_EXT_OSC (0x1 << 5) -#define PHYCLK_COMMON_ON_N (0x1 << 4) -#define PHYCLK_ID_PULL (0x1 << 2) -#define PHYCLK_CLKSEL_MASK (0x3 << 0) -#define PHYCLK_CLKSEL_48M (0x0 << 0) -#define PHYCLK_CLKSEL_12M (0x2 << 0) -#define PHYCLK_CLKSEL_24M (0x3 << 0) - -#define SAMSUNG_RSTCON (0x08) - -#define RSTCON_PHYLINK_SWRST (0x1 << 2) -#define RSTCON_HLINK_SWRST (0x1 << 1) -#define RSTCON_SWRST (0x1 << 0) - -/* EXYNOS5 */ -#define EXYNOS5_PHY_HOST_CTRL0 (0x00) - -#define HOST_CTRL0_PHYSWRSTALL (0x1 << 31) - -#define HOST_CTRL0_REFCLKSEL_MASK (0x3 << 19) -#define HOST_CTRL0_REFCLKSEL_XTAL (0x0 << 19) -#define HOST_CTRL0_REFCLKSEL_EXTL (0x1 << 19) -#define HOST_CTRL0_REFCLKSEL_CLKCORE (0x2 << 19) - -#define HOST_CTRL0_FSEL_MASK (0x7 << 16) -#define HOST_CTRL0_FSEL(_x) ((_x) << 16) - -#define FSEL_CLKSEL_50M (0x7) -#define FSEL_CLKSEL_24M (0x5) -#define FSEL_CLKSEL_20M (0x4) -#define FSEL_CLKSEL_19200K (0x3) -#define FSEL_CLKSEL_12M (0x2) -#define FSEL_CLKSEL_10M (0x1) -#define FSEL_CLKSEL_9600K (0x0) - -#define HOST_CTRL0_TESTBURNIN (0x1 << 11) -#define HOST_CTRL0_RETENABLE (0x1 << 10) -#define HOST_CTRL0_COMMONON_N (0x1 << 9) -#define HOST_CTRL0_SIDDQ (0x1 << 6) -#define HOST_CTRL0_FORCESLEEP (0x1 << 5) -#define HOST_CTRL0_FORCESUSPEND (0x1 << 4) -#define HOST_CTRL0_WORDINTERFACE (0x1 << 3) -#define HOST_CTRL0_UTMISWRST (0x1 << 2) -#define HOST_CTRL0_LINKSWRST (0x1 << 1) -#define HOST_CTRL0_PHYSWRST (0x1 << 0) - -#define EXYNOS5_PHY_HOST_TUNE0 (0x04) - -#define EXYNOS5_PHY_HSIC_CTRL1 (0x10) - -#define EXYNOS5_PHY_HSIC_TUNE1 (0x14) - -#define EXYNOS5_PHY_HSIC_CTRL2 (0x20) - -#define EXYNOS5_PHY_HSIC_TUNE2 (0x24) - -#define HSIC_CTRL_REFCLKSEL_MASK (0x3 << 23) -#define HSIC_CTRL_REFCLKSEL (0x2 << 23) - -#define HSIC_CTRL_REFCLKDIV_MASK (0x7f << 16) -#define HSIC_CTRL_REFCLKDIV(_x) ((_x) << 16) -#define HSIC_CTRL_REFCLKDIV_12 (0x24 << 16) -#define HSIC_CTRL_REFCLKDIV_15 (0x1c << 16) -#define HSIC_CTRL_REFCLKDIV_16 (0x1a << 16) -#define HSIC_CTRL_REFCLKDIV_19_2 (0x15 << 16) -#define HSIC_CTRL_REFCLKDIV_20 (0x14 << 16) - -#define HSIC_CTRL_SIDDQ (0x1 << 6) -#define HSIC_CTRL_FORCESLEEP (0x1 << 5) -#define HSIC_CTRL_FORCESUSPEND (0x1 << 4) -#define HSIC_CTRL_WORDINTERFACE (0x1 << 3) -#define HSIC_CTRL_UTMISWRST (0x1 << 2) -#define HSIC_CTRL_PHYSWRST (0x1 << 0) - -#define EXYNOS5_PHY_HOST_EHCICTRL (0x30) - -#define HOST_EHCICTRL_ENAINCRXALIGN (0x1 << 29) -#define HOST_EHCICTRL_ENAINCR4 (0x1 << 28) -#define HOST_EHCICTRL_ENAINCR8 (0x1 << 27) -#define HOST_EHCICTRL_ENAINCR16 (0x1 << 26) - -#define EXYNOS5_PHY_HOST_OHCICTRL (0x34) - -#define HOST_OHCICTRL_SUSPLGCY (0x1 << 3) -#define HOST_OHCICTRL_APPSTARTCLK (0x1 << 2) -#define HOST_OHCICTRL_CNTSEL (0x1 << 1) -#define HOST_OHCICTRL_CLKCKTRST (0x1 << 0) - -#define EXYNOS5_PHY_OTG_SYS (0x38) - -#define OTG_SYS_PHYLINK_SWRESET (0x1 << 14) -#define OTG_SYS_LINKSWRST_UOTG (0x1 << 13) -#define OTG_SYS_PHY0_SWRST (0x1 << 12) - -#define OTG_SYS_REFCLKSEL_MASK (0x3 << 9) -#define OTG_SYS_REFCLKSEL_XTAL (0x0 << 9) -#define OTG_SYS_REFCLKSEL_EXTL (0x1 << 9) -#define OTG_SYS_REFCLKSEL_CLKCORE (0x2 << 9) - -#define OTG_SYS_IDPULLUP_UOTG (0x1 << 8) -#define OTG_SYS_COMMON_ON (0x1 << 7) - -#define OTG_SYS_FSEL_MASK (0x7 << 4) -#define OTG_SYS_FSEL(_x) ((_x) << 4) - -#define OTG_SYS_FORCESLEEP (0x1 << 3) -#define OTG_SYS_OTGDISABLE (0x1 << 2) -#define OTG_SYS_SIDDQ_UOTG (0x1 << 1) -#define OTG_SYS_FORCESUSPEND (0x1 << 0) - -#define EXYNOS5_PHY_OTG_TUNE (0x40) - -#ifndef MHZ -#define MHZ (1000*1000) -#endif - -#ifndef KHZ -#define KHZ (1000) -#endif - -#define EXYNOS_USBHOST_PHY_CTRL_OFFSET (0x4) -#define S3C64XX_USBPHY_ENABLE (0x1 << 16) -#define EXYNOS_USBPHY_ENABLE (0x1 << 0) -#define EXYNOS_USB20PHY_CFG_HOST_LINK (0x1 << 0) - -enum samsung_cpu_type { - TYPE_S3C64XX, - TYPE_EXYNOS4210, - TYPE_EXYNOS5250, -}; - -/* - * struct samsung_usbphy_drvdata - driver data for various SoC variants - * @cpu_type: machine identifier - * @devphy_en_mask: device phy enable mask for PHY CONTROL register - * @hostphy_en_mask: host phy enable mask for PHY CONTROL register - * @devphy_reg_offset: offset to DEVICE PHY CONTROL register from - * mapped address of system controller. - * @hostphy_reg_offset: offset to HOST PHY CONTROL register from - * mapped address of system controller. - * - * Here we have a separate mask for device type phy. - * Having different masks for host and device type phy helps - * in setting independent masks in case of SoCs like S5PV210, - * in which PHY0 and PHY1 enable bits belong to same register - * placed at position 0 and 1 respectively. - * Although for newer SoCs like exynos these bits belong to - * different registers altogether placed at position 0. - */ -struct samsung_usbphy_drvdata { - int cpu_type; - int devphy_en_mask; - int hostphy_en_mask; - u32 devphy_reg_offset; - u32 hostphy_reg_offset; -}; - -/* - * struct samsung_usbphy - transceiver driver state - * @phy: transceiver structure - * @plat: platform data - * @dev: The parent device supplied to the probe function - * @clk: usb phy clock - * @regs: usb phy controller registers memory base - * @pmuregs: USB device PHY_CONTROL register memory base - * @sysreg: USB2.0 PHY_CFG register memory base - * @ref_clk_freq: reference clock frequency selection - * @drv_data: driver data available for different SoCs - * @phy_type: Samsung SoCs specific phy types: #HOST - * #DEVICE - * @phy_usage: usage count for phy - * @lock: lock for phy operations - */ -struct samsung_usbphy { - struct usb_phy phy; - struct samsung_usbphy_data *plat; - struct device *dev; - struct clk *clk; - void __iomem *regs; - void __iomem *pmuregs; - void __iomem *sysreg; - int ref_clk_freq; - const struct samsung_usbphy_drvdata *drv_data; - enum samsung_usb_phy_type phy_type; - atomic_t phy_usage; - spinlock_t lock; -}; - -#define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) - -int samsung_usbphy_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - if (!otg) - return -ENODEV; - - if (!otg->host) - otg->host = host; - - return 0; -} - -static int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) +int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) { struct device_node *usbphy_sys; @@ -282,13 +66,14 @@ err0: of_node_put(usbphy_sys); return -ENXIO; } +EXPORT_SYMBOL_GPL(samsung_usbphy_parse_dt); /* * Set isolation here for phy. * Here 'on = true' would mean USB PHY block is isolated, hence * de-activated and vice-versa. */ -static void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) +void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) { void __iomem *reg = NULL; u32 reg_val; @@ -336,11 +121,12 @@ static void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) writel(reg_val, reg); } +EXPORT_SYMBOL_GPL(samsung_usbphy_set_isolation); /* * Configure the mode of working of usb-phy here: HOST/DEVICE. */ -static void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy) +void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy) { u32 reg; @@ -358,13 +144,14 @@ static void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy) writel(reg, sphy->sysreg); } +EXPORT_SYMBOL_GPL(samsung_usbphy_cfg_sel); /* * PHYs are different for USB Device and USB Host. * This make sure that correct PHY type is selected before * any operation on PHY. */ -static int samsung_usbphy_set_type(struct usb_phy *phy, +int samsung_usbphy_set_type(struct usb_phy *phy, enum samsung_usb_phy_type phy_type) { struct samsung_usbphy *sphy = phy_to_sphy(phy); @@ -373,11 +160,12 @@ static int samsung_usbphy_set_type(struct usb_phy *phy, return 0; } +EXPORT_SYMBOL_GPL(samsung_usbphy_set_type); /* * Returns reference clock frequency selection value */ -static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) +int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) { struct clk *ref_clk; int refclk_freq = 0; @@ -387,9 +175,9 @@ static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) * external crystal clock XXTI */ if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) - ref_clk = clk_get(sphy->dev, "ext_xtal"); + ref_clk = devm_clk_get(sphy->dev, "ext_xtal"); else - ref_clk = clk_get(sphy->dev, "xusbxti"); + ref_clk = devm_clk_get(sphy->dev, "xusbxti"); if (IS_ERR(ref_clk)) { dev_err(sphy->dev, "Failed to get reference clock\n"); return PTR_ERR(ref_clk); @@ -445,484 +233,4 @@ static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) return refclk_freq; } - -static bool exynos5_phyhost_is_on(void *regs) -{ - u32 reg; - - reg = readl(regs + EXYNOS5_PHY_HOST_CTRL0); - - return !(reg & HOST_CTRL0_SIDDQ); -} - -static void samsung_exynos5_usbphy_enable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phyclk = sphy->ref_clk_freq; - u32 phyhost; - u32 phyotg; - u32 phyhsic; - u32 ehcictrl; - u32 ohcictrl; - - /* - * phy_usage helps in keeping usage count for phy - * so that the first consumer enabling the phy is also - * the last consumer to disable it. - */ - - atomic_inc(&sphy->phy_usage); - - if (exynos5_phyhost_is_on(regs)) { - dev_info(sphy->dev, "Already power on PHY\n"); - return; - } - - /* Host configuration */ - phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); - - /* phy reference clock configuration */ - phyhost &= ~HOST_CTRL0_FSEL_MASK; - phyhost |= HOST_CTRL0_FSEL(phyclk); - - /* host phy reset */ - phyhost &= ~(HOST_CTRL0_PHYSWRST | - HOST_CTRL0_PHYSWRSTALL | - HOST_CTRL0_SIDDQ | - /* Enable normal mode of operation */ - HOST_CTRL0_FORCESUSPEND | - HOST_CTRL0_FORCESLEEP); - - /* Link reset */ - phyhost |= (HOST_CTRL0_LINKSWRST | - HOST_CTRL0_UTMISWRST | - /* COMMON Block configuration during suspend */ - HOST_CTRL0_COMMONON_N); - writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); - udelay(10); - phyhost &= ~(HOST_CTRL0_LINKSWRST | - HOST_CTRL0_UTMISWRST); - writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); - - /* OTG configuration */ - phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); - - /* phy reference clock configuration */ - phyotg &= ~OTG_SYS_FSEL_MASK; - phyotg |= OTG_SYS_FSEL(phyclk); - - /* Enable normal mode of operation */ - phyotg &= ~(OTG_SYS_FORCESUSPEND | - OTG_SYS_SIDDQ_UOTG | - OTG_SYS_FORCESLEEP | - OTG_SYS_REFCLKSEL_MASK | - /* COMMON Block configuration during suspend */ - OTG_SYS_COMMON_ON); - - /* OTG phy & link reset */ - phyotg |= (OTG_SYS_PHY0_SWRST | - OTG_SYS_LINKSWRST_UOTG | - OTG_SYS_PHYLINK_SWRESET | - OTG_SYS_OTGDISABLE | - /* Set phy refclk */ - OTG_SYS_REFCLKSEL_CLKCORE); - - writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); - udelay(10); - phyotg &= ~(OTG_SYS_PHY0_SWRST | - OTG_SYS_LINKSWRST_UOTG | - OTG_SYS_PHYLINK_SWRESET); - writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); - - /* HSIC phy configuration */ - phyhsic = (HSIC_CTRL_REFCLKDIV_12 | - HSIC_CTRL_REFCLKSEL | - HSIC_CTRL_PHYSWRST); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); - udelay(10); - phyhsic &= ~HSIC_CTRL_PHYSWRST; - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); - - udelay(80); - - /* enable EHCI DMA burst */ - ehcictrl = readl(regs + EXYNOS5_PHY_HOST_EHCICTRL); - ehcictrl |= (HOST_EHCICTRL_ENAINCRXALIGN | - HOST_EHCICTRL_ENAINCR4 | - HOST_EHCICTRL_ENAINCR8 | - HOST_EHCICTRL_ENAINCR16); - writel(ehcictrl, regs + EXYNOS5_PHY_HOST_EHCICTRL); - - /* set ohci_suspend_on_n */ - ohcictrl = readl(regs + EXYNOS5_PHY_HOST_OHCICTRL); - ohcictrl |= HOST_OHCICTRL_SUSPLGCY; - writel(ohcictrl, regs + EXYNOS5_PHY_HOST_OHCICTRL); -} - -static void samsung_usbphy_enable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phypwr; - u32 phyclk; - u32 rstcon; - - /* set clock frequency for PLL */ - phyclk = sphy->ref_clk_freq; - phypwr = readl(regs + SAMSUNG_PHYPWR); - rstcon = readl(regs + SAMSUNG_RSTCON); - - switch (sphy->drv_data->cpu_type) { - case TYPE_S3C64XX: - phyclk &= ~PHYCLK_COMMON_ON_N; - phypwr &= ~PHYPWR_NORMAL_MASK; - rstcon |= RSTCON_SWRST; - break; - case TYPE_EXYNOS4210: - phypwr &= ~PHYPWR_NORMAL_MASK_PHY0; - rstcon |= RSTCON_SWRST; - default: - break; - } - - writel(phyclk, regs + SAMSUNG_PHYCLK); - /* Configure PHY0 for normal operation*/ - writel(phypwr, regs + SAMSUNG_PHYPWR); - /* reset all ports of PHY and Link */ - writel(rstcon, regs + SAMSUNG_RSTCON); - udelay(10); - rstcon &= ~RSTCON_SWRST; - writel(rstcon, regs + SAMSUNG_RSTCON); -} - -static void samsung_exynos5_usbphy_disable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phyhost; - u32 phyotg; - u32 phyhsic; - - if (atomic_dec_return(&sphy->phy_usage) > 0) { - dev_info(sphy->dev, "still being used\n"); - return; - } - - phyhsic = (HSIC_CTRL_REFCLKDIV_12 | - HSIC_CTRL_REFCLKSEL | - HSIC_CTRL_SIDDQ | - HSIC_CTRL_FORCESLEEP | - HSIC_CTRL_FORCESUSPEND); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); - - phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); - phyhost |= (HOST_CTRL0_SIDDQ | - HOST_CTRL0_FORCESUSPEND | - HOST_CTRL0_FORCESLEEP | - HOST_CTRL0_PHYSWRST | - HOST_CTRL0_PHYSWRSTALL); - writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); - - phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); - phyotg |= (OTG_SYS_FORCESUSPEND | - OTG_SYS_SIDDQ_UOTG | - OTG_SYS_FORCESLEEP); - writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); -} - -static void samsung_usbphy_disable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phypwr; - - phypwr = readl(regs + SAMSUNG_PHYPWR); - - switch (sphy->drv_data->cpu_type) { - case TYPE_S3C64XX: - phypwr |= PHYPWR_NORMAL_MASK; - break; - case TYPE_EXYNOS4210: - phypwr |= PHYPWR_NORMAL_MASK_PHY0; - default: - break; - } - - /* Disable analog and otg block power */ - writel(phypwr, regs + SAMSUNG_PHYPWR); -} - -/* - * The function passed to the usb driver for phy initialization - */ -static int samsung_usbphy_init(struct usb_phy *phy) -{ - struct samsung_usbphy *sphy; - struct usb_bus *host = NULL; - unsigned long flags; - int ret = 0; - - sphy = phy_to_sphy(phy); - - host = phy->otg->host; - - /* Enable the phy clock */ - ret = clk_prepare_enable(sphy->clk); - if (ret) { - dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); - return ret; - } - - spin_lock_irqsave(&sphy->lock, flags); - - if (host) { - /* setting default phy-type for USB 2.0 */ - if (!strstr(dev_name(host->controller), "ehci") || - !strstr(dev_name(host->controller), "ohci")) - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); - } else { - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); - } - - /* Disable phy isolation */ - if (sphy->plat && sphy->plat->pmu_isolation) - sphy->plat->pmu_isolation(false); - else - samsung_usbphy_set_isolation(sphy, false); - - /* Selecting Host/OTG mode; After reset USB2.0PHY_CFG: HOST */ - samsung_usbphy_cfg_sel(sphy); - - /* Initialize usb phy registers */ - if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) - samsung_exynos5_usbphy_enable(sphy); - else - samsung_usbphy_enable(sphy); - - spin_unlock_irqrestore(&sphy->lock, flags); - - /* Disable the phy clock */ - clk_disable_unprepare(sphy->clk); - - return ret; -} - -/* - * The function passed to the usb driver for phy shutdown - */ -static void samsung_usbphy_shutdown(struct usb_phy *phy) -{ - struct samsung_usbphy *sphy; - struct usb_bus *host = NULL; - unsigned long flags; - - sphy = phy_to_sphy(phy); - - host = phy->otg->host; - - if (clk_prepare_enable(sphy->clk)) { - dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); - return; - } - - spin_lock_irqsave(&sphy->lock, flags); - - if (host) { - /* setting default phy-type for USB 2.0 */ - if (!strstr(dev_name(host->controller), "ehci") || - !strstr(dev_name(host->controller), "ohci")) - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); - } else { - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); - } - - /* De-initialize usb phy registers */ - if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) - samsung_exynos5_usbphy_disable(sphy); - else - samsung_usbphy_disable(sphy); - - /* Enable phy isolation */ - if (sphy->plat && sphy->plat->pmu_isolation) - sphy->plat->pmu_isolation(true); - else - samsung_usbphy_set_isolation(sphy, true); - - spin_unlock_irqrestore(&sphy->lock, flags); - - clk_disable_unprepare(sphy->clk); -} - -static const struct of_device_id samsung_usbphy_dt_match[]; - -static inline const struct samsung_usbphy_drvdata -*samsung_usbphy_get_driver_data(struct platform_device *pdev) -{ - if (pdev->dev.of_node) { - const struct of_device_id *match; - match = of_match_node(samsung_usbphy_dt_match, - pdev->dev.of_node); - return match->data; - } - - return (struct samsung_usbphy_drvdata *) - platform_get_device_id(pdev)->driver_data; -} - -static int samsung_usbphy_probe(struct platform_device *pdev) -{ - struct samsung_usbphy *sphy; - struct usb_otg *otg; - struct samsung_usbphy_data *pdata = pdev->dev.platform_data; - const struct samsung_usbphy_drvdata *drv_data; - struct device *dev = &pdev->dev; - struct resource *phy_mem; - void __iomem *phy_base; - struct clk *clk; - int ret; - - phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!phy_mem) { - dev_err(dev, "%s: missing mem resource\n", __func__); - return -ENODEV; - } - - phy_base = devm_ioremap_resource(dev, phy_mem); - if (IS_ERR(phy_base)) - return PTR_ERR(phy_base); - - sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); - if (!sphy) - return -ENOMEM; - - otg = devm_kzalloc(dev, sizeof(*otg), GFP_KERNEL); - if (!otg) - return -ENOMEM; - - drv_data = samsung_usbphy_get_driver_data(pdev); - - if (drv_data->cpu_type == TYPE_EXYNOS5250) - clk = devm_clk_get(dev, "usbhost"); - else - clk = devm_clk_get(dev, "otg"); - - if (IS_ERR(clk)) { - dev_err(dev, "Failed to get otg clock\n"); - return PTR_ERR(clk); - } - - sphy->dev = dev; - - if (dev->of_node) { - ret = samsung_usbphy_parse_dt(sphy); - if (ret < 0) - return ret; - } else { - if (!pdata) { - dev_err(dev, "no platform data specified\n"); - return -EINVAL; - } - } - - sphy->plat = pdata; - sphy->regs = phy_base; - sphy->clk = clk; - sphy->drv_data = drv_data; - sphy->phy.dev = sphy->dev; - sphy->phy.label = "samsung-usbphy"; - sphy->phy.init = samsung_usbphy_init; - sphy->phy.shutdown = samsung_usbphy_shutdown; - sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); - - sphy->phy.otg = otg; - sphy->phy.otg->phy = &sphy->phy; - sphy->phy.otg->set_host = samsung_usbphy_set_host; - - spin_lock_init(&sphy->lock); - - platform_set_drvdata(pdev, sphy); - - return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2); -} - -static int samsung_usbphy_remove(struct platform_device *pdev) -{ - struct samsung_usbphy *sphy = platform_get_drvdata(pdev); - - usb_remove_phy(&sphy->phy); - - if (sphy->pmuregs) - iounmap(sphy->pmuregs); - if (sphy->sysreg) - iounmap(sphy->sysreg); - - return 0; -} - -static const struct samsung_usbphy_drvdata usbphy_s3c64xx = { - .cpu_type = TYPE_S3C64XX, - .devphy_en_mask = S3C64XX_USBPHY_ENABLE, -}; - -static const struct samsung_usbphy_drvdata usbphy_exynos4 = { - .cpu_type = TYPE_EXYNOS4210, - .devphy_en_mask = EXYNOS_USBPHY_ENABLE, - .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, -}; - -static struct samsung_usbphy_drvdata usbphy_exynos5 = { - .cpu_type = TYPE_EXYNOS5250, - .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, - .hostphy_reg_offset = EXYNOS_USBHOST_PHY_CTRL_OFFSET, -}; - -#ifdef CONFIG_OF -static const struct of_device_id samsung_usbphy_dt_match[] = { - { - .compatible = "samsung,s3c64xx-usbphy", - .data = &usbphy_s3c64xx, - }, { - .compatible = "samsung,exynos4210-usbphy", - .data = &usbphy_exynos4, - }, { - .compatible = "samsung,exynos5250-usbphy", - .data = &usbphy_exynos5 - }, - {}, -}; -MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); -#endif - -static struct platform_device_id samsung_usbphy_driver_ids[] = { - { - .name = "s3c64xx-usbphy", - .driver_data = (unsigned long)&usbphy_s3c64xx, - }, { - .name = "exynos4210-usbphy", - .driver_data = (unsigned long)&usbphy_exynos4, - }, { - .name = "exynos5250-usbphy", - .driver_data = (unsigned long)&usbphy_exynos5, - }, - {}, -}; - -MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids); - -static struct platform_driver samsung_usbphy_driver = { - .probe = samsung_usbphy_probe, - .remove = samsung_usbphy_remove, - .id_table = samsung_usbphy_driver_ids, - .driver = { - .name = "samsung-usbphy", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(samsung_usbphy_dt_match), - }, -}; - -module_platform_driver(samsung_usbphy_driver); - -MODULE_DESCRIPTION("Samsung USB phy controller"); -MODULE_AUTHOR("Praveen Paneri "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:samsung-usbphy"); +EXPORT_SYMBOL_GPL(samsung_usbphy_get_refclk_freq); diff --git a/drivers/usb/phy/phy-samsung-usb.h b/drivers/usb/phy/phy-samsung-usb.h new file mode 100644 index 000000000000..481737d743d5 --- /dev/null +++ b/drivers/usb/phy/phy-samsung-usb.h @@ -0,0 +1,247 @@ +/* linux/drivers/usb/phy/phy-samsung-usb.h + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Samsung USB-PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and + * OHCI-EXYNOS controllers. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +/* Register definitions */ + +#define SAMSUNG_PHYPWR (0x00) + +#define PHYPWR_NORMAL_MASK (0x19 << 0) +#define PHYPWR_OTG_DISABLE (0x1 << 4) +#define PHYPWR_ANALOG_POWERDOWN (0x1 << 3) +#define PHYPWR_FORCE_SUSPEND (0x1 << 1) +/* For Exynos4 */ +#define PHYPWR_NORMAL_MASK_PHY0 (0x39 << 0) +#define PHYPWR_SLEEP_PHY0 (0x1 << 5) + +#define SAMSUNG_PHYCLK (0x04) + +#define PHYCLK_MODE_USB11 (0x1 << 6) +#define PHYCLK_EXT_OSC (0x1 << 5) +#define PHYCLK_COMMON_ON_N (0x1 << 4) +#define PHYCLK_ID_PULL (0x1 << 2) +#define PHYCLK_CLKSEL_MASK (0x3 << 0) +#define PHYCLK_CLKSEL_48M (0x0 << 0) +#define PHYCLK_CLKSEL_12M (0x2 << 0) +#define PHYCLK_CLKSEL_24M (0x3 << 0) + +#define SAMSUNG_RSTCON (0x08) + +#define RSTCON_PHYLINK_SWRST (0x1 << 2) +#define RSTCON_HLINK_SWRST (0x1 << 1) +#define RSTCON_SWRST (0x1 << 0) + +/* EXYNOS5 */ +#define EXYNOS5_PHY_HOST_CTRL0 (0x00) + +#define HOST_CTRL0_PHYSWRSTALL (0x1 << 31) + +#define HOST_CTRL0_REFCLKSEL_MASK (0x3 << 19) +#define HOST_CTRL0_REFCLKSEL_XTAL (0x0 << 19) +#define HOST_CTRL0_REFCLKSEL_EXTL (0x1 << 19) +#define HOST_CTRL0_REFCLKSEL_CLKCORE (0x2 << 19) + +#define HOST_CTRL0_FSEL_MASK (0x7 << 16) +#define HOST_CTRL0_FSEL(_x) ((_x) << 16) + +#define FSEL_CLKSEL_50M (0x7) +#define FSEL_CLKSEL_24M (0x5) +#define FSEL_CLKSEL_20M (0x4) +#define FSEL_CLKSEL_19200K (0x3) +#define FSEL_CLKSEL_12M (0x2) +#define FSEL_CLKSEL_10M (0x1) +#define FSEL_CLKSEL_9600K (0x0) + +#define HOST_CTRL0_TESTBURNIN (0x1 << 11) +#define HOST_CTRL0_RETENABLE (0x1 << 10) +#define HOST_CTRL0_COMMONON_N (0x1 << 9) +#define HOST_CTRL0_SIDDQ (0x1 << 6) +#define HOST_CTRL0_FORCESLEEP (0x1 << 5) +#define HOST_CTRL0_FORCESUSPEND (0x1 << 4) +#define HOST_CTRL0_WORDINTERFACE (0x1 << 3) +#define HOST_CTRL0_UTMISWRST (0x1 << 2) +#define HOST_CTRL0_LINKSWRST (0x1 << 1) +#define HOST_CTRL0_PHYSWRST (0x1 << 0) + +#define EXYNOS5_PHY_HOST_TUNE0 (0x04) + +#define EXYNOS5_PHY_HSIC_CTRL1 (0x10) + +#define EXYNOS5_PHY_HSIC_TUNE1 (0x14) + +#define EXYNOS5_PHY_HSIC_CTRL2 (0x20) + +#define EXYNOS5_PHY_HSIC_TUNE2 (0x24) + +#define HSIC_CTRL_REFCLKSEL_MASK (0x3 << 23) +#define HSIC_CTRL_REFCLKSEL (0x2 << 23) + +#define HSIC_CTRL_REFCLKDIV_MASK (0x7f << 16) +#define HSIC_CTRL_REFCLKDIV(_x) ((_x) << 16) +#define HSIC_CTRL_REFCLKDIV_12 (0x24 << 16) +#define HSIC_CTRL_REFCLKDIV_15 (0x1c << 16) +#define HSIC_CTRL_REFCLKDIV_16 (0x1a << 16) +#define HSIC_CTRL_REFCLKDIV_19_2 (0x15 << 16) +#define HSIC_CTRL_REFCLKDIV_20 (0x14 << 16) + +#define HSIC_CTRL_SIDDQ (0x1 << 6) +#define HSIC_CTRL_FORCESLEEP (0x1 << 5) +#define HSIC_CTRL_FORCESUSPEND (0x1 << 4) +#define HSIC_CTRL_WORDINTERFACE (0x1 << 3) +#define HSIC_CTRL_UTMISWRST (0x1 << 2) +#define HSIC_CTRL_PHYSWRST (0x1 << 0) + +#define EXYNOS5_PHY_HOST_EHCICTRL (0x30) + +#define HOST_EHCICTRL_ENAINCRXALIGN (0x1 << 29) +#define HOST_EHCICTRL_ENAINCR4 (0x1 << 28) +#define HOST_EHCICTRL_ENAINCR8 (0x1 << 27) +#define HOST_EHCICTRL_ENAINCR16 (0x1 << 26) + +#define EXYNOS5_PHY_HOST_OHCICTRL (0x34) + +#define HOST_OHCICTRL_SUSPLGCY (0x1 << 3) +#define HOST_OHCICTRL_APPSTARTCLK (0x1 << 2) +#define HOST_OHCICTRL_CNTSEL (0x1 << 1) +#define HOST_OHCICTRL_CLKCKTRST (0x1 << 0) + +#define EXYNOS5_PHY_OTG_SYS (0x38) + +#define OTG_SYS_PHYLINK_SWRESET (0x1 << 14) +#define OTG_SYS_LINKSWRST_UOTG (0x1 << 13) +#define OTG_SYS_PHY0_SWRST (0x1 << 12) + +#define OTG_SYS_REFCLKSEL_MASK (0x3 << 9) +#define OTG_SYS_REFCLKSEL_XTAL (0x0 << 9) +#define OTG_SYS_REFCLKSEL_EXTL (0x1 << 9) +#define OTG_SYS_REFCLKSEL_CLKCORE (0x2 << 9) + +#define OTG_SYS_IDPULLUP_UOTG (0x1 << 8) +#define OTG_SYS_COMMON_ON (0x1 << 7) + +#define OTG_SYS_FSEL_MASK (0x7 << 4) +#define OTG_SYS_FSEL(_x) ((_x) << 4) + +#define OTG_SYS_FORCESLEEP (0x1 << 3) +#define OTG_SYS_OTGDISABLE (0x1 << 2) +#define OTG_SYS_SIDDQ_UOTG (0x1 << 1) +#define OTG_SYS_FORCESUSPEND (0x1 << 0) + +#define EXYNOS5_PHY_OTG_TUNE (0x40) + +#ifndef MHZ +#define MHZ (1000*1000) +#endif + +#ifndef KHZ +#define KHZ (1000) +#endif + +#define EXYNOS_USBHOST_PHY_CTRL_OFFSET (0x4) +#define S3C64XX_USBPHY_ENABLE (0x1 << 16) +#define EXYNOS_USBPHY_ENABLE (0x1 << 0) +#define EXYNOS_USB20PHY_CFG_HOST_LINK (0x1 << 0) + +enum samsung_cpu_type { + TYPE_S3C64XX, + TYPE_EXYNOS4210, + TYPE_EXYNOS5250, +}; + +/* + * struct samsung_usbphy_drvdata - driver data for various SoC variants + * @cpu_type: machine identifier + * @devphy_en_mask: device phy enable mask for PHY CONTROL register + * @hostphy_en_mask: host phy enable mask for PHY CONTROL register + * @devphy_reg_offset: offset to DEVICE PHY CONTROL register from + * mapped address of system controller. + * @hostphy_reg_offset: offset to HOST PHY CONTROL register from + * mapped address of system controller. + * + * Here we have a separate mask for device type phy. + * Having different masks for host and device type phy helps + * in setting independent masks in case of SoCs like S5PV210, + * in which PHY0 and PHY1 enable bits belong to same register + * placed at position 0 and 1 respectively. + * Although for newer SoCs like exynos these bits belong to + * different registers altogether placed at position 0. + */ +struct samsung_usbphy_drvdata { + int cpu_type; + int devphy_en_mask; + int hostphy_en_mask; + u32 devphy_reg_offset; + u32 hostphy_reg_offset; +}; + +/* + * struct samsung_usbphy - transceiver driver state + * @phy: transceiver structure + * @plat: platform data + * @dev: The parent device supplied to the probe function + * @clk: usb phy clock + * @regs: usb phy controller registers memory base + * @pmuregs: USB device PHY_CONTROL register memory base + * @sysreg: USB2.0 PHY_CFG register memory base + * @ref_clk_freq: reference clock frequency selection + * @drv_data: driver data available for different SoCs + * @phy_type: Samsung SoCs specific phy types: #HOST + * #DEVICE + * @phy_usage: usage count for phy + * @lock: lock for phy operations + */ +struct samsung_usbphy { + struct usb_phy phy; + struct samsung_usbphy_data *plat; + struct device *dev; + struct clk *clk; + void __iomem *regs; + void __iomem *pmuregs; + void __iomem *sysreg; + int ref_clk_freq; + const struct samsung_usbphy_drvdata *drv_data; + enum samsung_usb_phy_type phy_type; + atomic_t phy_usage; + spinlock_t lock; +}; + +#define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) + +static const struct of_device_id samsung_usbphy_dt_match[]; + +static inline const struct samsung_usbphy_drvdata +*samsung_usbphy_get_driver_data(struct platform_device *pdev) +{ + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(samsung_usbphy_dt_match, + pdev->dev.of_node); + return match->data; + } + + return (struct samsung_usbphy_drvdata *) + platform_get_device_id(pdev)->driver_data; +} + +extern int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy); +extern void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on); +extern void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy); +extern int samsung_usbphy_set_type(struct usb_phy *phy, + enum samsung_usb_phy_type phy_type); +extern int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy); diff --git a/drivers/usb/phy/phy-samsung-usb2.c b/drivers/usb/phy/phy-samsung-usb2.c new file mode 100644 index 000000000000..dce968151505 --- /dev/null +++ b/drivers/usb/phy/phy-samsung-usb2.c @@ -0,0 +1,509 @@ +/* linux/drivers/usb/phy/phy-samsung-usb2.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Praveen Paneri + * + * Samsung USB2.0 PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and + * OHCI-EXYNOS controllers. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phy-samsung-usb.h" + +static int samsung_usbphy_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + if (!otg) + return -ENODEV; + + if (!otg->host) + otg->host = host; + + return 0; +} + +static bool exynos5_phyhost_is_on(void *regs) +{ + u32 reg; + + reg = readl(regs + EXYNOS5_PHY_HOST_CTRL0); + + return !(reg & HOST_CTRL0_SIDDQ); +} + +static void samsung_exynos5_usb2phy_enable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phyclk = sphy->ref_clk_freq; + u32 phyhost; + u32 phyotg; + u32 phyhsic; + u32 ehcictrl; + u32 ohcictrl; + + /* + * phy_usage helps in keeping usage count for phy + * so that the first consumer enabling the phy is also + * the last consumer to disable it. + */ + + atomic_inc(&sphy->phy_usage); + + if (exynos5_phyhost_is_on(regs)) { + dev_info(sphy->dev, "Already power on PHY\n"); + return; + } + + /* Host configuration */ + phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); + + /* phy reference clock configuration */ + phyhost &= ~HOST_CTRL0_FSEL_MASK; + phyhost |= HOST_CTRL0_FSEL(phyclk); + + /* host phy reset */ + phyhost &= ~(HOST_CTRL0_PHYSWRST | + HOST_CTRL0_PHYSWRSTALL | + HOST_CTRL0_SIDDQ | + /* Enable normal mode of operation */ + HOST_CTRL0_FORCESUSPEND | + HOST_CTRL0_FORCESLEEP); + + /* Link reset */ + phyhost |= (HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST | + /* COMMON Block configuration during suspend */ + HOST_CTRL0_COMMONON_N); + writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); + udelay(10); + phyhost &= ~(HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST); + writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); + + /* OTG configuration */ + phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); + + /* phy reference clock configuration */ + phyotg &= ~OTG_SYS_FSEL_MASK; + phyotg |= OTG_SYS_FSEL(phyclk); + + /* Enable normal mode of operation */ + phyotg &= ~(OTG_SYS_FORCESUSPEND | + OTG_SYS_SIDDQ_UOTG | + OTG_SYS_FORCESLEEP | + OTG_SYS_REFCLKSEL_MASK | + /* COMMON Block configuration during suspend */ + OTG_SYS_COMMON_ON); + + /* OTG phy & link reset */ + phyotg |= (OTG_SYS_PHY0_SWRST | + OTG_SYS_LINKSWRST_UOTG | + OTG_SYS_PHYLINK_SWRESET | + OTG_SYS_OTGDISABLE | + /* Set phy refclk */ + OTG_SYS_REFCLKSEL_CLKCORE); + + writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); + udelay(10); + phyotg &= ~(OTG_SYS_PHY0_SWRST | + OTG_SYS_LINKSWRST_UOTG | + OTG_SYS_PHYLINK_SWRESET); + writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); + + /* HSIC phy configuration */ + phyhsic = (HSIC_CTRL_REFCLKDIV_12 | + HSIC_CTRL_REFCLKSEL | + HSIC_CTRL_PHYSWRST); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); + udelay(10); + phyhsic &= ~HSIC_CTRL_PHYSWRST; + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); + + udelay(80); + + /* enable EHCI DMA burst */ + ehcictrl = readl(regs + EXYNOS5_PHY_HOST_EHCICTRL); + ehcictrl |= (HOST_EHCICTRL_ENAINCRXALIGN | + HOST_EHCICTRL_ENAINCR4 | + HOST_EHCICTRL_ENAINCR8 | + HOST_EHCICTRL_ENAINCR16); + writel(ehcictrl, regs + EXYNOS5_PHY_HOST_EHCICTRL); + + /* set ohci_suspend_on_n */ + ohcictrl = readl(regs + EXYNOS5_PHY_HOST_OHCICTRL); + ohcictrl |= HOST_OHCICTRL_SUSPLGCY; + writel(ohcictrl, regs + EXYNOS5_PHY_HOST_OHCICTRL); +} + +static void samsung_usb2phy_enable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phypwr; + u32 phyclk; + u32 rstcon; + + /* set clock frequency for PLL */ + phyclk = sphy->ref_clk_freq; + phypwr = readl(regs + SAMSUNG_PHYPWR); + rstcon = readl(regs + SAMSUNG_RSTCON); + + switch (sphy->drv_data->cpu_type) { + case TYPE_S3C64XX: + phyclk &= ~PHYCLK_COMMON_ON_N; + phypwr &= ~PHYPWR_NORMAL_MASK; + rstcon |= RSTCON_SWRST; + break; + case TYPE_EXYNOS4210: + phypwr &= ~PHYPWR_NORMAL_MASK_PHY0; + rstcon |= RSTCON_SWRST; + default: + break; + } + + writel(phyclk, regs + SAMSUNG_PHYCLK); + /* Configure PHY0 for normal operation*/ + writel(phypwr, regs + SAMSUNG_PHYPWR); + /* reset all ports of PHY and Link */ + writel(rstcon, regs + SAMSUNG_RSTCON); + udelay(10); + rstcon &= ~RSTCON_SWRST; + writel(rstcon, regs + SAMSUNG_RSTCON); +} + +static void samsung_exynos5_usb2phy_disable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phyhost; + u32 phyotg; + u32 phyhsic; + + if (atomic_dec_return(&sphy->phy_usage) > 0) { + dev_info(sphy->dev, "still being used\n"); + return; + } + + phyhsic = (HSIC_CTRL_REFCLKDIV_12 | + HSIC_CTRL_REFCLKSEL | + HSIC_CTRL_SIDDQ | + HSIC_CTRL_FORCESLEEP | + HSIC_CTRL_FORCESUSPEND); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); + + phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); + phyhost |= (HOST_CTRL0_SIDDQ | + HOST_CTRL0_FORCESUSPEND | + HOST_CTRL0_FORCESLEEP | + HOST_CTRL0_PHYSWRST | + HOST_CTRL0_PHYSWRSTALL); + writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); + + phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); + phyotg |= (OTG_SYS_FORCESUSPEND | + OTG_SYS_SIDDQ_UOTG | + OTG_SYS_FORCESLEEP); + writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); +} + +static void samsung_usb2phy_disable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phypwr; + + phypwr = readl(regs + SAMSUNG_PHYPWR); + + switch (sphy->drv_data->cpu_type) { + case TYPE_S3C64XX: + phypwr |= PHYPWR_NORMAL_MASK; + break; + case TYPE_EXYNOS4210: + phypwr |= PHYPWR_NORMAL_MASK_PHY0; + default: + break; + } + + /* Disable analog and otg block power */ + writel(phypwr, regs + SAMSUNG_PHYPWR); +} + +/* + * The function passed to the usb driver for phy initialization + */ +static int samsung_usb2phy_init(struct usb_phy *phy) +{ + struct samsung_usbphy *sphy; + struct usb_bus *host = NULL; + unsigned long flags; + int ret = 0; + + sphy = phy_to_sphy(phy); + + host = phy->otg->host; + + /* Enable the phy clock */ + ret = clk_prepare_enable(sphy->clk); + if (ret) { + dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); + return ret; + } + + spin_lock_irqsave(&sphy->lock, flags); + + if (host) { + /* setting default phy-type for USB 2.0 */ + if (!strstr(dev_name(host->controller), "ehci") || + !strstr(dev_name(host->controller), "ohci")) + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); + } else { + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); + } + + /* Disable phy isolation */ + if (sphy->plat && sphy->plat->pmu_isolation) + sphy->plat->pmu_isolation(false); + else + samsung_usbphy_set_isolation(sphy, false); + + /* Selecting Host/OTG mode; After reset USB2.0PHY_CFG: HOST */ + samsung_usbphy_cfg_sel(sphy); + + /* Initialize usb phy registers */ + if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) + samsung_exynos5_usb2phy_enable(sphy); + else + samsung_usb2phy_enable(sphy); + + spin_unlock_irqrestore(&sphy->lock, flags); + + /* Disable the phy clock */ + clk_disable_unprepare(sphy->clk); + + return ret; +} + +/* + * The function passed to the usb driver for phy shutdown + */ +static void samsung_usb2phy_shutdown(struct usb_phy *phy) +{ + struct samsung_usbphy *sphy; + struct usb_bus *host = NULL; + unsigned long flags; + + sphy = phy_to_sphy(phy); + + host = phy->otg->host; + + if (clk_prepare_enable(sphy->clk)) { + dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); + return; + } + + spin_lock_irqsave(&sphy->lock, flags); + + if (host) { + /* setting default phy-type for USB 2.0 */ + if (!strstr(dev_name(host->controller), "ehci") || + !strstr(dev_name(host->controller), "ohci")) + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); + } else { + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); + } + + /* De-initialize usb phy registers */ + if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) + samsung_exynos5_usb2phy_disable(sphy); + else + samsung_usb2phy_disable(sphy); + + /* Enable phy isolation */ + if (sphy->plat && sphy->plat->pmu_isolation) + sphy->plat->pmu_isolation(true); + else + samsung_usbphy_set_isolation(sphy, true); + + spin_unlock_irqrestore(&sphy->lock, flags); + + clk_disable_unprepare(sphy->clk); +} + +static int samsung_usb2phy_probe(struct platform_device *pdev) +{ + struct samsung_usbphy *sphy; + struct usb_otg *otg; + struct samsung_usbphy_data *pdata = pdev->dev.platform_data; + const struct samsung_usbphy_drvdata *drv_data; + struct device *dev = &pdev->dev; + struct resource *phy_mem; + void __iomem *phy_base; + struct clk *clk; + int ret; + + phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!phy_mem) { + dev_err(dev, "%s: missing mem resource\n", __func__); + return -ENODEV; + } + + phy_base = devm_ioremap_resource(dev, phy_mem); + if (IS_ERR(phy_base)) + return PTR_ERR(phy_base); + + sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); + if (!sphy) + return -ENOMEM; + + otg = devm_kzalloc(dev, sizeof(*otg), GFP_KERNEL); + if (!otg) + return -ENOMEM; + + drv_data = samsung_usbphy_get_driver_data(pdev); + + if (drv_data->cpu_type == TYPE_EXYNOS5250) + clk = devm_clk_get(dev, "usbhost"); + else + clk = devm_clk_get(dev, "otg"); + + if (IS_ERR(clk)) { + dev_err(dev, "Failed to get otg clock\n"); + return PTR_ERR(clk); + } + + sphy->dev = dev; + + if (dev->of_node) { + ret = samsung_usbphy_parse_dt(sphy); + if (ret < 0) + return ret; + } else { + if (!pdata) { + dev_err(dev, "no platform data specified\n"); + return -EINVAL; + } + } + + sphy->plat = pdata; + sphy->regs = phy_base; + sphy->clk = clk; + sphy->drv_data = drv_data; + sphy->phy.dev = sphy->dev; + sphy->phy.label = "samsung-usb2phy"; + sphy->phy.init = samsung_usb2phy_init; + sphy->phy.shutdown = samsung_usb2phy_shutdown; + sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); + + sphy->phy.otg = otg; + sphy->phy.otg->phy = &sphy->phy; + sphy->phy.otg->set_host = samsung_usbphy_set_host; + + spin_lock_init(&sphy->lock); + + platform_set_drvdata(pdev, sphy); + + return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2); +} + +static int samsung_usb2phy_remove(struct platform_device *pdev) +{ + struct samsung_usbphy *sphy = platform_get_drvdata(pdev); + + usb_remove_phy(&sphy->phy); + + if (sphy->pmuregs) + iounmap(sphy->pmuregs); + if (sphy->sysreg) + iounmap(sphy->sysreg); + + return 0; +} + +static const struct samsung_usbphy_drvdata usb2phy_s3c64xx = { + .cpu_type = TYPE_S3C64XX, + .devphy_en_mask = S3C64XX_USBPHY_ENABLE, +}; + +static const struct samsung_usbphy_drvdata usb2phy_exynos4 = { + .cpu_type = TYPE_EXYNOS4210, + .devphy_en_mask = EXYNOS_USBPHY_ENABLE, + .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, +}; + +static struct samsung_usbphy_drvdata usb2phy_exynos5 = { + .cpu_type = TYPE_EXYNOS5250, + .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, + .hostphy_reg_offset = EXYNOS_USBHOST_PHY_CTRL_OFFSET, +}; + +#ifdef CONFIG_OF +static const struct of_device_id samsung_usbphy_dt_match[] = { + { + .compatible = "samsung,s3c64xx-usb2phy", + .data = &usb2phy_s3c64xx, + }, { + .compatible = "samsung,exynos4210-usb2phy", + .data = &usb2phy_exynos4, + }, { + .compatible = "samsung,exynos5250-usb2phy", + .data = &usb2phy_exynos5 + }, + {}, +}; +MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); +#endif + +static struct platform_device_id samsung_usbphy_driver_ids[] = { + { + .name = "s3c64xx-usb2phy", + .driver_data = (unsigned long)&usb2phy_s3c64xx, + }, { + .name = "exynos4210-usb2phy", + .driver_data = (unsigned long)&usb2phy_exynos4, + }, { + .name = "exynos5250-usb2phy", + .driver_data = (unsigned long)&usb2phy_exynos5, + }, + {}, +}; + +MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids); + +static struct platform_driver samsung_usb2phy_driver = { + .probe = samsung_usb2phy_probe, + .remove = samsung_usb2phy_remove, + .id_table = samsung_usbphy_driver_ids, + .driver = { + .name = "samsung-usb2phy", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(samsung_usbphy_dt_match), + }, +}; + +module_platform_driver(samsung_usb2phy_driver); + +MODULE_DESCRIPTION("Samsung USB 2.0 phy controller"); +MODULE_AUTHOR("Praveen Paneri "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:samsung-usb2phy"); -- cgit v1.2.3 From b52767581765d3d1a1ba7106674791e540574704 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Thu, 14 Mar 2013 15:59:11 +0530 Subject: usb: phy: samsung: Add PHY support for USB 3.0 controller Adding PHY driver support for USB 3.0 controller for Samsung's SoCs. Signed-off-by: Vivek Gautam Acked-by: Kukjin Kim Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/samsung-usbphy.txt | 54 ++++ drivers/usb/phy/Kconfig | 7 + drivers/usb/phy/Makefile | 1 + drivers/usb/phy/phy-samsung-usb.h | 80 +++++ drivers/usb/phy/phy-samsung-usb3.c | 349 +++++++++++++++++++++ 5 files changed, 491 insertions(+) create mode 100644 drivers/usb/phy/phy-samsung-usb3.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt index 96940abe9a57..f575302e5173 100644 --- a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt +++ b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt @@ -61,3 +61,57 @@ Example: reg = <0x10020704 0x8>; }; }; + + +** Samsung's usb 3.0 phy transceiver + +Starting exynso5250, Samsung's SoC have usb 3.0 phy transceiver +which is used for controlling usb 3.0 phy for dwc3-exynos usb 3.0 +controllers across Samsung SOCs. + +Required properties: + +Exynos5250: +- compatible : should be "samsung,exynos5250-usb3phy" +- reg : base physical address of the phy registers and length of memory mapped + region. +- clocks: Clock IDs array as required by the controller. +- clock-names: names of clocks correseponding to IDs in the clock property + as requested by the controller driver. + +Optional properties: +- #address-cells: should be '1' when usbphy node has a child node with 'reg' + property. +- #size-cells: should be '1' when usbphy node has a child node with 'reg' + property. +- ranges: allows valid translation between child's address space and parent's + address space. + +- The child node 'usbphy-sys' to the node 'usbphy' is for the system controller + interface for usb-phy. It should provide the following information required by + usb-phy controller to control phy. + - reg : base physical address of PHY_CONTROL registers. + The size of this register is the total sum of size of all PHY_CONTROL + registers that the SoC has. For example, the size will be + '0x4' in case we have only one PHY_CONTROL register (e.g. + OTHERS register in S3C64XX or USB_PHY_CONTROL register in S5PV210) + and, '0x8' in case we have two PHY_CONTROL registers (e.g. + USBDEVICE_PHY_CONTROL and USBHOST_PHY_CONTROL registers in exynos4x). + and so on. + +Example: + usbphy@12100000 { + compatible = "samsung,exynos5250-usb3phy"; + reg = <0x12100000 0x100>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + clocks = <&clock 1>, <&clock 286>; + clock-names = "ext_xtal", "usbdrd30"; + + usbphy-sys { + /* USB device and host PHY_CONTROL registers */ + reg = <0x10040704 0x8>; + }; + }; diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index e8cd52ac5c05..7e8fe0f0b8c6 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -99,6 +99,13 @@ config SAMSUNG_USB2PHY Enable this to support Samsung USB 2.0 (High Speed) PHY controller driver for Samsung SoCs. +config SAMSUNG_USB3PHY + tristate "Samsung USB 3.0 PHY controller Driver" + select SAMSUNG_USBPHY + help + Enable this to support Samsung USB 3.0 (Super Speed) phy controller + for samsung SoCs. + config TWL4030_USB tristate "TWL4030 USB Transceiver Driver" depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 8cd355f051f6..33863c09f3dc 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o +obj-$(CONFIG_SAMSUNG_USB3PHY) += phy-samsung-usb3.o obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o obj-$(CONFIG_TWL6030_USB) += phy-twl6030-usb.o obj-$(CONFIG_USB_EHCI_TEGRA) += phy-tegra-usb.o diff --git a/drivers/usb/phy/phy-samsung-usb.h b/drivers/usb/phy/phy-samsung-usb.h index 481737d743d5..70a9cae5e37f 100644 --- a/drivers/usb/phy/phy-samsung-usb.h +++ b/drivers/usb/phy/phy-samsung-usb.h @@ -145,6 +145,86 @@ #define EXYNOS5_PHY_OTG_TUNE (0x40) +/* EXYNOS5: USB 3.0 DRD */ +#define EXYNOS5_DRD_LINKSYSTEM (0x04) + +#define LINKSYSTEM_FLADJ_MASK (0x3f << 1) +#define LINKSYSTEM_FLADJ(_x) ((_x) << 1) +#define LINKSYSTEM_XHCI_VERSION_CONTROL (0x1 << 27) + +#define EXYNOS5_DRD_PHYUTMI (0x08) + +#define PHYUTMI_OTGDISABLE (0x1 << 6) +#define PHYUTMI_FORCESUSPEND (0x1 << 1) +#define PHYUTMI_FORCESLEEP (0x1 << 0) + +#define EXYNOS5_DRD_PHYPIPE (0x0c) + +#define EXYNOS5_DRD_PHYCLKRST (0x10) + +#define PHYCLKRST_SSC_REFCLKSEL_MASK (0xff << 23) +#define PHYCLKRST_SSC_REFCLKSEL(_x) ((_x) << 23) + +#define PHYCLKRST_SSC_RANGE_MASK (0x03 << 21) +#define PHYCLKRST_SSC_RANGE(_x) ((_x) << 21) + +#define PHYCLKRST_SSC_EN (0x1 << 20) +#define PHYCLKRST_REF_SSP_EN (0x1 << 19) +#define PHYCLKRST_REF_CLKDIV2 (0x1 << 18) + +#define PHYCLKRST_MPLL_MULTIPLIER_MASK (0x7f << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF (0x19 << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_50M_REF (0x02 << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF (0x68 << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF (0x7d << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF (0x02 << 11) + +#define PHYCLKRST_FSEL_MASK (0x3f << 5) +#define PHYCLKRST_FSEL(_x) ((_x) << 5) +#define PHYCLKRST_FSEL_PAD_100MHZ (0x27 << 5) +#define PHYCLKRST_FSEL_PAD_24MHZ (0x2a << 5) +#define PHYCLKRST_FSEL_PAD_20MHZ (0x31 << 5) +#define PHYCLKRST_FSEL_PAD_19_2MHZ (0x38 << 5) + +#define PHYCLKRST_RETENABLEN (0x1 << 4) + +#define PHYCLKRST_REFCLKSEL_MASK (0x03 << 2) +#define PHYCLKRST_REFCLKSEL_PAD_REFCLK (0x2 << 2) +#define PHYCLKRST_REFCLKSEL_EXT_REFCLK (0x3 << 2) + +#define PHYCLKRST_PORTRESET (0x1 << 1) +#define PHYCLKRST_COMMONONN (0x1 << 0) + +#define EXYNOS5_DRD_PHYREG0 (0x14) +#define EXYNOS5_DRD_PHYREG1 (0x18) + +#define EXYNOS5_DRD_PHYPARAM0 (0x1c) + +#define PHYPARAM0_REF_USE_PAD (0x1 << 31) +#define PHYPARAM0_REF_LOSLEVEL_MASK (0x1f << 26) +#define PHYPARAM0_REF_LOSLEVEL (0x9 << 26) + +#define EXYNOS5_DRD_PHYPARAM1 (0x20) + +#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0) +#define PHYPARAM1_PCS_TXDEEMPH (0x1c) + +#define EXYNOS5_DRD_PHYTERM (0x24) + +#define EXYNOS5_DRD_PHYTEST (0x28) + +#define PHYTEST_POWERDOWN_SSP (0x1 << 3) +#define PHYTEST_POWERDOWN_HSP (0x1 << 2) + +#define EXYNOS5_DRD_PHYADP (0x2c) + +#define EXYNOS5_DRD_PHYBATCHG (0x30) + +#define PHYBATCHG_UTMI_CLKSEL (0x1 << 2) + +#define EXYNOS5_DRD_PHYRESUME (0x34) +#define EXYNOS5_DRD_LINKPORT (0x44) + #ifndef MHZ #define MHZ (1000*1000) #endif diff --git a/drivers/usb/phy/phy-samsung-usb3.c b/drivers/usb/phy/phy-samsung-usb3.c new file mode 100644 index 000000000000..54f641860f9e --- /dev/null +++ b/drivers/usb/phy/phy-samsung-usb3.c @@ -0,0 +1,349 @@ +/* linux/drivers/usb/phy/phy-samsung-usb3.c + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Vivek Gautam + * + * Samsung USB 3.0 PHY transceiver; talks to DWC3 controller. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phy-samsung-usb.h" + +/* + * Sets the phy clk as EXTREFCLK (XXTI) which is internal clock from clock core. + */ +static u32 samsung_usb3phy_set_refclk(struct samsung_usbphy *sphy) +{ + u32 reg; + u32 refclk; + + refclk = sphy->ref_clk_freq; + + reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK | + PHYCLKRST_FSEL(refclk); + + switch (refclk) { + case FSEL_CLKSEL_50M: + reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF | + PHYCLKRST_SSC_REFCLKSEL(0x00)); + break; + case FSEL_CLKSEL_20M: + reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF | + PHYCLKRST_SSC_REFCLKSEL(0x00)); + break; + case FSEL_CLKSEL_19200K: + reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF | + PHYCLKRST_SSC_REFCLKSEL(0x88)); + break; + case FSEL_CLKSEL_24M: + default: + reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF | + PHYCLKRST_SSC_REFCLKSEL(0x88)); + break; + } + + return reg; +} + +static int samsung_exynos5_usb3phy_enable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phyparam0; + u32 phyparam1; + u32 linksystem; + u32 phybatchg; + u32 phytest; + u32 phyclkrst; + + /* Reset USB 3.0 PHY */ + writel(0x0, regs + EXYNOS5_DRD_PHYREG0); + + phyparam0 = readl(regs + EXYNOS5_DRD_PHYPARAM0); + /* Select PHY CLK source */ + phyparam0 &= ~PHYPARAM0_REF_USE_PAD; + /* Set Loss-of-Signal Detector sensitivity */ + phyparam0 &= ~PHYPARAM0_REF_LOSLEVEL_MASK; + phyparam0 |= PHYPARAM0_REF_LOSLEVEL; + writel(phyparam0, regs + EXYNOS5_DRD_PHYPARAM0); + + writel(0x0, regs + EXYNOS5_DRD_PHYRESUME); + + /* + * Setting the Frame length Adj value[6:1] to default 0x20 + * See xHCI 1.0 spec, 5.2.4 + */ + linksystem = LINKSYSTEM_XHCI_VERSION_CONTROL | + LINKSYSTEM_FLADJ(0x20); + writel(linksystem, regs + EXYNOS5_DRD_LINKSYSTEM); + + phyparam1 = readl(regs + EXYNOS5_DRD_PHYPARAM1); + /* Set Tx De-Emphasis level */ + phyparam1 &= ~PHYPARAM1_PCS_TXDEEMPH_MASK; + phyparam1 |= PHYPARAM1_PCS_TXDEEMPH; + writel(phyparam1, regs + EXYNOS5_DRD_PHYPARAM1); + + phybatchg = readl(regs + EXYNOS5_DRD_PHYBATCHG); + phybatchg |= PHYBATCHG_UTMI_CLKSEL; + writel(phybatchg, regs + EXYNOS5_DRD_PHYBATCHG); + + /* PHYTEST POWERDOWN Control */ + phytest = readl(regs + EXYNOS5_DRD_PHYTEST); + phytest &= ~(PHYTEST_POWERDOWN_SSP | + PHYTEST_POWERDOWN_HSP); + writel(phytest, regs + EXYNOS5_DRD_PHYTEST); + + /* UTMI Power Control */ + writel(PHYUTMI_OTGDISABLE, regs + EXYNOS5_DRD_PHYUTMI); + + phyclkrst = samsung_usb3phy_set_refclk(sphy); + + phyclkrst |= PHYCLKRST_PORTRESET | + /* Digital power supply in normal operating mode */ + PHYCLKRST_RETENABLEN | + /* Enable ref clock for SS function */ + PHYCLKRST_REF_SSP_EN | + /* Enable spread spectrum */ + PHYCLKRST_SSC_EN | + /* Power down HS Bias and PLL blocks in suspend mode */ + PHYCLKRST_COMMONONN; + + writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST); + + udelay(10); + + phyclkrst &= ~(PHYCLKRST_PORTRESET); + writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST); + + return 0; +} + +static void samsung_exynos5_usb3phy_disable(struct samsung_usbphy *sphy) +{ + u32 phyutmi; + u32 phyclkrst; + u32 phytest; + void __iomem *regs = sphy->regs; + + phyutmi = PHYUTMI_OTGDISABLE | + PHYUTMI_FORCESUSPEND | + PHYUTMI_FORCESLEEP; + writel(phyutmi, regs + EXYNOS5_DRD_PHYUTMI); + + /* Resetting the PHYCLKRST enable bits to reduce leakage current */ + phyclkrst = readl(regs + EXYNOS5_DRD_PHYCLKRST); + phyclkrst &= ~(PHYCLKRST_REF_SSP_EN | + PHYCLKRST_SSC_EN | + PHYCLKRST_COMMONONN); + writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST); + + /* Control PHYTEST to remove leakage current */ + phytest = readl(regs + EXYNOS5_DRD_PHYTEST); + phytest |= (PHYTEST_POWERDOWN_SSP | + PHYTEST_POWERDOWN_HSP); + writel(phytest, regs + EXYNOS5_DRD_PHYTEST); +} + +static int samsung_usb3phy_init(struct usb_phy *phy) +{ + struct samsung_usbphy *sphy; + unsigned long flags; + int ret = 0; + + sphy = phy_to_sphy(phy); + + /* Enable the phy clock */ + ret = clk_prepare_enable(sphy->clk); + if (ret) { + dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); + return ret; + } + + spin_lock_irqsave(&sphy->lock, flags); + + /* setting default phy-type for USB 3.0 */ + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); + + /* Disable phy isolation */ + samsung_usbphy_set_isolation(sphy, false); + + /* Initialize usb phy registers */ + samsung_exynos5_usb3phy_enable(sphy); + + spin_unlock_irqrestore(&sphy->lock, flags); + + /* Disable the phy clock */ + clk_disable_unprepare(sphy->clk); + + return ret; +} + +/* + * The function passed to the usb driver for phy shutdown + */ +static void samsung_usb3phy_shutdown(struct usb_phy *phy) +{ + struct samsung_usbphy *sphy; + unsigned long flags; + + sphy = phy_to_sphy(phy); + + if (clk_prepare_enable(sphy->clk)) { + dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); + return; + } + + spin_lock_irqsave(&sphy->lock, flags); + + /* setting default phy-type for USB 3.0 */ + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); + + /* De-initialize usb phy registers */ + samsung_exynos5_usb3phy_disable(sphy); + + /* Enable phy isolation */ + samsung_usbphy_set_isolation(sphy, true); + + spin_unlock_irqrestore(&sphy->lock, flags); + + clk_disable_unprepare(sphy->clk); +} + +static int samsung_usb3phy_probe(struct platform_device *pdev) +{ + struct samsung_usbphy *sphy; + struct samsung_usbphy_data *pdata = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct resource *phy_mem; + void __iomem *phy_base; + struct clk *clk; + int ret; + + phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!phy_mem) { + dev_err(dev, "%s: missing mem resource\n", __func__); + return -ENODEV; + } + + phy_base = devm_request_and_ioremap(dev, phy_mem); + if (!phy_base) { + dev_err(dev, "%s: register mapping failed\n", __func__); + return -ENXIO; + } + + sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); + if (!sphy) + return -ENOMEM; + + clk = devm_clk_get(dev, "usbdrd30"); + if (IS_ERR(clk)) { + dev_err(dev, "Failed to get device clock\n"); + return PTR_ERR(clk); + } + + sphy->dev = dev; + + if (dev->of_node) { + ret = samsung_usbphy_parse_dt(sphy); + if (ret < 0) + return ret; + } else { + if (!pdata) { + dev_err(dev, "no platform data specified\n"); + return -EINVAL; + } + } + + sphy->plat = pdata; + sphy->regs = phy_base; + sphy->clk = clk; + sphy->phy.dev = sphy->dev; + sphy->phy.label = "samsung-usb3phy"; + sphy->phy.init = samsung_usb3phy_init; + sphy->phy.shutdown = samsung_usb3phy_shutdown; + sphy->drv_data = samsung_usbphy_get_driver_data(pdev); + sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); + + spin_lock_init(&sphy->lock); + + platform_set_drvdata(pdev, sphy); + + return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB3); +} + +static int samsung_usb3phy_remove(struct platform_device *pdev) +{ + struct samsung_usbphy *sphy = platform_get_drvdata(pdev); + + usb_remove_phy(&sphy->phy); + + if (sphy->pmuregs) + iounmap(sphy->pmuregs); + if (sphy->sysreg) + iounmap(sphy->sysreg); + + return 0; +} + +static struct samsung_usbphy_drvdata usb3phy_exynos5 = { + .cpu_type = TYPE_EXYNOS5250, + .devphy_en_mask = EXYNOS_USBPHY_ENABLE, +}; + +#ifdef CONFIG_OF +static const struct of_device_id samsung_usbphy_dt_match[] = { + { + .compatible = "samsung,exynos5250-usb3phy", + .data = &usb3phy_exynos5 + }, + {}, +}; +MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); +#endif + +static struct platform_device_id samsung_usbphy_driver_ids[] = { + { + .name = "exynos5250-usb3phy", + .driver_data = (unsigned long)&usb3phy_exynos5, + }, + {}, +}; + +MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids); + +static struct platform_driver samsung_usb3phy_driver = { + .probe = samsung_usb3phy_probe, + .remove = samsung_usb3phy_remove, + .id_table = samsung_usbphy_driver_ids, + .driver = { + .name = "samsung-usb3phy", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(samsung_usbphy_dt_match), + }, +}; + +module_platform_driver(samsung_usb3phy_driver); + +MODULE_DESCRIPTION("Samsung USB 3.0 phy controller"); +MODULE_AUTHOR("Vivek Gautam "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:samsung-usb3phy"); -- cgit v1.2.3 From eee44da0453cfe9125f4297e4244fe1d6fb1c653 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 7 Mar 2013 18:51:46 +0530 Subject: usb: musb: omap2430: replace *_* with *-* in property names No functional change. Replace *_* with *-* in property names of otg to follow the general convention. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/omap-usb.txt | 12 ++++++------ drivers/usb/musb/omap2430.c | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/omap-usb.txt b/Documentation/devicetree/bindings/usb/omap-usb.txt index 1b9f55fd96c0..662f0f1d2315 100644 --- a/Documentation/devicetree/bindings/usb/omap-usb.txt +++ b/Documentation/devicetree/bindings/usb/omap-usb.txt @@ -8,10 +8,10 @@ OMAP MUSB GLUE and disconnect. - multipoint : Should be "1" indicating the musb controller supports multipoint. This is a MUSB configuration-specific setting. - - num_eps : Specifies the number of endpoints. This is also a + - num-eps : Specifies the number of endpoints. This is also a MUSB configuration-specific setting. Should be set to "16" - - ram_bits : Specifies the ram address size. Should be set to "12" - - interface_type : This is a board specific setting to describe the type of + - ram-bits : Specifies the ram address size. Should be set to "12" + - interface-type : This is a board specific setting to describe the type of interface between the controller and the phy. It should be "0" or "1" specifying ULPI and UTMI respectively. - mode : Should be "3" to represent OTG. "1" signifies HOST and "2" @@ -29,14 +29,14 @@ usb_otg_hs: usb_otg_hs@4a0ab000 { ti,hwmods = "usb_otg_hs"; ti,has-mailbox; multipoint = <1>; - num_eps = <16>; - ram_bits = <12>; + num-eps = <16>; + ram-bits = <12>; ctrl-module = <&omap_control_usb>; }; Board specific device node entry &usb_otg_hs { - interface_type = <1>; + interface-type = <1>; mode = <3>; power = <50>; }; diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 8ba9bb2a91a7..e7b5eae5a141 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -526,10 +526,10 @@ static int omap2430_probe(struct platform_device *pdev) } of_property_read_u32(np, "mode", (u32 *)&pdata->mode); - of_property_read_u32(np, "interface_type", + of_property_read_u32(np, "interface-type", (u32 *)&data->interface_type); - of_property_read_u32(np, "num_eps", (u32 *)&config->num_eps); - of_property_read_u32(np, "ram_bits", (u32 *)&config->ram_bits); + of_property_read_u32(np, "num-eps", (u32 *)&config->num_eps); + of_property_read_u32(np, "ram-bits", (u32 *)&config->ram_bits); of_property_read_u32(np, "power", (u32 *)&pdata->power); config->multipoint = of_property_read_bool(np, "multipoint"); pdata->has_mailbox = of_property_read_bool(np, -- cgit v1.2.3 From fdba2d065cb2891b3006424b50fbc6406ce66672 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 15 Mar 2013 12:01:20 +0100 Subject: pinctrl: document the "GPIO mode" pitfall Recently as adoption of the pinctrl framework is reaching niches where the pins are reconfigured during system sleep and datasheets often talk about something called "GPIO mode", some engineers become confused by this, thinking that since it is named "GPIO (something something)" it must be modeled in the kernel using . To clarify things, let's put in this piece of documentation, or just start off the discussion here. Cc: Laurent Pinchart Cc: Pankaj Dev Reviewed-by: Stephen Warren Signed-off-by: Linus Walleij --- Documentation/pinctrl.txt | 112 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) (limited to 'Documentation') diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index a2b57e0a1db0..447fd4cd54ec 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -736,6 +736,13 @@ All the above functions are mandatory to implement for a pinmux driver. Pin control interaction with the GPIO subsystem =============================================== +Note that the following implies that the use case is to use a certain pin +from the Linux kernel using the API in with gpio_request() +and similar functions. There are cases where you may be using something +that your datasheet calls "GPIO mode" but actually is just an electrical +configuration for a certain device. See the section below named +"GPIO mode pitfalls" for more details on this scenario. + The public pinmux API contains two functions named pinctrl_request_gpio() and pinctrl_free_gpio(). These two functions shall *ONLY* be called from gpiolib-based drivers as part of their gpio_request() and @@ -774,6 +781,111 @@ obtain the function "gpioN" where "N" is the global GPIO pin number if no special GPIO-handler is registered. +GPIO mode pitfalls +================== + +Sometime the developer may be confused by a datasheet talking about a pin +being possible to set into "GPIO mode". It appears that what hardware +engineers mean with "GPIO mode" is not necessarily the use case that is +implied in the kernel interface : a pin that you grab from +kernel code and then either listen for input or drive high/low to +assert/deassert some external line. + +Rather hardware engineers think that "GPIO mode" means that you can +software-control a few electrical properties of the pin that you would +not be able to control if the pin was in some other mode, such as muxed in +for a device. + +Example: a pin is usually muxed in to be used as a UART TX line. But during +system sleep, we need to put this pin into "GPIO mode" and ground it. + +If you make a 1-to-1 map to the GPIO subsystem for this pin, you may start +to think that you need to come up with something real complex, that the +pin shall be used for UART TX and GPIO at the same time, that you will grab +a pin control handle and set it to a certain state to enable UART TX to be +muxed in, then twist it over to GPIO mode and use gpio_direction_output() +to drive it low during sleep, then mux it over to UART TX again when you +wake up and maybe even gpio_request/gpio_free as part of this cycle. This +all gets very complicated. + +The solution is to not think that what the datasheet calls "GPIO mode" +has to be handled by the interface. Instead view this as +a certain pin config setting. Look in e.g. +and you find this in the documentation: + + PIN_CONFIG_OUTPUT: this will configure the pin in output, use argument + 1 to indicate high level, argument 0 to indicate low level. + +So it is perfectly possible to push a pin into "GPIO mode" and drive the +line low as part of the usual pin control map. So for example your UART +driver may look like this: + +#include + +struct pinctrl *pinctrl; +struct pinctrl_state *pins_default; +struct pinctrl_state *pins_sleep; + +pins_default = pinctrl_lookup_state(uap->pinctrl, PINCTRL_STATE_DEFAULT); +pins_sleep = pinctrl_lookup_state(uap->pinctrl, PINCTRL_STATE_SLEEP); + +/* Normal mode */ +retval = pinctrl_select_state(pinctrl, pins_default); +/* Sleep mode */ +retval = pinctrl_select_state(pinctrl, pins_sleep); + +And your machine configuration may look like this: +-------------------------------------------------- + +static unsigned long uart_default_mode[] = { + PIN_CONF_PACKED(PIN_CONFIG_DRIVE_PUSH_PULL, 0), +}; + +static unsigned long uart_sleep_mode[] = { + PIN_CONF_PACKED(PIN_CONFIG_OUTPUT, 0), +}; + +static struct pinctrl_map __initdata pinmap[] = { + PIN_MAP_MUX_GROUP("uart", PINCTRL_STATE_DEFAULT, "pinctrl-foo", + "u0_group", "u0"), + PIN_MAP_CONFIGS_PIN("uart", PINCTRL_STATE_DEFAULT, "pinctrl-foo", + "UART_TX_PIN", uart_default_mode), + PIN_MAP_MUX_GROUP("uart", PINCTRL_STATE_SLEEP, "pinctrl-foo", + "u0_group", "gpio-mode"), + PIN_MAP_CONFIGS_PIN("uart", PINCTRL_STATE_SLEEP, "pinctrl-foo", + "UART_TX_PIN", uart_sleep_mode), +}; + +foo_init(void) { + pinctrl_register_mappings(pinmap, ARRAY_SIZE(pinmap)); +} + +Here the pins we want to control are in the "u0_group" and there is some +function called "u0" that can be enabled on this group of pins, and then +everything is UART business as usual. But there is also some function +named "gpio-mode" that can be mapped onto the same pins to move them into +GPIO mode. + +This will give the desired effect without any bogus interaction with the +GPIO subsystem. It is just an electrical configuration used by that device +when going to sleep, it might imply that the pin is set into something the +datasheet calls "GPIO mode" but that is not the point: it is still used +by that UART device to control the pins that pertain to that very UART +driver, putting them into modes needed by the UART. GPIO in the Linux +kernel sense are just some 1-bit line, and is a different use case. + +How the registers are poked to attain the push/pull and output low +configuration and the muxing of the "u0" or "gpio-mode" group onto these +pins is a question for the driver. + +Some datasheets will be more helpful and refer to the "GPIO mode" as +"low power mode" rather than anything to do with GPIO. This often means +the same thing electrically speaking, but in this latter case the +software engineers will usually quickly identify that this is some +specific muxing/configuration rather than anything related to the GPIO +API. + + Board/machine configuration ================================== -- cgit v1.2.3 From 78f7bcedf8ba70027e0f9f94ec420998a273a95c Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Tue, 12 Mar 2013 18:08:08 -0400 Subject: power_supply: Add OF bindings documentation for tps65090-charger This change adds the binding documentation for the tps65090-charger. Signed-off-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- .../devicetree/bindings/power_supply/tps65090.txt | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/power_supply/tps65090.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/power_supply/tps65090.txt b/Documentation/devicetree/bindings/power_supply/tps65090.txt new file mode 100644 index 000000000000..56370c724e35 --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/tps65090.txt @@ -0,0 +1,23 @@ +TPS65090 Frontend PMU with Switchmode Charger + +Required Properties: +-compatible: "ti,tps65090" +-reg: I2C slave address +-interrupts: the interrupt output to which this device connects + +Optional Properties: +-ti,enable-low-current-chrg: Enables charging when a low current is detected + while the default logic is to stop charging. + +Example: + + tps65090@48 { + compatible = "ti,tps65090"; + reg = <0x48>; + interrupts = <0 88 0x4>; + + ti,enable-low-current-chrg; + + regulators { + ... + }; -- cgit v1.2.3 From 4159d01bea38ee82f6e49383b7e73e328c118755 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 28 Feb 2013 10:35:56 -0300 Subject: [media] em28xx: Add ISDB support for c3tech Digital duo This is an hybrid board. However, for analog, it requires a new driver for saa7136. So, for now, let's just add support for Digital TV. Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.em28xx | 1 + drivers/media/usb/em28xx/Kconfig | 1 + drivers/media/usb/em28xx/em28xx-cards.c | 24 ++++++++++++++++++++++++ drivers/media/usb/em28xx/em28xx-dvb.c | 26 ++++++++++++++++++++++++++ drivers/media/usb/em28xx/em28xx.h | 1 + 5 files changed, 53 insertions(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index 3f12865b2a88..c59181431df5 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -85,3 +85,4 @@ 85 -> PCTV QuatroStick (510e) (em2884) [2304:0242] 86 -> PCTV QuatroStick nano (520e) (em2884) [2013:0251] 87 -> Terratec Cinergy HTC USB XS (em2884) [0ccd:008e,0ccd:00ac] + 88 -> C3 Tech Digital Duo HDTV/SDTV USB (em2884) [1b80:e755] diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index c754a80a8d8b..ca5ee6aceb62 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -46,6 +46,7 @@ config VIDEO_EM28XX_DVB select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT ---help--- diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 6e62b72376b0..46fff5c3335b 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -345,6 +345,18 @@ static struct em28xx_reg_seq pctv_460e[] = { { -1, -1, -1, -1}, }; +static struct em28xx_reg_seq c3tech_digital_duo_digital[] = { + {EM2874_R80_GPIO, 0xff, 0xff, 10}, + {EM2874_R80_GPIO, 0xfd, 0xff, 10}, /* xc5000 reset */ + {EM2874_R80_GPIO, 0xf9, 0xff, 35}, + {EM2874_R80_GPIO, 0xfd, 0xff, 10}, + {EM2874_R80_GPIO, 0xff, 0xff, 10}, + {EM2874_R80_GPIO, 0xfe, 0xff, 10}, + {EM2874_R80_GPIO, 0xbe, 0xff, 10}, + {EM2874_R80_GPIO, 0xfe, 0xff, 20}, + { -1, -1, -1, -1}, +}; + #if 0 static struct em28xx_reg_seq hauppauge_930c_gpio[] = { {EM2874_R80_GPIO, 0x6f, 0xff, 10}, @@ -978,6 +990,16 @@ struct em28xx_board em28xx_boards[] = { .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, }, + [EM2884_BOARD_C3TECH_DIGITAL_DUO] = { + .name = "C3 Tech Digital Duo HDTV/SDTV USB", + .has_dvb = 1, + /* FIXME: Add analog support - need a saa7136 driver */ + .tuner_type = TUNER_ABSENT, /* Digital-only TDA18271HD */ + .ir_codes = RC_MAP_EMPTY, + .def_i2c_bus = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE, + .dvb_gpio = c3tech_digital_duo_digital, + }, [EM2884_BOARD_CINERGY_HTC_STICK] = { .name = "Terratec Cinergy HTC Stick", .has_dvb = 1, @@ -2144,6 +2166,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM28174_BOARD_PCTV_460E }, { USB_DEVICE(0x2040, 0x1605), .driver_info = EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C }, + { USB_DEVICE(0x1b80, 0xe755), + .driver_info = EM2884_BOARD_C3TECH_DIGITAL_DUO }, { USB_DEVICE(0xeb1a, 0x5006), .driver_info = EM2860_BOARD_HT_VIDBOX_NW03 }, { USB_DEVICE(0x1b80, 0xe309), /* Sveon STV40 */ diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 98b95be3be6e..42a6a2696224 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -50,6 +50,7 @@ #include "tda10071.h" #include "a8293.h" #include "qt1010.h" +#include "mb86a20s.h" MODULE_DESCRIPTION("driver for em28xx based DVB cards"); MODULE_AUTHOR("Mauro Carvalho Chehab "); @@ -766,9 +767,25 @@ static struct zl10353_config em28xx_zl10353_no_i2c_gate_dev = { }; static struct qt1010_config em28xx_qt1010_config = { .i2c_address = 0x62 +}; + +static const struct mb86a20s_config c3tech_duo_mb86a20s_config = { + .demod_address = 0x10, + .is_serial = true, +}; + +static struct tda18271_std_map mb86a20s_tda18271_config = { + .dvbt_6 = { .if_freq = 4000, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x37, }, +}; +static struct tda18271_config c3tech_duo_tda18271_config = { + .std_map = &mb86a20s_tda18271_config, + .gate = TDA18271_GATE_DIGITAL, + .small_i2c = TDA18271_03_BYTE_CHUNK_INIT, }; + /* ------------------------------------------------------------------ */ static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev) @@ -1177,6 +1194,15 @@ static int em28xx_dvb_init(struct em28xx *dev) dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 0); break; + case EM2884_BOARD_C3TECH_DIGITAL_DUO: + dvb->fe[0] = dvb_attach(mb86a20s_attach, + &c3tech_duo_mb86a20s_config, + &dev->i2c_adap[dev->def_i2c_bus]); + if (dvb->fe[0] != NULL) + dvb_attach(tda18271_attach, dvb->fe[0], 0x60, + &dev->i2c_adap[dev->def_i2c_bus], + &c3tech_duo_tda18271_config); + break; case EM28174_BOARD_PCTV_460E: /* attach demod */ dvb->fe[0] = dvb_attach(tda10071_attach, diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index f6ac1df83816..4c667fd1661d 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -129,6 +129,7 @@ #define EM2884_BOARD_PCTV_510E 85 #define EM2884_BOARD_PCTV_520E 86 #define EM2884_BOARD_TERRATEC_HTC_USB_XS 87 +#define EM2884_BOARD_C3TECH_DIGITAL_DUO 88 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 -- cgit v1.2.3 From fc3a62e9f5ff5585ad9b9c27c2b800338aa3f751 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 19 Mar 2013 15:15:59 -0300 Subject: [media] em28xx: update cardlist There's one missing USB ID at the card list. Add it. Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.em28xx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index c59181431df5..e81864405102 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -76,7 +76,7 @@ 76 -> KWorld PlusTV 340U or UB435-Q (ATSC) (em2870) [1b80:a340] 77 -> EM2874 Leadership ISDBT (em2874) 78 -> PCTV nanoStick T2 290e (em28174) - 79 -> Terratec Cinergy H5 (em2884) [0ccd:10a2,0ccd:10ad] + 79 -> Terratec Cinergy H5 (em2884) [0ccd:10a2,0ccd:10ad,0ccd:10b6] 80 -> PCTV DVB-S2 Stick (460e) (em28174) 81 -> Hauppauge WinTV HVR 930C (em2884) [2040:1605] 82 -> Terratec Cinergy HTC Stick (em2884) [0ccd:00b2] -- cgit v1.2.3 From e05b6f982a049113a88a1750e13fdb15298cbed4 Mon Sep 17 00:00:00 2001 From: Rafal Krypa Date: Thu, 10 Jan 2013 19:42:00 +0100 Subject: Smack: add support for modification of existing rules Rule modifications are enabled via /smack/change-rule. Format is as follows: "Subject Object rwaxt rwaxt" First two strings are subject and object labels up to 255 characters. Third string contains permissions to enable. Fourth string contains permissions to disable. All unmentioned permissions will be left unchanged. If no rule previously existed, it will be created. Targeted for git://git.gitorious.org/smack-next/kernel.git Signed-off-by: Rafal Krypa --- Documentation/security/Smack.txt | 11 ++ security/smack/smackfs.c | 249 ++++++++++++++++++++++++++------------- 2 files changed, 181 insertions(+), 79 deletions(-) (limited to 'Documentation') diff --git a/Documentation/security/Smack.txt b/Documentation/security/Smack.txt index 8a177e4b6e21..7a2d30c132e3 100644 --- a/Documentation/security/Smack.txt +++ b/Documentation/security/Smack.txt @@ -117,6 +117,17 @@ access2 ambient This contains the Smack label applied to unlabeled network packets. +change-rule + This interface allows modification of existing access control rules. + The format accepted on write is: + "%s %s %s %s" + where the first string is the subject label, the second the + object label, the third the access to allow and the fourth the + access to deny. The access strings may contain only the characters + "rwxat-". If a rule for a given subject and object exists it will be + modified by enabling the permissions in the third string and disabling + those in the fourth string. If there is no such rule it will be + created using the access specified in the third and the fourth strings. cipso This interface allows a specific CIPSO header to be assigned to a Smack label. The format accepted on write is: diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 337e32c551da..2479a41a7dff 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -50,12 +50,12 @@ enum smk_inos { SMK_ACCESS2 = 16, /* make an access check with long labels */ SMK_CIPSO2 = 17, /* load long label -> CIPSO mapping */ SMK_REVOKE_SUBJ = 18, /* set rules with subject label to '-' */ + SMK_CHANGE_RULE = 19, /* change or add rules (long labels) */ }; /* * List locks */ -static DEFINE_MUTEX(smack_list_lock); static DEFINE_MUTEX(smack_cipso_lock); static DEFINE_MUTEX(smack_ambient_lock); static DEFINE_MUTEX(smk_netlbladdr_lock); @@ -110,6 +110,13 @@ struct smack_master_list { LIST_HEAD(smack_rule_list); +struct smack_parsed_rule { + char *smk_subject; + char *smk_object; + int smk_access1; + int smk_access2; +}; + static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT; const char *smack_cipso_option = SMACK_CIPSO_OPTION; @@ -167,25 +174,28 @@ static void smk_netlabel_audit_set(struct netlbl_audit *nap) #define SMK_NETLBLADDRMIN 9 /** - * smk_set_access - add a rule to the rule list - * @srp: the new rule to add + * smk_set_access - add a rule to the rule list or replace an old rule + * @srp: the rule to add or replace * @rule_list: the list of rules * @rule_lock: the rule list lock + * @global: if non-zero, indicates a global rule * * Looks through the current subject/object/access list for * the subject/object pair and replaces the access that was * there. If the pair isn't found add it with the specified * access. * - * Returns 1 if a rule was found to exist already, 0 if it is new * Returns 0 if nothing goes wrong or -ENOMEM if it fails * during the allocation of the new pair to add. */ -static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list, - struct mutex *rule_lock) +static int smk_set_access(struct smack_parsed_rule *srp, + struct list_head *rule_list, + struct mutex *rule_lock, int global) { struct smack_rule *sp; + struct smack_master_list *smlp; int found = 0; + int rc = 0; mutex_lock(rule_lock); @@ -197,23 +207,89 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list, if (sp->smk_object == srp->smk_object && sp->smk_subject == srp->smk_subject) { found = 1; - sp->smk_access = srp->smk_access; + sp->smk_access |= srp->smk_access1; + sp->smk_access &= ~srp->smk_access2; break; } } - if (found == 0) - list_add_rcu(&srp->list, rule_list); + if (found == 0) { + sp = kzalloc(sizeof(*sp), GFP_KERNEL); + if (sp == NULL) { + rc = -ENOMEM; + goto out; + } + + sp->smk_subject = srp->smk_subject; + sp->smk_object = srp->smk_object; + sp->smk_access = srp->smk_access1 & ~srp->smk_access2; + + list_add_rcu(&sp->list, rule_list); + /* + * If this is a global as opposed to self and a new rule + * it needs to get added for reporting. + */ + if (global) { + smlp = kzalloc(sizeof(*smlp), GFP_KERNEL); + if (smlp != NULL) { + smlp->smk_rule = sp; + list_add_rcu(&smlp->list, &smack_rule_list); + } else + rc = -ENOMEM; + } + } + +out: mutex_unlock(rule_lock); + return rc; +} + +/** + * smk_perm_from_str - parse smack accesses from a text string + * @string: a text string that contains a Smack accesses code + * + * Returns an integer with respective bits set for specified accesses. + */ +static int smk_perm_from_str(const char *string) +{ + int perm = 0; + const char *cp; - return found; + for (cp = string; ; cp++) + switch (*cp) { + case '-': + break; + case 'r': + case 'R': + perm |= MAY_READ; + break; + case 'w': + case 'W': + perm |= MAY_WRITE; + break; + case 'x': + case 'X': + perm |= MAY_EXEC; + break; + case 'a': + case 'A': + perm |= MAY_APPEND; + break; + case 't': + case 'T': + perm |= MAY_TRANSMUTE; + break; + default: + return perm; + } } /** * smk_fill_rule - Fill Smack rule from strings * @subject: subject label string * @object: object label string - * @access: access string + * @access1: access string + * @access2: string with permissions to be removed * @rule: Smack rule * @import: if non-zero, import labels * @len: label length limit @@ -221,8 +297,9 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list, * Returns 0 on success, -1 on failure */ static int smk_fill_rule(const char *subject, const char *object, - const char *access, struct smack_rule *rule, - int import, int len) + const char *access1, const char *access2, + struct smack_parsed_rule *rule, int import, + int len) { const char *cp; struct smack_known *skp; @@ -255,36 +332,11 @@ static int smk_fill_rule(const char *subject, const char *object, rule->smk_object = skp->smk_known; } - rule->smk_access = 0; - - for (cp = access; *cp != '\0'; cp++) { - switch (*cp) { - case '-': - break; - case 'r': - case 'R': - rule->smk_access |= MAY_READ; - break; - case 'w': - case 'W': - rule->smk_access |= MAY_WRITE; - break; - case 'x': - case 'X': - rule->smk_access |= MAY_EXEC; - break; - case 'a': - case 'A': - rule->smk_access |= MAY_APPEND; - break; - case 't': - case 'T': - rule->smk_access |= MAY_TRANSMUTE; - break; - default: - return 0; - } - } + rule->smk_access1 = smk_perm_from_str(access1); + if (access2) + rule->smk_access2 = smk_perm_from_str(access2); + else + rule->smk_access2 = ~rule->smk_access1; return 0; } @@ -297,30 +349,33 @@ static int smk_fill_rule(const char *subject, const char *object, * * Returns 0 on success, -1 on errors. */ -static int smk_parse_rule(const char *data, struct smack_rule *rule, int import) +static int smk_parse_rule(const char *data, struct smack_parsed_rule *rule, + int import) { int rc; rc = smk_fill_rule(data, data + SMK_LABELLEN, - data + SMK_LABELLEN + SMK_LABELLEN, rule, import, - SMK_LABELLEN); + data + SMK_LABELLEN + SMK_LABELLEN, NULL, rule, + import, SMK_LABELLEN); return rc; } /** * smk_parse_long_rule - parse Smack rule from rule string * @data: string to be parsed, null terminated - * @rule: Smack rule + * @rule: Will be filled with Smack parsed rule * @import: if non-zero, import labels + * @change: if non-zero, data is from /smack/change-rule * * Returns 0 on success, -1 on failure */ -static int smk_parse_long_rule(const char *data, struct smack_rule *rule, - int import) +static int smk_parse_long_rule(const char *data, struct smack_parsed_rule *rule, + int import, int change) { char *subject; char *object; - char *access; + char *access1; + char *access2; int datalen; int rc = -1; @@ -334,14 +389,27 @@ static int smk_parse_long_rule(const char *data, struct smack_rule *rule, object = kzalloc(datalen, GFP_KERNEL); if (object == NULL) goto free_out_s; - access = kzalloc(datalen, GFP_KERNEL); - if (access == NULL) + access1 = kzalloc(datalen, GFP_KERNEL); + if (access1 == NULL) goto free_out_o; + access2 = kzalloc(datalen, GFP_KERNEL); + if (access2 == NULL) + goto free_out_a; + + if (change) { + if (sscanf(data, "%s %s %s %s", + subject, object, access1, access2) == 4) + rc = smk_fill_rule(subject, object, access1, access2, + rule, import, 0); + } else { + if (sscanf(data, "%s %s %s", subject, object, access1) == 3) + rc = smk_fill_rule(subject, object, access1, NULL, + rule, import, 0); + } - if (sscanf(data, "%s %s %s", subject, object, access) == 3) - rc = smk_fill_rule(subject, object, access, rule, import, 0); - - kfree(access); + kfree(access2); +free_out_a: + kfree(access1); free_out_o: kfree(object); free_out_s: @@ -351,6 +419,7 @@ free_out_s: #define SMK_FIXED24_FMT 0 /* Fixed 24byte label format */ #define SMK_LONG_FMT 1 /* Variable long label format */ +#define SMK_CHANGE_FMT 2 /* Rule modification format */ /** * smk_write_rules_list - write() for any /smack rule file * @file: file pointer, not actually used @@ -359,22 +428,24 @@ free_out_s: * @ppos: where to start - must be 0 * @rule_list: the list of rules to write to * @rule_lock: lock for the rule list - * @format: /smack/load or /smack/load2 format. + * @format: /smack/load or /smack/load2 or /smack/change-rule format. * * Get one smack access rule from above. * The format for SMK_LONG_FMT is: * "subjectobjectaccess[...]" * The format for SMK_FIXED24_FMT is exactly: * "subject object rwxat" + * The format for SMK_CHANGE_FMT is: + * "subjectobject + * acc_enableacc_disable[...]" */ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, size_t count, loff_t *ppos, struct list_head *rule_list, struct mutex *rule_lock, int format) { - struct smack_master_list *smlp; struct smack_known *skp; - struct smack_rule *rule; + struct smack_parsed_rule *rule; char *data; int datalen; int rc = -EINVAL; @@ -417,7 +488,11 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, * Be sure the data string is terminated. */ data[count] = '\0'; - if (smk_parse_long_rule(data, rule, 1)) + if (smk_parse_long_rule(data, rule, 1, 0)) + goto out_free_rule; + } else if (format == SMK_CHANGE_FMT) { + data[count] = '\0'; + if (smk_parse_long_rule(data, rule, 1, 1)) goto out_free_rule; } else { /* @@ -437,22 +512,9 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, rule_lock = &skp->smk_rules_lock; } - rc = count; - /* - * If this is a global as opposed to self and a new rule - * it needs to get added for reporting. - * smk_set_access returns true if there was already a rule - * for the subject/object pair, and false if it was new. - */ - if (!smk_set_access(rule, rule_list, rule_lock)) { - if (load) { - smlp = kzalloc(sizeof(*smlp), GFP_KERNEL); - if (smlp != NULL) { - smlp->smk_rule = rule; - list_add_rcu(&smlp->list, &smack_rule_list); - } else - rc = -ENOMEM; - } + rc = smk_set_access(rule, rule_list, rule_lock, load); + if (rc == 0) { + rc = count; goto out; } @@ -1774,7 +1836,7 @@ static const struct file_operations smk_load_self_ops = { static ssize_t smk_user_access(struct file *file, const char __user *buf, size_t count, loff_t *ppos, int format) { - struct smack_rule rule; + struct smack_parsed_rule rule; char *data; char *cod; int res; @@ -1796,14 +1858,14 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf, return -ENOMEM; memcpy(cod, data, count); cod[count] = '\0'; - res = smk_parse_long_rule(cod, &rule, 0); + res = smk_parse_long_rule(cod, &rule, 0, 0); kfree(cod); } if (res) return -EINVAL; - res = smk_access(rule.smk_subject, rule.smk_object, rule.smk_access, + res = smk_access(rule.smk_subject, rule.smk_object, rule.smk_access1, NULL); data[0] = res == 0 ? '1' : '0'; data[1] = '\0'; @@ -2074,6 +2136,33 @@ static int smk_init_sysfs(void) return 0; } +/** + * smk_write_change_rule - write() for /smack/change-rule + * @file: file pointer + * @buf: data from user space + * @count: bytes sent + * @ppos: where to start - must be 0 + */ +static ssize_t smk_write_change_rule(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + /* + * Must have privilege. + */ + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + + return smk_write_rules_list(file, buf, count, ppos, NULL, NULL, + SMK_CHANGE_FMT); +} + +static const struct file_operations smk_change_rule_ops = { + .write = smk_write_change_rule, + .read = simple_transaction_read, + .release = simple_transaction_release, + .llseek = generic_file_llseek, +}; + /** * smk_fill_super - fill the /smackfs superblock * @sb: the empty superblock @@ -2123,6 +2212,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent) [SMK_REVOKE_SUBJ] = { "revoke-subject", &smk_revoke_subj_ops, S_IRUGO|S_IWUSR}, + [SMK_CHANGE_RULE] = { + "change-rule", &smk_change_rule_ops, S_IRUGO|S_IWUSR}, /* last one */ {""} }; -- cgit v1.2.3 From 0e646c52cf0ee186ec50b41c4db8cf81500c8dd1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 11 Mar 2013 16:22:29 +0100 Subject: clk: Add axi-clkgen driver This driver adds support for the AXI clkgen pcore to the common clock framework. The AXI clkgen pcore is a AXI front-end to the MMCM_ADV frequency synthesizer commonly found in Xilinx FPGAs. The AXI clkgen pcore is used in Analog Devices' reference designs targeting Xilinx FPGAs. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mike Turquette --- .../devicetree/bindings/clock/axi-clkgen.txt | 22 ++ drivers/clk/Kconfig | 8 + drivers/clk/Makefile | 1 + drivers/clk/clk-axi-clkgen.c | 331 +++++++++++++++++++++ 4 files changed, 362 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/axi-clkgen.txt create mode 100644 drivers/clk/clk-axi-clkgen.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/clock/axi-clkgen.txt b/Documentation/devicetree/bindings/clock/axi-clkgen.txt new file mode 100644 index 000000000000..028b493e97ff --- /dev/null +++ b/Documentation/devicetree/bindings/clock/axi-clkgen.txt @@ -0,0 +1,22 @@ +Binding for the axi-clkgen clock generator + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- compatible : shall be "adi,axi-clkgen". +- #clock-cells : from common clock binding; Should always be set to 0. +- reg : Address and length of the axi-clkgen register set. +- clocks : Phandle and clock specifier for the parent clock. + +Optional properties: +- clock-output-names : From common clock binding. + +Example: + clock@0xff000000 { + compatible = "adi,axi-clkgen"; + #clock-cells = <0>; + reg = <0xff000000 0x1000>; + clocks = <&osc 1>; + }; diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index a47e6ee98b8c..a64caefdba12 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -63,6 +63,14 @@ config CLK_TWL6040 McPDM. McPDM module is using the external bit clock on the McPDM bus as functional clock. +config COMMON_CLK_AXI_CLKGEN + tristate "AXI clkgen driver" + depends on ARCH_ZYNQ || MICROBLAZE + help + ---help--- + Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx + FPGAs. It is commonly used in Analog Devices' reference designs. + endmenu source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 300d4775d926..1c22f9dc721d 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_X86) += x86/ # Chip specific +obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c new file mode 100644 index 000000000000..8137327847c3 --- /dev/null +++ b/drivers/clk/clk-axi-clkgen.c @@ -0,0 +1,331 @@ +/* + * AXI clkgen driver + * + * Copyright 2012-2013 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define AXI_CLKGEN_REG_UPDATE_ENABLE 0x04 +#define AXI_CLKGEN_REG_CLK_OUT1 0x08 +#define AXI_CLKGEN_REG_CLK_OUT2 0x0c +#define AXI_CLKGEN_REG_CLK_DIV 0x10 +#define AXI_CLKGEN_REG_CLK_FB1 0x14 +#define AXI_CLKGEN_REG_CLK_FB2 0x18 +#define AXI_CLKGEN_REG_LOCK1 0x1c +#define AXI_CLKGEN_REG_LOCK2 0x20 +#define AXI_CLKGEN_REG_LOCK3 0x24 +#define AXI_CLKGEN_REG_FILTER1 0x28 +#define AXI_CLKGEN_REG_FILTER2 0x2c + +struct axi_clkgen { + void __iomem *base; + struct clk_hw clk_hw; +}; + +static uint32_t axi_clkgen_lookup_filter(unsigned int m) +{ + switch (m) { + case 0: + return 0x01001990; + case 1: + return 0x01001190; + case 2: + return 0x01009890; + case 3: + return 0x01001890; + case 4: + return 0x01008890; + case 5 ... 8: + return 0x01009090; + case 9 ... 11: + return 0x01000890; + case 12: + return 0x08009090; + case 13 ... 22: + return 0x01001090; + case 23 ... 36: + return 0x01008090; + case 37 ... 46: + return 0x08001090; + default: + return 0x08008090; + } +} + +static const uint32_t axi_clkgen_lock_table[] = { + 0x060603e8, 0x060603e8, 0x080803e8, 0x0b0b03e8, + 0x0e0e03e8, 0x111103e8, 0x131303e8, 0x161603e8, + 0x191903e8, 0x1c1c03e8, 0x1f1f0384, 0x1f1f0339, + 0x1f1f02ee, 0x1f1f02bc, 0x1f1f028a, 0x1f1f0271, + 0x1f1f023f, 0x1f1f0226, 0x1f1f020d, 0x1f1f01f4, + 0x1f1f01db, 0x1f1f01c2, 0x1f1f01a9, 0x1f1f0190, + 0x1f1f0190, 0x1f1f0177, 0x1f1f015e, 0x1f1f015e, + 0x1f1f0145, 0x1f1f0145, 0x1f1f012c, 0x1f1f012c, + 0x1f1f012c, 0x1f1f0113, 0x1f1f0113, 0x1f1f0113, +}; + +static uint32_t axi_clkgen_lookup_lock(unsigned int m) +{ + if (m < ARRAY_SIZE(axi_clkgen_lock_table)) + return axi_clkgen_lock_table[m]; + return 0x1f1f00fa; +} + +static const unsigned int fpfd_min = 10000; +static const unsigned int fpfd_max = 300000; +static const unsigned int fvco_min = 600000; +static const unsigned int fvco_max = 1200000; + +static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout, + unsigned int *best_d, unsigned int *best_m, unsigned int *best_dout) +{ + unsigned long d, d_min, d_max, _d_min, _d_max; + unsigned long m, m_min, m_max; + unsigned long f, dout, best_f, fvco; + + fin /= 1000; + fout /= 1000; + + best_f = ULONG_MAX; + *best_d = 0; + *best_m = 0; + *best_dout = 0; + + d_min = max_t(unsigned long, DIV_ROUND_UP(fin, fpfd_max), 1); + d_max = min_t(unsigned long, fin / fpfd_min, 80); + + m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min, fin) * d_min, 1); + m_max = min_t(unsigned long, fvco_max * d_max / fin, 64); + + for (m = m_min; m <= m_max; m++) { + _d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max)); + _d_max = min(d_max, fin * m / fvco_min); + + for (d = _d_min; d <= _d_max; d++) { + fvco = fin * m / d; + + dout = DIV_ROUND_CLOSEST(fvco, fout); + dout = clamp_t(unsigned long, dout, 1, 128); + f = fvco / dout; + if (abs(f - fout) < abs(best_f - fout)) { + best_f = f; + *best_d = d; + *best_m = m; + *best_dout = dout; + if (best_f == fout) + return; + } + } + } +} + +static void axi_clkgen_calc_clk_params(unsigned int divider, unsigned int *low, + unsigned int *high, unsigned int *edge, unsigned int *nocount) +{ + if (divider == 1) + *nocount = 1; + else + *nocount = 0; + + *high = divider / 2; + *edge = divider % 2; + *low = divider - *high; +} + +static void axi_clkgen_write(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int val) +{ + writel(val, axi_clkgen->base + reg); +} + +static void axi_clkgen_read(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int *val) +{ + *val = readl(axi_clkgen->base + reg); +} + +static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw) +{ + return container_of(clk_hw, struct axi_clkgen, clk_hw); +} + +static int axi_clkgen_set_rate(struct clk_hw *clk_hw, + unsigned long rate, unsigned long parent_rate) +{ + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); + unsigned int d, m, dout; + unsigned int nocount; + unsigned int high; + unsigned int edge; + unsigned int low; + uint32_t filter; + uint32_t lock; + + if (parent_rate == 0 || rate == 0) + return -EINVAL; + + axi_clkgen_calc_params(parent_rate, rate, &d, &m, &dout); + + if (d == 0 || dout == 0 || m == 0) + return -EINVAL; + + filter = axi_clkgen_lookup_filter(m - 1); + lock = axi_clkgen_lookup_lock(m - 1); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 0); + + axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, + (high << 6) | low); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT2, + (edge << 7) | (nocount << 6)); + + axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, + (edge << 13) | (nocount << 12) | (high << 6) | low); + + axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, + (high << 6) | low); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB2, + (edge << 7) | (nocount << 6)); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK1, lock & 0x3ff); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK2, + (((lock >> 16) & 0x1f) << 10) | 0x1); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK3, + (((lock >> 24) & 0x1f) << 10) | 0x3e9); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER1, filter >> 16); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER2, filter); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 1); + + return 0; +} + +static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned int d, m, dout; + + axi_clkgen_calc_params(*parent_rate, rate, &d, &m, &dout); + + if (d == 0 || dout == 0 || m == 0) + return -EINVAL; + + return *parent_rate / d * m / dout; +} + +static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, + unsigned long parent_rate) +{ + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); + unsigned int d, m, dout; + unsigned int reg; + unsigned long long tmp; + + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, ®); + dout = (reg & 0x3f) + ((reg >> 6) & 0x3f); + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, ®); + d = (reg & 0x3f) + ((reg >> 6) & 0x3f); + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, ®); + m = (reg & 0x3f) + ((reg >> 6) & 0x3f); + + if (d == 0 || dout == 0) + return 0; + + tmp = (unsigned long long)(parent_rate / d) * m; + do_div(tmp, dout); + + if (tmp > ULONG_MAX) + return ULONG_MAX; + + return tmp; +} + +static const struct clk_ops axi_clkgen_ops = { + .recalc_rate = axi_clkgen_recalc_rate, + .round_rate = axi_clkgen_round_rate, + .set_rate = axi_clkgen_set_rate, +}; + +static int axi_clkgen_probe(struct platform_device *pdev) +{ + struct axi_clkgen *axi_clkgen; + struct clk_init_data init; + const char *parent_name; + const char *clk_name; + struct resource *mem; + struct clk *clk; + + axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL); + if (!axi_clkgen) + return -ENOMEM; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(axi_clkgen->base)) + return PTR_ERR(axi_clkgen->base); + + parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0); + if (!parent_name) + return -EINVAL; + + clk_name = pdev->dev.of_node->name; + of_property_read_string(pdev->dev.of_node, "clock-output-names", + &clk_name); + + init.name = clk_name; + init.ops = &axi_clkgen_ops; + init.flags = 0; + init.parent_names = &parent_name; + init.num_parents = 1; + + axi_clkgen->clk_hw.init = &init; + clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, + clk); +} + +static int axi_clkgen_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + + return 0; +} + +static const struct of_device_id axi_clkgen_ids[] = { + { .compatible = "adi,axi-clkgen-1.00.a" }, + { }, +}; +MODULE_DEVICE_TABLE(of, axi_clkgen_ids); + +static struct platform_driver axi_clkgen_driver = { + .driver = { + .name = "adi-axi-clkgen", + .owner = THIS_MODULE, + .of_match_table = axi_clkgen_ids, + }, + .probe = axi_clkgen_probe, + .remove = axi_clkgen_remove, +}; +module_platform_driver(axi_clkgen_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Driver for the Analog Devices' AXI clkgen pcore clock generator"); -- cgit v1.2.3 From bd2953ebbb533aeda9b86c82a53d5197a9a38f1b Mon Sep 17 00:00:00 2001 From: Aristeu Rozanski Date: Fri, 15 Feb 2013 11:55:47 -0500 Subject: devcg: propagate local changes down the hierarchy This patch makes exception changes to propagate down in hierarchy respecting when possible local exceptions. New exceptions allowing additional access to devices won't be propagated, but it'll be possible to add an exception to access all of part of the newly allowed device(s). New exceptions disallowing access to devices will be propagated down and the local group's exceptions will be revalidated for the new situation. Example: A / \ B group behavior exceptions A allow "b 8:* rwm", "c 116:1 rw" B deny "c 1:3 rwm", "c 116:2 rwm", "b 3:* rwm" If a new exception is added to group A: # echo "c 116:* r" > A/devices.deny it'll propagate down and after revalidating B's local exceptions, the exception "c 116:2 rwm" will be removed. In case parent's exceptions change and local exceptions are not allowed anymore, they'll be deleted. v7: - do not allow behavior change when the cgroup has children - update documentation v6: fixed issues pointed by Serge Hallyn - only copy parent's exceptions while propagating behavior if the local behavior is different - while propagating exceptions, do not clear and copy parent's: it'd be against the premise we don't propagate access to more devices v5: fixed issues pointed by Serge Hallyn - updated documentation - not propagating when an exception is written to devices.allow - when propagating a new behavior, clean the local exceptions list if they're for a different behavior v4: fixed issues pointed by Tejun Heo - separated function to walk the tree and collect valid propagation targets v3: fixed issues pointed by Tejun Heo - update documentation - move css_online/css_offline changes to a new patch - use cgroup_for_each_descendant_pre() instead of own descendant walk - move exception_copy rework to a separared patch - move exception_clean rework to a separated patch v2: fixed issues pointed by Tejun Heo - instead of keeping the local settings that won't apply anymore, remove them Cc: Tejun Heo Cc: Serge Hallyn Signed-off-by: Aristeu Rozanski Signed-off-by: Tejun Heo --- Documentation/cgroups/devices.txt | 70 ++++++++++++++++++- security/device_cgroup.c | 139 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 199 insertions(+), 10 deletions(-) (limited to 'Documentation') diff --git a/Documentation/cgroups/devices.txt b/Documentation/cgroups/devices.txt index 16624a7f8222..3c1095ca02ea 100644 --- a/Documentation/cgroups/devices.txt +++ b/Documentation/cgroups/devices.txt @@ -13,9 +13,7 @@ either an integer or * for all. Access is a composition of r The root device cgroup starts with rwm to 'all'. A child device cgroup gets a copy of the parent. Administrators can then remove devices from the whitelist or add new entries. A child cgroup can -never receive a device access which is denied by its parent. However -when a device access is removed from a parent it will not also be -removed from the child(ren). +never receive a device access which is denied by its parent. 2. User Interface @@ -50,3 +48,69 @@ task to a new cgroup. (Again we'll probably want to change that). A cgroup may not be granted more permissions than the cgroup's parent has. + +4. Hierarchy + +device cgroups maintain hierarchy by making sure a cgroup never has more +access permissions than its parent. Every time an entry is written to +a cgroup's devices.deny file, all its children will have that entry removed +from their whitelist and all the locally set whitelist entries will be +re-evaluated. In case one of the locally set whitelist entries would provide +more access than the cgroup's parent, it'll be removed from the whitelist. + +Example: + A + / \ + B + + group behavior exceptions + A allow "b 8:* rwm", "c 116:1 rw" + B deny "c 1:3 rwm", "c 116:2 rwm", "b 3:* rwm" + +If a device is denied in group A: + # echo "c 116:* r" > A/devices.deny +it'll propagate down and after revalidating B's entries, the whitelist entry +"c 116:2 rwm" will be removed: + + group whitelist entries denied devices + A all "b 8:* rwm", "c 116:* rw" + B "c 1:3 rwm", "b 3:* rwm" all the rest + +In case parent's exceptions change and local exceptions are not allowed +anymore, they'll be deleted. + +Notice that new whitelist entries will not be propagated: + A + / \ + B + + group whitelist entries denied devices + A "c 1:3 rwm", "c 1:5 r" all the rest + B "c 1:3 rwm", "c 1:5 r" all the rest + +when adding "c *:3 rwm": + # echo "c *:3 rwm" >A/devices.allow + +the result: + group whitelist entries denied devices + A "c *:3 rwm", "c 1:5 r" all the rest + B "c 1:3 rwm", "c 1:5 r" all the rest + +but now it'll be possible to add new entries to B: + # echo "c 2:3 rwm" >B/devices.allow + # echo "c 50:3 r" >B/devices.allow +or even + # echo "c *:3 rwm" >B/devices.allow + +Allowing or denying all by writing 'a' to devices.allow or devices.deny will +not be possible once the device cgroups has children. + +4.1 Hierarchy (internal implementation) + +device cgroups is implemented internally using a behavior (ALLOW, DENY) and a +list of exceptions. The internal state is controlled using the same user +interface to preserve compatibility with the previous whitelist-only +implementation. Removal or addition of exceptions that will reduce the access +to devices will be propagated down the hierarchy. +For every propagated exception, the effective rules will be re-evaluated based +on current parent's access rules. diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 16c9e1069be6..221967d4690c 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -49,6 +49,8 @@ struct dev_cgroup { struct cgroup_subsys_state css; struct list_head exceptions; enum devcg_behavior behavior; + /* temporary list for pending propagation operations */ + struct list_head propagate_pending; }; static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s) @@ -185,6 +187,11 @@ static void dev_exception_clean(struct dev_cgroup *dev_cgroup) __dev_exception_clean(dev_cgroup); } +static inline bool is_devcg_online(const struct dev_cgroup *devcg) +{ + return (devcg->behavior != DEVCG_DEFAULT_NONE); +} + /** * devcgroup_online - initializes devcgroup's behavior and exceptions based on * parent's @@ -235,6 +242,7 @@ static struct cgroup_subsys_state *devcgroup_css_alloc(struct cgroup *cgroup) if (!dev_cgroup) return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(&dev_cgroup->exceptions); + INIT_LIST_HEAD(&dev_cgroup->propagate_pending); dev_cgroup->behavior = DEVCG_DEFAULT_NONE; parent_cgroup = cgroup->parent; @@ -413,6 +421,111 @@ static inline int may_allow_all(struct dev_cgroup *parent) return parent->behavior == DEVCG_DEFAULT_ALLOW; } +/** + * revalidate_active_exceptions - walks through the active exception list and + * revalidates the exceptions based on parent's + * behavior and exceptions. The exceptions that + * are no longer valid will be removed. + * Called with devcgroup_mutex held. + * @devcg: cgroup which exceptions will be checked + * + * This is one of the three key functions for hierarchy implementation. + * This function is responsible for re-evaluating all the cgroup's active + * exceptions due to a parent's exception change. + * Refer to Documentation/cgroups/devices.txt for more details. + */ +static void revalidate_active_exceptions(struct dev_cgroup *devcg) +{ + struct dev_exception_item *ex; + struct list_head *this, *tmp; + + list_for_each_safe(this, tmp, &devcg->exceptions) { + ex = container_of(this, struct dev_exception_item, list); + if (!parent_has_perm(devcg, ex)) + dev_exception_rm(devcg, ex); + } +} + +/** + * get_online_devcg - walks the cgroup tree and fills a list with the online + * groups + * @root: cgroup used as starting point + * @online: list that will be filled with online groups + * + * Must be called with devcgroup_mutex held. Grabs RCU lock. + * Because devcgroup_mutex is held, no devcg will become online or offline + * during the tree walk (see devcgroup_online, devcgroup_offline) + * A separated list is needed because propagate_behavior() and + * propagate_exception() need to allocate memory and can block. + */ +static void get_online_devcg(struct cgroup *root, struct list_head *online) +{ + struct cgroup *pos; + struct dev_cgroup *devcg; + + lockdep_assert_held(&devcgroup_mutex); + + rcu_read_lock(); + cgroup_for_each_descendant_pre(pos, root) { + devcg = cgroup_to_devcgroup(pos); + if (is_devcg_online(devcg)) + list_add_tail(&devcg->propagate_pending, online); + } + rcu_read_unlock(); +} + +/** + * propagate_exception - propagates a new exception to the children + * @devcg_root: device cgroup that added a new exception + * @ex: new exception to be propagated + * + * returns: 0 in case of success, != 0 in case of error + */ +static int propagate_exception(struct dev_cgroup *devcg_root, + struct dev_exception_item *ex) +{ + struct cgroup *root = devcg_root->css.cgroup; + struct dev_cgroup *devcg, *parent, *tmp; + int rc = 0; + LIST_HEAD(pending); + + get_online_devcg(root, &pending); + + list_for_each_entry_safe(devcg, tmp, &pending, propagate_pending) { + parent = cgroup_to_devcgroup(devcg->css.cgroup->parent); + + /* + * in case both root's behavior and devcg is allow, a new + * restriction means adding to the exception list + */ + if (devcg_root->behavior == DEVCG_DEFAULT_ALLOW && + devcg->behavior == DEVCG_DEFAULT_ALLOW) { + rc = dev_exception_add(devcg, ex); + if (rc) + break; + } else { + /* + * in the other possible cases: + * root's behavior: allow, devcg's: deny + * root's behavior: deny, devcg's: deny + * the exception will be removed + */ + dev_exception_rm(devcg, ex); + } + revalidate_active_exceptions(devcg); + + list_del_init(&devcg->propagate_pending); + } + return rc; +} + +static inline bool has_children(struct dev_cgroup *devcgroup) +{ + struct cgroup *cgrp = devcgroup->css.cgroup; + + return !list_empty(&cgrp->children); +} + /* * Modify the exception list using allow/deny rules. * CAP_SYS_ADMIN is needed for this. It's at least separate from CAP_MKNOD @@ -449,6 +562,9 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, case 'a': switch (filetype) { case DEVCG_ALLOW: + if (has_children(devcgroup)) + return -EINVAL; + if (!may_allow_all(parent)) return -EPERM; dev_exception_clean(devcgroup); @@ -462,6 +578,9 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, return rc; break; case DEVCG_DENY: + if (has_children(devcgroup)) + return -EINVAL; + dev_exception_clean(devcgroup); devcgroup->behavior = DEVCG_DEFAULT_DENY; break; @@ -556,22 +675,28 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, dev_exception_rm(devcgroup, &ex); return 0; } - return dev_exception_add(devcgroup, &ex); + rc = dev_exception_add(devcgroup, &ex); + break; case DEVCG_DENY: /* * If the default policy is to deny by default, try to remove * an matching exception instead. And be silent about it: we * don't want to break compatibility */ - if (devcgroup->behavior == DEVCG_DEFAULT_DENY) { + if (devcgroup->behavior == DEVCG_DEFAULT_DENY) dev_exception_rm(devcgroup, &ex); - return 0; - } - return dev_exception_add(devcgroup, &ex); + else + rc = dev_exception_add(devcgroup, &ex); + + if (rc) + break; + /* we only propagate new restrictions */ + rc = propagate_exception(devcgroup, &ex); + break; default: - return -EINVAL; + rc = -EINVAL; } - return 0; + return rc; } static int devcgroup_access_write(struct cgroup *cgrp, struct cftype *cft, -- cgit v1.2.3 From 5de8875281e1db024d67cbd5c792264194bfca2a Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Fri, 1 Mar 2013 12:37:53 +0100 Subject: crypto: sahara - Add driver for SAHARA2 accelerator. SAHARA2 HW module is included in the i.MX27 SoC from Freescale. It is capable of performing cipher algorithms such as AES, 3DES..., hashing and RNG too. This driver provides support for AES-CBC and AES-ECB by now. Reviewed-by: Arnd Bergmann Signed-off-by: Javier Martin Signed-off-by: Herbert Xu --- .../devicetree/bindings/crypto/fsl-imx-sahara.txt | 15 + drivers/crypto/Kconfig | 10 + drivers/crypto/Makefile | 1 + drivers/crypto/sahara.c | 1070 ++++++++++++++++++++ 4 files changed, 1096 insertions(+) create mode 100644 Documentation/devicetree/bindings/crypto/fsl-imx-sahara.txt create mode 100644 drivers/crypto/sahara.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/crypto/fsl-imx-sahara.txt b/Documentation/devicetree/bindings/crypto/fsl-imx-sahara.txt new file mode 100644 index 000000000000..5c65eccd0e56 --- /dev/null +++ b/Documentation/devicetree/bindings/crypto/fsl-imx-sahara.txt @@ -0,0 +1,15 @@ +Freescale SAHARA Cryptographic Accelerator included in some i.MX chips. +Currently only i.MX27 is supported. + +Required properties: +- compatible : Should be "fsl,-sahara" +- reg : Should contain SAHARA registers location and length +- interrupts : Should contain SAHARA interrupt number + +Example: + +sah@10025000 { + compatible = "fsl,imx27-sahara"; + reg = < 0x10025000 0x800>; + interrupts = <75>; +}; diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index e66fb0a332d4..dffb85525368 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -276,6 +276,16 @@ config CRYPTO_DEV_PICOXCELL Saying m here will build a module named pipcoxcell_crypto. +config CRYPTO_DEV_SAHARA + tristate "Support for SAHARA crypto accelerator" + depends on ARCH_MXC && EXPERIMENTAL && OF + select CRYPTO_BLKCIPHER + select CRYPTO_AES + select CRYPTO_ECB + help + This option enables support for the SAHARA HW crypto accelerator + found in some Freescale i.MX chips. + config CRYPTO_DEV_S5P tristate "Support for Samsung S5PV210 crypto accelerator" depends on ARCH_S5PV210 diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index 880a47b0b023..38ce13d3b79b 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_CRYPTO_DEV_PPC4XX) += amcc/ obj-$(CONFIG_CRYPTO_DEV_OMAP_SHAM) += omap-sham.o obj-$(CONFIG_CRYPTO_DEV_OMAP_AES) += omap-aes.o obj-$(CONFIG_CRYPTO_DEV_PICOXCELL) += picoxcell_crypto.o +obj-$(CONFIG_CRYPTO_DEV_SAHARA) += sahara.o obj-$(CONFIG_CRYPTO_DEV_S5P) += s5p-sss.o obj-$(CONFIG_CRYPTO_DEV_TEGRA_AES) += tegra-aes.o obj-$(CONFIG_CRYPTO_DEV_UX500) += ux500/ diff --git a/drivers/crypto/sahara.c b/drivers/crypto/sahara.c new file mode 100644 index 000000000000..a97bb6c1596c --- /dev/null +++ b/drivers/crypto/sahara.c @@ -0,0 +1,1070 @@ +/* + * Cryptographic API. + * + * Support for SAHARA cryptographic accelerator. + * + * Copyright (c) 2013 Vista Silicon S.L. + * Author: Javier Martin + * + * 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. + * + * Based on omap-aes.c and tegra-aes.c + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SAHARA_NAME "sahara" +#define SAHARA_VERSION_3 3 +#define SAHARA_TIMEOUT_MS 1000 +#define SAHARA_MAX_HW_DESC 2 +#define SAHARA_MAX_HW_LINK 20 + +#define FLAGS_MODE_MASK 0x000f +#define FLAGS_ENCRYPT BIT(0) +#define FLAGS_CBC BIT(1) +#define FLAGS_NEW_KEY BIT(3) +#define FLAGS_BUSY 4 + +#define SAHARA_HDR_BASE 0x00800000 +#define SAHARA_HDR_SKHA_ALG_AES 0 +#define SAHARA_HDR_SKHA_OP_ENC (1 << 2) +#define SAHARA_HDR_SKHA_MODE_ECB (0 << 3) +#define SAHARA_HDR_SKHA_MODE_CBC (1 << 3) +#define SAHARA_HDR_FORM_DATA (5 << 16) +#define SAHARA_HDR_FORM_KEY (8 << 16) +#define SAHARA_HDR_LLO (1 << 24) +#define SAHARA_HDR_CHA_SKHA (1 << 28) +#define SAHARA_HDR_CHA_MDHA (2 << 28) +#define SAHARA_HDR_PARITY_BIT (1 << 31) + +/* SAHARA can only process one request at a time */ +#define SAHARA_QUEUE_LENGTH 1 + +#define SAHARA_REG_VERSION 0x00 +#define SAHARA_REG_DAR 0x04 +#define SAHARA_REG_CONTROL 0x08 +#define SAHARA_CONTROL_SET_THROTTLE(x) (((x) & 0xff) << 24) +#define SAHARA_CONTROL_SET_MAXBURST(x) (((x) & 0xff) << 16) +#define SAHARA_CONTROL_RNG_AUTORSD (1 << 7) +#define SAHARA_CONTROL_ENABLE_INT (1 << 4) +#define SAHARA_REG_CMD 0x0C +#define SAHARA_CMD_RESET (1 << 0) +#define SAHARA_CMD_CLEAR_INT (1 << 8) +#define SAHARA_CMD_CLEAR_ERR (1 << 9) +#define SAHARA_CMD_SINGLE_STEP (1 << 10) +#define SAHARA_CMD_MODE_BATCH (1 << 16) +#define SAHARA_CMD_MODE_DEBUG (1 << 18) +#define SAHARA_REG_STATUS 0x10 +#define SAHARA_STATUS_GET_STATE(x) ((x) & 0x7) +#define SAHARA_STATE_IDLE 0 +#define SAHARA_STATE_BUSY 1 +#define SAHARA_STATE_ERR 2 +#define SAHARA_STATE_FAULT 3 +#define SAHARA_STATE_COMPLETE 4 +#define SAHARA_STATE_COMP_FLAG (1 << 2) +#define SAHARA_STATUS_DAR_FULL (1 << 3) +#define SAHARA_STATUS_ERROR (1 << 4) +#define SAHARA_STATUS_SECURE (1 << 5) +#define SAHARA_STATUS_FAIL (1 << 6) +#define SAHARA_STATUS_INIT (1 << 7) +#define SAHARA_STATUS_RNG_RESEED (1 << 8) +#define SAHARA_STATUS_ACTIVE_RNG (1 << 9) +#define SAHARA_STATUS_ACTIVE_MDHA (1 << 10) +#define SAHARA_STATUS_ACTIVE_SKHA (1 << 11) +#define SAHARA_STATUS_MODE_BATCH (1 << 16) +#define SAHARA_STATUS_MODE_DEDICATED (1 << 17) +#define SAHARA_STATUS_MODE_DEBUG (1 << 18) +#define SAHARA_STATUS_GET_ISTATE(x) (((x) >> 24) & 0xff) +#define SAHARA_REG_ERRSTATUS 0x14 +#define SAHARA_ERRSTATUS_GET_SOURCE(x) ((x) & 0xf) +#define SAHARA_ERRSOURCE_CHA 14 +#define SAHARA_ERRSOURCE_DMA 15 +#define SAHARA_ERRSTATUS_DMA_DIR (1 << 8) +#define SAHARA_ERRSTATUS_GET_DMASZ(x)(((x) >> 9) & 0x3) +#define SAHARA_ERRSTATUS_GET_DMASRC(x) (((x) >> 13) & 0x7) +#define SAHARA_ERRSTATUS_GET_CHASRC(x) (((x) >> 16) & 0xfff) +#define SAHARA_ERRSTATUS_GET_CHAERR(x) (((x) >> 28) & 0x3) +#define SAHARA_REG_FADDR 0x18 +#define SAHARA_REG_CDAR 0x1C +#define SAHARA_REG_IDAR 0x20 + +struct sahara_hw_desc { + u32 hdr; + u32 len1; + dma_addr_t p1; + u32 len2; + dma_addr_t p2; + dma_addr_t next; +}; + +struct sahara_hw_link { + u32 len; + dma_addr_t p; + dma_addr_t next; +}; + +struct sahara_ctx { + struct sahara_dev *dev; + unsigned long flags; + int keylen; + u8 key[AES_KEYSIZE_128]; + struct crypto_ablkcipher *fallback; +}; + +struct sahara_aes_reqctx { + unsigned long mode; +}; + +struct sahara_dev { + struct device *device; + void __iomem *regs_base; + struct clk *clk_ipg; + struct clk *clk_ahb; + + struct sahara_ctx *ctx; + spinlock_t lock; + struct crypto_queue queue; + unsigned long flags; + + struct tasklet_struct done_task; + struct tasklet_struct queue_task; + + struct sahara_hw_desc *hw_desc[SAHARA_MAX_HW_DESC]; + dma_addr_t hw_phys_desc[SAHARA_MAX_HW_DESC]; + + u8 *key_base; + dma_addr_t key_phys_base; + + u8 *iv_base; + dma_addr_t iv_phys_base; + + struct sahara_hw_link *hw_link[SAHARA_MAX_HW_LINK]; + dma_addr_t hw_phys_link[SAHARA_MAX_HW_LINK]; + + struct ablkcipher_request *req; + size_t total; + struct scatterlist *in_sg; + unsigned int nb_in_sg; + struct scatterlist *out_sg; + unsigned int nb_out_sg; + + u32 error; + struct timer_list watchdog; +}; + +static struct sahara_dev *dev_ptr; + +static inline void sahara_write(struct sahara_dev *dev, u32 data, u32 reg) +{ + writel(data, dev->regs_base + reg); +} + +static inline unsigned int sahara_read(struct sahara_dev *dev, u32 reg) +{ + return readl(dev->regs_base + reg); +} + +static u32 sahara_aes_key_hdr(struct sahara_dev *dev) +{ + u32 hdr = SAHARA_HDR_BASE | SAHARA_HDR_SKHA_ALG_AES | + SAHARA_HDR_FORM_KEY | SAHARA_HDR_LLO | + SAHARA_HDR_CHA_SKHA | SAHARA_HDR_PARITY_BIT; + + if (dev->flags & FLAGS_CBC) { + hdr |= SAHARA_HDR_SKHA_MODE_CBC; + hdr ^= SAHARA_HDR_PARITY_BIT; + } + + if (dev->flags & FLAGS_ENCRYPT) { + hdr |= SAHARA_HDR_SKHA_OP_ENC; + hdr ^= SAHARA_HDR_PARITY_BIT; + } + + return hdr; +} + +static u32 sahara_aes_data_link_hdr(struct sahara_dev *dev) +{ + return SAHARA_HDR_BASE | SAHARA_HDR_FORM_DATA | + SAHARA_HDR_CHA_SKHA | SAHARA_HDR_PARITY_BIT; +} + +static int sahara_sg_length(struct scatterlist *sg, + unsigned int total) +{ + int sg_nb; + unsigned int len; + struct scatterlist *sg_list; + + sg_nb = 0; + sg_list = sg; + + while (total) { + len = min(sg_list->length, total); + + sg_nb++; + total -= len; + + sg_list = sg_next(sg_list); + if (!sg_list) + total = 0; + } + + return sg_nb; +} + +static char *sahara_err_src[16] = { + "No error", + "Header error", + "Descriptor length error", + "Descriptor length or pointer error", + "Link length error", + "Link pointer error", + "Input buffer error", + "Output buffer error", + "Output buffer starvation", + "Internal state fault", + "General descriptor problem", + "Reserved", + "Descriptor address error", + "Link address error", + "CHA error", + "DMA error" +}; + +static char *sahara_err_dmasize[4] = { + "Byte transfer", + "Half-word transfer", + "Word transfer", + "Reserved" +}; + +static char *sahara_err_dmasrc[8] = { + "No error", + "AHB bus error", + "Internal IP bus error", + "Parity error", + "DMA crosses 256 byte boundary", + "DMA is busy", + "Reserved", + "DMA HW error" +}; + +static char *sahara_cha_errsrc[12] = { + "Input buffer non-empty", + "Illegal address", + "Illegal mode", + "Illegal data size", + "Illegal key size", + "Write during processing", + "CTX read during processing", + "HW error", + "Input buffer disabled/underflow", + "Output buffer disabled/overflow", + "DES key parity error", + "Reserved" +}; + +static char *sahara_cha_err[4] = { "No error", "SKHA", "MDHA", "RNG" }; + +static void sahara_decode_error(struct sahara_dev *dev, unsigned int error) +{ + u8 source = SAHARA_ERRSTATUS_GET_SOURCE(error); + u16 chasrc = ffs(SAHARA_ERRSTATUS_GET_CHASRC(error)); + + dev_err(dev->device, "%s: Error Register = 0x%08x\n", __func__, error); + + dev_err(dev->device, " - %s.\n", sahara_err_src[source]); + + if (source == SAHARA_ERRSOURCE_DMA) { + if (error & SAHARA_ERRSTATUS_DMA_DIR) + dev_err(dev->device, " * DMA read.\n"); + else + dev_err(dev->device, " * DMA write.\n"); + + dev_err(dev->device, " * %s.\n", + sahara_err_dmasize[SAHARA_ERRSTATUS_GET_DMASZ(error)]); + dev_err(dev->device, " * %s.\n", + sahara_err_dmasrc[SAHARA_ERRSTATUS_GET_DMASRC(error)]); + } else if (source == SAHARA_ERRSOURCE_CHA) { + dev_err(dev->device, " * %s.\n", + sahara_cha_errsrc[chasrc]); + dev_err(dev->device, " * %s.\n", + sahara_cha_err[SAHARA_ERRSTATUS_GET_CHAERR(error)]); + } + dev_err(dev->device, "\n"); +} + +static char *sahara_state[4] = { "Idle", "Busy", "Error", "HW Fault" }; + +static void sahara_decode_status(struct sahara_dev *dev, unsigned int status) +{ + u8 state; + + if (!IS_ENABLED(DEBUG)) + return; + + state = SAHARA_STATUS_GET_STATE(status); + + dev_dbg(dev->device, "%s: Status Register = 0x%08x\n", + __func__, status); + + dev_dbg(dev->device, " - State = %d:\n", state); + if (state & SAHARA_STATE_COMP_FLAG) + dev_dbg(dev->device, " * Descriptor completed. IRQ pending.\n"); + + dev_dbg(dev->device, " * %s.\n", + sahara_state[state & ~SAHARA_STATE_COMP_FLAG]); + + if (status & SAHARA_STATUS_DAR_FULL) + dev_dbg(dev->device, " - DAR Full.\n"); + if (status & SAHARA_STATUS_ERROR) + dev_dbg(dev->device, " - Error.\n"); + if (status & SAHARA_STATUS_SECURE) + dev_dbg(dev->device, " - Secure.\n"); + if (status & SAHARA_STATUS_FAIL) + dev_dbg(dev->device, " - Fail.\n"); + if (status & SAHARA_STATUS_RNG_RESEED) + dev_dbg(dev->device, " - RNG Reseed Request.\n"); + if (status & SAHARA_STATUS_ACTIVE_RNG) + dev_dbg(dev->device, " - RNG Active.\n"); + if (status & SAHARA_STATUS_ACTIVE_MDHA) + dev_dbg(dev->device, " - MDHA Active.\n"); + if (status & SAHARA_STATUS_ACTIVE_SKHA) + dev_dbg(dev->device, " - SKHA Active.\n"); + + if (status & SAHARA_STATUS_MODE_BATCH) + dev_dbg(dev->device, " - Batch Mode.\n"); + else if (status & SAHARA_STATUS_MODE_DEDICATED) + dev_dbg(dev->device, " - Decidated Mode.\n"); + else if (status & SAHARA_STATUS_MODE_DEBUG) + dev_dbg(dev->device, " - Debug Mode.\n"); + + dev_dbg(dev->device, " - Internal state = 0x%02x\n", + SAHARA_STATUS_GET_ISTATE(status)); + + dev_dbg(dev->device, "Current DAR: 0x%08x\n", + sahara_read(dev, SAHARA_REG_CDAR)); + dev_dbg(dev->device, "Initial DAR: 0x%08x\n\n", + sahara_read(dev, SAHARA_REG_IDAR)); +} + +static void sahara_dump_descriptors(struct sahara_dev *dev) +{ + int i; + + if (!IS_ENABLED(DEBUG)) + return; + + for (i = 0; i < SAHARA_MAX_HW_DESC; i++) { + dev_dbg(dev->device, "Descriptor (%d) (0x%08x):\n", + i, dev->hw_phys_desc[i]); + dev_dbg(dev->device, "\thdr = 0x%08x\n", dev->hw_desc[i]->hdr); + dev_dbg(dev->device, "\tlen1 = %u\n", dev->hw_desc[i]->len1); + dev_dbg(dev->device, "\tp1 = 0x%08x\n", dev->hw_desc[i]->p1); + dev_dbg(dev->device, "\tlen2 = %u\n", dev->hw_desc[i]->len2); + dev_dbg(dev->device, "\tp2 = 0x%08x\n", dev->hw_desc[i]->p2); + dev_dbg(dev->device, "\tnext = 0x%08x\n", + dev->hw_desc[i]->next); + } + dev_dbg(dev->device, "\n"); +} + +static void sahara_dump_links(struct sahara_dev *dev) +{ + int i; + + if (!IS_ENABLED(DEBUG)) + return; + + for (i = 0; i < SAHARA_MAX_HW_LINK; i++) { + dev_dbg(dev->device, "Link (%d) (0x%08x):\n", + i, dev->hw_phys_link[i]); + dev_dbg(dev->device, "\tlen = %u\n", dev->hw_link[i]->len); + dev_dbg(dev->device, "\tp = 0x%08x\n", dev->hw_link[i]->p); + dev_dbg(dev->device, "\tnext = 0x%08x\n", + dev->hw_link[i]->next); + } + dev_dbg(dev->device, "\n"); +} + +static void sahara_aes_done_task(unsigned long data) +{ + struct sahara_dev *dev = (struct sahara_dev *)data; + + dma_unmap_sg(dev->device, dev->out_sg, dev->nb_out_sg, + DMA_TO_DEVICE); + dma_unmap_sg(dev->device, dev->in_sg, dev->nb_in_sg, + DMA_FROM_DEVICE); + + spin_lock(&dev->lock); + clear_bit(FLAGS_BUSY, &dev->flags); + spin_unlock(&dev->lock); + + dev->req->base.complete(&dev->req->base, dev->error); +} + +void sahara_watchdog(unsigned long data) +{ + struct sahara_dev *dev = (struct sahara_dev *)data; + unsigned int err = sahara_read(dev, SAHARA_REG_ERRSTATUS); + unsigned int stat = sahara_read(dev, SAHARA_REG_STATUS); + + sahara_decode_status(dev, stat); + sahara_decode_error(dev, err); + dev->error = -ETIMEDOUT; + sahara_aes_done_task(data); +} + +static int sahara_hw_descriptor_create(struct sahara_dev *dev) +{ + struct sahara_ctx *ctx = dev->ctx; + struct scatterlist *sg; + int ret; + int i, j; + + /* Copy new key if necessary */ + if (ctx->flags & FLAGS_NEW_KEY) { + memcpy(dev->key_base, ctx->key, ctx->keylen); + ctx->flags &= ~FLAGS_NEW_KEY; + + if (dev->flags & FLAGS_CBC) { + dev->hw_desc[0]->len1 = AES_BLOCK_SIZE; + dev->hw_desc[0]->p1 = dev->iv_phys_base; + } else { + dev->hw_desc[0]->len1 = 0; + dev->hw_desc[0]->p1 = 0; + } + dev->hw_desc[0]->len2 = ctx->keylen; + dev->hw_desc[0]->p2 = dev->key_phys_base; + dev->hw_desc[0]->next = dev->hw_phys_desc[1]; + } + dev->hw_desc[0]->hdr = sahara_aes_key_hdr(dev); + + dev->nb_in_sg = sahara_sg_length(dev->in_sg, dev->total); + dev->nb_out_sg = sahara_sg_length(dev->out_sg, dev->total); + if ((dev->nb_in_sg + dev->nb_out_sg) > SAHARA_MAX_HW_LINK) { + dev_err(dev->device, "not enough hw links (%d)\n", + dev->nb_in_sg + dev->nb_out_sg); + return -EINVAL; + } + + ret = dma_map_sg(dev->device, dev->in_sg, dev->nb_in_sg, + DMA_TO_DEVICE); + if (ret != dev->nb_in_sg) { + dev_err(dev->device, "couldn't map in sg\n"); + goto unmap_in; + } + ret = dma_map_sg(dev->device, dev->out_sg, dev->nb_out_sg, + DMA_FROM_DEVICE); + if (ret != dev->nb_out_sg) { + dev_err(dev->device, "couldn't map out sg\n"); + goto unmap_out; + } + + /* Create input links */ + dev->hw_desc[1]->p1 = dev->hw_phys_link[0]; + sg = dev->in_sg; + for (i = 0; i < dev->nb_in_sg; i++) { + dev->hw_link[i]->len = sg->length; + dev->hw_link[i]->p = sg->dma_address; + if (i == (dev->nb_in_sg - 1)) { + dev->hw_link[i]->next = 0; + } else { + dev->hw_link[i]->next = dev->hw_phys_link[i + 1]; + sg = sg_next(sg); + } + } + + /* Create output links */ + dev->hw_desc[1]->p2 = dev->hw_phys_link[i]; + sg = dev->out_sg; + for (j = i; j < dev->nb_out_sg + i; j++) { + dev->hw_link[j]->len = sg->length; + dev->hw_link[j]->p = sg->dma_address; + if (j == (dev->nb_out_sg + i - 1)) { + dev->hw_link[j]->next = 0; + } else { + dev->hw_link[j]->next = dev->hw_phys_link[j + 1]; + sg = sg_next(sg); + } + } + + /* Fill remaining fields of hw_desc[1] */ + dev->hw_desc[1]->hdr = sahara_aes_data_link_hdr(dev); + dev->hw_desc[1]->len1 = dev->total; + dev->hw_desc[1]->len2 = dev->total; + dev->hw_desc[1]->next = 0; + + sahara_dump_descriptors(dev); + sahara_dump_links(dev); + + /* Start processing descriptor chain. */ + mod_timer(&dev->watchdog, + jiffies + msecs_to_jiffies(SAHARA_TIMEOUT_MS)); + sahara_write(dev, dev->hw_phys_desc[0], SAHARA_REG_DAR); + + return 0; + +unmap_out: + dma_unmap_sg(dev->device, dev->out_sg, dev->nb_out_sg, + DMA_TO_DEVICE); +unmap_in: + dma_unmap_sg(dev->device, dev->in_sg, dev->nb_in_sg, + DMA_FROM_DEVICE); + + return -EINVAL; +} + +static void sahara_aes_queue_task(unsigned long data) +{ + struct sahara_dev *dev = (struct sahara_dev *)data; + struct crypto_async_request *async_req, *backlog; + struct sahara_ctx *ctx; + struct sahara_aes_reqctx *rctx; + struct ablkcipher_request *req; + int ret; + + spin_lock(&dev->lock); + backlog = crypto_get_backlog(&dev->queue); + async_req = crypto_dequeue_request(&dev->queue); + if (!async_req) + clear_bit(FLAGS_BUSY, &dev->flags); + spin_unlock(&dev->lock); + + if (!async_req) + return; + + if (backlog) + backlog->complete(backlog, -EINPROGRESS); + + req = ablkcipher_request_cast(async_req); + + /* Request is ready to be dispatched by the device */ + dev_dbg(dev->device, + "dispatch request (nbytes=%d, src=%p, dst=%p)\n", + req->nbytes, req->src, req->dst); + + /* assign new request to device */ + dev->req = req; + dev->total = req->nbytes; + dev->in_sg = req->src; + dev->out_sg = req->dst; + + rctx = ablkcipher_request_ctx(req); + ctx = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req)); + rctx->mode &= FLAGS_MODE_MASK; + dev->flags = (dev->flags & ~FLAGS_MODE_MASK) | rctx->mode; + + if ((dev->flags & FLAGS_CBC) && req->info) + memcpy(dev->iv_base, req->info, AES_KEYSIZE_128); + + /* assign new context to device */ + ctx->dev = dev; + dev->ctx = ctx; + + ret = sahara_hw_descriptor_create(dev); + if (ret < 0) { + spin_lock(&dev->lock); + clear_bit(FLAGS_BUSY, &dev->flags); + spin_unlock(&dev->lock); + dev->req->base.complete(&dev->req->base, ret); + } +} + +static int sahara_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key, + unsigned int keylen) +{ + struct sahara_ctx *ctx = crypto_ablkcipher_ctx(tfm); + int ret; + + ctx->keylen = keylen; + + /* SAHARA only supports 128bit keys */ + if (keylen == AES_KEYSIZE_128) { + memcpy(ctx->key, key, keylen); + ctx->flags |= FLAGS_NEW_KEY; + return 0; + } + + if (keylen != AES_KEYSIZE_128 && + keylen != AES_KEYSIZE_192 && keylen != AES_KEYSIZE_256) + return -EINVAL; + + /* + * The requested key size is not supported by HW, do a fallback. + */ + ctx->fallback->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK; + ctx->fallback->base.crt_flags |= + (tfm->base.crt_flags & CRYPTO_TFM_REQ_MASK); + + ret = crypto_ablkcipher_setkey(ctx->fallback, key, keylen); + if (ret) { + struct crypto_tfm *tfm_aux = crypto_ablkcipher_tfm(tfm); + + tfm_aux->crt_flags &= ~CRYPTO_TFM_RES_MASK; + tfm_aux->crt_flags |= + (ctx->fallback->base.crt_flags & CRYPTO_TFM_RES_MASK); + } + return ret; +} + +static int sahara_aes_crypt(struct ablkcipher_request *req, unsigned long mode) +{ + struct sahara_ctx *ctx = crypto_ablkcipher_ctx( + crypto_ablkcipher_reqtfm(req)); + struct sahara_aes_reqctx *rctx = ablkcipher_request_ctx(req); + struct sahara_dev *dev = dev_ptr; + int err = 0; + int busy; + + dev_dbg(dev->device, "nbytes: %d, enc: %d, cbc: %d\n", + req->nbytes, !!(mode & FLAGS_ENCRYPT), !!(mode & FLAGS_CBC)); + + if (!IS_ALIGNED(req->nbytes, AES_BLOCK_SIZE)) { + dev_err(dev->device, + "request size is not exact amount of AES blocks\n"); + return -EINVAL; + } + + ctx->dev = dev; + + rctx->mode = mode; + spin_lock_bh(&dev->lock); + err = ablkcipher_enqueue_request(&dev->queue, req); + busy = test_and_set_bit(FLAGS_BUSY, &dev->flags); + spin_unlock_bh(&dev->lock); + + if (!busy) + tasklet_schedule(&dev->queue_task); + + return err; +} + +static int sahara_aes_ecb_encrypt(struct ablkcipher_request *req) +{ + struct crypto_tfm *tfm = + crypto_ablkcipher_tfm(crypto_ablkcipher_reqtfm(req)); + struct sahara_ctx *ctx = crypto_ablkcipher_ctx( + crypto_ablkcipher_reqtfm(req)); + int err; + + if (unlikely(ctx->keylen != AES_KEYSIZE_128)) { + ablkcipher_request_set_tfm(req, ctx->fallback); + err = crypto_ablkcipher_encrypt(req); + ablkcipher_request_set_tfm(req, __crypto_ablkcipher_cast(tfm)); + return err; + } + + return sahara_aes_crypt(req, FLAGS_ENCRYPT); +} + +static int sahara_aes_ecb_decrypt(struct ablkcipher_request *req) +{ + struct crypto_tfm *tfm = + crypto_ablkcipher_tfm(crypto_ablkcipher_reqtfm(req)); + struct sahara_ctx *ctx = crypto_ablkcipher_ctx( + crypto_ablkcipher_reqtfm(req)); + int err; + + if (unlikely(ctx->keylen != AES_KEYSIZE_128)) { + ablkcipher_request_set_tfm(req, ctx->fallback); + err = crypto_ablkcipher_decrypt(req); + ablkcipher_request_set_tfm(req, __crypto_ablkcipher_cast(tfm)); + return err; + } + + return sahara_aes_crypt(req, 0); +} + +static int sahara_aes_cbc_encrypt(struct ablkcipher_request *req) +{ + struct crypto_tfm *tfm = + crypto_ablkcipher_tfm(crypto_ablkcipher_reqtfm(req)); + struct sahara_ctx *ctx = crypto_ablkcipher_ctx( + crypto_ablkcipher_reqtfm(req)); + int err; + + if (unlikely(ctx->keylen != AES_KEYSIZE_128)) { + ablkcipher_request_set_tfm(req, ctx->fallback); + err = crypto_ablkcipher_encrypt(req); + ablkcipher_request_set_tfm(req, __crypto_ablkcipher_cast(tfm)); + return err; + } + + return sahara_aes_crypt(req, FLAGS_ENCRYPT | FLAGS_CBC); +} + +static int sahara_aes_cbc_decrypt(struct ablkcipher_request *req) +{ + struct crypto_tfm *tfm = + crypto_ablkcipher_tfm(crypto_ablkcipher_reqtfm(req)); + struct sahara_ctx *ctx = crypto_ablkcipher_ctx( + crypto_ablkcipher_reqtfm(req)); + int err; + + if (unlikely(ctx->keylen != AES_KEYSIZE_128)) { + ablkcipher_request_set_tfm(req, ctx->fallback); + err = crypto_ablkcipher_decrypt(req); + ablkcipher_request_set_tfm(req, __crypto_ablkcipher_cast(tfm)); + return err; + } + + return sahara_aes_crypt(req, FLAGS_CBC); +} + +static int sahara_aes_cra_init(struct crypto_tfm *tfm) +{ + const char *name = tfm->__crt_alg->cra_name; + struct sahara_ctx *ctx = crypto_tfm_ctx(tfm); + + ctx->fallback = crypto_alloc_ablkcipher(name, 0, + CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK); + if (IS_ERR(ctx->fallback)) { + pr_err("Error allocating fallback algo %s\n", name); + return PTR_ERR(ctx->fallback); + } + + tfm->crt_ablkcipher.reqsize = sizeof(struct sahara_aes_reqctx); + + return 0; +} + +static void sahara_aes_cra_exit(struct crypto_tfm *tfm) +{ + struct sahara_ctx *ctx = crypto_tfm_ctx(tfm); + + if (ctx->fallback) + crypto_free_ablkcipher(ctx->fallback); + ctx->fallback = NULL; +} + +static struct crypto_alg aes_algs[] = { +{ + .cra_name = "ecb(aes)", + .cra_driver_name = "sahara-ecb-aes", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | + CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct sahara_ctx), + .cra_alignmask = 0x0, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = sahara_aes_cra_init, + .cra_exit = sahara_aes_cra_exit, + .cra_u.ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE , + .max_keysize = AES_MAX_KEY_SIZE, + .setkey = sahara_aes_setkey, + .encrypt = sahara_aes_ecb_encrypt, + .decrypt = sahara_aes_ecb_decrypt, + } +}, { + .cra_name = "cbc(aes)", + .cra_driver_name = "sahara-cbc-aes", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | + CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct sahara_ctx), + .cra_alignmask = 0x0, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = sahara_aes_cra_init, + .cra_exit = sahara_aes_cra_exit, + .cra_u.ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE , + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = sahara_aes_setkey, + .encrypt = sahara_aes_cbc_encrypt, + .decrypt = sahara_aes_cbc_decrypt, + } +} +}; + +static irqreturn_t sahara_irq_handler(int irq, void *data) +{ + struct sahara_dev *dev = (struct sahara_dev *)data; + unsigned int stat = sahara_read(dev, SAHARA_REG_STATUS); + unsigned int err = sahara_read(dev, SAHARA_REG_ERRSTATUS); + + del_timer(&dev->watchdog); + + sahara_write(dev, SAHARA_CMD_CLEAR_INT | SAHARA_CMD_CLEAR_ERR, + SAHARA_REG_CMD); + + sahara_decode_status(dev, stat); + + if (SAHARA_STATUS_GET_STATE(stat) == SAHARA_STATE_BUSY) { + return IRQ_NONE; + } else if (SAHARA_STATUS_GET_STATE(stat) == SAHARA_STATE_COMPLETE) { + dev->error = 0; + } else { + sahara_decode_error(dev, err); + dev->error = -EINVAL; + } + + tasklet_schedule(&dev->done_task); + + return IRQ_HANDLED; +} + + +static int sahara_register_algs(struct sahara_dev *dev) +{ + int err, i, j; + + for (i = 0; i < ARRAY_SIZE(aes_algs); i++) { + INIT_LIST_HEAD(&aes_algs[i].cra_list); + err = crypto_register_alg(&aes_algs[i]); + if (err) + goto err_aes_algs; + } + + return 0; + +err_aes_algs: + for (j = 0; j < i; j++) + crypto_unregister_alg(&aes_algs[j]); + + return err; +} + +static void sahara_unregister_algs(struct sahara_dev *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(aes_algs); i++) + crypto_unregister_alg(&aes_algs[i]); +} + +static struct platform_device_id sahara_platform_ids[] = { + { .name = "sahara-imx27" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, sahara_platform_ids); + +static struct of_device_id sahara_dt_ids[] = { + { .compatible = "fsl,imx27-sahara" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, sahara_dt_ids); + +static int sahara_probe(struct platform_device *pdev) +{ + struct sahara_dev *dev; + struct resource *res; + u32 version; + int irq; + int err; + int i; + + dev = devm_kzalloc(&pdev->dev, sizeof(struct sahara_dev), GFP_KERNEL); + if (dev == NULL) { + dev_err(&pdev->dev, "unable to alloc data struct.\n"); + return -ENOMEM; + } + + dev->device = &pdev->dev; + platform_set_drvdata(pdev, dev); + + /* Get the base address */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get memory region resource\n"); + return -ENODEV; + } + + if (devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), SAHARA_NAME) == NULL) { + dev_err(&pdev->dev, "failed to request memory region\n"); + return -ENOENT; + } + dev->regs_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!dev->regs_base) { + dev_err(&pdev->dev, "failed to ioremap address region\n"); + return -ENOENT; + } + + /* Get the IRQ */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get irq resource\n"); + return irq; + } + + if (devm_request_irq(&pdev->dev, irq, sahara_irq_handler, + 0, SAHARA_NAME, dev) < 0) { + dev_err(&pdev->dev, "failed to request irq\n"); + return -ENOENT; + } + + /* clocks */ + dev->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(dev->clk_ipg)) { + dev_err(&pdev->dev, "Could not get ipg clock\n"); + return PTR_ERR(dev->clk_ipg); + } + + dev->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(dev->clk_ahb)) { + dev_err(&pdev->dev, "Could not get ahb clock\n"); + return PTR_ERR(dev->clk_ahb); + } + + /* Allocate HW descriptors */ + dev->hw_desc[0] = dma_alloc_coherent(&pdev->dev, + SAHARA_MAX_HW_DESC * sizeof(struct sahara_hw_desc), + &dev->hw_phys_desc[0], GFP_KERNEL); + if (!dev->hw_desc[0]) { + dev_err(&pdev->dev, "Could not allocate hw descriptors\n"); + return -ENOMEM; + } + dev->hw_desc[1] = dev->hw_desc[0] + 1; + dev->hw_phys_desc[1] = dev->hw_phys_desc[0] + + sizeof(struct sahara_hw_desc); + + /* Allocate space for iv and key */ + dev->key_base = dma_alloc_coherent(&pdev->dev, 2 * AES_KEYSIZE_128, + &dev->key_phys_base, GFP_KERNEL); + if (!dev->key_base) { + dev_err(&pdev->dev, "Could not allocate memory for key\n"); + err = -ENOMEM; + goto err_key; + } + dev->iv_base = dev->key_base + AES_KEYSIZE_128; + dev->iv_phys_base = dev->key_phys_base + AES_KEYSIZE_128; + + /* Allocate space for HW links */ + dev->hw_link[0] = dma_alloc_coherent(&pdev->dev, + SAHARA_MAX_HW_LINK * sizeof(struct sahara_hw_link), + &dev->hw_phys_link[0], GFP_KERNEL); + if (!dev->hw_link) { + dev_err(&pdev->dev, "Could not allocate hw links\n"); + err = -ENOMEM; + goto err_link; + } + for (i = 1; i < SAHARA_MAX_HW_LINK; i++) { + dev->hw_phys_link[i] = dev->hw_phys_link[i - 1] + + sizeof(struct sahara_hw_link); + dev->hw_link[i] = dev->hw_link[i - 1] + 1; + } + + crypto_init_queue(&dev->queue, SAHARA_QUEUE_LENGTH); + + dev_ptr = dev; + + tasklet_init(&dev->queue_task, sahara_aes_queue_task, + (unsigned long)dev); + tasklet_init(&dev->done_task, sahara_aes_done_task, + (unsigned long)dev); + + init_timer(&dev->watchdog); + dev->watchdog.function = &sahara_watchdog; + dev->watchdog.data = (unsigned long)dev; + + clk_prepare_enable(dev->clk_ipg); + clk_prepare_enable(dev->clk_ahb); + + version = sahara_read(dev, SAHARA_REG_VERSION); + if (version != SAHARA_VERSION_3) { + dev_err(&pdev->dev, "SAHARA version %d not supported\n", + version); + err = -ENODEV; + goto err_algs; + } + + sahara_write(dev, SAHARA_CMD_RESET | SAHARA_CMD_MODE_BATCH, + SAHARA_REG_CMD); + sahara_write(dev, SAHARA_CONTROL_SET_THROTTLE(0) | + SAHARA_CONTROL_SET_MAXBURST(8) | + SAHARA_CONTROL_RNG_AUTORSD | + SAHARA_CONTROL_ENABLE_INT, + SAHARA_REG_CONTROL); + + err = sahara_register_algs(dev); + if (err) + goto err_algs; + + dev_info(&pdev->dev, "SAHARA version %d initialized\n", version); + + return 0; + +err_algs: + dma_free_coherent(&pdev->dev, + SAHARA_MAX_HW_LINK * sizeof(struct sahara_hw_link), + dev->hw_link[0], dev->hw_phys_link[0]); + clk_disable_unprepare(dev->clk_ipg); + clk_disable_unprepare(dev->clk_ahb); + dev_ptr = NULL; +err_link: + dma_free_coherent(&pdev->dev, + 2 * AES_KEYSIZE_128, + dev->key_base, dev->key_phys_base); +err_key: + dma_free_coherent(&pdev->dev, + SAHARA_MAX_HW_DESC * sizeof(struct sahara_hw_desc), + dev->hw_desc[0], dev->hw_phys_desc[0]); + + return err; +} + +static int sahara_remove(struct platform_device *pdev) +{ + struct sahara_dev *dev = platform_get_drvdata(pdev); + + dma_free_coherent(&pdev->dev, + SAHARA_MAX_HW_LINK * sizeof(struct sahara_hw_link), + dev->hw_link[0], dev->hw_phys_link[0]); + dma_free_coherent(&pdev->dev, + 2 * AES_KEYSIZE_128, + dev->key_base, dev->key_phys_base); + dma_free_coherent(&pdev->dev, + SAHARA_MAX_HW_DESC * sizeof(struct sahara_hw_desc), + dev->hw_desc[0], dev->hw_phys_desc[0]); + + tasklet_kill(&dev->done_task); + tasklet_kill(&dev->queue_task); + + sahara_unregister_algs(dev); + + clk_disable_unprepare(dev->clk_ipg); + clk_disable_unprepare(dev->clk_ahb); + + dev_ptr = NULL; + + return 0; +} + +static struct platform_driver sahara_driver = { + .probe = sahara_probe, + .remove = sahara_remove, + .driver = { + .name = SAHARA_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(sahara_dt_ids), + }, + .id_table = sahara_platform_ids, +}; + +module_platform_driver(sahara_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Javier Martin "); +MODULE_DESCRIPTION("SAHARA2 HW crypto accelerator"); -- cgit v1.2.3 From c25a4a09c3fa3d4b058ac612d82ddfd172410fbb Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Mon, 18 Mar 2013 09:34:10 +0100 Subject: doc: Remove text on tracepoint samples The tracepoint sample code got removed. Remove a few lines on its usage too. Acked-by: Mathieu Desnoyers Acked-by: Rob Landley Acked-by: Steven Rostedt Signed-off-by: Paul Bolle Signed-off-by: Jiri Kosina --- Documentation/trace/tracepoints.txt | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'Documentation') diff --git a/Documentation/trace/tracepoints.txt b/Documentation/trace/tracepoints.txt index c0e1ceed75a4..da49437d5aeb 100644 --- a/Documentation/trace/tracepoints.txt +++ b/Documentation/trace/tracepoints.txt @@ -81,7 +81,6 @@ tracepoint_synchronize_unregister() must be called before the end of the module exit function to make sure there is no caller left using the probe. This, and the fact that preemption is disabled around the probe call, make sure that probe removal and module unload are safe. -See the "Probe example" section below for a sample probe module. The tracepoint mechanism supports inserting multiple instances of the same tracepoint, but a single definition must be made of a given @@ -100,17 +99,3 @@ core kernel image or in modules. If the tracepoint has to be used in kernel modules, an EXPORT_TRACEPOINT_SYMBOL_GPL() or EXPORT_TRACEPOINT_SYMBOL() can be used to export the defined tracepoints. - -* Probe / tracepoint example - -See the example provided in samples/tracepoints - -Compile them with your kernel. They are built during 'make' (not -'make modules') when CONFIG_SAMPLE_TRACEPOINTS=m. - -Run, as root : -modprobe tracepoint-sample (insmod order is not important) -modprobe tracepoint-probe-sample -cat /proc/tracepoint-sample (returns an expected error) -rmmod tracepoint-sample tracepoint-probe-sample -dmesg -- cgit v1.2.3 From 9b44190dc114c1720b34975b5bfc65aece112ced Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Wed, 20 Mar 2013 13:32:58 +0000 Subject: tcp: refactor F-RTO The patch series refactor the F-RTO feature (RFC4138/5682). This is to simplify the loss recovery processing. Existing F-RTO was developed during the experimental stage (RFC4138) and has many experimental features. It takes a separate code path from the traditional timeout processing by overloading CA_Disorder instead of using CA_Loss state. This complicates CA_Disorder state handling because it's also used for handling dubious ACKs and undos. While the algorithm in the RFC does not change the congestion control, the implementation intercepts congestion control in various places (e.g., frto_cwnd in tcp_ack()). The new code implements newer F-RTO RFC5682 using CA_Loss processing path. F-RTO becomes a small extension in the timeout processing and interfaces with congestion control and Eifel undo modules. It lets congestion control (module) determines how many to send independently. F-RTO only chooses what to send in order to detect spurious retranmission. If timeout is found spurious it invokes existing Eifel undo algorithms like DSACK or TCP timestamp based detection. The first patch removes all F-RTO code except the sysctl_tcp_frto is left for the new implementation. Since CA_EVENT_FRTO is removed, TCP westwood now computes ssthresh on regular timeout CA_EVENT_LOSS event. Signed-off-by: Yuchung Cheng Acked-by: Neal Cardwell Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 17 -- include/linux/tcp.h | 6 +- include/net/tcp.h | 4 - net/ipv4/sysctl_net_ipv4.c | 7 - net/ipv4/tcp_input.c | 375 +-------------------------------- net/ipv4/tcp_minisocks.c | 3 - net/ipv4/tcp_output.c | 11 +- net/ipv4/tcp_timer.c | 6 +- net/ipv4/tcp_westwood.c | 2 +- 9 files changed, 10 insertions(+), 421 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 17953e2bc3e9..8a977a0aaede 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -239,23 +239,6 @@ tcp_frto - INTEGER interacts badly with the packet counting of the SACK enabled TCP flow. -tcp_frto_response - INTEGER - When F-RTO has detected that a TCP retransmission timeout was - spurious (i.e, the timeout would have been avoided had TCP set a - longer retransmission timeout), TCP has several options what to do - next. Possible values are: - 0 Rate halving based; a smooth and conservative response, - results in halved cwnd and ssthresh after one RTT - 1 Very conservative response; not recommended because even - though being valid, it interacts poorly with the rest of - Linux TCP, halves cwnd and ssthresh immediately - 2 Aggressive response; undoes congestion control measures - that are now known to be unnecessary (ignoring the - possibility of a lost retransmission that would require - TCP to be more cautious), cwnd and ssthresh are restored - to the values prior timeout - Default: 0 (rate halving based) - tcp_keepalive_time - INTEGER How often TCP sends out keepalive messages when keepalive is enabled. Default: 2hours. diff --git a/include/linux/tcp.h b/include/linux/tcp.h index ed6a7456eecd..f5f203b36379 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -187,14 +187,12 @@ struct tcp_sock { u32 window_clamp; /* Maximal window to advertise */ u32 rcv_ssthresh; /* Current window clamp */ - u32 frto_highmark; /* snd_nxt when RTO occurred */ u16 advmss; /* Advertised MSS */ - u8 frto_counter; /* Number of new acks after RTO */ + u8 unused; u8 nonagle : 4,/* Disable Nagle algorithm? */ thin_lto : 1,/* Use linear timeouts for thin streams */ thin_dupack : 1,/* Fast retransmit on first dupack */ - repair : 1, - unused : 1; + repair : 1; u8 repair_queue; u8 do_early_retrans:1,/* Enable RFC5827 early-retransmit */ syn_data:1, /* SYN includes data */ diff --git a/include/net/tcp.h b/include/net/tcp.h index 7f2f17198d75..d1dcb596230e 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -272,7 +272,6 @@ extern int sysctl_tcp_app_win; extern int sysctl_tcp_adv_win_scale; extern int sysctl_tcp_tw_reuse; extern int sysctl_tcp_frto; -extern int sysctl_tcp_frto_response; extern int sysctl_tcp_low_latency; extern int sysctl_tcp_dma_copybreak; extern int sysctl_tcp_nometrics_save; @@ -424,8 +423,6 @@ extern struct sock * tcp_check_req(struct sock *sk,struct sk_buff *skb, bool fastopen); extern int tcp_child_process(struct sock *parent, struct sock *child, struct sk_buff *skb); -extern bool tcp_use_frto(struct sock *sk); -extern void tcp_enter_frto(struct sock *sk); extern void tcp_enter_loss(struct sock *sk, int how); extern void tcp_clear_retrans(struct tcp_sock *tp); extern void tcp_update_metrics(struct sock *sk); @@ -756,7 +753,6 @@ enum tcp_ca_event { CA_EVENT_TX_START, /* first transmit when no packets in flight */ CA_EVENT_CWND_RESTART, /* congestion window restart */ CA_EVENT_COMPLETE_CWR, /* end of congestion recovery */ - CA_EVENT_FRTO, /* fast recovery timeout */ CA_EVENT_LOSS, /* loss timeout */ CA_EVENT_FAST_ACK, /* in sequence ack */ CA_EVENT_SLOW_ACK, /* other ack */ diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index cb45062c8be0..fa2f63fc453b 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -591,13 +591,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, - { - .procname = "tcp_frto_response", - .data = &sysctl_tcp_frto_response, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { .procname = "tcp_low_latency", .data = &sysctl_tcp_low_latency, diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 19f0149fb6a2..231c79fe91f3 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -93,7 +93,6 @@ int sysctl_tcp_stdurg __read_mostly; int sysctl_tcp_rfc1337 __read_mostly; int sysctl_tcp_max_orphans __read_mostly = NR_FILE; int sysctl_tcp_frto __read_mostly = 2; -int sysctl_tcp_frto_response __read_mostly; int sysctl_tcp_thin_dupack __read_mostly; @@ -108,17 +107,14 @@ int sysctl_tcp_early_retrans __read_mostly = 3; #define FLAG_DATA_SACKED 0x20 /* New SACK. */ #define FLAG_ECE 0x40 /* ECE in this ACK */ #define FLAG_SLOWPATH 0x100 /* Do not skip RFC checks for window update.*/ -#define FLAG_ONLY_ORIG_SACKED 0x200 /* SACKs only non-rexmit sent before RTO */ #define FLAG_SND_UNA_ADVANCED 0x400 /* Snd_una was changed (!= FLAG_DATA_ACKED) */ #define FLAG_DSACKING_ACK 0x800 /* SACK blocks contained D-SACK info */ -#define FLAG_NONHEAD_RETRANS_ACKED 0x1000 /* Non-head rexmitted data was ACKed */ #define FLAG_SACK_RENEGING 0x2000 /* snd_una advanced to a sacked seq */ #define FLAG_ACKED (FLAG_DATA_ACKED|FLAG_SYN_ACKED) #define FLAG_NOT_DUP (FLAG_DATA|FLAG_WIN_UPDATE|FLAG_ACKED) #define FLAG_CA_ALERT (FLAG_DATA_SACKED|FLAG_ECE) #define FLAG_FORWARD_PROGRESS (FLAG_ACKED|FLAG_DATA_SACKED) -#define FLAG_ANY_PROGRESS (FLAG_FORWARD_PROGRESS|FLAG_SND_UNA_ADVANCED) #define TCP_REMNANT (TCP_FLAG_FIN|TCP_FLAG_URG|TCP_FLAG_SYN|TCP_FLAG_PSH) #define TCP_HP_BITS (~(TCP_RESERVED_BITS|TCP_FLAG_PSH)) @@ -1159,10 +1155,6 @@ static u8 tcp_sacktag_one(struct sock *sk, tcp_highest_sack_seq(tp))) state->reord = min(fack_count, state->reord); - - /* SACK enhanced F-RTO (RFC4138; Appendix B) */ - if (!after(end_seq, tp->frto_highmark)) - state->flag |= FLAG_ONLY_ORIG_SACKED; } if (sacked & TCPCB_LOST) { @@ -1555,7 +1547,6 @@ static int tcp_sacktag_write_queue(struct sock *sk, const struct sk_buff *ack_skb, u32 prior_snd_una) { - const struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); const unsigned char *ptr = (skb_transport_header(ack_skb) + TCP_SKB_CB(ack_skb)->sacked); @@ -1728,12 +1719,6 @@ walk: start_seq, end_seq, dup_sack); advance_sp: - /* SACK enhanced FRTO (RFC4138, Appendix B): Clearing correct - * due to in-order walk - */ - if (after(end_seq, tp->frto_highmark)) - state.flag &= ~FLAG_ONLY_ORIG_SACKED; - i++; } @@ -1750,8 +1735,7 @@ advance_sp: tcp_verify_left_out(tp); if ((state.reord < tp->fackets_out) && - ((icsk->icsk_ca_state != TCP_CA_Loss) || tp->undo_marker) && - (!tp->frto_highmark || after(tp->snd_una, tp->frto_highmark))) + ((inet_csk(sk)->icsk_ca_state != TCP_CA_Loss) || tp->undo_marker)) tcp_update_reordering(sk, tp->fackets_out - state.reord, 0); out: @@ -1825,197 +1809,6 @@ static inline void tcp_reset_reno_sack(struct tcp_sock *tp) tp->sacked_out = 0; } -static int tcp_is_sackfrto(const struct tcp_sock *tp) -{ - return (sysctl_tcp_frto == 0x2) && !tcp_is_reno(tp); -} - -/* F-RTO can only be used if TCP has never retransmitted anything other than - * head (SACK enhanced variant from Appendix B of RFC4138 is more robust here) - */ -bool tcp_use_frto(struct sock *sk) -{ - const struct tcp_sock *tp = tcp_sk(sk); - const struct inet_connection_sock *icsk = inet_csk(sk); - struct sk_buff *skb; - - if (!sysctl_tcp_frto) - return false; - - /* MTU probe and F-RTO won't really play nicely along currently */ - if (icsk->icsk_mtup.probe_size) - return false; - - if (tcp_is_sackfrto(tp)) - return true; - - /* Avoid expensive walking of rexmit queue if possible */ - if (tp->retrans_out > 1) - return false; - - skb = tcp_write_queue_head(sk); - if (tcp_skb_is_last(sk, skb)) - return true; - skb = tcp_write_queue_next(sk, skb); /* Skips head */ - tcp_for_write_queue_from(skb, sk) { - if (skb == tcp_send_head(sk)) - break; - if (TCP_SKB_CB(skb)->sacked & TCPCB_RETRANS) - return false; - /* Short-circuit when first non-SACKed skb has been checked */ - if (!(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED)) - break; - } - return true; -} - -/* RTO occurred, but do not yet enter Loss state. Instead, defer RTO - * recovery a bit and use heuristics in tcp_process_frto() to detect if - * the RTO was spurious. Only clear SACKED_RETRANS of the head here to - * keep retrans_out counting accurate (with SACK F-RTO, other than head - * may still have that bit set); TCPCB_LOST and remaining SACKED_RETRANS - * bits are handled if the Loss state is really to be entered (in - * tcp_enter_frto_loss). - * - * Do like tcp_enter_loss() would; when RTO expires the second time it - * does: - * "Reduce ssthresh if it has not yet been made inside this window." - */ -void tcp_enter_frto(struct sock *sk) -{ - const struct inet_connection_sock *icsk = inet_csk(sk); - struct tcp_sock *tp = tcp_sk(sk); - struct sk_buff *skb; - - if ((!tp->frto_counter && icsk->icsk_ca_state <= TCP_CA_Disorder) || - tp->snd_una == tp->high_seq || - ((icsk->icsk_ca_state == TCP_CA_Loss || tp->frto_counter) && - !icsk->icsk_retransmits)) { - tp->prior_ssthresh = tcp_current_ssthresh(sk); - /* Our state is too optimistic in ssthresh() call because cwnd - * is not reduced until tcp_enter_frto_loss() when previous F-RTO - * recovery has not yet completed. Pattern would be this: RTO, - * Cumulative ACK, RTO (2xRTO for the same segment does not end - * up here twice). - * RFC4138 should be more specific on what to do, even though - * RTO is quite unlikely to occur after the first Cumulative ACK - * due to back-off and complexity of triggering events ... - */ - if (tp->frto_counter) { - u32 stored_cwnd; - stored_cwnd = tp->snd_cwnd; - tp->snd_cwnd = 2; - tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk); - tp->snd_cwnd = stored_cwnd; - } else { - tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk); - } - /* ... in theory, cong.control module could do "any tricks" in - * ssthresh(), which means that ca_state, lost bits and lost_out - * counter would have to be faked before the call occurs. We - * consider that too expensive, unlikely and hacky, so modules - * using these in ssthresh() must deal these incompatibility - * issues if they receives CA_EVENT_FRTO and frto_counter != 0 - */ - tcp_ca_event(sk, CA_EVENT_FRTO); - } - - tp->undo_marker = tp->snd_una; - tp->undo_retrans = 0; - - skb = tcp_write_queue_head(sk); - if (TCP_SKB_CB(skb)->sacked & TCPCB_RETRANS) - tp->undo_marker = 0; - if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS) { - TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS; - tp->retrans_out -= tcp_skb_pcount(skb); - } - tcp_verify_left_out(tp); - - /* Too bad if TCP was application limited */ - tp->snd_cwnd = min(tp->snd_cwnd, tcp_packets_in_flight(tp) + 1); - - /* Earlier loss recovery underway (see RFC4138; Appendix B). - * The last condition is necessary at least in tp->frto_counter case. - */ - if (tcp_is_sackfrto(tp) && (tp->frto_counter || - ((1 << icsk->icsk_ca_state) & (TCPF_CA_Recovery|TCPF_CA_Loss))) && - after(tp->high_seq, tp->snd_una)) { - tp->frto_highmark = tp->high_seq; - } else { - tp->frto_highmark = tp->snd_nxt; - } - tcp_set_ca_state(sk, TCP_CA_Disorder); - tp->high_seq = tp->snd_nxt; - tp->frto_counter = 1; -} - -/* Enter Loss state after F-RTO was applied. Dupack arrived after RTO, - * which indicates that we should follow the traditional RTO recovery, - * i.e. mark everything lost and do go-back-N retransmission. - */ -static void tcp_enter_frto_loss(struct sock *sk, int allowed_segments, int flag) -{ - struct tcp_sock *tp = tcp_sk(sk); - struct sk_buff *skb; - - tp->lost_out = 0; - tp->retrans_out = 0; - if (tcp_is_reno(tp)) - tcp_reset_reno_sack(tp); - - tcp_for_write_queue(skb, sk) { - if (skb == tcp_send_head(sk)) - break; - - TCP_SKB_CB(skb)->sacked &= ~TCPCB_LOST; - /* - * Count the retransmission made on RTO correctly (only when - * waiting for the first ACK and did not get it)... - */ - if ((tp->frto_counter == 1) && !(flag & FLAG_DATA_ACKED)) { - /* For some reason this R-bit might get cleared? */ - if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS) - tp->retrans_out += tcp_skb_pcount(skb); - /* ...enter this if branch just for the first segment */ - flag |= FLAG_DATA_ACKED; - } else { - if (TCP_SKB_CB(skb)->sacked & TCPCB_RETRANS) - tp->undo_marker = 0; - TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS; - } - - /* Marking forward transmissions that were made after RTO lost - * can cause unnecessary retransmissions in some scenarios, - * SACK blocks will mitigate that in some but not in all cases. - * We used to not mark them but it was causing break-ups with - * receivers that do only in-order receival. - * - * TODO: we could detect presence of such receiver and select - * different behavior per flow. - */ - if (!(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED)) { - TCP_SKB_CB(skb)->sacked |= TCPCB_LOST; - tp->lost_out += tcp_skb_pcount(skb); - tp->retransmit_high = TCP_SKB_CB(skb)->end_seq; - } - } - tcp_verify_left_out(tp); - - tp->snd_cwnd = tcp_packets_in_flight(tp) + allowed_segments; - tp->snd_cwnd_cnt = 0; - tp->snd_cwnd_stamp = tcp_time_stamp; - tp->frto_counter = 0; - - tp->reordering = min_t(unsigned int, tp->reordering, - sysctl_tcp_reordering); - tcp_set_ca_state(sk, TCP_CA_Loss); - tp->high_seq = tp->snd_nxt; - TCP_ECN_queue_cwr(tp); - - tcp_clear_all_retrans_hints(tp); -} - static void tcp_clear_retrans_partial(struct tcp_sock *tp) { tp->retrans_out = 0; @@ -2090,8 +1883,6 @@ void tcp_enter_loss(struct sock *sk, int how) tcp_set_ca_state(sk, TCP_CA_Loss); tp->high_seq = tp->snd_nxt; TCP_ECN_queue_cwr(tp); - /* Abort F-RTO algorithm if one is in progress */ - tp->frto_counter = 0; } /* If ACK arrived pointing to a remembered SACK, it means that our @@ -2275,10 +2066,6 @@ static bool tcp_time_to_recover(struct sock *sk, int flag) struct tcp_sock *tp = tcp_sk(sk); __u32 packets_out; - /* Do not perform any recovery during F-RTO algorithm */ - if (tp->frto_counter) - return false; - /* Trick#1: The loss is proven. */ if (tp->lost_out) return true; @@ -2760,7 +2547,7 @@ static void tcp_try_to_open(struct sock *sk, int flag, int newly_acked_sacked) tcp_verify_left_out(tp); - if (!tp->frto_counter && !tcp_any_retrans_done(sk)) + if (!tcp_any_retrans_done(sk)) tp->retrans_stamp = 0; if (flag & FLAG_ECE) @@ -3198,8 +2985,6 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets, flag |= FLAG_RETRANS_DATA_ACKED; ca_seq_rtt = -1; seq_rtt = -1; - if ((flag & FLAG_DATA_ACKED) || (acked_pcount > 1)) - flag |= FLAG_NONHEAD_RETRANS_ACKED; } else { ca_seq_rtt = now - scb->when; last_ackt = skb->tstamp; @@ -3408,150 +3193,6 @@ static int tcp_ack_update_window(struct sock *sk, const struct sk_buff *skb, u32 return flag; } -/* A very conservative spurious RTO response algorithm: reduce cwnd and - * continue in congestion avoidance. - */ -static void tcp_conservative_spur_to_response(struct tcp_sock *tp) -{ - tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh); - tp->snd_cwnd_cnt = 0; - TCP_ECN_queue_cwr(tp); - tcp_moderate_cwnd(tp); -} - -/* A conservative spurious RTO response algorithm: reduce cwnd using - * PRR and continue in congestion avoidance. - */ -static void tcp_cwr_spur_to_response(struct sock *sk) -{ - tcp_enter_cwr(sk, 0); -} - -static void tcp_undo_spur_to_response(struct sock *sk, int flag) -{ - if (flag & FLAG_ECE) - tcp_cwr_spur_to_response(sk); - else - tcp_undo_cwr(sk, true); -} - -/* F-RTO spurious RTO detection algorithm (RFC4138) - * - * F-RTO affects during two new ACKs following RTO (well, almost, see inline - * comments). State (ACK number) is kept in frto_counter. When ACK advances - * window (but not to or beyond highest sequence sent before RTO): - * On First ACK, send two new segments out. - * On Second ACK, RTO was likely spurious. Do spurious response (response - * algorithm is not part of the F-RTO detection algorithm - * given in RFC4138 but can be selected separately). - * Otherwise (basically on duplicate ACK), RTO was (likely) caused by a loss - * and TCP falls back to conventional RTO recovery. F-RTO allows overriding - * of Nagle, this is done using frto_counter states 2 and 3, when a new data - * segment of any size sent during F-RTO, state 2 is upgraded to 3. - * - * Rationale: if the RTO was spurious, new ACKs should arrive from the - * original window even after we transmit two new data segments. - * - * SACK version: - * on first step, wait until first cumulative ACK arrives, then move to - * the second step. In second step, the next ACK decides. - * - * F-RTO is implemented (mainly) in four functions: - * - tcp_use_frto() is used to determine if TCP is can use F-RTO - * - tcp_enter_frto() prepares TCP state on RTO if F-RTO is used, it is - * called when tcp_use_frto() showed green light - * - tcp_process_frto() handles incoming ACKs during F-RTO algorithm - * - tcp_enter_frto_loss() is called if there is not enough evidence - * to prove that the RTO is indeed spurious. It transfers the control - * from F-RTO to the conventional RTO recovery - */ -static bool tcp_process_frto(struct sock *sk, int flag) -{ - struct tcp_sock *tp = tcp_sk(sk); - - tcp_verify_left_out(tp); - - /* Duplicate the behavior from Loss state (fastretrans_alert) */ - if (flag & FLAG_DATA_ACKED) - inet_csk(sk)->icsk_retransmits = 0; - - if ((flag & FLAG_NONHEAD_RETRANS_ACKED) || - ((tp->frto_counter >= 2) && (flag & FLAG_RETRANS_DATA_ACKED))) - tp->undo_marker = 0; - - if (!before(tp->snd_una, tp->frto_highmark)) { - tcp_enter_frto_loss(sk, (tp->frto_counter == 1 ? 2 : 3), flag); - return true; - } - - if (!tcp_is_sackfrto(tp)) { - /* RFC4138 shortcoming in step 2; should also have case c): - * ACK isn't duplicate nor advances window, e.g., opposite dir - * data, winupdate - */ - if (!(flag & FLAG_ANY_PROGRESS) && (flag & FLAG_NOT_DUP)) - return true; - - if (!(flag & FLAG_DATA_ACKED)) { - tcp_enter_frto_loss(sk, (tp->frto_counter == 1 ? 0 : 3), - flag); - return true; - } - } else { - if (!(flag & FLAG_DATA_ACKED) && (tp->frto_counter == 1)) { - if (!tcp_packets_in_flight(tp)) { - tcp_enter_frto_loss(sk, 2, flag); - return true; - } - - /* Prevent sending of new data. */ - tp->snd_cwnd = min(tp->snd_cwnd, - tcp_packets_in_flight(tp)); - return true; - } - - if ((tp->frto_counter >= 2) && - (!(flag & FLAG_FORWARD_PROGRESS) || - ((flag & FLAG_DATA_SACKED) && - !(flag & FLAG_ONLY_ORIG_SACKED)))) { - /* RFC4138 shortcoming (see comment above) */ - if (!(flag & FLAG_FORWARD_PROGRESS) && - (flag & FLAG_NOT_DUP)) - return true; - - tcp_enter_frto_loss(sk, 3, flag); - return true; - } - } - - if (tp->frto_counter == 1) { - /* tcp_may_send_now needs to see updated state */ - tp->snd_cwnd = tcp_packets_in_flight(tp) + 2; - tp->frto_counter = 2; - - if (!tcp_may_send_now(sk)) - tcp_enter_frto_loss(sk, 2, flag); - - return true; - } else { - switch (sysctl_tcp_frto_response) { - case 2: - tcp_undo_spur_to_response(sk, flag); - break; - case 1: - tcp_conservative_spur_to_response(tp); - break; - default: - tcp_cwr_spur_to_response(sk); - break; - } - tp->frto_counter = 0; - tp->undo_marker = 0; - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSPURIOUSRTOS); - } - return false; -} - /* RFC 5961 7 [ACK Throttling] */ static void tcp_send_challenge_ack(struct sock *sk) { @@ -3616,7 +3257,6 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) int prior_packets; int prior_sacked = tp->sacked_out; int pkts_acked = 0; - bool frto_cwnd = false; /* If the ack is older than previous acks * then we can probably ignore it. @@ -3690,22 +3330,15 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) pkts_acked = prior_packets - tp->packets_out; - if (tp->frto_counter) - frto_cwnd = tcp_process_frto(sk, flag); - /* Guarantee sacktag reordering detection against wrap-arounds */ - if (before(tp->frto_highmark, tp->snd_una)) - tp->frto_highmark = 0; - if (tcp_ack_is_dubious(sk, flag)) { /* Advance CWND, if state allows this. */ - if ((flag & FLAG_DATA_ACKED) && !frto_cwnd && - tcp_may_raise_cwnd(sk, flag)) + if ((flag & FLAG_DATA_ACKED) && tcp_may_raise_cwnd(sk, flag)) tcp_cong_avoid(sk, ack, prior_in_flight); is_dupack = !(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP)); tcp_fastretrans_alert(sk, pkts_acked, prior_sacked, is_dupack, flag); } else { - if ((flag & FLAG_DATA_ACKED) && !frto_cwnd) + if (flag & FLAG_DATA_ACKED) tcp_cong_avoid(sk, ack, prior_in_flight); } diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 8f0234f8bb95..05eaf8904613 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -422,9 +422,6 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, newtp->snd_cwnd = TCP_INIT_CWND; newtp->snd_cwnd_cnt = 0; - newtp->frto_counter = 0; - newtp->frto_highmark = 0; - if (newicsk->icsk_ca_ops != &tcp_init_congestion_ops && !try_module_get(newicsk->icsk_ca_ops->owner)) newicsk->icsk_ca_ops = &tcp_init_congestion_ops; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index e787ecec505e..163cf5fc0119 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -78,10 +78,6 @@ static void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb) tcp_advance_send_head(sk, skb); tp->snd_nxt = TCP_SKB_CB(skb)->end_seq; - /* Don't override Nagle indefinitely with F-RTO */ - if (tp->frto_counter == 2) - tp->frto_counter = 3; - tp->packets_out += tcp_skb_pcount(skb); if (!prior_packets || icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS || icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) @@ -1470,11 +1466,8 @@ static inline bool tcp_nagle_test(const struct tcp_sock *tp, const struct sk_buf if (nonagle & TCP_NAGLE_PUSH) return true; - /* Don't use the nagle rule for urgent data (or for the final FIN). - * Nagle can be ignored during F-RTO too (see RFC4138). - */ - if (tcp_urg_mode(tp) || (tp->frto_counter == 2) || - (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)) + /* Don't use the nagle rule for urgent data (or for the final FIN). */ + if (tcp_urg_mode(tp) || (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)) return true; if (!tcp_nagle_check(tp, skb, cur_mss, nonagle)) diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index eeccf795e917..4b85e6f636c9 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -416,11 +416,7 @@ void tcp_retransmit_timer(struct sock *sk) NET_INC_STATS_BH(sock_net(sk), mib_idx); } - if (tcp_use_frto(sk)) { - tcp_enter_frto(sk); - } else { - tcp_enter_loss(sk, 0); - } + tcp_enter_loss(sk, 0); if (tcp_retransmit_skb(sk, tcp_write_queue_head(sk)) > 0) { /* Retransmission failed because of local congestion, diff --git a/net/ipv4/tcp_westwood.c b/net/ipv4/tcp_westwood.c index 1b91bf48e277..76a1e23259e1 100644 --- a/net/ipv4/tcp_westwood.c +++ b/net/ipv4/tcp_westwood.c @@ -236,7 +236,7 @@ static void tcp_westwood_event(struct sock *sk, enum tcp_ca_event event) tp->snd_cwnd = tp->snd_ssthresh = tcp_westwood_bw_rttmin(sk); break; - case CA_EVENT_FRTO: + case CA_EVENT_LOSS: tp->snd_ssthresh = tcp_westwood_bw_rttmin(sk); /* Update RTT_min when next ack arrives */ w->reset_rtt_min = 1; -- cgit v1.2.3 From e33099f96d99c391b3325caa9c44258de04aae86 Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Wed, 20 Mar 2013 13:33:00 +0000 Subject: tcp: implement RFC5682 F-RTO This patch implements F-RTO (foward RTO recovery): When the first retransmission after timeout is acknowledged, F-RTO sends new data instead of old data. If the next ACK acknowledges some never-retransmitted data, then the timeout was spurious and the congestion state is reverted. Otherwise if the next ACK selectively acknowledges the new data, then the timeout was genuine and the loss recovery continues. This idea applies to recurring timeouts as well. While F-RTO sends different data during timeout recovery, it does not (and should not) change the congestion control. The implementaion follows the three steps of SACK enhanced algorithm (section 3) in RFC5682. Step 1 is in tcp_enter_loss(). Step 2 and 3 are in tcp_process_loss(). The basic version is not supported because SACK enhanced version also works for non-SACK connections. The new implementation is functionally in parity with the old F-RTO implementation except the one case where it increases undo events: In addition to the RFC algorithm, a spurious timeout may be detected without sending data in step 2, as long as the SACK confirms not all the original data are dropped. When this happens, the sender will undo the cwnd and perhaps enter fast recovery instead. This additional check increases the F-RTO undo events by 5x compared to the prior implementation on Google Web servers, since the sender often does not have new data to send for HTTP. Note F-RTO may detect spurious timeout before Eifel with timestamps does so. Signed-off-by: Yuchung Cheng Acked-by: Eric Dumazet Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 18 +++------ include/linux/tcp.h | 3 +- net/ipv4/tcp_input.c | 73 ++++++++++++++++++++++++++++------ 3 files changed, 68 insertions(+), 26 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 8a977a0aaede..f98ca633b528 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -225,19 +225,13 @@ tcp_fin_timeout - INTEGER Default: 60 seconds tcp_frto - INTEGER - Enables Forward RTO-Recovery (F-RTO) defined in RFC4138. + Enables Forward RTO-Recovery (F-RTO) defined in RFC5682. F-RTO is an enhanced recovery algorithm for TCP retransmission - timeouts. It is particularly beneficial in wireless environments - where packet loss is typically due to random radio interference - rather than intermediate router congestion. F-RTO is sender-side - only modification. Therefore it does not require any support from - the peer. - - If set to 1, basic version is enabled. 2 enables SACK enhanced - F-RTO if flow uses SACK. The basic version can be used also when - SACK is in use though scenario(s) with it exists where F-RTO - interacts badly with the packet counting of the SACK enabled TCP - flow. + timeouts. It is particularly beneficial in networks where the + RTT fluctuates (e.g., wireless). F-RTO is sender-side only + modification. It does not require any support from the peer. + + By default it's enabled with a non-zero value. 0 disables F-RTO. tcp_keepalive_time - INTEGER How often TCP sends out keepalive messages when keepalive is enabled. diff --git a/include/linux/tcp.h b/include/linux/tcp.h index f5f203b36379..5adbc33d1ab3 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -192,7 +192,8 @@ struct tcp_sock { u8 nonagle : 4,/* Disable Nagle algorithm? */ thin_lto : 1,/* Use linear timeouts for thin streams */ thin_dupack : 1,/* Fast retransmit on first dupack */ - repair : 1; + repair : 1, + frto : 1;/* F-RTO (RFC5682) activated in CA_Loss */ u8 repair_queue; u8 do_early_retrans:1,/* Enable RFC5827 early-retransmit */ syn_data:1, /* SYN includes data */ diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 8d821e45b917..b2b36196b342 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -107,6 +107,7 @@ int sysctl_tcp_early_retrans __read_mostly = 3; #define FLAG_DATA_SACKED 0x20 /* New SACK. */ #define FLAG_ECE 0x40 /* ECE in this ACK */ #define FLAG_SLOWPATH 0x100 /* Do not skip RFC checks for window update.*/ +#define FLAG_ORIG_SACK_ACKED 0x200 /* Never retransmitted data are (s)acked */ #define FLAG_SND_UNA_ADVANCED 0x400 /* Snd_una was changed (!= FLAG_DATA_ACKED) */ #define FLAG_DSACKING_ACK 0x800 /* SACK blocks contained D-SACK info */ #define FLAG_SACK_RENEGING 0x2000 /* snd_una advanced to a sacked seq */ @@ -1155,6 +1156,8 @@ static u8 tcp_sacktag_one(struct sock *sk, tcp_highest_sack_seq(tp))) state->reord = min(fack_count, state->reord); + if (!after(end_seq, tp->high_seq)) + state->flag |= FLAG_ORIG_SACK_ACKED; } if (sacked & TCPCB_LOST) { @@ -1835,10 +1838,13 @@ void tcp_enter_loss(struct sock *sk, int how) const struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; + bool new_recovery = false; /* Reduce ssthresh if it has not yet been made inside this window. */ - if (icsk->icsk_ca_state <= TCP_CA_Disorder || tp->snd_una == tp->high_seq || + if (icsk->icsk_ca_state <= TCP_CA_Disorder || + !after(tp->high_seq, tp->snd_una) || (icsk->icsk_ca_state == TCP_CA_Loss && !icsk->icsk_retransmits)) { + new_recovery = true; tp->prior_ssthresh = tcp_current_ssthresh(sk); tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk); tcp_ca_event(sk, CA_EVENT_LOSS); @@ -1883,6 +1889,14 @@ void tcp_enter_loss(struct sock *sk, int how) tcp_set_ca_state(sk, TCP_CA_Loss); tp->high_seq = tp->snd_nxt; TCP_ECN_queue_cwr(tp); + + /* F-RTO RFC5682 sec 3.1 step 1: retransmit SND.UNA if no previous + * loss recovery is underway except recurring timeout(s) on + * the same SND.UNA (sec 3.2). Disable F-RTO on path MTU probing + */ + tp->frto = sysctl_tcp_frto && + (new_recovery || icsk->icsk_retransmits) && + !inet_csk(sk)->icsk_mtup.probe_size; } /* If ACK arrived pointing to a remembered SACK, it means that our @@ -2426,12 +2440,12 @@ static int tcp_try_undo_partial(struct sock *sk, int acked) return failed; } -/* Undo during loss recovery after partial ACK. */ -static bool tcp_try_undo_loss(struct sock *sk) +/* Undo during loss recovery after partial ACK or using F-RTO. */ +static bool tcp_try_undo_loss(struct sock *sk, bool frto_undo) { struct tcp_sock *tp = tcp_sk(sk); - if (tcp_may_undo(tp)) { + if (frto_undo || tcp_may_undo(tp)) { struct sk_buff *skb; tcp_for_write_queue(skb, sk) { if (skb == tcp_send_head(sk)) @@ -2445,9 +2459,12 @@ static bool tcp_try_undo_loss(struct sock *sk) tp->lost_out = 0; tcp_undo_cwr(sk, true); NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPLOSSUNDO); + if (frto_undo) + NET_INC_STATS_BH(sock_net(sk), + LINUX_MIB_TCPSPURIOUSRTOS); inet_csk(sk)->icsk_retransmits = 0; tp->undo_marker = 0; - if (tcp_is_sack(tp)) + if (frto_undo || tcp_is_sack(tp)) tcp_set_ca_state(sk, TCP_CA_Open); return true; } @@ -2667,24 +2684,52 @@ static void tcp_enter_recovery(struct sock *sk, bool ece_ack) /* Process an ACK in CA_Loss state. Move to CA_Open if lost data are * recovered or spurious. Otherwise retransmits more on partial ACKs. */ -static void tcp_process_loss(struct sock *sk, int flag) +static void tcp_process_loss(struct sock *sk, int flag, bool is_dupack) { struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); + bool recovered = !before(tp->snd_una, tp->high_seq); - if (!before(tp->snd_una, tp->high_seq)) { + if (tp->frto) { /* F-RTO RFC5682 sec 3.1 (sack enhanced version). */ + if (flag & FLAG_ORIG_SACK_ACKED) { + /* Step 3.b. A timeout is spurious if not all data are + * lost, i.e., never-retransmitted data are (s)acked. + */ + tcp_try_undo_loss(sk, true); + return; + } + if (after(tp->snd_nxt, tp->high_seq) && + (flag & FLAG_DATA_SACKED || is_dupack)) { + tp->frto = 0; /* Loss was real: 2nd part of step 3.a */ + } else if (flag & FLAG_SND_UNA_ADVANCED && !recovered) { + tp->high_seq = tp->snd_nxt; + __tcp_push_pending_frames(sk, tcp_current_mss(sk), + TCP_NAGLE_OFF); + if (after(tp->snd_nxt, tp->high_seq)) + return; /* Step 2.b */ + tp->frto = 0; + } + } + + if (recovered) { + /* F-RTO RFC5682 sec 3.1 step 2.a and 1st part of step 3.a */ icsk->icsk_retransmits = 0; tcp_try_undo_recovery(sk); return; } - if (flag & FLAG_DATA_ACKED) icsk->icsk_retransmits = 0; - if (tcp_is_reno(tp) && flag & FLAG_SND_UNA_ADVANCED) - tcp_reset_reno_sack(tp); - if (tcp_try_undo_loss(sk)) + if (tcp_is_reno(tp)) { + /* A Reno DUPACK means new data in F-RTO step 2.b above are + * delivered. Lower inflight to clock out (re)tranmissions. + */ + if (after(tp->snd_nxt, tp->high_seq) && is_dupack) + tcp_add_reno_sack(sk); + else if (flag & FLAG_SND_UNA_ADVANCED) + tcp_reset_reno_sack(tp); + } + if (tcp_try_undo_loss(sk, false)) return; - tcp_moderate_cwnd(tp); tcp_xmit_retransmit_queue(sk); } @@ -2764,7 +2809,7 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, newly_acked_sacked = pkts_acked + tp->sacked_out - prior_sacked; break; case TCP_CA_Loss: - tcp_process_loss(sk, flag); + tcp_process_loss(sk, flag, is_dupack); if (icsk->icsk_ca_state != TCP_CA_Open) return; /* Fall through to processing in Open state. */ @@ -3003,6 +3048,8 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets, } if (!(sacked & TCPCB_SACKED_ACKED)) reord = min(pkts_acked, reord); + if (!after(scb->end_seq, tp->high_seq)) + flag |= FLAG_ORIG_SACK_ACKED; } if (sacked & TCPCB_SACKED_ACKED) -- cgit v1.2.3 From e64171b97b88a1adf297d429826fdbb9e232ab53 Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Thu, 7 Feb 2013 13:48:51 -0300 Subject: [media] media: add support for decoder as one of media entity types A lot of SOCs including Texas Instruments Davinci family mainly use video decoders as input devices. This patch adds a flag 'MEDIA_ENT_T_V4L2_SUBDEV_DECODER' media entity type for decoder's. Along side updates the documentation for this media entity type. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/media-ioc-enum-entities.xml | 10 ++++++++++ include/uapi/linux/media.h | 2 ++ 2 files changed, 12 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/media-ioc-enum-entities.xml b/Documentation/DocBook/media/v4l/media-ioc-enum-entities.xml index 576b68b33f2c..116c301656e0 100644 --- a/Documentation/DocBook/media/v4l/media-ioc-enum-entities.xml +++ b/Documentation/DocBook/media/v4l/media-ioc-enum-entities.xml @@ -272,6 +272,16 @@ MEDIA_ENT_T_V4L2_SUBDEV_LENS Lens controller + + MEDIA_ENT_T_V4L2_SUBDEV_DECODER + Video decoder, the basic function of the video decoder is to + accept analogue video from a wide variety of sources such as + broadcast, DVD players, cameras and video cassette recorders, in + either NTSC, PAL or HD format and still occasionally SECAM, separate + it into its component parts, luminance and chrominance, and output + it in some digital video standard, with appropriate embedded timing + signals. + diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index 0ef883327de2..ed49574ad757 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -56,6 +56,8 @@ struct media_device_info { #define MEDIA_ENT_T_V4L2_SUBDEV_SENSOR (MEDIA_ENT_T_V4L2_SUBDEV + 1) #define MEDIA_ENT_T_V4L2_SUBDEV_FLASH (MEDIA_ENT_T_V4L2_SUBDEV + 2) #define MEDIA_ENT_T_V4L2_SUBDEV_LENS (MEDIA_ENT_T_V4L2_SUBDEV + 3) +/* A converter of analogue video to its digital representation. */ +#define MEDIA_ENT_T_V4L2_SUBDEV_DECODER (MEDIA_ENT_T_V4L2_SUBDEV + 4) #define MEDIA_ENT_FL_DEFAULT (1 << 0) -- cgit v1.2.3 From a368a6a33b107d680e955c799e6e1e3d6b4bbe8a Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Thu, 28 Feb 2013 09:59:07 -0400 Subject: documentation: clk: fix couple of misspelling Correcting misspelling inside the clk.txt. Signed-off-by: Eduardo Valentin Signed-off-by: Mike Turquette --- Documentation/clk.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/clk.txt b/Documentation/clk.txt index 1943fae014fd..4274a546eb57 100644 --- a/Documentation/clk.txt +++ b/Documentation/clk.txt @@ -174,9 +174,9 @@ int clk_foo_enable(struct clk_hw *hw) }; Below is a matrix detailing which clk_ops are mandatory based upon the -hardware capbilities of that clock. A cell marked as "y" means +hardware capabilities of that clock. A cell marked as "y" means mandatory, a cell marked as "n" implies that either including that -callback is invalid or otherwise uneccesary. Empty cells are either +callback is invalid or otherwise unnecessary. Empty cells are either optional or must be evaluated on a case-by-case basis. clock hardware characteristics -- cgit v1.2.3 From 2e0f8822d1a6d839e66786e99ce1043e4ad1cd72 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 6 Mar 2013 23:46:28 +0100 Subject: ACPI / LPSS: Add support for exposing LTR registers to user space Devices on the Intel Lynxpoint Low Power Subsystem (LPSS) have registers providing access to LTR (Latency Tolerance Reporting) functionality that allows software to monitor and possibly influence the aggressiveness of the platform's active-state power management. For each LPSS device, there are two modes of operation related to LTR, the auto mode and the software mode. In the auto mode the LTR is set up by the platform firmware and managed by hardware. Software can only read the LTR register values to monitor the platform's behavior. In the software mode it is possible to use LTR to control the extent to which the platform will use its built-in power management features. This changeset adds support for reading the LPSS devices' LTR registers and exposing their values to user space for monitoring and diagnostics purposes. It re-uses the MMIO mappings created to access the LPSS devices' clock registers for reading the values of the LTR registers and exposes them to user space through sysfs device attributes. Namely, a new atrribute group, lpss_ltr, is created for each LPSS device. It contains three new attributes: ltr_mode, auto_ltr, sw_ltr. The value of the ltr_mode attribute reflects the LTR mode being used at the moment (software vs auto) and the other two contain the actual register values (raw) whose meaning depends on the LTR mode. All of these attributes are read-only. Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki --- Documentation/ABI/testing/sysfs-devices-lpss_ltr | 44 +++++++ drivers/acpi/acpi_lpss.c | 139 ++++++++++++++++++++++- 2 files changed, 178 insertions(+), 5 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-devices-lpss_ltr (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-devices-lpss_ltr b/Documentation/ABI/testing/sysfs-devices-lpss_ltr new file mode 100644 index 000000000000..ea9298d9bbaf --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-lpss_ltr @@ -0,0 +1,44 @@ +What: /sys/devices/.../lpss_ltr/ +Date: March 2013 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../lpss_ltr/ directory is only present for + devices included into the Intel Lynxpoint Low Power Subsystem + (LPSS). If present, it contains attributes containing the LTR + mode and the values of LTR registers of the device. + +What: /sys/devices/.../lpss_ltr/ltr_mode +Date: March 2013 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../lpss_ltr/ltr_mode attribute contains an + integer number (0 or 1) indicating whether or not the devices' + LTR functionality is working in the software mode (1). + + This attribute is read-only. If the device's runtime PM status + is not "active", attempts to read from this attribute cause + -EAGAIN to be returned. + +What: /sys/devices/.../lpss_ltr/auto_ltr +Date: March 2013 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../lpss_ltr/auto_ltr attribute contains the + current value of the device's AUTO_LTR register (raw) + represented as an 8-digit hexadecimal number. + + This attribute is read-only. If the device's runtime PM status + is not "active", attempts to read from this attribute cause + -EAGAIN to be returned. + +What: /sys/devices/.../lpss_ltr/sw_ltr +Date: March 2013 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../lpss_ltr/auto_ltr attribute contains the + current value of the device's SW_LTR register (raw) represented + as an 8-digit hexadecimal number. + + This attribute is read-only. If the device's runtime PM status + is not "active", attempts to read from this attribute cause + -EAGAIN to be returned. diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 823df46a3deb..c87db0e47d09 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -18,17 +18,26 @@ #include #include #include +#include #include "internal.h" ACPI_MODULE_NAME("acpi_lpss"); -#define LPSS_CLK_OFFSET 0x800 #define LPSS_CLK_SIZE 0x04 +#define LPSS_LTR_SIZE 0x18 + +/* Offsets relative to LPSS_PRIVATE_OFFSET */ +#define LPSS_GENERAL 0x08 +#define LPSS_GENERAL_LTR_MODE_SW BIT(2) +#define LPSS_SW_LTR 0x10 +#define LPSS_AUTO_LTR 0x14 struct lpss_device_desc { bool clk_required; const char *clk_parent; + bool ltr_required; + unsigned int prv_offset; }; struct lpss_private_data { @@ -41,6 +50,13 @@ struct lpss_private_data { static struct lpss_device_desc lpt_dev_desc = { .clk_required = true, .clk_parent = "lpss_clk", + .prv_offset = 0x800, + .ltr_required = true, +}; + +static struct lpss_device_desc lpt_sdio_dev_desc = { + .prv_offset = 0x1000, + .ltr_required = true, }; static const struct acpi_device_id acpi_lpss_device_ids[] = { @@ -51,7 +67,7 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = { { "INT33C3", (unsigned long)&lpt_dev_desc }, { "INT33C4", (unsigned long)&lpt_dev_desc }, { "INT33C5", (unsigned long)&lpt_dev_desc }, - { "INT33C6", }, + { "INT33C6", (unsigned long)&lpt_sdio_dev_desc }, { "INT33C7", }, { } @@ -80,12 +96,12 @@ static int register_device_clock(struct acpi_device *adev, lpt_register_clock_device(); if (!dev_desc->clk_parent || !pdata->mmio_base - || pdata->mmio_size < LPSS_CLK_OFFSET + LPSS_CLK_SIZE) + || pdata->mmio_size < dev_desc->prv_offset + LPSS_CLK_SIZE) return -ENODATA; pdata->clk = clk_register_gate(NULL, dev_name(&adev->dev), dev_desc->clk_parent, 0, - pdata->mmio_base + LPSS_CLK_OFFSET, + pdata->mmio_base + dev_desc->prv_offset, 0, 0, NULL); if (IS_ERR(pdata->clk)) return PTR_ERR(pdata->clk); @@ -151,6 +167,117 @@ static int acpi_lpss_create_device(struct acpi_device *adev, return ret; } +static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val) +{ + struct acpi_device *adev; + struct lpss_private_data *pdata; + unsigned long flags; + int ret; + + ret = acpi_bus_get_device(ACPI_HANDLE(dev), &adev); + if (WARN_ON(ret)) + return ret; + + spin_lock_irqsave(&dev->power.lock, flags); + if (pm_runtime_suspended(dev)) { + ret = -EAGAIN; + goto out; + } + pdata = acpi_driver_data(adev); + if (WARN_ON(!pdata || !pdata->mmio_base)) { + ret = -ENODEV; + goto out; + } + *val = readl(pdata->mmio_base + pdata->dev_desc->prv_offset + reg); + + out: + spin_unlock_irqrestore(&dev->power.lock, flags); + return ret; +} + +static ssize_t lpss_ltr_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + u32 ltr_value = 0; + unsigned int reg; + int ret; + + reg = strcmp(attr->attr.name, "auto_ltr") ? LPSS_SW_LTR : LPSS_AUTO_LTR; + ret = lpss_reg_read(dev, reg, <r_value); + if (ret) + return ret; + + return snprintf(buf, PAGE_SIZE, "%08x\n", ltr_value); +} + +static ssize_t lpss_ltr_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 ltr_mode = 0; + char *outstr; + int ret; + + ret = lpss_reg_read(dev, LPSS_GENERAL, <r_mode); + if (ret) + return ret; + + outstr = (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) ? "sw" : "auto"; + return sprintf(buf, "%s\n", outstr); +} + +static DEVICE_ATTR(auto_ltr, S_IRUSR, lpss_ltr_show, NULL); +static DEVICE_ATTR(sw_ltr, S_IRUSR, lpss_ltr_show, NULL); +static DEVICE_ATTR(ltr_mode, S_IRUSR, lpss_ltr_mode_show, NULL); + +static struct attribute *lpss_attrs[] = { + &dev_attr_auto_ltr.attr, + &dev_attr_sw_ltr.attr, + &dev_attr_ltr_mode.attr, + NULL, +}; + +static struct attribute_group lpss_attr_group = { + .attrs = lpss_attrs, + .name = "lpss_ltr", +}; + +static int acpi_lpss_platform_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct platform_device *pdev = to_platform_device(data); + struct lpss_private_data *pdata; + struct acpi_device *adev; + const struct acpi_device_id *id; + int ret = 0; + + id = acpi_match_device(acpi_lpss_device_ids, &pdev->dev); + if (!id || !id->driver_data) + return 0; + + if (acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev)) + return 0; + + pdata = acpi_driver_data(adev); + if (!pdata || !pdata->mmio_base || !pdata->dev_desc->ltr_required) + return 0; + + if (pdata->mmio_size < pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) { + dev_err(&pdev->dev, "MMIO size insufficient to access LTR\n"); + return 0; + } + + if (action == BUS_NOTIFY_ADD_DEVICE) + ret = sysfs_create_group(&pdev->dev.kobj, &lpss_attr_group); + else if (action == BUS_NOTIFY_DEL_DEVICE) + sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group); + + return ret; +} + +static struct notifier_block acpi_lpss_nb = { + .notifier_call = acpi_lpss_platform_notify, +}; + static struct acpi_scan_handler lpss_handler = { .ids = acpi_lpss_device_ids, .attach = acpi_lpss_create_device, @@ -158,6 +285,8 @@ static struct acpi_scan_handler lpss_handler = { void __init acpi_lpss_init(void) { - if (!lpt_clk_init()) + if (!lpt_clk_init()) { + bus_register_notifier(&platform_bus_type, &acpi_lpss_nb); acpi_scan_add_handler(&lpss_handler); + } } -- cgit v1.2.3 From 2ec985213864cb64c45dc0284d7316142eefb5d4 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 22 Mar 2013 03:39:27 +0000 Subject: net: mvmdio: enhance driver to support SMI error/done interrupts This patch enhances the "mvmdio" to support a SMI error/done interrupt line which can be used along with a wait queue instead of doing busy-waiting on the registers. This is a feature which is available in the mv643xx_eth SMI code and thus reduces again the gap between the two. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- .../devicetree/bindings/net/marvell-orion-mdio.txt | 3 + drivers/net/ethernet/marvell/mvmdio.c | 98 ++++++++++++++++++---- 2 files changed, 83 insertions(+), 18 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt b/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt index 34e7aafa321c..052b5f28a624 100644 --- a/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt +++ b/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt @@ -9,6 +9,9 @@ Required properties: - compatible: "marvell,orion-mdio" - reg: address and length of the SMI register +Optional properties: +- interrupts: interrupt line number for the SMI error/done interrupt + The child nodes of the MDIO driver are the individual PHY devices connected to this MDIO bus. They must have a "reg" property given the PHY address on the MDIO bus. diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c index 3e2711d22451..3472574602b2 100644 --- a/drivers/net/ethernet/marvell/mvmdio.c +++ b/drivers/net/ethernet/marvell/mvmdio.c @@ -24,10 +24,13 @@ #include #include #include +#include #include #include #include #include +#include +#include #define MVMDIO_SMI_DATA_SHIFT 0 #define MVMDIO_SMI_PHY_ADDR_SHIFT 16 @@ -36,33 +39,58 @@ #define MVMDIO_SMI_WRITE_OPERATION 0 #define MVMDIO_SMI_READ_VALID BIT(27) #define MVMDIO_SMI_BUSY BIT(28) +#define MVMDIO_ERR_INT_CAUSE 0x007C +#define MVMDIO_ERR_INT_SMI_DONE 0x00000010 +#define MVMDIO_ERR_INT_MASK 0x0080 struct orion_mdio_dev { struct mutex lock; void __iomem *regs; + /* + * If we have access to the error interrupt pin (which is + * somewhat misnamed as it not only reflects internal errors + * but also reflects SMI completion), use that to wait for + * SMI access completion instead of polling the SMI busy bit. + */ + int err_interrupt; + wait_queue_head_t smi_busy_wait; }; +static int orion_mdio_smi_is_done(struct orion_mdio_dev *dev) +{ + return !(readl(dev->regs) & MVMDIO_SMI_BUSY); +} + /* Wait for the SMI unit to be ready for another operation */ static int orion_mdio_wait_ready(struct mii_bus *bus) { struct orion_mdio_dev *dev = bus->priv; int count; - u32 val; - count = 0; - while (1) { - val = readl(dev->regs); - if (!(val & MVMDIO_SMI_BUSY)) - break; + if (dev->err_interrupt <= 0) { + count = 0; + while (1) { + if (orion_mdio_smi_is_done(dev)) + break; - if (count > 100) { - dev_err(bus->parent, "Timeout: SMI busy for too long\n"); - return -ETIMEDOUT; - } + if (count > 100) { + dev_err(bus->parent, + "Timeout: SMI busy for too long\n"); + return -ETIMEDOUT; + } - udelay(10); - count++; + udelay(10); + count++; + } + } else { + if (!orion_mdio_smi_is_done(dev)) { + wait_event_timeout(dev->smi_busy_wait, + orion_mdio_smi_is_done(dev), + msecs_to_jiffies(100)); + if (!orion_mdio_smi_is_done(dev)) + return -ETIMEDOUT; + } } return 0; @@ -141,6 +169,21 @@ static int orion_mdio_reset(struct mii_bus *bus) return 0; } +static irqreturn_t orion_mdio_err_irq(int irq, void *dev_id) +{ + struct orion_mdio_dev *dev = dev_id; + + if (readl(dev->regs + MVMDIO_ERR_INT_CAUSE) & + MVMDIO_ERR_INT_SMI_DONE) { + writel(~MVMDIO_ERR_INT_SMI_DONE, + dev->regs + MVMDIO_ERR_INT_CAUSE); + wake_up(&dev->smi_busy_wait); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + static int orion_mdio_probe(struct platform_device *pdev) { struct resource *r; @@ -181,9 +224,22 @@ static int orion_mdio_probe(struct platform_device *pdev) dev->regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); if (!dev->regs) { dev_err(&pdev->dev, "Unable to remap SMI register\n"); - kfree(bus->irq); - mdiobus_free(bus); - return -ENODEV; + ret = -ENODEV; + goto out_mdio; + } + + init_waitqueue_head(&dev->smi_busy_wait); + + dev->err_interrupt = platform_get_irq(pdev, 0); + if (dev->err_interrupt != -ENXIO) { + ret = devm_request_irq(&pdev->dev, dev->err_interrupt, + orion_mdio_err_irq, + IRQF_SHARED, pdev->name, dev); + if (ret) + goto out_mdio; + + writel(MVMDIO_ERR_INT_SMI_DONE, + dev->regs + MVMDIO_ERR_INT_MASK); } mutex_init(&dev->lock); @@ -194,19 +250,25 @@ static int orion_mdio_probe(struct platform_device *pdev) ret = mdiobus_register(bus); if (ret < 0) { dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret); - kfree(bus->irq); - mdiobus_free(bus); - return ret; + goto out_mdio; } platform_set_drvdata(pdev, bus); return 0; + +out_mdio: + kfree(bus->irq); + mdiobus_free(bus); + return ret; } static int orion_mdio_remove(struct platform_device *pdev) { struct mii_bus *bus = platform_get_drvdata(pdev); + struct orion_mdio_dev *dev = bus->priv; + + writel(0, dev->regs + MVMDIO_ERR_INT_MASK); mdiobus_unregister(bus); kfree(bus->irq); mdiobus_free(bus); -- cgit v1.2.3 From eebdb0c1e1d63532399f7cbb65ade5969d63df06 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 14 Mar 2013 20:31:38 -0700 Subject: ARM: msm: Rework timer binding to be more general The msm timer binding I wrote is bad. First off, the clock frequency in the binding for the dgt is wrong. Software divides down the input rate by 4 to achieve the rate listed in the binding. We also treat each individual timer as a separate hardware component, when in reality there is one timer block (that may be duplicated per cpu) with multiple timers within it. Depending on the version of the hardware there can be one or two general purpose timers, status and divider control registers, and an entirely different register layout. In the next patch we'll need to know about the different register layouts so that we can properly check the status register after clearing the count. The current binding makes this complicated because the general purpose timer's reg property doesn't indicate where that status register is, and in fact it is beyond the size of the reg property. Clean all this up by just having one node for the timer hardware, and describe all the interrupts and clock frequencies supported while having one reg property that covers the entire timer register region. We'll use the compatible field in the future to determine different register layouts and if we should read the status registers, etc. Signed-off-by: Stephen Boyd Signed-off-by: David Brown --- .../devicetree/bindings/arm/msm/timer.txt | 41 ++++++----- arch/arm/boot/dts/msm8660-surf.dts | 20 ++---- arch/arm/boot/dts/msm8960-cdp.dts | 22 +++--- arch/arm/mach-msm/timer.c | 79 +++++++++------------- 4 files changed, 68 insertions(+), 94 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/msm/timer.txt b/Documentation/devicetree/bindings/arm/msm/timer.txt index 8c5907b9cae8..c6ef8f13dc7e 100644 --- a/Documentation/devicetree/bindings/arm/msm/timer.txt +++ b/Documentation/devicetree/bindings/arm/msm/timer.txt @@ -3,36 +3,35 @@ Properties: - compatible : Should at least contain "qcom,msm-timer". More specific - properties such as "qcom,msm-gpt" and "qcom,msm-dgt" specify a general - purpose timer and a debug timer respectively. + properties specify which subsystem the timers are paired with. -- interrupts : Interrupt indicating a match event. + "qcom,kpss-timer" - krait subsystem + "qcom,scss-timer" - scorpion subsystem -- reg : Specifies the base address of the timer registers. The second region - specifies an optional register used to configure the clock divider. +- interrupts : Interrupts for the the debug timer, the first general purpose + timer, and optionally a second general purpose timer in that + order. -- clock-frequency : The frequency of the timer in Hz. +- reg : Specifies the base address of the timer registers. + +- clock-frequency : The frequency of the debug timer and the general purpose + timer(s) in Hz in that order. Optional: - cpu-offset : per-cpu offset used when the timer is accessed without the - CPU remapping facilities. The offset is cpu-offset * cpu-nr. + CPU remapping facilities. The offset is + cpu-offset + (0x10000 * cpu-nr). Example: - timer@200a004 { - compatible = "qcom,msm-gpt", "qcom,msm-timer"; - interrupts = <1 2 0x301>; - reg = <0x0200a004 0x10>; - clock-frequency = <32768>; - cpu-offset = <0x40000>; - }; - - timer@200a024 { - compatible = "qcom,msm-dgt", "qcom,msm-timer"; - interrupts = <1 3 0x301>; - reg = <0x0200a024 0x10>, - <0x0200a034 0x4>; - clock-frequency = <6750000>; + timer@200a000 { + compatible = "qcom,scss-timer", "qcom,msm-timer"; + interrupts = <1 1 0x301>, + <1 2 0x301>, + <1 3 0x301>; + reg = <0x0200a000 0x100>; + clock-frequency = <19200000>, + <32768>; cpu-offset = <0x40000>; }; diff --git a/arch/arm/boot/dts/msm8660-surf.dts b/arch/arm/boot/dts/msm8660-surf.dts index 31f2157cd7d7..743ef42df23e 100644 --- a/arch/arm/boot/dts/msm8660-surf.dts +++ b/arch/arm/boot/dts/msm8660-surf.dts @@ -16,19 +16,13 @@ }; timer@2000004 { - compatible = "qcom,msm-gpt", "qcom,msm-timer"; - interrupts = <1 1 0x301>; - reg = <0x02000004 0x10>; - clock-frequency = <32768>; - cpu-offset = <0x40000>; - }; - - timer@2000024 { - compatible = "qcom,msm-dgt", "qcom,msm-timer"; - interrupts = <1 0 0x301>; - reg = <0x02000024 0x10>, - <0x02000034 0x4>; - clock-frequency = <6750000>; + compatible = "qcom,scss-timer", "qcom,msm-timer"; + interrupts = <1 0 0x301>, + <1 1 0x301>, + <1 2 0x301>; + reg = <0x02000000 0x100>; + clock-frequency = <27000000>, + <32768>; cpu-offset = <0x40000>; }; diff --git a/arch/arm/boot/dts/msm8960-cdp.dts b/arch/arm/boot/dts/msm8960-cdp.dts index 9e621b5ad3dd..3ae51fb02e17 100644 --- a/arch/arm/boot/dts/msm8960-cdp.dts +++ b/arch/arm/boot/dts/msm8960-cdp.dts @@ -15,20 +15,14 @@ < 0x02002000 0x1000 >; }; - timer@200a004 { - compatible = "qcom,msm-gpt", "qcom,msm-timer"; - interrupts = <1 2 0x301>; - reg = <0x0200a004 0x10>; - clock-frequency = <32768>; - cpu-offset = <0x80000>; - }; - - timer@200a024 { - compatible = "qcom,msm-dgt", "qcom,msm-timer"; - interrupts = <1 1 0x301>; - reg = <0x0200a024 0x10>, - <0x0200a034 0x4>; - clock-frequency = <6750000>; + timer@200a000 { + compatible = "qcom,kpss-timer", "qcom,msm-timer"; + interrupts = <1 1 0x301>, + <1 2 0x301>, + <1 3 0x301>; + reg = <0x0200a000 0x100>; + clock-frequency = <27000000>, + <32768>; cpu-offset = <0x80000>; }; diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c index 2969027f02fa..165e33b9b1ee 100644 --- a/arch/arm/mach-msm/timer.c +++ b/arch/arm/mach-msm/timer.c @@ -36,6 +36,7 @@ #define TIMER_ENABLE_CLR_ON_MATCH_EN BIT(1) #define TIMER_ENABLE_EN BIT(0) #define TIMER_CLEAR 0x000C +#define DGT_CLK_CTL 0x10 #define DGT_CLK_CTL_DIV_4 0x3 #define GPT_HZ 32768 @@ -214,13 +215,9 @@ err: } #ifdef CONFIG_OF -static const struct of_device_id msm_dgt_match[] __initconst = { - { .compatible = "qcom,msm-dgt" }, - { }, -}; - -static const struct of_device_id msm_gpt_match[] __initconst = { - { .compatible = "qcom,msm-gpt" }, +static const struct of_device_id msm_timer_match[] __initconst = { + { .compatible = "qcom,kpss-timer" }, + { .compatible = "qcom,scss-timer" }, { }, }; @@ -231,33 +228,29 @@ void __init msm_dt_timer_init(void) int irq; struct resource res; u32 percpu_offset; - void __iomem *dgt_clk_ctl; + void __iomem *base; + void __iomem *cpu0_base; - np = of_find_matching_node(NULL, msm_gpt_match); + np = of_find_matching_node(NULL, msm_timer_match); if (!np) { - pr_err("Can't find GPT DT node\n"); + pr_err("Can't find msm timer DT node\n"); return; } - event_base = of_iomap(np, 0); - if (!event_base) { + base = of_iomap(np, 0); + if (!base) { pr_err("Failed to map event base\n"); return; } - irq = irq_of_parse_and_map(np, 0); + /* We use GPT0 for the clockevent */ + irq = irq_of_parse_and_map(np, 1); if (irq <= 0) { pr_err("Can't get irq\n"); return; } - of_node_put(np); - - np = of_find_matching_node(NULL, msm_dgt_match); - if (!np) { - pr_err("Can't find DGT DT node\n"); - return; - } + /* We use CPU0's DGT for the clocksource */ if (of_property_read_u32(np, "cpu-offset", &percpu_offset)) percpu_offset = 0; @@ -266,45 +259,39 @@ void __init msm_dt_timer_init(void) return; } - source_base = ioremap(res.start + percpu_offset, resource_size(&res)); - if (!source_base) { + cpu0_base = ioremap(res.start + percpu_offset, resource_size(&res)); + if (!cpu0_base) { pr_err("Failed to map source base\n"); return; } - if (!of_address_to_resource(np, 1, &res)) { - dgt_clk_ctl = ioremap(res.start + percpu_offset, - resource_size(&res)); - if (!dgt_clk_ctl) { - pr_err("Failed to map DGT control base\n"); - return; - } - writel_relaxed(DGT_CLK_CTL_DIV_4, dgt_clk_ctl); - iounmap(dgt_clk_ctl); - } - if (of_property_read_u32(np, "clock-frequency", &freq)) { pr_err("Unknown frequency\n"); return; } of_node_put(np); + event_base = base + 0x4; + source_base = cpu0_base + 0x24; + freq /= 4; + writel_relaxed(DGT_CLK_CTL_DIV_4, source_base + DGT_CLK_CTL); + msm_timer_init(freq, 32, irq, !!percpu_offset); } #endif -static int __init msm_timer_map(phys_addr_t event, phys_addr_t source) +static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source) { - event_base = ioremap(event, SZ_64); - if (!event_base) { - pr_err("Failed to map event base\n"); - return 1; - } - source_base = ioremap(source, SZ_64); - if (!source_base) { - pr_err("Failed to map source base\n"); - return 1; + void __iomem *base; + + base = ioremap(addr, SZ_256); + if (!base) { + pr_err("Failed to map timer base\n"); + return -ENOMEM; } + event_base = base + event; + source_base = base + source; + return 0; } @@ -312,7 +299,7 @@ void __init msm7x01_timer_init(void) { struct clocksource *cs = &msm_clocksource; - if (msm_timer_map(0xc0100000, 0xc0100010)) + if (msm_timer_map(0xc0100000, 0x0, 0x10)) return; cs->read = msm_read_timer_count_shift; cs->mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)); @@ -323,14 +310,14 @@ void __init msm7x01_timer_init(void) void __init msm7x30_timer_init(void) { - if (msm_timer_map(0xc0100004, 0xc0100024)) + if (msm_timer_map(0xc0100000, 0x4, 0x24)) return; msm_timer_init(24576000 / 4, 32, 1, false); } void __init qsd8x50_timer_init(void) { - if (msm_timer_map(0xAC100000, 0xAC100010)) + if (msm_timer_map(0xAC100000, 0x0, 0x10)) return; msm_timer_init(19200000 / 4, 32, 7, false); } -- cgit v1.2.3 From 842059aa4796d7c59bc3801d48896ba06b1a1287 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Sun, 24 Mar 2013 02:25:56 -0300 Subject: [media] documentation: DocBook/media : Fix typo in dvbproperty.xml Correct spelling typos. Signed-off-by: Masanari Iida Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/dvb/dvbproperty.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/dvb/dvbproperty.xml b/Documentation/DocBook/media/dvb/dvbproperty.xml index 4a5eaeed0b9e..31dc4dfd1d6a 100644 --- a/Documentation/DocBook/media/dvb/dvbproperty.xml +++ b/Documentation/DocBook/media/dvb/dvbproperty.xml @@ -1,6 +1,6 @@
<constant>FE_GET_PROPERTY/FE_SET_PROPERTY</constant> -This section describes the DVB version 5 extention of the DVB-API, also +This section describes the DVB version 5 extension of the DVB-API, also called "S2API", as this API were added to provide support for DVB-S2. It was designed to be able to replace the old frontend API. Yet, the DISEQC and the capability ioctls weren't implemented yet via the new way. @@ -952,7 +952,7 @@ enum fe_interleaving { Measures the amount of bits received before the inner code block, during the same period as DTV_STAT_PRE_ERROR_BIT_COUNT measurement was taken. It should be noticed that this measurement can be smaller than the total amount of bits on the transport stream, - as the frontend may need to manually restart the measurement, loosing some data between each measurement interval. + as the frontend may need to manually restart the measurement, losing some data between each measurement interval. This measurement is monotonically increased, as the frontend gets more bit count measurements. The frontend may reset it when a channel/transponder is tuned. Possible scales for this metric are: @@ -981,7 +981,7 @@ enum fe_interleaving { Measures the amount of bits received after the inner coding, during the same period as DTV_STAT_POST_ERROR_BIT_COUNT measurement was taken. It should be noticed that this measurement can be smaller than the total amount of bits on the transport stream, - as the frontend may need to manually restart the measurement, loosing some data between each measurement interval. + as the frontend may need to manually restart the measurement, losing some data between each measurement interval. This measurement is monotonically increased, as the frontend gets more bit count measurements. The frontend may reset it when a channel/transponder is tuned. Possible scales for this metric are: -- cgit v1.2.3 From 9ca5470cc1433200698a43de2d6e683815e536e6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 17 Mar 2013 10:34:04 -0300 Subject: [media] v4l2-ctrls: add V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER control Control whether video sequence headers should be repeated. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 6 ++++++ drivers/media/v4l2-core/v4l2-ctrls.c | 2 ++ include/uapi/linux/v4l2-controls.h | 1 + 3 files changed, 9 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 9e8f85498678..b4952e23201b 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -2299,6 +2299,12 @@ Possible values are: + + V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER  + boolean + Repeat the video sequence headers. Repeating these +headers makes random access to the video stream easier. Applicable to the MPEG1, 2 and 4 encoder. + V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER  boolean diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index b36d1ec13ee6..f662df3bfe2d 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -695,6 +695,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_DEC_PTS: return "Video Decoder PTS"; case V4L2_CID_MPEG_VIDEO_DEC_FRAME: return "Video Decoder Frame Count"; case V4L2_CID_MPEG_VIDEO_VBV_DELAY: return "Initial Delay for VBV Control"; + case V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER: return "Repeat Sequence Header"; /* CAMERA controls */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -844,6 +845,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE: case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL: + case V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER: case V4L2_CID_WIDE_DYNAMIC_RANGE: case V4L2_CID_IMAGE_STABILIZATION: *type = V4L2_CTRL_TYPE_BOOLEAN; diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 7eab0b91827b..844dc0205037 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -360,6 +360,7 @@ enum v4l2_mpeg_video_multi_slice_mode { #define V4L2_CID_MPEG_VIDEO_DEC_PTS (V4L2_CID_MPEG_BASE+223) #define V4L2_CID_MPEG_VIDEO_DEC_FRAME (V4L2_CID_MPEG_BASE+224) #define V4L2_CID_MPEG_VIDEO_VBV_DELAY (V4L2_CID_MPEG_BASE+225) +#define V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER (V4L2_CID_MPEG_BASE+226) #define V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP (V4L2_CID_MPEG_BASE+300) #define V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP (V4L2_CID_MPEG_BASE+301) -- cgit v1.2.3 From e4d2a6162d2a0a27be16b75da36f6bba64af63bc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 9 Mar 2013 13:14:32 -0300 Subject: [media] tuner: add Sony BTF tuners This adds support for three Sony BTF tuners: TUNER_SONY_BTF_PG472Z: PAL+SECAM TUNER_SONY_BTF_PK467Z: NTSC-M-JP TUNER_SONY_BTF_PB463Z: NTSC-M These come from the go7007 staging driver where they were implemented in the wis-sony-tuner i2c driver. Adding support for these tuners to tuner-types.c is the first step towards removing the wis-sony-tuner driver. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.tuner | 3 ++ drivers/media/tuners/tuner-types.c | 69 ++++++++++++++++++++++++++++++ drivers/staging/media/go7007/go7007-usb.c | 1 + drivers/staging/media/go7007/go7007-v4l2.c | 1 + drivers/staging/media/go7007/wis-i2c.h | 6 --- include/media/tuner.h | 4 ++ 6 files changed, 78 insertions(+), 6 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.tuner b/Documentation/video4linux/CARDLIST.tuner index c83f6e418879..5b83a3ff15c2 100644 --- a/Documentation/video4linux/CARDLIST.tuner +++ b/Documentation/video4linux/CARDLIST.tuner @@ -86,3 +86,6 @@ tuner=85 - Philips FQ1236 MK5 tuner=86 - Tena TNF5337 MFD tuner=87 - Xceive 4000 tuner tuner=88 - Xceive 5000C tuner +tuner=89 - Sony PAL+SECAM (BTF-PG472Z) +tuner=90 - Sony NTSC-M-JP (BTF-PK467Z) +tuner=91 - Sony NTSC-M (BTF-PB463Z) diff --git a/drivers/media/tuners/tuner-types.c b/drivers/media/tuners/tuner-types.c index 2da4440c16ee..98bc15a388be 100644 --- a/drivers/media/tuners/tuner-types.c +++ b/drivers/media/tuners/tuner-types.c @@ -1381,6 +1381,58 @@ static struct tuner_params tuner_philips_fq1236_mk5_params[] = { }, }; +/* --------- Sony BTF-PG472Z PAL/SECAM ------- */ + +static struct tuner_range tuner_sony_btf_pg472z_ranges[] = { + { 16 * 144.25 /*MHz*/, 0xc6, 0x01, }, + { 16 * 427.25 /*MHz*/, 0xc6, 0x02, }, + { 16 * 999.99 , 0xc6, 0x04, }, +}; + +static struct tuner_params tuner_sony_btf_pg472z_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_sony_btf_pg472z_ranges, + .count = ARRAY_SIZE(tuner_sony_btf_pg472z_ranges), + .has_tda9887 = 1, + .port1_active = 1, + .port2_invert_for_secam_lc = 1, + }, +}; + +/* 90-99 */ +/* --------- Sony BTF-PG467Z NTSC-M-JP ------- */ + +static struct tuner_range tuner_sony_btf_pg467z_ranges[] = { + { 16 * 220.25 /*MHz*/, 0xc6, 0x01, }, + { 16 * 467.25 /*MHz*/, 0xc6, 0x02, }, + { 16 * 999.99 , 0xc6, 0x04, }, +}; + +static struct tuner_params tuner_sony_btf_pg467z_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_sony_btf_pg467z_ranges, + .count = ARRAY_SIZE(tuner_sony_btf_pg467z_ranges), + }, +}; + +/* --------- Sony BTF-PG463Z NTSC-M ------- */ + +static struct tuner_range tuner_sony_btf_pg463z_ranges[] = { + { 16 * 130.25 /*MHz*/, 0xc6, 0x01, }, + { 16 * 364.25 /*MHz*/, 0xc6, 0x02, }, + { 16 * 999.99 , 0xc6, 0x04, }, +}; + +static struct tuner_params tuner_sony_btf_pg463z_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_sony_btf_pg463z_ranges, + .count = ARRAY_SIZE(tuner_sony_btf_pg463z_ranges), + }, +}; + /* --------------------------------------------------------------------- */ struct tunertype tuners[] = { @@ -1872,6 +1924,23 @@ struct tunertype tuners[] = { .name = "Xceive 5000C tuner", /* see xc5000.c for details */ }, + [TUNER_SONY_BTF_PG472Z] = { + .name = "Sony BTF-PG472Z PAL/SECAM", + .params = tuner_sony_btf_pg472z_params, + .count = ARRAY_SIZE(tuner_sony_btf_pg472z_params), + }, + + /* 90-99 */ + [TUNER_SONY_BTF_PK467Z] = { + .name = "Sony BTF-PK467Z NTSC-M-JP", + .params = tuner_sony_btf_pg467z_params, + .count = ARRAY_SIZE(tuner_sony_btf_pg467z_params), + }, + [TUNER_SONY_BTF_PB463Z] = { + .name = "Sony BTF-PB463Z NTSC-M", + .params = tuner_sony_btf_pg463z_params, + .count = ARRAY_SIZE(tuner_sony_btf_pg463z_params), + }, }; EXPORT_SYMBOL(tuners); diff --git a/drivers/staging/media/go7007/go7007-usb.c b/drivers/staging/media/go7007/go7007-usb.c index 914b247e9652..3333a8f3b654 100644 --- a/drivers/staging/media/go7007/go7007-usb.c +++ b/drivers/staging/media/go7007/go7007-usb.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "go7007-priv.h" #include "wis-i2c.h" diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index 29fe94da5f71..4ad383ad8758 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -1238,6 +1238,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, if (!go->i2c_adapter_online) return -EIO; + strlcpy(t->name, "Tuner", sizeof(t->name)); return call_all(&go->v4l2_dev, tuner, g_tuner, t); } diff --git a/drivers/staging/media/go7007/wis-i2c.h b/drivers/staging/media/go7007/wis-i2c.h index 6d09c06c8560..97763db80bac 100644 --- a/drivers/staging/media/go7007/wis-i2c.h +++ b/drivers/staging/media/go7007/wis-i2c.h @@ -34,9 +34,3 @@ struct video_decoder_resolution { #define DECODER_SET_RESOLUTION _IOW('d', 200, struct video_decoder_resolution) #define DECODER_SET_CHANNEL _IOW('d', 201, int) - -/* Sony tuner types */ - -#define TUNER_SONY_BTF_PG472Z 200 -#define TUNER_SONY_BTF_PK467Z 201 -#define TUNER_SONY_BTF_PB463Z 202 diff --git a/include/media/tuner.h b/include/media/tuner.h index 926aff9bdf65..24eaafe461bd 100644 --- a/include/media/tuner.h +++ b/include/media/tuner.h @@ -138,6 +138,10 @@ #define TUNER_XC4000 87 /* Xceive Silicon Tuner */ #define TUNER_XC5000C 88 /* Xceive Silicon Tuner */ +#define TUNER_SONY_BTF_PG472Z 89 /* PAL+SECAM */ +#define TUNER_SONY_BTF_PK467Z 90 /* NTSC_JP */ +#define TUNER_SONY_BTF_PB463Z 91 /* NTSC */ + /* tv card specific */ #define TDA9887_PRESENT (1<<0) #define TDA9887_PORT1_INACTIVE (1<<1) -- cgit v1.2.3 From 098cbc38c27dee66e5bc7f088c30b96e5cc96510 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 24 Mar 2013 06:09:29 -0300 Subject: [media] DocBook/media/v4l: remove the documentation of the obsolete dv_preset API This API is no longer used by any driver and so can be removed from the documentation. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/common.xml | 14 -- Documentation/DocBook/media/v4l/compat.xml | 4 +- Documentation/DocBook/media/v4l/v4l2.xml | 3 - .../DocBook/media/v4l/vidioc-enum-dv-presets.xml | 240 --------------------- .../DocBook/media/v4l/vidioc-enuminput.xml | 5 - .../DocBook/media/v4l/vidioc-enumoutput.xml | 5 - .../DocBook/media/v4l/vidioc-g-dv-preset.xml | 113 ---------- .../DocBook/media/v4l/vidioc-query-dv-preset.xml | 78 ------- 8 files changed, 2 insertions(+), 460 deletions(-) delete mode 100644 Documentation/DocBook/media/v4l/vidioc-enum-dv-presets.xml delete mode 100644 Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml delete mode 100644 Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/common.xml b/Documentation/DocBook/media/v4l/common.xml index ae06afbbb3a9..1ddf354aa997 100644 --- a/Documentation/DocBook/media/v4l/common.xml +++ b/Documentation/DocBook/media/v4l/common.xml @@ -749,15 +749,6 @@ polarities, frontporch, backporch etc. The linux/v4l2-dv-timings.h and standards. - - - DV Presets: Digital Video (DV) presets (deprecated). - These are IDs representing a -video timing at the input/output. Presets are pre-defined timings implemented -by the hardware according to video standards. A __u32 data type is used to represent -a preset unlike the bit mask that is used in &v4l2-std-id; allowing future extensions -to support as many different presets as needed. This API is deprecated in favor of the DV Timings -API. To enumerate and query the attributes of the DV timings supported by a device, @@ -766,11 +757,6 @@ API. &VIDIOC-S-DV-TIMINGS; ioctl and to get current DV timings they use the &VIDIOC-G-DV-TIMINGS; ioctl. To detect the DV timings as seen by the video receiver applications use the &VIDIOC-QUERY-DV-TIMINGS; ioctl. - To enumerate and query the attributes of DV presets supported by a device, -applications use the &VIDIOC-ENUM-DV-PRESETS; ioctl. To get the current DV preset, -applications use the &VIDIOC-G-DV-PRESET; ioctl and to set a preset they use the -&VIDIOC-S-DV-PRESET; ioctl. To detect the preset as seen by the video receiver applications -use the &VIDIOC-QUERY-DV-PRESET; ioctl. Applications can make use of the and flags to decide what ioctls are available to set the video timings for the device. diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index 104a1a2b8849..effa5094a2f8 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2625,8 +2625,8 @@ interfaces and should not be implemented in new drivers. . - &VIDIOC-G-DV-PRESET;, &VIDIOC-S-DV-PRESET;, &VIDIOC-ENUM-DV-PRESETS; and - &VIDIOC-QUERY-DV-PRESET; ioctls. Use the DV Timings API (). + VIDIOC_G_DV_PRESET, VIDIOC_S_DV_PRESET, VIDIOC_ENUM_DV_PRESETS and + VIDIOC_QUERY_DV_PRESET ioctls. Use the DV Timings API (). VIDIOC_SUBDEV_G_CROP and diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index a3cce18384e9..32a10eef3958 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -544,7 +544,6 @@ and discussions on the V4L mailing list. &sub-encoder-cmd; &sub-enumaudio; &sub-enumaudioout; - &sub-enum-dv-presets; &sub-enum-dv-timings; &sub-enum-fmt; &sub-enum-framesizes; @@ -558,7 +557,6 @@ and discussions on the V4L mailing list. &sub-g-audioout; &sub-g-crop; &sub-g-ctrl; - &sub-g-dv-preset; &sub-g-dv-timings; &sub-g-enc-index; &sub-g-ext-ctrls; @@ -582,7 +580,6 @@ and discussions on the V4L mailing list. &sub-querybuf; &sub-querycap; &sub-queryctrl; - &sub-query-dv-preset; &sub-query-dv-timings; &sub-querystd; &sub-reqbufs; diff --git a/Documentation/DocBook/media/v4l/vidioc-enum-dv-presets.xml b/Documentation/DocBook/media/v4l/vidioc-enum-dv-presets.xml deleted file mode 100644 index fced5fb0dbf0..000000000000 --- a/Documentation/DocBook/media/v4l/vidioc-enum-dv-presets.xml +++ /dev/null @@ -1,240 +0,0 @@ - - - ioctl VIDIOC_ENUM_DV_PRESETS - &manvol; - - - - VIDIOC_ENUM_DV_PRESETS - Enumerate supported Digital Video presets - - - - - - int ioctl - int fd - int request - struct v4l2_dv_enum_preset *argp - - - - - - Arguments - - - - fd - - &fd; - - - - request - - VIDIOC_ENUM_DV_PRESETS - - - - argp - - - - - - - - - Description - - This ioctl is deprecated. - New drivers and applications should use &VIDIOC-ENUM-DV-TIMINGS; instead. - - - To query the attributes of a DV preset, applications initialize the -index field and zero the reserved array of &v4l2-dv-enum-preset; -and call the VIDIOC_ENUM_DV_PRESETS ioctl with a pointer to this -structure. Drivers fill the rest of the structure or return an -&EINVAL; when the index is out of bounds. To enumerate all DV Presets supported, -applications shall begin at index zero, incrementing by one until the -driver returns EINVAL. Drivers may enumerate a -different set of DV presets after switching the video input or -output. - - - struct <structname>v4l2_dv_enum_presets</structname> - - &cs-str; - - - __u32 - index - Number of the DV preset, set by the -application. - - - __u32 - preset - This field identifies one of the DV preset values listed in . - - - __u8 - name[24] - Name of the preset, a NUL-terminated ASCII string, for example: "720P-60", "1080I-60". This information is -intended for the user. - - - __u32 - width - Width of the active video in pixels for the DV preset. - - - __u32 - height - Height of the active video in lines for the DV preset. - - - __u32 - reserved[4] - Reserved for future extensions. Drivers must set the array to zero. - - - -
- - - struct <structname>DV Presets</structname> - - &cs-str; - - - Preset - Preset value - Description - - - - - - - - V4L2_DV_INVALID - 0 - Invalid preset value. - - - V4L2_DV_480P59_94 - 1 - 720x480 progressive video at 59.94 fps as per BT.1362. - - - V4L2_DV_576P50 - 2 - 720x576 progressive video at 50 fps as per BT.1362. - - - V4L2_DV_720P24 - 3 - 1280x720 progressive video at 24 fps as per SMPTE 296M. - - - V4L2_DV_720P25 - 4 - 1280x720 progressive video at 25 fps as per SMPTE 296M. - - - V4L2_DV_720P30 - 5 - 1280x720 progressive video at 30 fps as per SMPTE 296M. - - - V4L2_DV_720P50 - 6 - 1280x720 progressive video at 50 fps as per SMPTE 296M. - - - V4L2_DV_720P59_94 - 7 - 1280x720 progressive video at 59.94 fps as per SMPTE 274M. - - - V4L2_DV_720P60 - 8 - 1280x720 progressive video at 60 fps as per SMPTE 274M/296M. - - - V4L2_DV_1080I29_97 - 9 - 1920x1080 interlaced video at 29.97 fps as per BT.1120/SMPTE 274M. - - - V4L2_DV_1080I30 - 10 - 1920x1080 interlaced video at 30 fps as per BT.1120/SMPTE 274M. - - - V4L2_DV_1080I25 - 11 - 1920x1080 interlaced video at 25 fps as per BT.1120. - - - V4L2_DV_1080I50 - 12 - 1920x1080 interlaced video at 50 fps as per SMPTE 296M. - - - V4L2_DV_1080I60 - 13 - 1920x1080 interlaced video at 60 fps as per SMPTE 296M. - - - V4L2_DV_1080P24 - 14 - 1920x1080 progressive video at 24 fps as per SMPTE 296M. - - - V4L2_DV_1080P25 - 15 - 1920x1080 progressive video at 25 fps as per SMPTE 296M. - - - V4L2_DV_1080P30 - 16 - 1920x1080 progressive video at 30 fps as per SMPTE 296M. - - - V4L2_DV_1080P50 - 17 - 1920x1080 progressive video at 50 fps as per BT.1120. - - - V4L2_DV_1080P60 - 18 - 1920x1080 progressive video at 60 fps as per BT.1120. - - - -
-
- - - &return-value; - - - - EINVAL - - The &v4l2-dv-enum-preset; index -is out of bounds. - - - - ENODATA - - Digital video presets are not supported for this input or output. - - - - -
diff --git a/Documentation/DocBook/media/v4l/vidioc-enuminput.xml b/Documentation/DocBook/media/v4l/vidioc-enuminput.xml index 3c9a81305ad4..493a39a8ef21 100644 --- a/Documentation/DocBook/media/v4l/vidioc-enuminput.xml +++ b/Documentation/DocBook/media/v4l/vidioc-enuminput.xml @@ -277,11 +277,6 @@ input/output interface to linux-media@vger.kernel.org on 19 Oct 2009. &cs-def; - - V4L2_IN_CAP_PRESETS - 0x00000001 - This input supports setting DV presets by using VIDIOC_S_DV_PRESET. - V4L2_IN_CAP_DV_TIMINGS 0x00000002 diff --git a/Documentation/DocBook/media/v4l/vidioc-enumoutput.xml b/Documentation/DocBook/media/v4l/vidioc-enumoutput.xml index f4ab0798545d..2654e097df39 100644 --- a/Documentation/DocBook/media/v4l/vidioc-enumoutput.xml +++ b/Documentation/DocBook/media/v4l/vidioc-enumoutput.xml @@ -162,11 +162,6 @@ input/output interface to linux-media@vger.kernel.org on 19 Oct 2009. &cs-def; - - V4L2_OUT_CAP_PRESETS - 0x00000001 - This output supports setting DV presets by using VIDIOC_S_DV_PRESET. - V4L2_OUT_CAP_DV_TIMINGS 0x00000002 diff --git a/Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml b/Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml deleted file mode 100644 index b9ea37634f6c..000000000000 --- a/Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml +++ /dev/null @@ -1,113 +0,0 @@ - - - ioctl VIDIOC_G_DV_PRESET, VIDIOC_S_DV_PRESET - &manvol; - - - - VIDIOC_G_DV_PRESET - VIDIOC_S_DV_PRESET - Query or select the DV preset of the current input or output - - - - - - int ioctl - int fd - int request - struct v4l2_dv_preset *argp - - - - - - Arguments - - - - fd - - &fd; - - - - request - - VIDIOC_G_DV_PRESET, VIDIOC_S_DV_PRESET - - - - argp - - - - - - - - - Description - - These ioctls are deprecated. - New drivers and applications should use &VIDIOC-G-DV-TIMINGS; and &VIDIOC-S-DV-TIMINGS; - instead. - - - To query and select the current DV preset, applications -use the VIDIOC_G_DV_PRESET and VIDIOC_S_DV_PRESET -ioctls which take a pointer to a &v4l2-dv-preset; type as argument. -Applications must zero the reserved array in &v4l2-dv-preset;. -VIDIOC_G_DV_PRESET returns a dv preset in the field -preset of &v4l2-dv-preset;. - - VIDIOC_S_DV_PRESET accepts a pointer to a &v4l2-dv-preset; -that has the preset value to be set. Applications must zero the reserved array in &v4l2-dv-preset;. -If the preset is not supported, it returns an &EINVAL; - - - - &return-value; - - - - EINVAL - - This ioctl is not supported, or the -VIDIOC_S_DV_PRESET,VIDIOC_S_DV_PRESET parameter was unsuitable. - - - - ENODATA - - Digital video presets are not supported for this input or output. - - - - EBUSY - - The device is busy and therefore can not change the preset. - - - - - - struct <structname>v4l2_dv_preset</structname> - - &cs-str; - - - __u32 - preset - Preset value to represent the digital video timings - - - __u32 - reserved[4] - Reserved fields for future use - - - -
-
-
diff --git a/Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml b/Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml deleted file mode 100644 index 68b49d09e245..000000000000 --- a/Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml +++ /dev/null @@ -1,78 +0,0 @@ - - - ioctl VIDIOC_QUERY_DV_PRESET - &manvol; - - - - VIDIOC_QUERY_DV_PRESET - Sense the DV preset received by the current -input - - - - - - int ioctl - int fd - int request - struct v4l2_dv_preset *argp - - - - - - Arguments - - - - fd - - &fd; - - - - request - - VIDIOC_QUERY_DV_PRESET - - - - argp - - - - - - - - - Description - - This ioctl is deprecated. - New drivers and applications should use &VIDIOC-QUERY-DV-TIMINGS; instead. - - - The hardware may be able to detect the current DV preset -automatically, similar to sensing the video standard. To do so, applications -call VIDIOC_QUERY_DV_PRESET with a pointer to a -&v4l2-dv-preset; type. Once the hardware detects a preset, that preset is -returned in the preset field of &v4l2-dv-preset;. If the preset could not be -detected because there was no signal, or the signal was unreliable, or the -signal did not map to a supported preset, then the value V4L2_DV_INVALID is -returned. - - - - &return-value; - - - - ENODATA - - Digital video presets are not supported for this input or output. - - - - - -- cgit v1.2.3 From 4e5f1130070ebfdde1b0ea6277e881404e5f34ce Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 24 Mar 2013 06:08:00 -0300 Subject: [media] DocBook/media/v4l: Update version number and document 3.10 changes Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/compat.xml | 13 +++++++++++++ Documentation/DocBook/media/v4l/v4l2.xml | 12 ++++++++++++ 2 files changed, 25 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index effa5094a2f8..5dab33837344 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2493,6 +2493,19 @@ that used it. It was originally scheduled for removal in 2.6.35.
+
+ V4L2 in Linux 3.10 + + + Removed obsolete and unused DV_PRESET ioctls + VIDIOC_G_DV_PRESET, VIDIOC_S_DV_PRESET, VIDIOC_QUERY_DV_PRESET and + VIDIOC_ENUM_DV_PRESET. Remove the related v4l2_input/output capability + flags V4L2_IN_CAP_PRESETS and V4L2_OUT_CAP_PRESETS. + + + +
+
Relation of V4L2 to other Linux multimedia APIs diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index 32a10eef3958..dae009aa2440 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -124,6 +124,7 @@ Remote Controller chapter. 2010 2011 2012 + 2013 Bill Dirks, Michael H. Schimek, Hans Verkuil, Martin Rubli, Andy Walls, Muralidharan Karicheri, Mauro Carvalho Chehab, Pawel Osciak @@ -139,6 +140,17 @@ structs, ioctls) must be noted in more detail in the history chapter (compat.xml), along with the possible impact on existing drivers and applications. --> + + 3.10 + 2013-03-24 + hv + Remove obsolete and unused DV_PRESET ioctls: + VIDIOC_G_DV_PRESET, VIDIOC_S_DV_PRESET, VIDIOC_QUERY_DV_PRESET and + VIDIOC_ENUM_DV_PRESET. Remove the related v4l2_input/output capability + flags V4L2_IN_CAP_PRESETS and V4L2_OUT_CAP_PRESETS. + + + 3.9 2012-12-03 -- cgit v1.2.3 From 5e95329b701c4edf6c4d72487ec0369fa148c0bd Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 22 Mar 2013 10:50:50 +0000 Subject: dsa: add device tree bindings to register DSA switches This patch adds support for registering DSA switches using Device Tree bindings. Note that we support programming the switch routing table even though no in-tree user seems to require it. I tested this on Armada 370 with a Marvell 88E6172 (not supported by mainline yet). Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/dsa/dsa.txt | 91 +++++++++ net/dsa/dsa.c | 233 +++++++++++++++++++++- 2 files changed, 319 insertions(+), 5 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/dsa/dsa.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/net/dsa/dsa.txt b/Documentation/devicetree/bindings/net/dsa/dsa.txt new file mode 100644 index 000000000000..db92f55ef838 --- /dev/null +++ b/Documentation/devicetree/bindings/net/dsa/dsa.txt @@ -0,0 +1,91 @@ +Marvell Distributed Switch Architecture Device Tree Bindings +------------------------------------------------------------ + +Required properties: +- compatible : Should be "marvell,dsa" +- #address-cells : Must be 2, first cell is the address on the MDIO bus + and second cell is the address in the switch tree. + Second cell is used only when cascading/chaining. +- #size-cells : Must be 0 +- dsa,ethernet : Should be a phandle to a valid Ethernet device node +- dsa,mii-bus : Should be a phandle to a valid MDIO bus device node + +Optionnal properties: +- interrupts : property with a value describing the switch + interrupt number (not supported by the driver) + +A DSA node can contain multiple switch chips which are therefore child nodes of +the parent DSA node. The maximum number of allowed child nodes is 4 +(DSA_MAX_SWITCHES). +Each of these switch child nodes should have the following required properties: + +- reg : Describes the switch address on the MII bus +- #address-cells : Must be 1 +- #size-cells : Must be 0 + +A switch may have multiple "port" children nodes + +Each port children node must have the following mandatory properties: +- reg : Describes the port address in the switch +- label : Describes the label associated with this port, special + labels are "cpu" to indicate a CPU port and "dsa" to + indicate an uplink/downlink port. + +Note that a port labelled "dsa" will imply checking for the uplink phandle +described below. + +Optionnal property: +- link : Should be a phandle to another switch's DSA port. + This property is only used when switches are being + chained/cascaded together. + +Example: + + dsa@0 { + compatible = "marvell,dsa"; + #address-cells = <1>; + #size-cells = <0>; + + interrupts = <10>; + dsa,ethernet = <ðernet0>; + dsa,mii-bus = <&mii_bus0>; + + switch@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <16 0>; /* MDIO address 16, switch 0 in tree */ + + port@0 { + reg = <0>; + label = "lan1"; + }; + + port@1 { + reg = <1>; + label = "lan2"; + }; + + port@5 { + reg = <5>; + label = "cpu"; + }; + + switch0uplink: port@6 { + reg = <6>; + label = "dsa"; + link = <&switch1uplink>; + }; + }; + + switch@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <17 1>; /* MDIO address 17, switch 1 in tree */ + + switch1uplink: port@0 { + reg = <0>; + label = "dsa"; + link = <&switch0uplink>; + }; + }; + }; diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 2bc62ea857c8..908bc11082db 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -1,6 +1,7 @@ /* * net/dsa/dsa.c - Hardware switch handling * Copyright (c) 2008-2009 Marvell Semiconductor + * Copyright (c) 2013 Florian Fainelli * * 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 @@ -14,6 +15,9 @@ #include #include #include +#include +#include +#include #include "dsa_priv.h" char dsa_driver_version[] = "0.1"; @@ -287,34 +291,239 @@ static struct net_device *dev_to_net_device(struct device *dev) return NULL; } +#ifdef CONFIG_OF +static int dsa_of_setup_routing_table(struct dsa_platform_data *pd, + struct dsa_chip_data *cd, + int chip_index, + struct device_node *link) +{ + int ret; + const __be32 *reg; + int link_port_addr; + int link_sw_addr; + struct device_node *parent_sw; + int len; + + parent_sw = of_get_parent(link); + if (!parent_sw) + return -EINVAL; + + reg = of_get_property(parent_sw, "reg", &len); + if (!reg || (len != sizeof(*reg) * 2)) + return -EINVAL; + + link_sw_addr = be32_to_cpup(reg + 1); + + if (link_sw_addr >= pd->nr_chips) + return -EINVAL; + + /* First time routing table allocation */ + if (!cd->rtable) { + cd->rtable = kmalloc(pd->nr_chips * sizeof(s8), GFP_KERNEL); + if (!cd->rtable) + return -ENOMEM; + + /* default to no valid uplink/downlink */ + memset(cd->rtable, -1, pd->nr_chips * sizeof(s8)); + } + + reg = of_get_property(link, "reg", NULL); + if (!reg) { + ret = -EINVAL; + goto out; + } + + link_port_addr = be32_to_cpup(reg); + + cd->rtable[link_sw_addr] = link_port_addr; + + return 0; +out: + kfree(cd->rtable); + return ret; +} + +static int dsa_of_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *child, *mdio, *ethernet, *port, *link; + struct mii_bus *mdio_bus; + struct platform_device *ethernet_dev; + struct dsa_platform_data *pd; + struct dsa_chip_data *cd; + const char *port_name; + int chip_index, port_index; + const unsigned int *sw_addr, *port_reg; + int ret, i; + + mdio = of_parse_phandle(np, "dsa,mii-bus", 0); + if (!mdio) + return -EINVAL; + + mdio_bus = of_mdio_find_bus(mdio); + if (!mdio_bus) + return -EINVAL; + + ethernet = of_parse_phandle(np, "dsa,ethernet", 0); + if (!ethernet) + return -EINVAL; + + ethernet_dev = of_find_device_by_node(ethernet); + if (!ethernet_dev) + return -ENODEV; + + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + + pdev->dev.platform_data = pd; + pd->netdev = ðernet_dev->dev; + pd->nr_chips = of_get_child_count(np); + if (pd->nr_chips > DSA_MAX_SWITCHES) + pd->nr_chips = DSA_MAX_SWITCHES; + + pd->chip = kzalloc(pd->nr_chips * sizeof(struct dsa_chip_data), + GFP_KERNEL); + if (!pd->chip) { + ret = -ENOMEM; + goto out_free; + } + + chip_index = 0; + for_each_available_child_of_node(np, child) { + cd = &pd->chip[chip_index]; + + cd->mii_bus = &mdio_bus->dev; + + sw_addr = of_get_property(child, "reg", NULL); + if (!sw_addr) + continue; + + cd->sw_addr = be32_to_cpup(sw_addr); + if (cd->sw_addr > PHY_MAX_ADDR) + continue; + + for_each_available_child_of_node(child, port) { + port_reg = of_get_property(port, "reg", NULL); + if (!port_reg) + continue; + + port_index = be32_to_cpup(port_reg); + + port_name = of_get_property(port, "label", NULL); + if (!port_name) + continue; + + cd->port_names[port_index] = kstrdup(port_name, + GFP_KERNEL); + if (!cd->port_names[port_index]) { + ret = -ENOMEM; + goto out_free_chip; + } + + link = of_parse_phandle(port, "link", 0); + + if (!strcmp(port_name, "dsa") && link && + pd->nr_chips > 1) { + ret = dsa_of_setup_routing_table(pd, cd, + chip_index, link); + if (ret) + goto out_free_chip; + } + + if (port_index == DSA_MAX_PORTS) + break; + } + } + + return 0; + +out_free_chip: + for (i = 0; i < pd->nr_chips; i++) { + port_index = 0; + while (pd->chip[i].port_names && + pd->chip[i].port_names[++port_index]) + kfree(pd->chip[i].port_names[port_index]); + kfree(pd->chip[i].rtable); + } + kfree(pd->chip); +out_free: + kfree(pd); + pdev->dev.platform_data = NULL; + return ret; +} + +static void dsa_of_remove(struct platform_device *pdev) +{ + struct dsa_platform_data *pd = pdev->dev.platform_data; + int i; + int port_index; + + if (!pdev->dev.of_node) + return; + + for (i = 0; i < pd->nr_chips; i++) { + port_index = 0; + while (pd->chip[i].port_names && + pd->chip[i].port_names[++port_index]) + kfree(pd->chip[i].port_names[port_index]); + kfree(pd->chip[i].rtable); + } + + kfree(pd->chip); + kfree(pd); +} +#else +static inline int dsa_of_probe(struct platform_device *pdev) +{ + return 0; +} + +static inline void dsa_of_remove(struct platform_device *pdev) +{ +} +#endif + static int dsa_probe(struct platform_device *pdev) { static int dsa_version_printed; struct dsa_platform_data *pd = pdev->dev.platform_data; struct net_device *dev; struct dsa_switch_tree *dst; - int i; + int i, ret; if (!dsa_version_printed++) printk(KERN_NOTICE "Distributed Switch Architecture " "driver version %s\n", dsa_driver_version); + if (pdev->dev.of_node) { + ret = dsa_of_probe(pdev); + if (ret) + return ret; + + pd = pdev->dev.platform_data; + } + if (pd == NULL || pd->netdev == NULL) return -EINVAL; dev = dev_to_net_device(pd->netdev); - if (dev == NULL) - return -EINVAL; + if (dev == NULL) { + ret = -EINVAL; + goto out; + } if (dev->dsa_ptr != NULL) { dev_put(dev); - return -EEXIST; + ret = -EEXIST; + goto out; } dst = kzalloc(sizeof(*dst), GFP_KERNEL); if (dst == NULL) { dev_put(dev); - return -ENOMEM; + ret = -ENOMEM; + goto out; } platform_set_drvdata(pdev, dst); @@ -366,6 +575,11 @@ static int dsa_probe(struct platform_device *pdev) } return 0; + +out: + dsa_of_remove(pdev); + + return ret; } static int dsa_remove(struct platform_device *pdev) @@ -385,6 +599,8 @@ static int dsa_remove(struct platform_device *pdev) dsa_switch_destroy(ds); } + dsa_of_remove(pdev); + return 0; } @@ -392,6 +608,12 @@ static void dsa_shutdown(struct platform_device *pdev) { } +static const struct of_device_id dsa_of_match_table[] = { + { .compatible = "marvell,dsa", }, + {} +}; +MODULE_DEVICE_TABLE(of, dsa_of_match_table); + static struct platform_driver dsa_driver = { .probe = dsa_probe, .remove = dsa_remove, @@ -399,6 +621,7 @@ static struct platform_driver dsa_driver = { .driver = { .name = "dsa", .owner = THIS_MODULE, + .of_match_table = dsa_of_match_table, }, }; -- cgit v1.2.3 From 820eac0ef8b86f7275acd1f8bccfb0b30f17bbe9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 18 Mar 2013 13:20:00 -0300 Subject: [media] DocBook media: fix syntax problems in dvbproperty.xml Caught by xmllint. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/dvb/dvbproperty.xml | 46 ++++++++++++------------- 1 file changed, 22 insertions(+), 24 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/dvb/dvbproperty.xml b/Documentation/DocBook/media/dvb/dvbproperty.xml index 31dc4dfd1d6a..a9b15e34c5b2 100644 --- a/Documentation/DocBook/media/dvb/dvbproperty.xml +++ b/Documentation/DocBook/media/dvb/dvbproperty.xml @@ -903,14 +903,12 @@ enum fe_interleaving { svalue is for signed values of the measure (dB measures) and uvalue is for unsigned values (counters, relative scale) scale - Scale for the value. It can be: -
- + FE_SCALE_NOT_AVAILABLE - The parameter is supported by the frontend, but it was not possible to collect it (could be a transitory or permanent condition) FE_SCALE_DECIBEL - parameter is a signed value, measured in 1/1000 dB FE_SCALE_RELATIVE - parameter is a unsigned value, where 0 means 0% and 65535 means 100%. FE_SCALE_COUNTER - parameter is a unsigned value that counts the occurrence of an event, like bit error, block error, or lapsed time. -
@@ -918,9 +916,9 @@ enum fe_interleaving { Indicates the signal strength level at the analog part of the tuner or of the demod. Possible scales for this metric are: - FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. - FE_SCALE_DECIBEL - signal strength is in 0.0001 dBm units, power measured in miliwatts. This value is generally negative. - FE_SCALE_RELATIVE - The frontend provides a 0% to 100% measurement for power (actually, 0 to 65535). + FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. + FE_SCALE_DECIBEL - signal strength is in 0.0001 dBm units, power measured in miliwatts. This value is generally negative. + FE_SCALE_RELATIVE - The frontend provides a 0% to 100% measurement for power (actually, 0 to 65535).
@@ -928,9 +926,9 @@ enum fe_interleaving { Indicates the Signal to Noise ratio for the main carrier. Possible scales for this metric are: - FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. - FE_SCALE_DECIBEL - Signal/Noise ratio is in 0.0001 dB units. - FE_SCALE_RELATIVE - The frontend provides a 0% to 100% measurement for Signal/Noise (actually, 0 to 65535). + FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. + FE_SCALE_DECIBEL - Signal/Noise ratio is in 0.0001 dB units. + FE_SCALE_RELATIVE - The frontend provides a 0% to 100% measurement for Signal/Noise (actually, 0 to 65535).
@@ -943,8 +941,8 @@ enum fe_interleaving { The frontend may reset it when a channel/transponder is tuned. Possible scales for this metric are: - FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. - FE_SCALE_COUNTER - Number of error bits counted before the inner coding. + FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. + FE_SCALE_COUNTER - Number of error bits counted before the inner coding.
@@ -957,9 +955,9 @@ enum fe_interleaving { The frontend may reset it when a channel/transponder is tuned. Possible scales for this metric are: - FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. - FE_SCALE_COUNTER - Number of bits counted while measuring - DTV_STAT_PRE_ERROR_BIT_COUNT. + FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. + FE_SCALE_COUNTER - Number of bits counted while measuring + DTV_STAT_PRE_ERROR_BIT_COUNT.
@@ -972,8 +970,8 @@ enum fe_interleaving { The frontend may reset it when a channel/transponder is tuned. Possible scales for this metric are: - FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. - FE_SCALE_COUNTER - Number of error bits counted after the inner coding. + FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. + FE_SCALE_COUNTER - Number of error bits counted after the inner coding.
@@ -986,9 +984,9 @@ enum fe_interleaving { The frontend may reset it when a channel/transponder is tuned. Possible scales for this metric are: - FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. - FE_SCALE_COUNTER - Number of bits counted while measuring - DTV_STAT_POST_ERROR_BIT_COUNT. + FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. + FE_SCALE_COUNTER - Number of bits counted while measuring + DTV_STAT_POST_ERROR_BIT_COUNT.
@@ -998,8 +996,8 @@ enum fe_interleaving { The frontend may reset it when a channel/transponder is tuned. Possible scales for this metric are: - FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. - FE_SCALE_COUNTER - Number of error blocks counted after the outer coding. + FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. + FE_SCALE_COUNTER - Number of error blocks counted after the outer coding.
@@ -1011,9 +1009,9 @@ enum fe_interleaving { by DTV-STAT-TOTAL-BLOCK-COUNT. Possible scales for this metric are: - FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. - FE_SCALE_COUNTER - Number of blocks counted while measuring - DTV_STAT_ERROR_BLOCK_COUNT. + FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. + FE_SCALE_COUNTER - Number of blocks counted while measuring + DTV_STAT_ERROR_BLOCK_COUNT.
-- cgit v1.2.3 From b5958dc329f0e4584511c5d8a177122e3d23a657 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 18 Mar 2013 13:28:12 -0300 Subject: [media] DocBook media: add VIDIOC_DBG_G_CHIP_NAME documentation And update the other debug ioctls accordingly. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/v4l2.xml | 4 +- .../DocBook/media/v4l/vidioc-dbg-g-chip-ident.xml | 14 +- .../DocBook/media/v4l/vidioc-dbg-g-chip-name.xml | 234 +++++++++++++++++++++ .../DocBook/media/v4l/vidioc-dbg-g-register.xml | 42 +++- 4 files changed, 280 insertions(+), 14 deletions(-) create mode 100644 Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-name.xml (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index dae009aa2440..ec3a0c7276df 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -156,8 +156,7 @@ applications. --> 2012-12-03 sa, sn Added timestamp types to v4l2_buffer. - Added V4L2_EVENT_CTRL_CH_RANGE control - event changes flag, see . + Added V4L2_EVENT_CTRL_CH_RANGE control event changes flag. @@ -549,6 +548,7 @@ and discussions on the V4L mailing list. &sub-create-bufs; &sub-cropcap; &sub-dbg-g-chip-ident; + &sub-dbg-g-chip-name; &sub-dbg-g-register; &sub-decoder-cmd; &sub-dqevent; diff --git a/Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-ident.xml b/Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-ident.xml index 4ecd966808de..82e43c6c72b8 100644 --- a/Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-ident.xml +++ b/Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-ident.xml @@ -200,10 +200,10 @@ the values from . &cs-def; - V4L2_CHIP_MATCH_HOST + V4L2_CHIP_MATCH_BRIDGE 0 Match the nth chip on the card, zero for the - host chip. Does not match &i2c; chips. + bridge chip. Does not match sub-devices. V4L2_CHIP_MATCH_I2C_DRIVER @@ -220,6 +220,16 @@ the values from . 3 Match the nth anciliary AC97 chip. + + V4L2_CHIP_MATCH_SUBDEV_NAME + 4 + Match the sub-device by name. Can't be used with this ioctl. + + + V4L2_CHIP_MATCH_SUBDEV_IDX + 5 + Match the nth sub-device. Can't be used with this ioctl. + diff --git a/Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-name.xml b/Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-name.xml new file mode 100644 index 000000000000..4921346fabd7 --- /dev/null +++ b/Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-name.xml @@ -0,0 +1,234 @@ + + + ioctl VIDIOC_DBG_G_CHIP_NAME + &manvol; + + + + VIDIOC_DBG_G_CHIP_NAME + Identify the chips on a TV card + + + + + + int ioctl + int fd + int request + struct v4l2_dbg_chip_name +*argp + + + + + + Arguments + + + + fd + + &fd; + + + + request + + VIDIOC_DBG_G_CHIP_NAME + + + + argp + + + + + + + + + Description + + + Experimental + + This is an experimental interface and may change in +the future. + + + For driver debugging purposes this ioctl allows test +applications to query the driver about the chips present on the TV +card. Regular applications must not use it. When you found a chip +specific bug, please contact the linux-media mailing list (&v4l-ml;) +so it can be fixed. + + To query the driver applications must initialize the +match.type and +match.addr or match.name +fields of a &v4l2-dbg-chip-name; +and call VIDIOC_DBG_G_CHIP_NAME with a pointer to +this structure. On success the driver stores information about the +selected chip in the name and +flags fields. On failure the structure +remains unchanged. + + When match.type is +V4L2_CHIP_MATCH_BRIDGE, +match.addr selects the nth bridge 'chip' +on the TV card. You can enumerate all chips by starting at zero and +incrementing match.addr by one until +VIDIOC_DBG_G_CHIP_NAME fails with an &EINVAL;. +The number zero always selects the bridge chip itself, ⪚ the chip +connected to the PCI or USB bus. Non-zero numbers identify specific +parts of the bridge chip such as an AC97 register block. + + When match.type is +V4L2_CHIP_MATCH_SUBDEV_NAME, +match.name contains the name of a sub-device. +For instance +"saa7127 6-0044" will match the saa7127 sub-device +at the given i2c bus. This match type is not very useful for this ioctl +and is here only for consistency. + + + When match.type is +V4L2_CHIP_MATCH_SUBDEV_IDX, +match.addr selects the nth sub-device. This +allows you to enumerate over all sub-devices. + + On success, the name field will +contain a chip name and the flags field will +contain V4L2_CHIP_FL_READABLE if the driver supports +reading registers from the device or V4L2_CHIP_FL_WRITABLE +if the driver supports writing registers to the device. + + We recommended the v4l2-dbg +utility over calling this ioctl directly. It is available from the +LinuxTV v4l-dvb repository; see http://linuxtv.org/repo/ for +access instructions. + + + + struct <structname>v4l2_dbg_match</structname> + + &cs-ustr; + + + __u32 + type + See for a list of +possible types. + + + union + (anonymous) + + + + __u32 + addr + Match a chip by this number, interpreted according +to the type field. + + + + char + name[32] + Match a chip by this name, interpreted according +to the type field. + + + +
+ + + struct <structname>v4l2_dbg_chip_name</structname> + + &cs-str; + + + struct v4l2_dbg_match + match + How to match the chip, see . + + + char + name[32] + The name of the chip. + + + __u32 + flags + Set by the driver. If V4L2_CHIP_FL_READABLE +is set, then the driver supports reading registers from the device. If +V4L2_CHIP_FL_WRITABLE is set, then it supports writing registers. + + + __u32 + reserved[8] + Reserved fields, both application and driver must set these to 0. + + + +
+ + + + Chip Match Types + + &cs-def; + + + V4L2_CHIP_MATCH_BRIDGE + 0 + Match the nth chip on the card, zero for the + bridge chip. Does not match sub-devices. + + + V4L2_CHIP_MATCH_I2C_DRIVER + 1 + Match an &i2c; chip by its driver name. Can't be used with this ioctl. + + + V4L2_CHIP_MATCH_I2C_ADDR + 2 + Match a chip by its 7 bit &i2c; bus address. Can't be used with this ioctl. + + + V4L2_CHIP_MATCH_AC97 + 3 + Match the nth anciliary AC97 chip. Can't be used with this ioctl. + + + V4L2_CHIP_MATCH_SUBDEV_NAME + 4 + Match the sub-device by name. + + + V4L2_CHIP_MATCH_SUBDEV_IDX + 5 + Match the nth sub-device. + + + +
+
+ + + &return-value; + + + + EINVAL + + The match_type is invalid or +no device could be matched. + + + + +
diff --git a/Documentation/DocBook/media/v4l/vidioc-dbg-g-register.xml b/Documentation/DocBook/media/v4l/vidioc-dbg-g-register.xml index a44aebc7997a..3082b4149dbe 100644 --- a/Documentation/DocBook/media/v4l/vidioc-dbg-g-register.xml +++ b/Documentation/DocBook/media/v4l/vidioc-dbg-g-register.xml @@ -87,7 +87,7 @@ written into the register. To read a register applications must initialize the match.type, -match.chip or match.name and +match.addr or match.name and reg fields, and call VIDIOC_DBG_G_REGISTER with a pointer to this structure. On success the driver stores the register value in the @@ -95,11 +95,11 @@ structure. On success the driver stores the register value in the unchanged. When match.type is -V4L2_CHIP_MATCH_HOST, -match.addr selects the nth non-&i2c; chip +V4L2_CHIP_MATCH_BRIDGE, +match.addr selects the nth non-sub-device chip on the TV card. The number zero always selects the host chip, ⪚ the chip connected to the PCI or USB bus. You can find out which chips are -present with the &VIDIOC-DBG-G-CHIP-IDENT; ioctl. +present with the &VIDIOC-DBG-G-CHIP-NAME; ioctl. When match.type is V4L2_CHIP_MATCH_I2C_DRIVER, @@ -109,7 +109,7 @@ For instance supported by the saa7127 driver, regardless of its &i2c; bus address. When multiple chips supported by the same driver are present, the effect of these ioctls is undefined. Again with the -&VIDIOC-DBG-G-CHIP-IDENT; ioctl you can find out which &i2c; chips are +&VIDIOC-DBG-G-CHIP-NAME; ioctl you can find out which &i2c; chips are present. When match.type is @@ -122,19 +122,31 @@ bus address. match.addr selects the nth AC97 chip on the TV card. + When match.type is +V4L2_CHIP_MATCH_SUBDEV_NAME, +match.name contains the sub-device name. +For instance +"saa7127 6-0044" will match this specific saa7127 +sub-device. Again with the &VIDIOC-DBG-G-CHIP-NAME; ioctl you can find +out which sub-devices are present. + + When match.type is +V4L2_CHIP_MATCH_SUBDEV_IDX, +match.addr selects the nth sub-device. + Success not guaranteed Due to a flaw in the Linux &i2c; bus driver these ioctls may return successfully without actually reading or writing a register. To -catch the most likely failure we recommend a &VIDIOC-DBG-G-CHIP-IDENT; +catch the most likely failure we recommend a &VIDIOC-DBG-G-CHIP-NAME; call confirming the presence of the selected &i2c; chip. These ioctls are optional, not all drivers may support them. However when a driver supports these ioctls it must also support -&VIDIOC-DBG-G-CHIP-IDENT;. Conversely it may support -VIDIOC_DBG_G_CHIP_IDENT but not these ioctls. +&VIDIOC-DBG-G-CHIP-NAME;. Conversely it may support +VIDIOC_DBG_G_CHIP_NAME but not these ioctls. VIDIOC_DBG_G_REGISTER and VIDIOC_DBG_S_REGISTER were introduced in Linux @@ -217,10 +229,10 @@ register. &cs-def; - V4L2_CHIP_MATCH_HOST + V4L2_CHIP_MATCH_BRIDGE 0 Match the nth chip on the card, zero for the - host chip. Does not match &i2c; chips. + bridge chip. Does not match sub-devices. V4L2_CHIP_MATCH_I2C_DRIVER @@ -237,6 +249,16 @@ register. 3 Match the nth anciliary AC97 chip. + + V4L2_CHIP_MATCH_SUBDEV_NAME + 4 + Match the sub-device by name. + + + V4L2_CHIP_MATCH_SUBDEV_IDX + 5 + Match the nth sub-device. + -- cgit v1.2.3 From e3f7586310e80be3f75fe03dabda0a64f54d6d73 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 25 Mar 2013 08:49:15 -0300 Subject: [media] DocBook media: document 3.10 changes Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/compat.xml | 4 ++++ Documentation/DocBook/media/v4l/v4l2.xml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index 5dab33837344..8c2b3687a6a4 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2503,6 +2503,10 @@ that used it. It was originally scheduled for removal in 2.6.35. flags V4L2_IN_CAP_PRESETS and V4L2_OUT_CAP_PRESETS. + + Added new debugging ioctl &VIDIOC-DBG-G-CHIP-NAME;. + + diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index ec3a0c7276df..c1f334084213 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -142,12 +142,12 @@ applications. --> 3.10 - 2013-03-24 + 2013-03-25 hv Remove obsolete and unused DV_PRESET ioctls: VIDIOC_G_DV_PRESET, VIDIOC_S_DV_PRESET, VIDIOC_QUERY_DV_PRESET and VIDIOC_ENUM_DV_PRESET. Remove the related v4l2_input/output capability - flags V4L2_IN_CAP_PRESETS and V4L2_OUT_CAP_PRESETS. + flags V4L2_IN_CAP_PRESETS and V4L2_OUT_CAP_PRESETS. Added VIDIOC_DBG_G_CHIP_NAME. -- cgit v1.2.3 From 4c64f1f70cf3e7860bae12d62a31c137a6a4f4a7 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 25 Mar 2013 05:03:38 +0000 Subject: dsa: fix device tree binding documentation typo on #address-cells The device tree binding documentation for dsa explicitely states that a DSA node should have its #address-cells property set to 2, yet the example still used 1, fix that typo. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/dsa/dsa.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/net/dsa/dsa.txt b/Documentation/devicetree/bindings/net/dsa/dsa.txt index db92f55ef838..49f4f7ae3f51 100644 --- a/Documentation/devicetree/bindings/net/dsa/dsa.txt +++ b/Documentation/devicetree/bindings/net/dsa/dsa.txt @@ -43,7 +43,7 @@ Example: dsa@0 { compatible = "marvell,dsa"; - #address-cells = <1>; + #address-cells = <2>; #size-cells = <0>; interrupts = <10>; -- cgit v1.2.3 From 97f00f7120fe3396302693cdc4b1d11bbacad963 Mon Sep 17 00:00:00 2001 From: David Brown Date: Tue, 12 Mar 2013 11:41:50 -0700 Subject: SSBI: Convert SSBI to device tree The SSBI bus is exclusive to the Qualcomm MSM targets, and all SoCs using it will be using device tree. Convert this driver to indentify with device tree. This makes the bus probing a good bit simpler, since the attaching of child nodes can be represented directly in the devicetree, rather than having to be inferred by name. Signed-off-by: David Brown Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/arm/msm/ssbi.txt | 18 +++++ arch/arm/boot/dts/msm8660-surf.dts | 6 ++ arch/arm/boot/dts/msm8960-cdp.dts | 6 ++ drivers/ssbi/ssbi.c | 81 +++++++++------------- 4 files changed, 62 insertions(+), 49 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/msm/ssbi.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/msm/ssbi.txt b/Documentation/devicetree/bindings/arm/msm/ssbi.txt new file mode 100644 index 000000000000..54fd5ced3401 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/ssbi.txt @@ -0,0 +1,18 @@ +* Qualcomm SSBI + +Some Qualcomm MSM devices contain a point-to-point serial bus used to +communicate with a limited range of devices (mostly power management +chips). + +These require the following properties: + +- compatible: "qcom,ssbi" + +- qcom,controller-type + indicates the SSBI bus variant the controller should use to talk + with the slave device. This should be one of "ssbi", "ssbi2", or + "pmic-arbiter". The type chosen is determined by the attached + slave. + +The slave device should be the single child node of the ssbi device +with a compatible field. diff --git a/arch/arm/boot/dts/msm8660-surf.dts b/arch/arm/boot/dts/msm8660-surf.dts index 31f2157cd7d7..67f8670c4d6a 100644 --- a/arch/arm/boot/dts/msm8660-surf.dts +++ b/arch/arm/boot/dts/msm8660-surf.dts @@ -38,4 +38,10 @@ <0x19c00000 0x1000>; interrupts = <0 195 0x0>; }; + + qcom,ssbi@500000 { + compatible = "qcom,ssbi"; + reg = <0x500000 0x1000>; + qcom,controller-type = "pmic-arbiter"; + }; }; diff --git a/arch/arm/boot/dts/msm8960-cdp.dts b/arch/arm/boot/dts/msm8960-cdp.dts index 9e621b5ad3dd..c9b09a813a4b 100644 --- a/arch/arm/boot/dts/msm8960-cdp.dts +++ b/arch/arm/boot/dts/msm8960-cdp.dts @@ -38,4 +38,10 @@ <0x16400000 0x1000>; interrupts = <0 154 0x0>; }; + + qcom,ssbi@500000 { + compatible = "qcom,ssbi"; + reg = <0x500000 0x1000>; + qcom,controller-type = "pmic-arbiter"; + }; }; diff --git a/drivers/ssbi/ssbi.c b/drivers/ssbi/ssbi.c index da086d49d35c..6fbcb25907ff 100644 --- a/drivers/ssbi/ssbi.c +++ b/drivers/ssbi/ssbi.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include /* SSBI 2.0 controller registers */ #define SSBI2_CMD 0x0008 @@ -261,56 +263,13 @@ int msm_ssbi_write(struct device *dev, u16 addr, u8 *buf, int len) } EXPORT_SYMBOL_GPL(msm_ssbi_write); -static int msm_ssbi_add_slave(struct msm_ssbi *ssbi, - const struct msm_ssbi_slave_info *slave) -{ - struct platform_device *slave_pdev; - int ret; - - if (ssbi->slave) { - pr_err("slave already attached??\n"); - return -EBUSY; - } - - slave_pdev = platform_device_alloc(slave->name, -1); - if (!slave_pdev) { - pr_err("cannot allocate pdev for slave '%s'", slave->name); - ret = -ENOMEM; - goto err; - } - - slave_pdev->dev.parent = ssbi->dev; - slave_pdev->dev.platform_data = slave->platform_data; - - ret = platform_device_add(slave_pdev); - if (ret) { - pr_err("cannot add slave platform device for '%s'\n", - slave->name); - goto err; - } - - ssbi->slave = &slave_pdev->dev; - return 0; - -err: - if (slave_pdev) - platform_device_put(slave_pdev); - return ret; -} - static int msm_ssbi_probe(struct platform_device *pdev) { - const struct msm_ssbi_platform_data *pdata = pdev->dev.platform_data; + struct device_node *np = pdev->dev.of_node; struct resource *mem_res; struct msm_ssbi *ssbi; int ret = 0; - - if (!pdata) { - pr_err("missing platform data\n"); - return -EINVAL; - } - - pr_debug("%s\n", pdata->slave.name); + const char *type; ssbi = kzalloc(sizeof(struct msm_ssbi), GFP_KERNEL); if (!ssbi) { @@ -334,7 +293,25 @@ static int msm_ssbi_probe(struct platform_device *pdev) ssbi->dev = &pdev->dev; platform_set_drvdata(pdev, ssbi); - ssbi->controller_type = pdata->controller_type; + type = of_get_property(np, "qcom,controller-type", NULL); + if (type == NULL) { + pr_err("Missing qcom,controller-type property\n"); + ret = -EINVAL; + goto err_ssbi_controller; + } + dev_info(&pdev->dev, "SSBI controller type: '%s'\n", type); + if (strcmp(type, "ssbi") == 0) + ssbi->controller_type = MSM_SBI_CTRL_SSBI; + else if (strcmp(type, "ssbi2") == 0) + ssbi->controller_type = MSM_SBI_CTRL_SSBI2; + else if (strcmp(type, "pmic-arbiter") == 0) + ssbi->controller_type = MSM_SBI_CTRL_PMIC_ARBITER; + else { + pr_err("Unknown qcom,controller-type\n"); + ret = -EINVAL; + goto err_ssbi_controller; + } + if (ssbi->controller_type == MSM_SBI_CTRL_PMIC_ARBITER) { ssbi->read = msm_ssbi_pa_read_bytes; ssbi->write = msm_ssbi_pa_write_bytes; @@ -345,13 +322,13 @@ static int msm_ssbi_probe(struct platform_device *pdev) spin_lock_init(&ssbi->lock); - ret = msm_ssbi_add_slave(ssbi, &pdata->slave); + ret = of_platform_populate(np, NULL, NULL, &pdev->dev); if (ret) - goto err_ssbi_add_slave; + goto err_ssbi_controller; return 0; -err_ssbi_add_slave: +err_ssbi_controller: platform_set_drvdata(pdev, NULL); iounmap(ssbi->base); err_ioremap: @@ -370,12 +347,18 @@ static int msm_ssbi_remove(struct platform_device *pdev) return 0; } +static struct of_device_id ssbi_match_table[] = { + { .compatible = "qcom,ssbi" }, + {} +}; + static struct platform_driver msm_ssbi_driver = { .probe = msm_ssbi_probe, .remove = msm_ssbi_remove, .driver = { .name = "msm_ssbi", .owner = THIS_MODULE, + .of_match_table = ssbi_match_table, }, }; -- cgit v1.2.3 From 3edce1cf813aa6a087df7730cec0e67d57288300 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Sun, 17 Mar 2013 21:00:06 +0100 Subject: USB: cdc-wdm: implement IOCTL_WDM_MAX_COMMAND MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Userspace applications need to know the maximum supported message size. The cdc-wdm driver translates between a character device stream and a message based protocol. Each message is transported as a usb control message with no further encapsulation or syncronization. Each read or write on the character device should translate to exactly one usb control message to ensure that message boundaries are kept intact. That means that the userspace application must know the maximum message size supported by the device and driver, making this size a vital part of the cdc-wdm character device API. CDC WDM and CDC MBIM functions export the maximum supported message size through CDC functional descriptors. The cdc-wdm and cdc_mbim drivers will parse these descriptors and use the value chosen by the device. The only current way for a userspace application to retrive the value is by duplicating the descriptor parsing. This is an unnecessary complex task, and application writers are likely to postpone it, using a fixed value and adding a "todo" item. QMI functions have no way to tell the host what message size they support. The qmi_wwan driver use a fixed value based on protocol recommendations and observed device behaviour. Userspace applications must know and hard code the same value. This scheme will break if we ever encounter a QMI device needing a device specific message size quirk. We are currently unable to support such a device because using a non default size would break the implicit userspace API. The message size is currently a hidden attribute of the cdc-wdm userspace API. Retrieving it is unnecessarily complex, increasing the possibility of drivers and applications using different limits. The resulting errors are hard to debug, and can only be replicated on identical hardware. Exporting the maximum message size from the driver simplifies the task for the userspace application, and creates a unified information source independent of device and function class. It also serves to document that the message size is part of the cdc-wdm userspace API. This proposed API extension has been presented for the authors of userspace applications and libraries using the current API: libmbim, libqmi, uqmi, oFono and ModemManager. The replies were: Aleksander Morgado: "We do really need max message size for MBIM; and as you say, it may be good to have the max message size info also for QMI, so the new ioctl seems a good addition. So +1 from my side, for what it's worth." Dan Williams: "Yeah, +1 here. I'd prefer the sysfs file, but the fact that that doesn't work for fd passing pretty much kills it." No negative replies are so far received. Cc: Aleksander Morgado Cc: Dan Williams Signed-off-by: Bjørn Mork Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- Documentation/ioctl/ioctl-number.txt | 1 + drivers/usb/class/cdc-wdm.c | 19 +++++++++++++++++++ include/linux/usb/cdc-wdm.h | 2 ++ include/uapi/linux/usb/cdc-wdm.h | 21 +++++++++++++++++++++ 4 files changed, 43 insertions(+) create mode 100644 include/uapi/linux/usb/cdc-wdm.h (limited to 'Documentation') diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 3210540f8bd3..237acab169dd 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -131,6 +131,7 @@ Code Seq#(hex) Include File Comments 'H' 40-4F sound/hdspm.h conflict! 'H' 40-4F sound/hdsp.h conflict! 'H' 90 sound/usb/usx2y/usb_stream.h +'H' A0 uapi/linux/usb/cdc-wdm.h 'H' C0-F0 net/bluetooth/hci.h conflict! 'H' C0-DF net/bluetooth/hidp/hidp.h conflict! 'H' C0-DF net/bluetooth/cmtp/cmtp.h conflict! diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 122d056d96d5..8a230f0ef77c 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -13,6 +13,7 @@ */ #include #include +#include #include #include #include @@ -644,6 +645,22 @@ static int wdm_release(struct inode *inode, struct file *file) return 0; } +static long wdm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct wdm_device *desc = file->private_data; + int rv = 0; + + switch (cmd) { + case IOCTL_WDM_MAX_COMMAND: + if (copy_to_user((void __user *)arg, &desc->wMaxCommand, sizeof(desc->wMaxCommand))) + rv = -EFAULT; + break; + default: + rv = -ENOTTY; + } + return rv; +} + static const struct file_operations wdm_fops = { .owner = THIS_MODULE, .read = wdm_read, @@ -652,6 +669,8 @@ static const struct file_operations wdm_fops = { .flush = wdm_flush, .release = wdm_release, .poll = wdm_poll, + .unlocked_ioctl = wdm_ioctl, + .compat_ioctl = wdm_ioctl, .llseek = noop_llseek, }; diff --git a/include/linux/usb/cdc-wdm.h b/include/linux/usb/cdc-wdm.h index 719c332620fa..0b3f4295c025 100644 --- a/include/linux/usb/cdc-wdm.h +++ b/include/linux/usb/cdc-wdm.h @@ -11,6 +11,8 @@ #ifndef __LINUX_USB_CDC_WDM_H #define __LINUX_USB_CDC_WDM_H +#include + extern struct usb_driver *usb_cdc_wdm_register(struct usb_interface *intf, struct usb_endpoint_descriptor *ep, int bufsize, diff --git a/include/uapi/linux/usb/cdc-wdm.h b/include/uapi/linux/usb/cdc-wdm.h new file mode 100644 index 000000000000..f03134feebd6 --- /dev/null +++ b/include/uapi/linux/usb/cdc-wdm.h @@ -0,0 +1,21 @@ +/* + * USB CDC Device Management userspace API definitions + * + * 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 _UAPI__LINUX_USB_CDC_WDM_H +#define _UAPI__LINUX_USB_CDC_WDM_H + +/* + * This IOCTL is used to retrieve the wMaxCommand for the device, + * defining the message limit for both reading and writing. + * + * For CDC WDM functions this will be the wMaxCommand field of the + * Device Management Functional Descriptor. + */ +#define IOCTL_WDM_MAX_COMMAND _IOR('H', 0xA0, __u16) + +#endif /* _UAPI__LINUX_USB_CDC_WDM_H */ -- cgit v1.2.3 From 9f1ca068ea9968e2bde4a2418d97fcd89005f4bf Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 25 Mar 2013 13:34:45 +0200 Subject: serial: of_serial: Handle fifo-size property This will reduce the need for extra types in 8250.c just in case the fifo size differs from the standard. Signed-off-by: Heikki Krogerus Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/tty/serial/of-serial.txt | 1 + drivers/tty/serial/of_serial.c | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/tty/serial/of-serial.txt b/Documentation/devicetree/bindings/tty/serial/of-serial.txt index 8f01cb190f25..c13f0ce74da6 100644 --- a/Documentation/devicetree/bindings/tty/serial/of-serial.txt +++ b/Documentation/devicetree/bindings/tty/serial/of-serial.txt @@ -33,6 +33,7 @@ Optional properties: RTAS and should not be registered. - no-loopback-test: set to indicate that the port does not implements loopback test mode +- fifo-size: the fifo size of the UART. Example: diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index b025d5438275..267711b5cb4d 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -97,6 +97,10 @@ static int of_platform_serial_setup(struct platform_device *ofdev, if (of_property_read_u32(np, "reg-shift", &prop) == 0) port->regshift = prop; + /* Check for fifo size */ + if (of_property_read_u32(np, "fifo-size", &prop) == 0) + port->fifosize = prop; + port->irq = irq_of_parse_and_map(np, 0); port->iotype = UPIO_MEM; if (of_property_read_u32(np, "reg-io-width", &prop) == 0) { -- cgit v1.2.3 From b0b8c84cf58d2486d48f486b5c47af7a7a33a497 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 25 Mar 2013 15:51:15 +0200 Subject: serial: of_serial: Handle auto-flow-control property Automatic Flow Control capability is not tied to this property. This is only one way of detecting it. The property is limited to be used only with 8250 driver. Signed-off-by: Heikki Krogerus Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/tty/serial/of-serial.txt | 3 +++ drivers/tty/serial/of_serial.c | 13 ++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/tty/serial/of-serial.txt b/Documentation/devicetree/bindings/tty/serial/of-serial.txt index c13f0ce74da6..1928a3e83cd0 100644 --- a/Documentation/devicetree/bindings/tty/serial/of-serial.txt +++ b/Documentation/devicetree/bindings/tty/serial/of-serial.txt @@ -34,6 +34,9 @@ Optional properties: - no-loopback-test: set to indicate that the port does not implements loopback test mode - fifo-size: the fifo size of the UART. +- auto-flow-control: one way to enable automatic flow control support. The + driver is allowed to detect support for the capability even without this + property. Example: diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 267711b5cb4d..39c7ea4cb14f 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -22,6 +21,8 @@ #include #include +#include "8250/8250.h" + struct of_serial_info { struct clk *clk; int type; @@ -171,11 +172,17 @@ static int of_platform_serial_probe(struct platform_device *ofdev) #ifdef CONFIG_SERIAL_8250 case PORT_8250 ... PORT_MAX_8250: { - /* For now the of bindings don't support the extra - 8250 specific bits */ struct uart_8250_port port8250; memset(&port8250, 0, sizeof(port8250)); port8250.port = port; + + if (port.fifosize) + port8250.capabilities = UART_CAP_FIFO; + + if (of_property_read_bool(ofdev->dev.of_node, + "auto-flow-control")) + port8250.capabilities |= UART_CAP_AFE; + ret = serial8250_register_8250_port(&port8250); break; } -- cgit v1.2.3 From 94fbbbf89492e460979cd10c6384a78a9dbf17ed Mon Sep 17 00:00:00 2001 From: Giuseppe CAVALLARO Date: Tue, 26 Mar 2013 04:43:12 +0000 Subject: stmmac: update the Doc and Version (PTP+SGMII) This patch updates the stmmac.txt file adding information related to the PTP and SGMII/RGMII supports. Also the patch updates the driver version to: March_2013. Signed-off-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- Documentation/networking/stmmac.txt | 33 ++++++++++++++++++++++++++-- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 2 +- 2 files changed, 32 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/stmmac.txt b/Documentation/networking/stmmac.txt index f9fa6db40a52..8efe0b3c8b83 100644 --- a/Documentation/networking/stmmac.txt +++ b/Documentation/networking/stmmac.txt @@ -326,6 +326,35 @@ To enter in Tx LPI mode the driver needs to have a software timer that enable and disable the LPI mode when there is nothing to be transmitted. -7) TODO: +7) Extended descriptors +The extended descriptors give us information about the receive Ethernet payload +when it is carrying PTP packets or TCP/UDP/ICMP over IP. +These are not available on GMAC Synopsys chips older than the 3.50. +At probe time the driver will decide if these can be actually used. +This support also is mandatory for PTPv2 because the extra descriptors 6 and 7 +are used for saving the hardware timestamps. + +8) Precision Time Protocol (PTP) +The driver supports the IEEE 1588-2002, Precision Time Protocol (PTP), +which enables precise synchronization of clocks in measurement and +control systems implemented with technologies such as network +communication. + +In addition to the basic timestamp features mentioned in IEEE 1588-2002 +Timestamps, new GMAC cores support the advanced timestamp features. +IEEE 1588-2008 that can be enabled when configure the Kernel. + +9) SGMII/RGMII supports +New GMAC devices provide own way to manage RGMII/SGMII. +This information is available at run-time by looking at the +HW capability register. This means that the stmmac can manage +auto-negotiation and link status w/o using the PHYLIB stuff +In fact, the HW provides a subset of extended registers to +restart the ANE, verify Full/Half duplex mode and Speed. +Also thanks to these registers it is possible to look at the +Auto-negotiated Link Parter Ability. + +10) TODO: o XGMAC is not supported. - o Add the PTP - precision time protocol + o Complete the TBI & RTBI support. + o extened VLAN support for 3.70a SYNP GMAC. diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 52002e7c59ae..75f997b467aa 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -24,7 +24,7 @@ #define __STMMAC_H__ #define STMMAC_RESOURCE_NAME "stmmaceth" -#define DRV_MODULE_VERSION "Nov_2012" +#define DRV_MODULE_VERSION "March_2013" #include #include -- cgit v1.2.3 From 7b3bde000a0c5b67d3ab84b3c3696ee6a6bb9a69 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Sun, 24 Mar 2013 15:25:56 +0900 Subject: doc: DocBook/media : Fix typo in dvbproperty.xml Correct spelling typos. Signed-off-by: Masanari Iida Signed-off-by: Jiri Kosina --- Documentation/DocBook/media/dvb/dvbproperty.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/dvb/dvbproperty.xml b/Documentation/DocBook/media/dvb/dvbproperty.xml index 4a5eaeed0b9e..31dc4dfd1d6a 100644 --- a/Documentation/DocBook/media/dvb/dvbproperty.xml +++ b/Documentation/DocBook/media/dvb/dvbproperty.xml @@ -1,6 +1,6 @@
<constant>FE_GET_PROPERTY/FE_SET_PROPERTY</constant> -This section describes the DVB version 5 extention of the DVB-API, also +This section describes the DVB version 5 extension of the DVB-API, also called "S2API", as this API were added to provide support for DVB-S2. It was designed to be able to replace the old frontend API. Yet, the DISEQC and the capability ioctls weren't implemented yet via the new way. @@ -952,7 +952,7 @@ enum fe_interleaving { Measures the amount of bits received before the inner code block, during the same period as DTV_STAT_PRE_ERROR_BIT_COUNT measurement was taken. It should be noticed that this measurement can be smaller than the total amount of bits on the transport stream, - as the frontend may need to manually restart the measurement, loosing some data between each measurement interval. + as the frontend may need to manually restart the measurement, losing some data between each measurement interval. This measurement is monotonically increased, as the frontend gets more bit count measurements. The frontend may reset it when a channel/transponder is tuned. Possible scales for this metric are: @@ -981,7 +981,7 @@ enum fe_interleaving { Measures the amount of bits received after the inner coding, during the same period as DTV_STAT_POST_ERROR_BIT_COUNT measurement was taken. It should be noticed that this measurement can be smaller than the total amount of bits on the transport stream, - as the frontend may need to manually restart the measurement, loosing some data between each measurement interval. + as the frontend may need to manually restart the measurement, losing some data between each measurement interval. This measurement is monotonically increased, as the frontend gets more bit count measurements. The frontend may reset it when a channel/transponder is tuned. Possible scales for this metric are: -- cgit v1.2.3 From 2a039be7fa4964a0e327c190cac87c4d3d25a22c Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Mon, 25 Mar 2013 20:42:06 +0100 Subject: doc: put proper reference to CONFIG_MODULE_SIG_ENFORCE Signed-off-by: Paul Bolle Signed-off-by: Jiri Kosina --- Documentation/kernel-parameters.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 4609e81dbc37..72bb2a8649df 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1625,7 +1625,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. module.sig_enforce [KNL] When CONFIG_MODULE_SIG is set, this means that modules without (valid) signatures will fail to load. - Note that if CONFIG_MODULE_SIG_ENFORCE is set, that + Note that if CONFIG_MODULE_SIG_FORCE is set, that is always true, so this option does nothing. mousedev.tap_time= -- cgit v1.2.3 From 3078cde792340280b761a0f46f99799a78f4395d Mon Sep 17 00:00:00 2001 From: Ludovic Desroches Date: Mon, 11 Mar 2013 18:26:03 +0100 Subject: can: at91_can: add dt support Add device tree support. Signed-off-by: Ludovic Desroches Signed-off-by: Marc Kleine-Budde --- .../devicetree/bindings/net/can/atmel-can.txt | 14 ++++ drivers/net/can/at91_can.c | 76 ++++++++++++++++------ 2 files changed, 71 insertions(+), 19 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/can/atmel-can.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/net/can/atmel-can.txt b/Documentation/devicetree/bindings/net/can/atmel-can.txt new file mode 100644 index 000000000000..72cf0c5daff4 --- /dev/null +++ b/Documentation/devicetree/bindings/net/can/atmel-can.txt @@ -0,0 +1,14 @@ +* AT91 CAN * + +Required properties: + - compatible: Should be "atmel,at91sam9263-can" or "atmel,at91sam9x5-can" + - reg: Should contain CAN controller registers location and length + - interrupts: Should contain IRQ line for the CAN controller + +Example: + + can0: can@f000c000 { + compatbile = "atmel,at91sam9x5-can"; + reg = <0xf000c000 0x300>; + interrupts = <40 4 5> + }; diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c index 44f363792b59..db52f4414def 100644 --- a/drivers/net/can/at91_can.c +++ b/drivers/net/can/at91_can.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -155,19 +156,20 @@ struct at91_priv { canid_t mb0_id; }; -static const struct at91_devtype_data at91_devtype_data[] = { - [AT91_DEVTYPE_SAM9263] = { - .rx_first = 1, - .rx_split = 8, - .rx_last = 11, - .tx_shift = 2, - }, - [AT91_DEVTYPE_SAM9X5] = { - .rx_first = 0, - .rx_split = 4, - .rx_last = 5, - .tx_shift = 1, - }, +static const struct at91_devtype_data at91_at91sam9263_data = { + .rx_first = 1, + .rx_split = 8, + .rx_last = 11, + .tx_shift = 2, + .type = AT91_DEVTYPE_SAM9263, +}; + +static const struct at91_devtype_data at91_at91sam9x5_data = { + .rx_first = 0, + .rx_split = 4, + .rx_last = 5, + .tx_shift = 1, + .type = AT91_DEVTYPE_SAM9X5, }; static const struct can_bittiming_const at91_bittiming_const = { @@ -1249,10 +1251,42 @@ static struct attribute_group at91_sysfs_attr_group = { .attrs = at91_sysfs_attrs, }; +#if defined(CONFIG_OF) +static const struct of_device_id at91_can_dt_ids[] = { + { + .compatible = "atmel,at91sam9x5-can", + .data = &at91_at91sam9x5_data, + }, { + .compatible = "atmel,at91sam9263-can", + .data = &at91_at91sam9263_data, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, at91_can_dt_ids); +#else +#define at91_can_dt_ids NULL +#endif + +static const struct at91_devtype_data *at91_can_get_driver_data(struct platform_device *pdev) +{ + if (pdev->dev.of_node) { + const struct of_device_id *match; + + match = of_match_node(at91_can_dt_ids, pdev->dev.of_node); + if (!match) { + dev_err(&pdev->dev, "no matching node found in dtb\n"); + return NULL; + } + return (const struct at91_devtype_data *)match->data; + } + return (const struct at91_devtype_data *) + platform_get_device_id(pdev)->driver_data; +} + static int at91_can_probe(struct platform_device *pdev) { const struct at91_devtype_data *devtype_data; - enum at91_devtype devtype; struct net_device *dev; struct at91_priv *priv; struct resource *res; @@ -1260,8 +1294,12 @@ static int at91_can_probe(struct platform_device *pdev) void __iomem *addr; int err, irq; - devtype = pdev->id_entry->driver_data; - devtype_data = &at91_devtype_data[devtype]; + devtype_data = at91_can_get_driver_data(pdev); + if (!devtype_data) { + dev_err(&pdev->dev, "no driver data\n"); + err = -ENODEV; + goto exit; + } clk = clk_get(&pdev->dev, "can_clk"); if (IS_ERR(clk)) { @@ -1310,7 +1348,6 @@ static int at91_can_probe(struct platform_device *pdev) priv->dev = dev; priv->reg_base = addr; priv->devtype_data = *devtype_data; - priv->devtype_data.type = devtype; priv->clk = clk; priv->pdata = pdev->dev.platform_data; priv->mb0_id = 0x7ff; @@ -1373,10 +1410,10 @@ static int at91_can_remove(struct platform_device *pdev) static const struct platform_device_id at91_can_id_table[] = { { .name = "at91_can", - .driver_data = AT91_DEVTYPE_SAM9263, + .driver_data = (kernel_ulong_t)&at91_at91sam9x5_data, }, { .name = "at91sam9x5_can", - .driver_data = AT91_DEVTYPE_SAM9X5, + .driver_data = (kernel_ulong_t)&at91_at91sam9263_data, }, { /* sentinel */ } @@ -1389,6 +1426,7 @@ static struct platform_driver at91_can_driver = { .driver = { .name = KBUILD_MODNAME, .owner = THIS_MODULE, + .of_match_table = at91_can_dt_ids, }, .id_table = at91_can_id_table, }; -- cgit v1.2.3 From e1ca3c7ac9caad278d05f0b30f4e1a03e4a65b7f Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Thu, 21 Mar 2013 12:02:00 +0100 Subject: kernel-parameters: remove outdated 'noresidual' parameter The PPC specific kernel parameter 'noresidual' was removed in v2.6.27, though commit 917f0af9e5a9ceecf9e72537fabb501254ba321d ("powerpc: Remove arch/ppc and include/asm-ppc"). Signed-off-by: Paul Bolle Signed-off-by: Jiri Kosina --- Documentation/kernel-parameters.txt | 2 -- 1 file changed, 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 72bb2a8649df..da519d3c6bf6 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1974,8 +1974,6 @@ bytes respectively. Such letter suffixes can also be entirely omitted. noreplace-smp [X86-32,SMP] Don't replace SMP instructions with UP alternatives - noresidual [PPC] Don't use residual data on PReP machines. - nordrand [X86] Disable the direct use of the RDRAND instruction even if it is supported by the processor. RDRAND is still available to user -- cgit v1.2.3 From df7c6b9927f62d332db612ef51c3234993aafd8c Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Mon, 25 Mar 2013 23:59:16 +0100 Subject: doc: Fix typo "CONFIG_CGROUP_CGROUP_MEMCG_SWAP" Signed-off-by: Paul Bolle Acked-by: Rob Landley Signed-off-by: Jiri Kosina --- Documentation/cgroups/memory.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/cgroups/memory.txt b/Documentation/cgroups/memory.txt index 8b8c28b9864c..17b91e012de0 100644 --- a/Documentation/cgroups/memory.txt +++ b/Documentation/cgroups/memory.txt @@ -194,7 +194,7 @@ the cgroup that brought it in -- this will happen on memory pressure). But see section 8.2: when moving a task to another cgroup, its pages may be recharged to the new cgroup, if move_charge_at_immigrate has been chosen. -Exception: If CONFIG_CGROUP_CGROUP_MEMCG_SWAP is not used. +Exception: If CONFIG_MEMCG_SWAP is not used. When you do swapoff and make swapped-out pages of shmem(tmpfs) to be backed into memory in force, charges for pages are accounted against the caller of swapoff rather than the users of shmem. -- cgit v1.2.3 From e874a6697710f52fa8ab29487a99034d5d96fdcc Mon Sep 17 00:00:00 2001 From: Emilio López Date: Mon, 25 Feb 2013 11:44:26 -0300 Subject: clk: arm: sunxi: Add a new clock driver for sunxi SOCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit implements the base CPU clocks for sunxi devices. It has been tested using a slightly modified cpufreq driver from the linux-sunxi 3.0 tree. Additionally, document the new bindings introduced by this patch. Idling: / # cat /sys/kernel/debug/clk/clk_summary clock enable_cnt prepare_cnt rate --------------------------------------------------------------------- osc32k 0 0 32768 osc24M_fixed 0 0 24000000 osc24M 0 0 24000000 apb1_mux 0 0 24000000 apb1 0 0 24000000 pll1 0 0 60000000 cpu 0 0 60000000 axi 0 0 60000000 ahb 0 0 60000000 apb0 0 0 30000000 dummy 0 0 0 After "yes >/dev/null &": / # cat /sys/kernel/debug/clk/clk_summary clock enable_cnt prepare_cnt rate --------------------------------------------------------------------- osc32k 0 0 32768 osc24M_fixed 0 0 24000000 osc24M 0 0 24000000 apb1_mux 0 0 24000000 apb1 0 0 24000000 pll1 0 0 1008000000 cpu 0 0 1008000000 axi 0 0 336000000 ahb 0 0 168000000 apb0 0 0 84000000 dummy 0 0 0 Signed-off-by: Emilio López Acked-by: Maxime Ripard Signed-off-by: Mike Turquette --- Documentation/devicetree/bindings/clock/sunxi.txt | 44 +++ drivers/clk/Makefile | 1 + drivers/clk/sunxi/Makefile | 5 + drivers/clk/sunxi/clk-factors.c | 180 +++++++++++ drivers/clk/sunxi/clk-factors.h | 27 ++ drivers/clk/sunxi/clk-sunxi.c | 362 ++++++++++++++++++++++ drivers/clocksource/sunxi_timer.c | 4 +- include/linux/clk/sunxi.h | 22 ++ 8 files changed, 643 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/clock/sunxi.txt create mode 100644 drivers/clk/sunxi/Makefile create mode 100644 drivers/clk/sunxi/clk-factors.c create mode 100644 drivers/clk/sunxi/clk-factors.h create mode 100644 drivers/clk/sunxi/clk-sunxi.c create mode 100644 include/linux/clk/sunxi.h (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt new file mode 100644 index 000000000000..b23cfbdbcd6d --- /dev/null +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -0,0 +1,44 @@ +Device Tree Clock bindings for arch-sunxi + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- compatible : shall be one of the following: + "allwinner,sunxi-osc-clk" - for a gatable oscillator + "allwinner,sunxi-pll1-clk" - for the main PLL clock + "allwinner,sunxi-cpu-clk" - for the CPU multiplexer clock + "allwinner,sunxi-axi-clk" - for the sunxi AXI clock + "allwinner,sunxi-ahb-clk" - for the sunxi AHB clock + "allwinner,sunxi-apb0-clk" - for the sunxi APB0 clock + "allwinner,sunxi-apb1-clk" - for the sunxi APB1 clock + "allwinner,sunxi-apb1-mux-clk" - for the sunxi APB1 clock muxing + +Required properties for all clocks: +- reg : shall be the control register address for the clock. +- clocks : shall be the input parent clock(s) phandle for the clock +- #clock-cells : from common clock binding; shall be set to 0. + +For example: + +osc24M: osc24M@01c20050 { + #clock-cells = <0>; + compatible = "allwinner,sunxi-osc-clk"; + reg = <0x01c20050 0x4>; + clocks = <&osc24M_fixed>; +}; + +pll1: pll1@01c20000 { + #clock-cells = <0>; + compatible = "allwinner,sunxi-pll1-clk"; + reg = <0x01c20000 0x4>; + clocks = <&osc24M>; +}; + +cpu: cpu@01c20054 { + #clock-cells = <0>; + compatible = "allwinner,sunxi-cpu-clk"; + reg = <0x01c20054 0x4>; + clocks = <&osc32k>, <&osc24M>, <&pll1>; +}; diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 41cb123a2d02..79e98e416724 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -24,6 +24,7 @@ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o +obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile new file mode 100644 index 000000000000..b5bac917612c --- /dev/null +++ b/drivers/clk/sunxi/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for sunxi specific clk +# + +obj-y += clk-sunxi.o clk-factors.o diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c new file mode 100644 index 000000000000..88523f91d9b7 --- /dev/null +++ b/drivers/clk/sunxi/clk-factors.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2013 Emilio López + * + * 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. + * + * Adjustable factor-based clock implementation + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "clk-factors.h" + +/* + * DOC: basic adjustable factor-based clock that cannot gate + * + * Traits of this clock: + * prepare - clk_prepare only ensures that parents are prepared + * enable - clk_enable only ensures that parents are enabled + * rate - rate is adjustable. + * clk->rate = (parent->rate * N * (K + 1) >> P) / (M + 1) + * parent - fixed parent. No clk_set_parent support + */ + +struct clk_factors { + struct clk_hw hw; + void __iomem *reg; + struct clk_factors_config *config; + void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p); + spinlock_t *lock; +}; + +#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw) + +#define SETMASK(len, pos) (((-1U) >> (31-len)) << (pos)) +#define CLRMASK(len, pos) (~(SETMASK(len, pos))) +#define FACTOR_GET(bit, len, reg) (((reg) & SETMASK(len, bit)) >> (bit)) + +#define FACTOR_SET(bit, len, reg, val) \ + (((reg) & CLRMASK(len, bit)) | (val << (bit))) + +static unsigned long clk_factors_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u8 n = 1, k = 0, p = 0, m = 0; + u32 reg; + unsigned long rate; + struct clk_factors *factors = to_clk_factors(hw); + struct clk_factors_config *config = factors->config; + + /* Fetch the register value */ + reg = readl(factors->reg); + + /* Get each individual factor if applicable */ + if (config->nwidth != SUNXI_FACTORS_NOT_APPLICABLE) + n = FACTOR_GET(config->nshift, config->nwidth, reg); + if (config->kwidth != SUNXI_FACTORS_NOT_APPLICABLE) + k = FACTOR_GET(config->kshift, config->kwidth, reg); + if (config->mwidth != SUNXI_FACTORS_NOT_APPLICABLE) + m = FACTOR_GET(config->mshift, config->mwidth, reg); + if (config->pwidth != SUNXI_FACTORS_NOT_APPLICABLE) + p = FACTOR_GET(config->pshift, config->pwidth, reg); + + /* Calculate the rate */ + rate = (parent_rate * n * (k + 1) >> p) / (m + 1); + + return rate; +} + +static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_factors *factors = to_clk_factors(hw); + factors->get_factors((u32 *)&rate, (u32)*parent_rate, + NULL, NULL, NULL, NULL); + + return rate; +} + +static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + u8 n, k, m, p; + u32 reg; + struct clk_factors *factors = to_clk_factors(hw); + struct clk_factors_config *config = factors->config; + unsigned long flags = 0; + + factors->get_factors((u32 *)&rate, (u32)parent_rate, &n, &k, &m, &p); + + if (factors->lock) + spin_lock_irqsave(factors->lock, flags); + + /* Fetch the register value */ + reg = readl(factors->reg); + + /* Set up the new factors - macros do not do anything if width is 0 */ + reg = FACTOR_SET(config->nshift, config->nwidth, reg, n); + reg = FACTOR_SET(config->kshift, config->kwidth, reg, k); + reg = FACTOR_SET(config->mshift, config->mwidth, reg, m); + reg = FACTOR_SET(config->pshift, config->pwidth, reg, p); + + /* Apply them now */ + writel(reg, factors->reg); + + /* delay 500us so pll stabilizes */ + __delay((rate >> 20) * 500 / 2); + + if (factors->lock) + spin_unlock_irqrestore(factors->lock, flags); + + return 0; +} + +static const struct clk_ops clk_factors_ops = { + .recalc_rate = clk_factors_recalc_rate, + .round_rate = clk_factors_round_rate, + .set_rate = clk_factors_set_rate, +}; + +/** + * clk_register_factors - register a factors clock with + * the clock framework + * @dev: device registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @flags: framework-specific flags + * @reg: register address to adjust factors + * @config: shift and width of factors n, k, m and p + * @get_factors: function to calculate the factors for a given frequency + * @lock: shared register lock for this clock + */ +struct clk *clk_register_factors(struct device *dev, const char *name, + const char *parent_name, + unsigned long flags, void __iomem *reg, + struct clk_factors_config *config, + void (*get_factors)(u32 *rate, u32 parent, + u8 *n, u8 *k, u8 *m, u8 *p), + spinlock_t *lock) +{ + struct clk_factors *factors; + struct clk *clk; + struct clk_init_data init; + + /* allocate the factors */ + factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL); + if (!factors) { + pr_err("%s: could not allocate factors clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.ops = &clk_factors_ops; + init.flags = flags; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + /* struct clk_factors assignments */ + factors->reg = reg; + factors->config = config; + factors->lock = lock; + factors->hw.init = &init; + factors->get_factors = get_factors; + + /* register the clock */ + clk = clk_register(dev, &factors->hw); + + if (IS_ERR(clk)) + kfree(factors); + + return clk; +} diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h new file mode 100644 index 000000000000..f49851cc4380 --- /dev/null +++ b/drivers/clk/sunxi/clk-factors.h @@ -0,0 +1,27 @@ +#ifndef __MACH_SUNXI_CLK_FACTORS_H +#define __MACH_SUNXI_CLK_FACTORS_H + +#include +#include + +#define SUNXI_FACTORS_NOT_APPLICABLE (0) + +struct clk_factors_config { + u8 nshift; + u8 nwidth; + u8 kshift; + u8 kwidth; + u8 mshift; + u8 mwidth; + u8 pshift; + u8 pwidth; +}; + +struct clk *clk_register_factors(struct device *dev, const char *name, + const char *parent_name, + unsigned long flags, void __iomem *reg, + struct clk_factors_config *config, + void (*get_factors) (u32 *rate, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p), + spinlock_t *lock); +#endif diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c new file mode 100644 index 000000000000..d4ad1c22859e --- /dev/null +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -0,0 +1,362 @@ +/* + * Copyright 2013 Emilio López + * + * Emilio López + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include "clk-factors.h" + +static DEFINE_SPINLOCK(clk_lock); + +/** + * sunxi_osc_clk_setup() - Setup function for gatable oscillator + */ + +#define SUNXI_OSC24M_GATE 0 + +static void __init sunxi_osc_clk_setup(struct device_node *node) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *parent; + void *reg; + + reg = of_iomap(node, 0); + + parent = of_clk_get_parent_name(node, 0); + + clk = clk_register_gate(NULL, clk_name, parent, CLK_IGNORE_UNUSED, + reg, SUNXI_OSC24M_GATE, 0, &clk_lock); + + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } +} + + + +/** + * sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1 + * PLL1 rate is calculated as follows + * rate = (parent_rate * n * (k + 1) >> p) / (m + 1); + * parent_rate is always 24Mhz + */ + +static void sunxi_get_pll1_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + u8 div; + + /* Normalize value to a 6M multiple */ + div = *freq / 6000000; + *freq = 6000000 * div; + + /* we were called to round the frequency, we can now return */ + if (n == NULL) + return; + + /* m is always zero for pll1 */ + *m = 0; + + /* k is 1 only on these cases */ + if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000) + *k = 1; + else + *k = 0; + + /* p will be 3 for divs under 10 */ + if (div < 10) + *p = 3; + + /* p will be 2 for divs between 10 - 20 and odd divs under 32 */ + else if (div < 20 || (div < 32 && (div & 1))) + *p = 2; + + /* p will be 1 for even divs under 32, divs under 40 and odd pairs + * of divs between 40-62 */ + else if (div < 40 || (div < 64 && (div & 2))) + *p = 1; + + /* any other entries have p = 0 */ + else + *p = 0; + + /* calculate a suitable n based on k and p */ + div <<= *p; + div /= (*k + 1); + *n = div / 4; +} + + + +/** + * sunxi_get_apb1_factors() - calculates m, p factors for APB1 + * APB1 rate is calculated as follows + * rate = (parent_rate >> p) / (m + 1); + */ + +static void sunxi_get_apb1_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + u8 calcm, calcp; + + if (parent_rate < *freq) + *freq = parent_rate; + + parent_rate = (parent_rate + (*freq - 1)) / *freq; + + /* Invalid rate! */ + if (parent_rate > 32) + return; + + if (parent_rate <= 4) + calcp = 0; + else if (parent_rate <= 8) + calcp = 1; + else if (parent_rate <= 16) + calcp = 2; + else + calcp = 3; + + calcm = (parent_rate >> calcp) - 1; + + *freq = (parent_rate >> calcp) / (calcm + 1); + + /* we were called to round the frequency, we can now return */ + if (n == NULL) + return; + + *m = calcm; + *p = calcp; +} + + + +/** + * sunxi_factors_clk_setup() - Setup function for factor clocks + */ + +struct factors_data { + struct clk_factors_config *table; + void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); +}; + +static struct clk_factors_config pll1_config = { + .nshift = 8, + .nwidth = 5, + .kshift = 4, + .kwidth = 2, + .mshift = 0, + .mwidth = 2, + .pshift = 16, + .pwidth = 2, +}; + +static struct clk_factors_config apb1_config = { + .mshift = 0, + .mwidth = 5, + .pshift = 16, + .pwidth = 2, +}; + +static const __initconst struct factors_data pll1_data = { + .table = &pll1_config, + .getter = sunxi_get_pll1_factors, +}; + +static const __initconst struct factors_data apb1_data = { + .table = &apb1_config, + .getter = sunxi_get_apb1_factors, +}; + +static void __init sunxi_factors_clk_setup(struct device_node *node, + struct factors_data *data) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *parent; + void *reg; + + reg = of_iomap(node, 0); + + parent = of_clk_get_parent_name(node, 0); + + clk = clk_register_factors(NULL, clk_name, parent, CLK_IGNORE_UNUSED, + reg, data->table, data->getter, &clk_lock); + + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } +} + + + +/** + * sunxi_mux_clk_setup() - Setup function for muxes + */ + +#define SUNXI_MUX_GATE_WIDTH 2 + +struct mux_data { + u8 shift; +}; + +static const __initconst struct mux_data cpu_data = { + .shift = 16, +}; + +static const __initconst struct mux_data apb1_mux_data = { + .shift = 24, +}; + +static void __init sunxi_mux_clk_setup(struct device_node *node, + struct mux_data *data) +{ + struct clk *clk; + const char *clk_name = node->name; + const char **parents = kmalloc(sizeof(char *) * 5, GFP_KERNEL); + void *reg; + int i = 0; + + reg = of_iomap(node, 0); + + while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL) + i++; + + clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg, + data->shift, SUNXI_MUX_GATE_WIDTH, + 0, &clk_lock); + + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } +} + + + +/** + * sunxi_divider_clk_setup() - Setup function for simple divider clocks + */ + +#define SUNXI_DIVISOR_WIDTH 2 + +struct div_data { + u8 shift; + u8 pow; +}; + +static const __initconst struct div_data axi_data = { + .shift = 0, + .pow = 0, +}; + +static const __initconst struct div_data ahb_data = { + .shift = 4, + .pow = 1, +}; + +static const __initconst struct div_data apb0_data = { + .shift = 8, + .pow = 1, +}; + +static void __init sunxi_divider_clk_setup(struct device_node *node, + struct div_data *data) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *clk_parent; + void *reg; + + reg = of_iomap(node, 0); + + clk_parent = of_clk_get_parent_name(node, 0); + + clk = clk_register_divider(NULL, clk_name, clk_parent, 0, + reg, data->shift, SUNXI_DIVISOR_WIDTH, + data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0, + &clk_lock); + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } +} + + +/* Matches for of_clk_init */ +static const __initconst struct of_device_id clk_match[] = { + {.compatible = "fixed-clock", .data = of_fixed_clk_setup,}, + {.compatible = "allwinner,sunxi-osc-clk", .data = sunxi_osc_clk_setup,}, + {} +}; + +/* Matches for factors clocks */ +static const __initconst struct of_device_id clk_factors_match[] = { + {.compatible = "allwinner,sunxi-pll1-clk", .data = &pll1_data,}, + {.compatible = "allwinner,sunxi-apb1-clk", .data = &apb1_data,}, + {} +}; + +/* Matches for divider clocks */ +static const __initconst struct of_device_id clk_div_match[] = { + {.compatible = "allwinner,sunxi-axi-clk", .data = &axi_data,}, + {.compatible = "allwinner,sunxi-ahb-clk", .data = &ahb_data,}, + {.compatible = "allwinner,sunxi-apb0-clk", .data = &apb0_data,}, + {} +}; + +/* Matches for mux clocks */ +static const __initconst struct of_device_id clk_mux_match[] = { + {.compatible = "allwinner,sunxi-cpu-clk", .data = &cpu_data,}, + {.compatible = "allwinner,sunxi-apb1-mux-clk", .data = &apb1_mux_data,}, + {} +}; + +static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_match, + void *function) +{ + struct device_node *np; + const struct div_data *data; + const struct of_device_id *match; + void (*setup_function)(struct device_node *, const void *) = function; + + for_each_matching_node(np, clk_match) { + match = of_match_node(clk_match, np); + data = match->data; + setup_function(np, data); + } +} + +void __init sunxi_init_clocks(void) +{ + /* Register all the simple sunxi clocks on DT */ + of_clk_init(clk_match); + + /* Register factor clocks */ + of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup); + + /* Register divider clocks */ + of_sunxi_table_clock_setup(clk_div_match, sunxi_divider_clk_setup); + + /* Register mux clocks */ + of_sunxi_table_clock_setup(clk_mux_match, sunxi_mux_clk_setup); +} diff --git a/drivers/clocksource/sunxi_timer.c b/drivers/clocksource/sunxi_timer.c index 4086b9167159..0ce85e29769b 100644 --- a/drivers/clocksource/sunxi_timer.c +++ b/drivers/clocksource/sunxi_timer.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #define TIMER_CTL_REG 0x00 #define TIMER_CTL_ENABLE (1 << 0) @@ -123,7 +123,7 @@ void __init sunxi_timer_init(void) if (irq <= 0) panic("Can't parse IRQ"); - of_clk_init(NULL); + sunxi_init_clocks(); clk = of_clk_get(node, 0); if (IS_ERR(clk)) diff --git a/include/linux/clk/sunxi.h b/include/linux/clk/sunxi.h new file mode 100644 index 000000000000..e074fdd5a236 --- /dev/null +++ b/include/linux/clk/sunxi.h @@ -0,0 +1,22 @@ +/* + * Copyright 2012 Maxime Ripard + * + * Maxime Ripard + * + * 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_CLK_SUNXI_H_ +#define __LINUX_CLK_SUNXI_H_ + +void __init sunxi_init_clocks(void); + +#endif -- cgit v1.2.3 From 8eb896ab7a716308698de77073a9265f584b12fc Mon Sep 17 00:00:00 2001 From: Emilio López Date: Mon, 25 Feb 2013 11:44:28 -0300 Subject: arm: sunxi: Add useful information about sunxi clocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch contains useful bits of information about the sunxi clocks that may help and/or be interesting for current and future developers. Signed-off-by: Emilio López Acked-by: Maxime Ripard Signed-off-by: Mike Turquette --- Documentation/arm/sunxi/clocks.txt | 56 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Documentation/arm/sunxi/clocks.txt (limited to 'Documentation') diff --git a/Documentation/arm/sunxi/clocks.txt b/Documentation/arm/sunxi/clocks.txt new file mode 100644 index 000000000000..e09a88aa3136 --- /dev/null +++ b/Documentation/arm/sunxi/clocks.txt @@ -0,0 +1,56 @@ +Frequently asked questions about the sunxi clock system +======================================================= + +This document contains useful bits of information that people tend to ask +about the sunxi clock system, as well as accompanying ASCII art when adequate. + +Q: Why is the main 24MHz oscillator gatable? Wouldn't that break the + system? + +A: The 24MHz oscillator allows gating to save power. Indeed, if gated + carelessly the system would stop functioning, but with the right + steps, one can gate it and keep the system running. Consider this + simplified suspend example: + + While the system is operational, you would see something like + + 24MHz 32kHz + | + PLL1 + \ + \_ CPU Mux + | + [CPU] + + When you are about to suspend, you switch the CPU Mux to the 32kHz + oscillator: + + 24Mhz 32kHz + | | + PLL1 | + / + CPU Mux _/ + | + [CPU] + + Finally you can gate the main oscillator + + 32kHz + | + | + / + CPU Mux _/ + | + [CPU] + +Q: Were can I learn more about the sunxi clocks? + +A: The linux-sunxi wiki contains a page documenting the clock registers, + you can find it at + + http://linux-sunxi.org/A10/CCM + + The authoritative source for information at this time is the ccmu driver + released by Allwinner, you can find it at + + https://github.com/linux-sunxi/linux-sunxi/tree/sunxi-3.0/arch/arm/mach-sun4i/clock/ccmu -- cgit v1.2.3 From e3276998da12c6ec093befd6e49be4848414d57e Mon Sep 17 00:00:00 2001 From: Emilio López Date: Tue, 26 Mar 2013 23:39:17 -0300 Subject: clk: sunxi: rename compatible strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During the introduction of the Allwinner SoC platforms, sunxi was initially meant as a generic name for all the variants of the Allwinner SoC. It was ok at the time of the support of only the A10 and A13 that look pretty much the same; but it's beginning to be troublesome with the future addition of the Allwinner A31 (sun6i) that is quite different, and would introduce some weird logic, where sunxi would actually mean in some case sun4i and sun5i but without sun6i... Moreover, it makes the compatible strings naming scheme not consistent with other architectures, where usually for this kind of compability, we just use the oldest SoC name that has this IP, so let's do just this. Signed-off-by: Emilio López Signed-off-by: Mike Turquette --- Documentation/devicetree/bindings/clock/sunxi.txt | 22 +++++++++++----------- drivers/clk/sunxi/clk-sunxi.c | 16 ++++++++-------- 2 files changed, 19 insertions(+), 19 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index b23cfbdbcd6d..20b8479c2760 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -6,14 +6,14 @@ This binding uses the common clock binding[1]. Required properties: - compatible : shall be one of the following: - "allwinner,sunxi-osc-clk" - for a gatable oscillator - "allwinner,sunxi-pll1-clk" - for the main PLL clock - "allwinner,sunxi-cpu-clk" - for the CPU multiplexer clock - "allwinner,sunxi-axi-clk" - for the sunxi AXI clock - "allwinner,sunxi-ahb-clk" - for the sunxi AHB clock - "allwinner,sunxi-apb0-clk" - for the sunxi APB0 clock - "allwinner,sunxi-apb1-clk" - for the sunxi APB1 clock - "allwinner,sunxi-apb1-mux-clk" - for the sunxi APB1 clock muxing + "allwinner,sun4i-osc-clk" - for a gatable oscillator + "allwinner,sun4i-pll1-clk" - for the main PLL clock + "allwinner,sun4i-cpu-clk" - for the CPU multiplexer clock + "allwinner,sun4i-axi-clk" - for the AXI clock + "allwinner,sun4i-ahb-clk" - for the AHB clock + "allwinner,sun4i-apb0-clk" - for the APB0 clock + "allwinner,sun4i-apb1-clk" - for the APB1 clock + "allwinner,sun4i-apb1-mux-clk" - for the APB1 clock muxing Required properties for all clocks: - reg : shall be the control register address for the clock. @@ -24,21 +24,21 @@ For example: osc24M: osc24M@01c20050 { #clock-cells = <0>; - compatible = "allwinner,sunxi-osc-clk"; + compatible = "allwinner,sun4i-osc-clk"; reg = <0x01c20050 0x4>; clocks = <&osc24M_fixed>; }; pll1: pll1@01c20000 { #clock-cells = <0>; - compatible = "allwinner,sunxi-pll1-clk"; + compatible = "allwinner,sun4i-pll1-clk"; reg = <0x01c20000 0x4>; clocks = <&osc24M>; }; cpu: cpu@01c20054 { #clock-cells = <0>; - compatible = "allwinner,sunxi-cpu-clk"; + compatible = "allwinner,sun4i-cpu-clk"; reg = <0x01c20054 0x4>; clocks = <&osc32k>, <&osc24M>, <&pll1>; }; diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index d4ad1c22859e..d528a2496690 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -305,29 +305,29 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, /* Matches for of_clk_init */ static const __initconst struct of_device_id clk_match[] = { {.compatible = "fixed-clock", .data = of_fixed_clk_setup,}, - {.compatible = "allwinner,sunxi-osc-clk", .data = sunxi_osc_clk_setup,}, + {.compatible = "allwinner,sun4i-osc-clk", .data = sunxi_osc_clk_setup,}, {} }; /* Matches for factors clocks */ static const __initconst struct of_device_id clk_factors_match[] = { - {.compatible = "allwinner,sunxi-pll1-clk", .data = &pll1_data,}, - {.compatible = "allwinner,sunxi-apb1-clk", .data = &apb1_data,}, + {.compatible = "allwinner,sun4i-pll1-clk", .data = &pll1_data,}, + {.compatible = "allwinner,sun4i-apb1-clk", .data = &apb1_data,}, {} }; /* Matches for divider clocks */ static const __initconst struct of_device_id clk_div_match[] = { - {.compatible = "allwinner,sunxi-axi-clk", .data = &axi_data,}, - {.compatible = "allwinner,sunxi-ahb-clk", .data = &ahb_data,}, - {.compatible = "allwinner,sunxi-apb0-clk", .data = &apb0_data,}, + {.compatible = "allwinner,sun4i-axi-clk", .data = &axi_data,}, + {.compatible = "allwinner,sun4i-ahb-clk", .data = &ahb_data,}, + {.compatible = "allwinner,sun4i-apb0-clk", .data = &apb0_data,}, {} }; /* Matches for mux clocks */ static const __initconst struct of_device_id clk_mux_match[] = { - {.compatible = "allwinner,sunxi-cpu-clk", .data = &cpu_data,}, - {.compatible = "allwinner,sunxi-apb1-mux-clk", .data = &apb1_mux_data,}, + {.compatible = "allwinner,sun4i-cpu-clk", .data = &cpu_data,}, + {.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &apb1_mux_data,}, {} }; -- cgit v1.2.3 From 4e9c8e5c5883c910926296e699c2f4e4d9f847cb Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 28 Mar 2013 15:31:13 -0400 Subject: USB: remove CONFIG_USB_SUSPEND from Documentation An earlier patch removed the CONFIG_USB_SUSPEND symbol but forgot to update the Documentation files. This patch (as1676) rectifies that omission. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-usb | 6 +++--- Documentation/usb/power-management.txt | 10 ++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index c8baaf53594a..f093e59cbe5f 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -32,7 +32,7 @@ Date: January 2008 KernelVersion: 2.6.25 Contact: Sarah Sharp Description: - If CONFIG_PM and CONFIG_USB_SUSPEND are enabled, then this file + If CONFIG_PM_RUNTIME is enabled then this file is present. When read, it returns the total time (in msec) that the USB device has been connected to the machine. This file is read-only. @@ -45,7 +45,7 @@ Date: January 2008 KernelVersion: 2.6.25 Contact: Sarah Sharp Description: - If CONFIG_PM and CONFIG_USB_SUSPEND are enabled, then this file + If CONFIG_PM_RUNTIME is enabled then this file is present. When read, it returns the total time (in msec) that the USB device has been active, i.e. not in a suspended state. This file is read-only. @@ -187,7 +187,7 @@ What: /sys/bus/usb/devices/.../power/usb2_hardware_lpm Date: September 2011 Contact: Andiry Xu Description: - If CONFIG_USB_SUSPEND is set and a USB 2.0 lpm-capable device + If CONFIG_PM_RUNTIME is set and a USB 2.0 lpm-capable device is plugged in to a xHCI host which support link PM, it will perform a LPM test; if the test is passed and host supports USB2 hardware LPM (xHCI 1.0 feature), USB2 hardware LPM will diff --git a/Documentation/usb/power-management.txt b/Documentation/usb/power-management.txt index 4204eb01fd38..1392b61d6ebe 100644 --- a/Documentation/usb/power-management.txt +++ b/Documentation/usb/power-management.txt @@ -33,6 +33,10 @@ built with CONFIG_USB_SUSPEND enabled (which depends on CONFIG_PM_RUNTIME). System PM support is present only if the kernel was built with CONFIG_SUSPEND or CONFIG_HIBERNATION enabled. +(Starting with the 3.10 kernel release, dynamic PM support for USB is +present whenever the kernel was built with CONFIG_PM_RUNTIME enabled. +The CONFIG_USB_SUSPEND option has been eliminated.) + What is Remote Wakeup? ---------------------- @@ -206,10 +210,8 @@ initialized to 5. (The idle-delay values for already existing devices will not be affected.) Setting the initial default idle-delay to -1 will prevent any -autosuspend of any USB device. This is a simple alternative to -disabling CONFIG_USB_SUSPEND and rebuilding the kernel, and it has the -added benefit of allowing you to enable autosuspend for selected -devices. +autosuspend of any USB device. This has the benefit of allowing you +then to enable autosuspend for selected devices. Warnings -- cgit v1.2.3 From df4b404b35e3e125b7e6d82f9affc6c704502cb3 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 26 Mar 2013 11:52:00 +0000 Subject: iio: exynos-adc: Fix typo in DT documentation Fixes some typos in the documentation of exynos-adc.txt. Signed-off-by: Sachin Kamat Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt b/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt index 05e9d95ede5c..1fc744bd7166 100644 --- a/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt +++ b/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt @@ -1,6 +1,6 @@ Samsung Exynos Analog to Digital Converter bindings -This devicetree binding are for the new adc driver written fori +The devicetree bindings are for the new ADC driver written for Exynos4 and upward SoCs from Samsung. New driver handles the following @@ -49,4 +49,4 @@ adc@12D10000 { }; Note: Does not apply to ADC driver under arch/arm/plat-samsung/ -Note: The child node can be added under the adc node or seperately. +Note: The child node can be added under the adc node or separately. -- cgit v1.2.3 From 2b684024b59c4b1a1440e8c7499b7060a22d1ec1 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Wed, 13 Mar 2013 20:39:00 +0000 Subject: iio: adc: Document the regulator/clocks for exynos-adc The exynos ADC won't work without a regulator called "vdd" and a clock called "adc". Document this fact in the device tree bindings. Signed-off-by: Doug Anderson Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt b/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt index 1fc744bd7166..47ada1dff216 100644 --- a/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt +++ b/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt @@ -20,6 +20,9 @@ Required properties: format is being dependent on which interrupt controller the Samsung device uses. - #io-channel-cells = <1>; As ADC has multiple outputs +- clocks From common clock binding: handle to adc clock. +- clock-names From common clock binding: Shall be "adc". +- vdd-supply VDD input supply. Note: child nodes can be added for auto probing from device tree. @@ -31,6 +34,11 @@ adc: adc@12D10000 { interrupts = <0 106 0>; #io-channel-cells = <1>; io-channel-ranges; + + clocks = <&clock 303>; + clock-names = "adc"; + + vdd-supply = <&buck5_reg>; }; -- cgit v1.2.3 From af720c7c414befed0aad1c92f6e33271cf4f83d3 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 26 Mar 2013 22:47:22 -0300 Subject: [media] v4l2: Fix the type of V4L2_CID_TUNE_PREEMPHASIS in the documentation Change the type of V4L2_CID_TUNE_PREEMPHASIS from 'integer' to 'enum v4l2_preemphasis' Acked-by: Hans Verkuil Signed-off-by: Andrey Smirnov Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index b4952e23201b..06e9a5facb40 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -3854,7 +3854,7 @@ in Hz. The range and step are driver-specific. V4L2_CID_TUNE_PREEMPHASIS  - integer + enum v4l2_preemphasis Configures the pre-emphasis value for broadcasting. A pre-emphasis filter is applied to the broadcast to accentuate the high audio frequencies. -- cgit v1.2.3 From b4e96a7e9f9511d1b57a10a08cd10af6e517966b Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 26 Mar 2013 22:47:24 -0300 Subject: [media] v4l2: Add documentation for the FM RX controls Add appropriate documentation for all the newly added standard controls. Based on the patch by Manjunatha Halli [1] [1] http://lists-archives.com/linux-kernel/27641303-media-update-docs-for-v4l2-fm-new-features.html Acked-by: Hans Verkuil Signed-off-by: Andrey Smirnov Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/compat.xml | 3 + Documentation/DocBook/media/v4l/controls.xml | 72 ++++++++++++++++++++++ .../DocBook/media/v4l/vidioc-g-ext-ctrls.xml | 9 +++ 3 files changed, 84 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index 8c2b3687a6a4..e44161ffdd07 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2310,6 +2310,9 @@ more information. Added FM Modulator (FM TX) Extended Control Class: V4L2_CTRL_CLASS_FM_TX and their Control IDs. + + Added FM Receiver (FM RX) Extended Control Class: V4L2_CTRL_CLASS_FM_RX and their Control IDs. + Added Remote Controller chapter, describing the default Remote Controller mapping for media devices. diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 06e9a5facb40..c8eb6c222274 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -4693,4 +4693,76 @@ interface and may change in the future.
+ +
+ FM Receiver Control Reference + + The FM Receiver (FM_RX) class includes controls for common features of + FM Reception capable devices. + + + FM_RX Control IDs + + + + + + + + + + + ID + Type + Description + + + + + + V4L2_CID_FM_RX_CLASS  + class + The FM_RX class +descriptor. Calling &VIDIOC-QUERYCTRL; for this control will return a +description of this control class. + + + V4L2_CID_RDS_RECEPTION  + boolean + Enables/disables RDS + reception by the radio tuner + + + V4L2_CID_TUNE_DEEMPHASIS  + enum v4l2_deemphasis + + Configures the de-emphasis value for reception. +A de-emphasis filter is applied to the broadcast to accentuate the high audio frequencies. +Depending on the region, a time constant of either 50 or 75 useconds is used. The enum v4l2_deemphasis +defines possible values for de-emphasis. Here they are: + + + + + V4L2_DEEMPHASIS_DISABLED  + No de-emphasis is applied. + + + V4L2_DEEMPHASIS_50_uS  + A de-emphasis of 50 uS is used. + + + V4L2_DEEMPHASIS_75_uS  + A de-emphasis of 75 uS is used. + + + + + + + + +
+ +
diff --git a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml index 4e16112df992..b3bb9575b2e0 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml @@ -319,6 +319,15 @@ These controls are described in . + + + V4L2_CTRL_CLASS_FM_RX + 0xa10000 + The class containing FM Receiver (FM RX) controls. +These controls are described in . + + -- cgit v1.2.3 From 30bac9110455402fa8888740c6819dd3daa2666f Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 26 Mar 2013 22:47:26 -0300 Subject: [media] v4l2: Add a V4L2 driver for SI476X MFD This commit adds a driver that exposes all the radio related functionality of the Si476x series of chips via the V4L2 subsystem. Acked-by: Hans Verkuil Signed-off-by: Andrey Smirnov Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/si476x.txt | 187 ++++ drivers/media/radio/Kconfig | 17 + drivers/media/radio/Makefile | 1 + drivers/media/radio/radio-si476x.c | 1599 ++++++++++++++++++++++++++++++++++ include/media/si476x.h | 426 +++++++++ 5 files changed, 2230 insertions(+) create mode 100644 Documentation/video4linux/si476x.txt create mode 100644 drivers/media/radio/radio-si476x.c create mode 100644 include/media/si476x.h (limited to 'Documentation') diff --git a/Documentation/video4linux/si476x.txt b/Documentation/video4linux/si476x.txt new file mode 100644 index 000000000000..d1a08db2cbd9 --- /dev/null +++ b/Documentation/video4linux/si476x.txt @@ -0,0 +1,187 @@ +SI476x Driver Readme +------------------------------------------------ + Copyright (C) 2013 Andrey Smirnov + +TODO for the driver +------------------------------ + +- According to the SiLabs' datasheet it is possible to update the + firmware of the radio chip in the run-time, thus bringing it to the + most recent version. Unfortunately I couldn't find any mentioning of + the said firmware update for the old chips that I tested the driver + against, so for chips like that the driver only exposes the old + functionality. + + +Parameters exposed over debugfs +------------------------------- +SI476x allow user to get multiple characteristics that can be very +useful for EoL testing/RF performance estimation, parameters that have +very little to do with V4L2 subsystem. Such parameters are exposed via +debugfs and can be accessed via regular file I/O operations. + +The drivers exposes following files: + +* /sys/kernel/debug//acf + This file contains ACF(Automatically Controlled Features) status + information. The contents of the file is binary data of the + following layout: + + Offset | Name | Description + ==================================================================== + 0x00 | blend_int | Flag, set when stereo separation has + | | crossed below the blend threshold + -------------------------------------------------------------------- + 0x01 | hblend_int | Flag, set when HiBlend cutoff + | | frequency is lower than threshold + -------------------------------------------------------------------- + 0x02 | hicut_int | Flag, set when HiCut cutoff + | | frequency is lower than threshold + -------------------------------------------------------------------- + 0x03 | chbw_int | Flag, set when channel filter + | | bandwidth is less than threshold + -------------------------------------------------------------------- + 0x04 | softmute_int | Flag indicating that softmute + | | attenuation has increased above + | | softmute threshold + -------------------------------------------------------------------- + 0x05 | smute | 0 - Audio is not soft muted + | | 1 - Audio is soft muted + -------------------------------------------------------------------- + 0x06 | smattn | Soft mute attenuation level in dB + -------------------------------------------------------------------- + 0x07 | chbw | Channel filter bandwidth in kHz + -------------------------------------------------------------------- + 0x08 | hicut | HiCut cutoff frequency in units of + | | 100Hz + -------------------------------------------------------------------- + 0x09 | hiblend | HiBlend cutoff frequency in units + | | of 100 Hz + -------------------------------------------------------------------- + 0x10 | pilot | 0 - Stereo pilot is not present + | | 1 - Stereo pilot is present + -------------------------------------------------------------------- + 0x11 | stblend | Stereo blend in % + -------------------------------------------------------------------- + + +* /sys/kernel/debug//rds_blckcnt + This file contains statistics about RDS receptions. It's binary data + has the following layout: + + Offset | Name | Description + ==================================================================== + 0x00 | expected | Number of expected RDS blocks + -------------------------------------------------------------------- + 0x02 | received | Number of received RDS blocks + -------------------------------------------------------------------- + 0x04 | uncorrectable | Number of uncorrectable RDS blocks + -------------------------------------------------------------------- + +* /sys/kernel/debug//agc + This file contains information about parameters pertaining to + AGC(Automatic Gain Control) + + The layout is: + Offset | Name | Description + ==================================================================== + 0x00 | mxhi | 0 - FM Mixer PD high threshold is + | | not tripped + | | 1 - FM Mixer PD high threshold is + | | tripped + -------------------------------------------------------------------- + 0x01 | mxlo | ditto for FM Mixer PD low + -------------------------------------------------------------------- + 0x02 | lnahi | ditto for FM LNA PD high + -------------------------------------------------------------------- + 0x03 | lnalo | ditto for FM LNA PD low + -------------------------------------------------------------------- + 0x04 | fmagc1 | FMAGC1 attenuator resistance + | | (see datasheet for more detail) + -------------------------------------------------------------------- + 0x05 | fmagc2 | ditto for FMAGC2 + -------------------------------------------------------------------- + 0x06 | pgagain | PGA gain in dB + -------------------------------------------------------------------- + 0x07 | fmwblang | FM/WB LNA Gain in dB + -------------------------------------------------------------------- + +* /sys/kernel/debug//rsq + This file contains information about parameters pertaining to + RSQ(Received Signal Quality) + + The layout is: + Offset | Name | Description + ==================================================================== + 0x00 | multhint | 0 - multipath value has not crossed + | | the Multipath high threshold + | | 1 - multipath value has crossed + | | the Multipath high threshold + -------------------------------------------------------------------- + 0x01 | multlint | ditto for Multipath low threshold + -------------------------------------------------------------------- + 0x02 | snrhint | 0 - received signal's SNR has not + | | crossed high threshold + | | 1 - received signal's SNR has + | | crossed high threshold + -------------------------------------------------------------------- + 0x03 | snrlint | ditto for low threshold + -------------------------------------------------------------------- + 0x04 | rssihint | ditto for RSSI high threshold + -------------------------------------------------------------------- + 0x05 | rssilint | ditto for RSSI low threshold + -------------------------------------------------------------------- + 0x06 | bltf | Flag indicating if seek command + | | reached/wrapped seek band limit + -------------------------------------------------------------------- + 0x07 | snr_ready | Indicates that SNR metrics is ready + -------------------------------------------------------------------- + 0x08 | rssiready | ditto for RSSI metrics + -------------------------------------------------------------------- + 0x09 | injside | 0 - Low-side injection is being used + | | 1 - High-side injection is used + -------------------------------------------------------------------- + 0x10 | afcrl | Flag indicating if AFC rails + -------------------------------------------------------------------- + 0x11 | valid | Flag indicating if channel is valid + -------------------------------------------------------------------- + 0x12 | readfreq | Current tuned frequency + -------------------------------------------------------------------- + 0x14 | freqoff | Singed frequency offset in units of + | | 2ppm + -------------------------------------------------------------------- + 0x15 | rssi | Signed value of RSSI in dBuV + -------------------------------------------------------------------- + 0x16 | snr | Signed RF SNR in dB + -------------------------------------------------------------------- + 0x17 | issi | Signed Image Strength Signal + | | indicator + -------------------------------------------------------------------- + 0x18 | lassi | Signed Low side adjacent Channel + | | Strength indicator + -------------------------------------------------------------------- + 0x19 | hassi | ditto fpr High side + -------------------------------------------------------------------- + 0x20 | mult | Multipath indicator + -------------------------------------------------------------------- + 0x21 | dev | Frequency deviation + -------------------------------------------------------------------- + 0x24 | assi | Adjascent channel SSI + -------------------------------------------------------------------- + 0x25 | usn | Ultrasonic noise indicator + -------------------------------------------------------------------- + 0x26 | pilotdev | Pilot deviation in units of 100 Hz + -------------------------------------------------------------------- + 0x27 | rdsdev | ditto for RDS + -------------------------------------------------------------------- + 0x28 | assidev | ditto for ASSI + -------------------------------------------------------------------- + 0x29 | strongdev | Frequency deviation + -------------------------------------------------------------------- + 0x30 | rdspi | RDS PI code + -------------------------------------------------------------------- + +* /sys/kernel/debug//rsq_primary + This file contains information about parameters pertaining to + RSQ(Received Signal Quality) for primary tuner only. Layout is as + the one above. diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 24e64a09884c..28ded247abc0 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -18,6 +18,23 @@ config RADIO_SI470X source "drivers/media/radio/si470x/Kconfig" +config RADIO_SI476X + tristate "Silicon Laboratories Si476x I2C FM Radio" + depends on I2C && VIDEO_V4L2 + select MFD_CORE + select MFD_SI476X_CORE + select SND_SOC_SI476X + ---help--- + Choose Y here if you have this FM radio chip. + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux 2 API. Information on + this API and pointers to "v4l2" programs may be found at + . + + To compile this driver as a module, choose M here: the + module will be called radio-si476x. + config USB_MR800 tristate "AverMedia MR 800 USB FM radio support" depends on USB && VIDEO_V4L2 diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index 303eaebdb85a..0dcdb320cfc7 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o obj-$(CONFIG_RADIO_TRUST) += radio-trust.o obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o +obj-$(CONFIG_RADIO_SI476X) += radio-si476x.o obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_RADIO_SI470X) += si470x/ diff --git a/drivers/media/radio/radio-si476x.c b/drivers/media/radio/radio-si476x.c new file mode 100644 index 000000000000..0895a0c23787 --- /dev/null +++ b/drivers/media/radio/radio-si476x.c @@ -0,0 +1,1599 @@ +/* + * drivers/media/radio/radio-si476x.c -- V4L2 driver for SI476X chips + * + * Copyright (C) 2012 Innovative Converged Devices(ICD) + * Copyright (C) 2013 Andrey Smirnov + * + * Author: Andrey Smirnov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define FM_FREQ_RANGE_LOW 64000000 +#define FM_FREQ_RANGE_HIGH 108000000 + +#define AM_FREQ_RANGE_LOW 520000 +#define AM_FREQ_RANGE_HIGH 30000000 + +#define PWRLINEFLTR (1 << 8) + +#define FREQ_MUL (10000000 / 625) + +#define SI476X_PHDIV_STATUS_LINK_LOCKED(status) (0b10000000 & (status)) + +#define DRIVER_NAME "si476x-radio" +#define DRIVER_CARD "SI476x AM/FM Receiver" + +enum si476x_freq_bands { + SI476X_BAND_FM, + SI476X_BAND_AM, +}; + +static const struct v4l2_frequency_band si476x_bands[] = { + [SI476X_BAND_FM] = { + .type = V4L2_TUNER_RADIO, + .index = SI476X_BAND_FM, + .capability = V4L2_TUNER_CAP_LOW + | V4L2_TUNER_CAP_STEREO + | V4L2_TUNER_CAP_RDS + | V4L2_TUNER_CAP_RDS_BLOCK_IO + | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 64 * FREQ_MUL, + .rangehigh = 108 * FREQ_MUL, + .modulation = V4L2_BAND_MODULATION_FM, + }, + [SI476X_BAND_AM] = { + .type = V4L2_TUNER_RADIO, + .index = SI476X_BAND_AM, + .capability = V4L2_TUNER_CAP_LOW + | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 0.52 * FREQ_MUL, + .rangehigh = 30 * FREQ_MUL, + .modulation = V4L2_BAND_MODULATION_AM, + }, +}; + +static inline bool si476x_radio_freq_is_inside_of_the_band(u32 freq, int band) +{ + return freq >= si476x_bands[band].rangelow && + freq <= si476x_bands[band].rangehigh; +} + +static inline bool si476x_radio_range_is_inside_of_the_band(u32 low, u32 high, + int band) +{ + return low >= si476x_bands[band].rangelow && + high <= si476x_bands[band].rangehigh; +} + +static int si476x_radio_s_ctrl(struct v4l2_ctrl *ctrl); +static int si476x_radio_g_volatile_ctrl(struct v4l2_ctrl *ctrl); + +enum phase_diversity_modes_idx { + SI476X_IDX_PHDIV_DISABLED, + SI476X_IDX_PHDIV_PRIMARY_COMBINING, + SI476X_IDX_PHDIV_PRIMARY_ANTENNA, + SI476X_IDX_PHDIV_SECONDARY_ANTENNA, + SI476X_IDX_PHDIV_SECONDARY_COMBINING, +}; + +static const char * const phase_diversity_modes[] = { + [SI476X_IDX_PHDIV_DISABLED] = "Disabled", + [SI476X_IDX_PHDIV_PRIMARY_COMBINING] = "Primary with Secondary", + [SI476X_IDX_PHDIV_PRIMARY_ANTENNA] = "Primary Antenna", + [SI476X_IDX_PHDIV_SECONDARY_ANTENNA] = "Secondary Antenna", + [SI476X_IDX_PHDIV_SECONDARY_COMBINING] = "Secondary with Primary", +}; + +static inline enum phase_diversity_modes_idx +si476x_phase_diversity_mode_to_idx(enum si476x_phase_diversity_mode mode) +{ + switch (mode) { + default: /* FALLTHROUGH */ + case SI476X_PHDIV_DISABLED: + return SI476X_IDX_PHDIV_DISABLED; + case SI476X_PHDIV_PRIMARY_COMBINING: + return SI476X_IDX_PHDIV_PRIMARY_COMBINING; + case SI476X_PHDIV_PRIMARY_ANTENNA: + return SI476X_IDX_PHDIV_PRIMARY_ANTENNA; + case SI476X_PHDIV_SECONDARY_ANTENNA: + return SI476X_IDX_PHDIV_SECONDARY_ANTENNA; + case SI476X_PHDIV_SECONDARY_COMBINING: + return SI476X_IDX_PHDIV_SECONDARY_COMBINING; + } +} + +static inline enum si476x_phase_diversity_mode +si476x_phase_diversity_idx_to_mode(enum phase_diversity_modes_idx idx) +{ + static const int idx_to_value[] = { + [SI476X_IDX_PHDIV_DISABLED] = SI476X_PHDIV_DISABLED, + [SI476X_IDX_PHDIV_PRIMARY_COMBINING] = SI476X_PHDIV_PRIMARY_COMBINING, + [SI476X_IDX_PHDIV_PRIMARY_ANTENNA] = SI476X_PHDIV_PRIMARY_ANTENNA, + [SI476X_IDX_PHDIV_SECONDARY_ANTENNA] = SI476X_PHDIV_SECONDARY_ANTENNA, + [SI476X_IDX_PHDIV_SECONDARY_COMBINING] = SI476X_PHDIV_SECONDARY_COMBINING, + }; + + return idx_to_value[idx]; +} + +static const struct v4l2_ctrl_ops si476x_ctrl_ops = { + .g_volatile_ctrl = si476x_radio_g_volatile_ctrl, + .s_ctrl = si476x_radio_s_ctrl, +}; + + +enum si476x_ctrl_idx { + SI476X_IDX_RSSI_THRESHOLD, + SI476X_IDX_SNR_THRESHOLD, + SI476X_IDX_MAX_TUNE_ERROR, + SI476X_IDX_HARMONICS_COUNT, + SI476X_IDX_DIVERSITY_MODE, + SI476X_IDX_INTERCHIP_LINK, +}; +static struct v4l2_ctrl_config si476x_ctrls[] = { + + /** + * SI476X during its station seeking(or tuning) process uses several + * parameters to detrmine if "the station" is valid: + * + * - Signal's SNR(in dBuV) must be lower than + * #V4L2_CID_SI476X_SNR_THRESHOLD + * - Signal's RSSI(in dBuV) must be greater than + * #V4L2_CID_SI476X_RSSI_THRESHOLD + * - Signal's frequency deviation(in units of 2ppm) must not be + * more than #V4L2_CID_SI476X_MAX_TUNE_ERROR + */ + [SI476X_IDX_RSSI_THRESHOLD] = { + .ops = &si476x_ctrl_ops, + .id = V4L2_CID_SI476X_RSSI_THRESHOLD, + .name = "Valid RSSI Threshold", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = -128, + .max = 127, + .step = 1, + }, + [SI476X_IDX_SNR_THRESHOLD] = { + .ops = &si476x_ctrl_ops, + .id = V4L2_CID_SI476X_SNR_THRESHOLD, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Valid SNR Threshold", + .min = -128, + .max = 127, + .step = 1, + }, + [SI476X_IDX_MAX_TUNE_ERROR] = { + .ops = &si476x_ctrl_ops, + .id = V4L2_CID_SI476X_MAX_TUNE_ERROR, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Max Tune Errors", + .min = 0, + .max = 126 * 2, + .step = 2, + }, + + /** + * #V4L2_CID_SI476X_HARMONICS_COUNT -- number of harmonics + * built-in power-line noise supression filter is to reject + * during AM-mode operation. + */ + [SI476X_IDX_HARMONICS_COUNT] = { + .ops = &si476x_ctrl_ops, + .id = V4L2_CID_SI476X_HARMONICS_COUNT, + .type = V4L2_CTRL_TYPE_INTEGER, + + .name = "Count of Harmonics to Reject", + .min = 0, + .max = 20, + .step = 1, + }, + + /** + * #V4L2_CID_SI476X_DIVERSITY_MODE -- configuration which + * two tuners working in diversity mode are to work in. + * + * - #SI476X_IDX_PHDIV_DISABLED diversity mode disabled + * - #SI476X_IDX_PHDIV_PRIMARY_COMBINING diversity mode is + * on, primary tuner's antenna is the main one. + * - #SI476X_IDX_PHDIV_PRIMARY_ANTENNA diversity mode is + * off, primary tuner's antenna is the main one. + * - #SI476X_IDX_PHDIV_SECONDARY_ANTENNA diversity mode is + * off, secondary tuner's antenna is the main one. + * - #SI476X_IDX_PHDIV_SECONDARY_COMBINING diversity mode is + * on, secondary tuner's antenna is the main one. + */ + [SI476X_IDX_DIVERSITY_MODE] = { + .ops = &si476x_ctrl_ops, + .id = V4L2_CID_SI476X_DIVERSITY_MODE, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Phase Diversity Mode", + .qmenu = phase_diversity_modes, + .min = 0, + .max = ARRAY_SIZE(phase_diversity_modes) - 1, + }, + + /** + * #V4L2_CID_SI476X_INTERCHIP_LINK -- inter-chip link in + * diversity mode indicator. Allows user to determine if two + * chips working in diversity mode have established a link + * between each other and if the system as a whole uses + * signals from both antennas to receive FM radio. + */ + [SI476X_IDX_INTERCHIP_LINK] = { + .ops = &si476x_ctrl_ops, + .id = V4L2_CID_SI476X_INTERCHIP_LINK, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE, + .name = "Inter-Chip Link", + .min = 0, + .max = 1, + .step = 1, + }, +}; + +struct si476x_radio; + +/** + * struct si476x_radio_ops - vtable of tuner functions + * + * This table holds pointers to functions implementing particular + * operations depending on the mode in which the tuner chip was + * configured to start in. If the function is not supported + * corresponding element is set to #NULL. + * + * @tune_freq: Tune chip to a specific frequency + * @seek_start: Star station seeking + * @rsq_status: Get Recieved Signal Quality(RSQ) status + * @rds_blckcnt: Get recived RDS blocks count + * @phase_diversity: Change phase diversity mode of the tuner + * @phase_div_status: Get phase diversity mode status + * @acf_status: Get the status of Automatically Controlled + * Features(ACF) + * @agc_status: Get Automatic Gain Control(AGC) status + */ +struct si476x_radio_ops { + int (*tune_freq)(struct si476x_core *, struct si476x_tune_freq_args *); + int (*seek_start)(struct si476x_core *, bool, bool); + int (*rsq_status)(struct si476x_core *, struct si476x_rsq_status_args *, + struct si476x_rsq_status_report *); + int (*rds_blckcnt)(struct si476x_core *, bool, + struct si476x_rds_blockcount_report *); + + int (*phase_diversity)(struct si476x_core *, + enum si476x_phase_diversity_mode); + int (*phase_div_status)(struct si476x_core *); + int (*acf_status)(struct si476x_core *, + struct si476x_acf_status_report *); + int (*agc_status)(struct si476x_core *, + struct si476x_agc_status_report *); +}; + +/** + * struct si476x_radio - radio device + * + * @core: Pointer to underlying core device + * @videodev: Pointer to video device created by V4L2 subsystem + * @ops: Vtable of functions. See struct si476x_radio_ops for details + * @kref: Reference counter + * @core_lock: An r/w semaphore to brebvent the deletion of underlying + * core structure is the radio device is being used + */ +struct si476x_radio { + struct v4l2_device v4l2dev; + struct video_device videodev; + struct v4l2_ctrl_handler ctrl_handler; + + struct si476x_core *core; + /* This field should not be accesses unless core lock is held */ + const struct si476x_radio_ops *ops; + + struct dentry *debugfs; + u32 audmode; +}; + +static inline struct si476x_radio * +v4l2_dev_to_radio(struct v4l2_device *d) +{ + return container_of(d, struct si476x_radio, v4l2dev); +} + +static inline struct si476x_radio * +v4l2_ctrl_handler_to_radio(struct v4l2_ctrl_handler *d) +{ + return container_of(d, struct si476x_radio, ctrl_handler); +} + +/* + * si476x_vidioc_querycap - query device capabilities + */ +static int si476x_radio_querycap(struct file *file, void *priv, + struct v4l2_capability *capability) +{ + struct si476x_radio *radio = video_drvdata(file); + + strlcpy(capability->driver, radio->v4l2dev.name, + sizeof(capability->driver)); + strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); + snprintf(capability->bus_info, sizeof(capability->bus_info), + "platform:%s", radio->v4l2dev.name); + + capability->device_caps = V4L2_CAP_TUNER + | V4L2_CAP_RADIO + | V4L2_CAP_HW_FREQ_SEEK; + + si476x_core_lock(radio->core); + if (!si476x_core_is_a_secondary_tuner(radio->core)) + capability->device_caps |= V4L2_CAP_RDS_CAPTURE + | V4L2_CAP_READWRITE; + si476x_core_unlock(radio->core); + + capability->capabilities = capability->device_caps + | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int si476x_radio_enum_freq_bands(struct file *file, void *priv, + struct v4l2_frequency_band *band) +{ + int err; + struct si476x_radio *radio = video_drvdata(file); + + if (band->tuner != 0) + return -EINVAL; + + switch (radio->core->chip_id) { + /* AM/FM tuners -- all bands are supported */ + case SI476X_CHIP_SI4761: + case SI476X_CHIP_SI4764: + if (band->index < ARRAY_SIZE(si476x_bands)) { + *band = si476x_bands[band->index]; + err = 0; + } else { + err = -EINVAL; + } + break; + /* FM companion tuner chips -- only FM bands are + * supported */ + case SI476X_CHIP_SI4768: + if (band->index == SI476X_BAND_FM) { + *band = si476x_bands[band->index]; + err = 0; + } else { + err = -EINVAL; + } + break; + default: + err = -EINVAL; + } + + return err; +} + +static int si476x_radio_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *tuner) +{ + int err; + struct si476x_rsq_status_report report; + struct si476x_radio *radio = video_drvdata(file); + + struct si476x_rsq_status_args args = { + .primary = false, + .rsqack = false, + .attune = false, + .cancel = false, + .stcack = false, + }; + + if (tuner->index != 0) + return -EINVAL; + + tuner->type = V4L2_TUNER_RADIO; + tuner->capability = V4L2_TUNER_CAP_LOW /* Measure frequencies + * in multiples of + * 62.5 Hz */ + | V4L2_TUNER_CAP_STEREO + | V4L2_TUNER_CAP_HWSEEK_BOUNDED + | V4L2_TUNER_CAP_HWSEEK_WRAP + | V4L2_TUNER_CAP_HWSEEK_PROG_LIM; + + si476x_core_lock(radio->core); + + if (si476x_core_is_a_secondary_tuner(radio->core)) { + strlcpy(tuner->name, "FM (secondary)", sizeof(tuner->name)); + tuner->rxsubchans = 0; + tuner->rangelow = si476x_bands[SI476X_BAND_FM].rangelow; + } else if (si476x_core_has_am(radio->core)) { + if (si476x_core_is_a_primary_tuner(radio->core)) + strlcpy(tuner->name, "AM/FM (primary)", + sizeof(tuner->name)); + else + strlcpy(tuner->name, "AM/FM", sizeof(tuner->name)); + + tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO + | V4L2_TUNER_SUB_RDS; + tuner->capability |= V4L2_TUNER_CAP_RDS + | V4L2_TUNER_CAP_RDS_BLOCK_IO + | V4L2_TUNER_CAP_FREQ_BANDS; + + tuner->rangelow = si476x_bands[SI476X_BAND_AM].rangelow; + } else { + strlcpy(tuner->name, "FM", sizeof(tuner->name)); + tuner->rxsubchans = V4L2_TUNER_SUB_RDS; + tuner->capability |= V4L2_TUNER_CAP_RDS + | V4L2_TUNER_CAP_RDS_BLOCK_IO + | V4L2_TUNER_CAP_FREQ_BANDS; + tuner->rangelow = si476x_bands[SI476X_BAND_FM].rangelow; + } + + tuner->audmode = radio->audmode; + + tuner->afc = 1; + tuner->rangehigh = si476x_bands[SI476X_BAND_FM].rangehigh; + + err = radio->ops->rsq_status(radio->core, + &args, &report); + if (err < 0) { + tuner->signal = 0; + } else { + /* + * tuner->signal value range: 0x0000 .. 0xFFFF, + * report.rssi: -128 .. 127 + */ + tuner->signal = (report.rssi + 128) * 257; + } + si476x_core_unlock(radio->core); + + return err; +} + +static int si476x_radio_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *tuner) +{ + struct si476x_radio *radio = video_drvdata(file); + + if (tuner->index != 0) + return -EINVAL; + + if (tuner->audmode == V4L2_TUNER_MODE_MONO || + tuner->audmode == V4L2_TUNER_MODE_STEREO) + radio->audmode = tuner->audmode; + else + radio->audmode = V4L2_TUNER_MODE_STEREO; + + return 0; +} + +static int si476x_radio_init_vtable(struct si476x_radio *radio, + enum si476x_func func) +{ + static const struct si476x_radio_ops fm_ops = { + .tune_freq = si476x_core_cmd_fm_tune_freq, + .seek_start = si476x_core_cmd_fm_seek_start, + .rsq_status = si476x_core_cmd_fm_rsq_status, + .rds_blckcnt = si476x_core_cmd_fm_rds_blockcount, + .phase_diversity = si476x_core_cmd_fm_phase_diversity, + .phase_div_status = si476x_core_cmd_fm_phase_div_status, + .acf_status = si476x_core_cmd_fm_acf_status, + .agc_status = si476x_core_cmd_agc_status, + }; + + static const struct si476x_radio_ops am_ops = { + .tune_freq = si476x_core_cmd_am_tune_freq, + .seek_start = si476x_core_cmd_am_seek_start, + .rsq_status = si476x_core_cmd_am_rsq_status, + .rds_blckcnt = NULL, + .phase_diversity = NULL, + .phase_div_status = NULL, + .acf_status = si476x_core_cmd_am_acf_status, + .agc_status = NULL, + }; + + switch (func) { + case SI476X_FUNC_FM_RECEIVER: + radio->ops = &fm_ops; + return 0; + + case SI476X_FUNC_AM_RECEIVER: + radio->ops = &am_ops; + return 0; + default: + WARN(1, "Unexpected tuner function value\n"); + return -EINVAL; + } +} + +static int si476x_radio_pretune(struct si476x_radio *radio, + enum si476x_func func) +{ + int retval; + + struct si476x_tune_freq_args args = { + .zifsr = false, + .hd = false, + .injside = SI476X_INJSIDE_AUTO, + .tunemode = SI476X_TM_VALIDATED_NORMAL_TUNE, + .smoothmetrics = SI476X_SM_INITIALIZE_AUDIO, + .antcap = 0, + }; + + switch (func) { + case SI476X_FUNC_FM_RECEIVER: + args.freq = v4l2_to_si476x(radio->core, + 92 * FREQ_MUL); + retval = radio->ops->tune_freq(radio->core, &args); + break; + case SI476X_FUNC_AM_RECEIVER: + args.freq = v4l2_to_si476x(radio->core, + 0.6 * FREQ_MUL); + retval = radio->ops->tune_freq(radio->core, &args); + break; + default: + WARN(1, "Unexpected tuner function value\n"); + retval = -EINVAL; + } + + return retval; +} +static int si476x_radio_do_post_powerup_init(struct si476x_radio *radio, + enum si476x_func func) +{ + int err; + + /* regcache_mark_dirty(radio->core->regmap); */ + err = regcache_sync_region(radio->core->regmap, + SI476X_PROP_DIGITAL_IO_INPUT_SAMPLE_RATE, + SI476X_PROP_DIGITAL_IO_OUTPUT_FORMAT); + if (err < 0) + return err; + + err = regcache_sync_region(radio->core->regmap, + SI476X_PROP_AUDIO_DEEMPHASIS, + SI476X_PROP_AUDIO_PWR_LINE_FILTER); + if (err < 0) + return err; + + err = regcache_sync_region(radio->core->regmap, + SI476X_PROP_INT_CTL_ENABLE, + SI476X_PROP_INT_CTL_ENABLE); + if (err < 0) + return err; + + /* + * Is there any point in restoring SNR and the like + * when switching between AM/FM? + */ + err = regcache_sync_region(radio->core->regmap, + SI476X_PROP_VALID_MAX_TUNE_ERROR, + SI476X_PROP_VALID_MAX_TUNE_ERROR); + if (err < 0) + return err; + + err = regcache_sync_region(radio->core->regmap, + SI476X_PROP_VALID_SNR_THRESHOLD, + SI476X_PROP_VALID_RSSI_THRESHOLD); + if (err < 0) + return err; + + if (func == SI476X_FUNC_FM_RECEIVER) { + if (si476x_core_has_diversity(radio->core)) { + err = si476x_core_cmd_fm_phase_diversity(radio->core, + radio->core->diversity_mode); + if (err < 0) + return err; + } + + err = regcache_sync_region(radio->core->regmap, + SI476X_PROP_FM_RDS_INTERRUPT_SOURCE, + SI476X_PROP_FM_RDS_CONFIG); + if (err < 0) + return err; + } + + return si476x_radio_init_vtable(radio, func); + +} + +static int si476x_radio_change_func(struct si476x_radio *radio, + enum si476x_func func) +{ + int err; + bool soft; + /* + * Since power/up down is a very time consuming operation, + * try to avoid doing it if the requested mode matches the one + * the tuner is in + */ + if (func == radio->core->power_up_parameters.func) + return 0; + + soft = true; + err = si476x_core_stop(radio->core, soft); + if (err < 0) { + /* + * OK, if the chip does not want to play nice let's + * try to reset it in more brutal way + */ + soft = false; + err = si476x_core_stop(radio->core, soft); + if (err < 0) + return err; + } + /* + Set the desired radio tuner function + */ + radio->core->power_up_parameters.func = func; + + err = si476x_core_start(radio->core, soft); + if (err < 0) + return err; + + /* + * No need to do the rest of manipulations for the bootlader + * mode + */ + if (func != SI476X_FUNC_FM_RECEIVER && + func != SI476X_FUNC_AM_RECEIVER) + return err; + + return si476x_radio_do_post_powerup_init(radio, func); +} + +static int si476x_radio_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + int err; + struct si476x_radio *radio = video_drvdata(file); + + if (f->tuner != 0 || + f->type != V4L2_TUNER_RADIO) + return -EINVAL; + + si476x_core_lock(radio->core); + + if (radio->ops->rsq_status) { + struct si476x_rsq_status_report report; + struct si476x_rsq_status_args args = { + .primary = false, + .rsqack = false, + .attune = true, + .cancel = false, + .stcack = false, + }; + + err = radio->ops->rsq_status(radio->core, &args, &report); + if (!err) + f->frequency = si476x_to_v4l2(radio->core, + report.readfreq); + } else { + err = -EINVAL; + } + + si476x_core_unlock(radio->core); + + return err; +} + +static int si476x_radio_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + int err; + struct si476x_tune_freq_args args; + struct si476x_radio *radio = video_drvdata(file); + + const u32 midrange = (si476x_bands[SI476X_BAND_AM].rangehigh + + si476x_bands[SI476X_BAND_FM].rangelow) / 2; + const int band = (f->frequency > midrange) ? + SI476X_BAND_FM : SI476X_BAND_AM; + const enum si476x_func func = (band == SI476X_BAND_AM) ? + SI476X_FUNC_AM_RECEIVER : SI476X_FUNC_FM_RECEIVER; + + if (f->tuner != 0 || + f->type != V4L2_TUNER_RADIO) + return -EINVAL; + + si476x_core_lock(radio->core); + + f->frequency = clamp(f->frequency, + si476x_bands[band].rangelow, + si476x_bands[band].rangehigh); + + if (si476x_radio_freq_is_inside_of_the_band(f->frequency, + SI476X_BAND_AM) && + (!si476x_core_has_am(radio->core) || + si476x_core_is_a_secondary_tuner(radio->core))) { + err = -EINVAL; + goto unlock; + } + + err = si476x_radio_change_func(radio, func); + if (err < 0) + goto unlock; + + args.zifsr = false; + args.hd = false; + args.injside = SI476X_INJSIDE_AUTO; + args.freq = v4l2_to_si476x(radio->core, + f->frequency); + args.tunemode = SI476X_TM_VALIDATED_NORMAL_TUNE; + args.smoothmetrics = SI476X_SM_INITIALIZE_AUDIO; + args.antcap = 0; + + err = radio->ops->tune_freq(radio->core, &args); + +unlock: + si476x_core_unlock(radio->core); + return err; +} + +static int si476x_radio_s_hw_freq_seek(struct file *file, void *priv, + const struct v4l2_hw_freq_seek *seek) +{ + int err; + enum si476x_func func; + u32 rangelow, rangehigh; + struct si476x_radio *radio = video_drvdata(file); + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + if (seek->tuner != 0 || + seek->type != V4L2_TUNER_RADIO) + return -EINVAL; + + si476x_core_lock(radio->core); + + if (!seek->rangelow) { + err = regmap_read(radio->core->regmap, + SI476X_PROP_SEEK_BAND_BOTTOM, + &rangelow); + if (!err) + rangelow = si476x_to_v4l2(radio->core, rangelow); + else + goto unlock; + } + if (!seek->rangehigh) { + err = regmap_read(radio->core->regmap, + SI476X_PROP_SEEK_BAND_TOP, + &rangehigh); + if (!err) + rangehigh = si476x_to_v4l2(radio->core, rangehigh); + else + goto unlock; + } + + if (rangelow > rangehigh) { + err = -EINVAL; + goto unlock; + } + + if (si476x_radio_range_is_inside_of_the_band(rangelow, rangehigh, + SI476X_BAND_FM)) { + func = SI476X_FUNC_FM_RECEIVER; + + } else if (si476x_core_has_am(radio->core) && + si476x_radio_range_is_inside_of_the_band(rangelow, rangehigh, + SI476X_BAND_AM)) { + func = SI476X_FUNC_AM_RECEIVER; + } else { + err = -EINVAL; + goto unlock; + } + + err = si476x_radio_change_func(radio, func); + if (err < 0) + goto unlock; + + if (seek->rangehigh) { + err = regmap_write(radio->core->regmap, + SI476X_PROP_SEEK_BAND_TOP, + v4l2_to_si476x(radio->core, + seek->rangehigh)); + if (err) + goto unlock; + } + if (seek->rangelow) { + err = regmap_write(radio->core->regmap, + SI476X_PROP_SEEK_BAND_BOTTOM, + v4l2_to_si476x(radio->core, + seek->rangelow)); + if (err) + goto unlock; + } + if (seek->spacing) { + err = regmap_write(radio->core->regmap, + SI476X_PROP_SEEK_FREQUENCY_SPACING, + v4l2_to_si476x(radio->core, + seek->spacing)); + if (err) + goto unlock; + } + + err = radio->ops->seek_start(radio->core, + seek->seek_upward, + seek->wrap_around); +unlock: + si476x_core_unlock(radio->core); + + + + return err; +} + +static int si476x_radio_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + int retval; + struct si476x_radio *radio = v4l2_ctrl_handler_to_radio(ctrl->handler); + + si476x_core_lock(radio->core); + + switch (ctrl->id) { + case V4L2_CID_SI476X_INTERCHIP_LINK: + if (si476x_core_has_diversity(radio->core)) { + if (radio->ops->phase_diversity) { + retval = radio->ops->phase_div_status(radio->core); + if (retval < 0) + break; + + ctrl->val = !!SI476X_PHDIV_STATUS_LINK_LOCKED(retval); + retval = 0; + break; + } else { + retval = -ENOTTY; + break; + } + } + retval = -EINVAL; + break; + default: + retval = -EINVAL; + break; + } + si476x_core_unlock(radio->core); + return retval; + +} + +static int si476x_radio_s_ctrl(struct v4l2_ctrl *ctrl) +{ + int retval; + enum si476x_phase_diversity_mode mode; + struct si476x_radio *radio = v4l2_ctrl_handler_to_radio(ctrl->handler); + + si476x_core_lock(radio->core); + + switch (ctrl->id) { + case V4L2_CID_SI476X_HARMONICS_COUNT: + retval = regmap_update_bits(radio->core->regmap, + SI476X_PROP_AUDIO_PWR_LINE_FILTER, + SI476X_PROP_PWR_HARMONICS_MASK, + ctrl->val); + break; + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (ctrl->val) { + case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: + retval = regmap_update_bits(radio->core->regmap, + SI476X_PROP_AUDIO_PWR_LINE_FILTER, + SI476X_PROP_PWR_ENABLE_MASK, + 0); + break; + case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: + retval = regmap_update_bits(radio->core->regmap, + SI476X_PROP_AUDIO_PWR_LINE_FILTER, + SI476X_PROP_PWR_GRID_MASK, + SI476X_PROP_PWR_GRID_50HZ); + break; + case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: + retval = regmap_update_bits(radio->core->regmap, + SI476X_PROP_AUDIO_PWR_LINE_FILTER, + SI476X_PROP_PWR_GRID_MASK, + SI476X_PROP_PWR_GRID_60HZ); + break; + default: + retval = -EINVAL; + break; + } + break; + case V4L2_CID_SI476X_RSSI_THRESHOLD: + retval = regmap_write(radio->core->regmap, + SI476X_PROP_VALID_RSSI_THRESHOLD, + ctrl->val); + break; + case V4L2_CID_SI476X_SNR_THRESHOLD: + retval = regmap_write(radio->core->regmap, + SI476X_PROP_VALID_SNR_THRESHOLD, + ctrl->val); + break; + case V4L2_CID_SI476X_MAX_TUNE_ERROR: + retval = regmap_write(radio->core->regmap, + SI476X_PROP_VALID_MAX_TUNE_ERROR, + ctrl->val); + break; + case V4L2_CID_RDS_RECEPTION: + /* + * It looks like RDS related properties are + * inaccesable when tuner is in AM mode, so cache the + * changes + */ + if (si476x_core_is_in_am_receiver_mode(radio->core)) + regcache_cache_only(radio->core->regmap, true); + + if (ctrl->val) { + retval = regmap_write(radio->core->regmap, + SI476X_PROP_FM_RDS_INTERRUPT_FIFO_COUNT, + radio->core->rds_fifo_depth); + if (retval < 0) + break; + + if (radio->core->client->irq) { + retval = regmap_write(radio->core->regmap, + SI476X_PROP_FM_RDS_INTERRUPT_SOURCE, + SI476X_RDSRECV); + if (retval < 0) + break; + } + + /* Drain RDS FIFO before enabling RDS processing */ + retval = si476x_core_cmd_fm_rds_status(radio->core, + false, + true, + true, + NULL); + if (retval < 0) + break; + + retval = regmap_update_bits(radio->core->regmap, + SI476X_PROP_FM_RDS_CONFIG, + SI476X_PROP_RDSEN_MASK, + SI476X_PROP_RDSEN); + } else { + retval = regmap_update_bits(radio->core->regmap, + SI476X_PROP_FM_RDS_CONFIG, + SI476X_PROP_RDSEN_MASK, + !SI476X_PROP_RDSEN); + } + + if (si476x_core_is_in_am_receiver_mode(radio->core)) + regcache_cache_only(radio->core->regmap, false); + break; + case V4L2_CID_TUNE_DEEMPHASIS: + retval = regmap_write(radio->core->regmap, + SI476X_PROP_AUDIO_DEEMPHASIS, + ctrl->val); + break; + + case V4L2_CID_SI476X_DIVERSITY_MODE: + mode = si476x_phase_diversity_idx_to_mode(ctrl->val); + + if (mode == radio->core->diversity_mode) { + retval = 0; + break; + } + + if (si476x_core_is_in_am_receiver_mode(radio->core)) { + /* + * Diversity cannot be configured while tuner + * is in AM mode so save the changes and carry on. + */ + radio->core->diversity_mode = mode; + retval = 0; + } else { + retval = radio->ops->phase_diversity(radio->core, mode); + if (!retval) + radio->core->diversity_mode = mode; + } + break; + + default: + retval = -EINVAL; + break; + } + + si476x_core_unlock(radio->core); + + return retval; +} + +static int si476x_radio_g_chip_ident(struct file *file, void *fh, + struct v4l2_dbg_chip_ident *chip) +{ + if (chip->match.type == V4L2_CHIP_MATCH_HOST && + v4l2_chip_match_host(&chip->match)) + return 0; + return -EINVAL; +} + + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int si476x_radio_g_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg) +{ + int err; + unsigned int value; + struct si476x_radio *radio = video_drvdata(file); + + si476x_core_lock(radio->core); + reg->size = 2; + err = regmap_read(radio->core->regmap, + (unsigned int)reg->reg, &value); + reg->val = value; + si476x_core_unlock(radio->core); + + return err; +} +static int si476x_radio_s_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg) +{ + + int err; + struct si476x_radio *radio = video_drvdata(file); + + si476x_core_lock(radio->core); + err = regmap_write(radio->core->regmap, + (unsigned int)reg->reg, + (unsigned int)reg->val); + si476x_core_unlock(radio->core); + + return err; +} +#endif + +static int si476x_radio_fops_open(struct file *file) +{ + struct si476x_radio *radio = video_drvdata(file); + int err; + + err = v4l2_fh_open(file); + if (err) + return err; + + if (v4l2_fh_is_singular_file(file)) { + si476x_core_lock(radio->core); + err = si476x_core_set_power_state(radio->core, + SI476X_POWER_UP_FULL); + if (err < 0) + goto done; + + err = si476x_radio_do_post_powerup_init(radio, + radio->core->power_up_parameters.func); + if (err < 0) + goto power_down; + + err = si476x_radio_pretune(radio, + radio->core->power_up_parameters.func); + if (err < 0) + goto power_down; + + si476x_core_unlock(radio->core); + /*Must be done after si476x_core_unlock to prevent a deadlock*/ + v4l2_ctrl_handler_setup(&radio->ctrl_handler); + } + + return err; + +power_down: + si476x_core_set_power_state(radio->core, + SI476X_POWER_DOWN); +done: + si476x_core_unlock(radio->core); + v4l2_fh_release(file); + + return err; +} + +static int si476x_radio_fops_release(struct file *file) +{ + int err; + struct si476x_radio *radio = video_drvdata(file); + + if (v4l2_fh_is_singular_file(file) && + atomic_read(&radio->core->is_alive)) + si476x_core_set_power_state(radio->core, + SI476X_POWER_DOWN); + + err = v4l2_fh_release(file); + + return err; +} + +static ssize_t si476x_radio_fops_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t rval; + size_t fifo_len; + unsigned int copied; + + struct si476x_radio *radio = video_drvdata(file); + + /* block if no new data available */ + if (kfifo_is_empty(&radio->core->rds_fifo)) { + if (file->f_flags & O_NONBLOCK) + return -EWOULDBLOCK; + + rval = wait_event_interruptible(radio->core->rds_read_queue, + (!kfifo_is_empty(&radio->core->rds_fifo) || + !atomic_read(&radio->core->is_alive))); + if (rval < 0) + return -EINTR; + + if (!atomic_read(&radio->core->is_alive)) + return -ENODEV; + } + + fifo_len = kfifo_len(&radio->core->rds_fifo); + + if (kfifo_to_user(&radio->core->rds_fifo, buf, + min(fifo_len, count), + &copied) != 0) { + dev_warn(&radio->videodev.dev, + "Error during FIFO to userspace copy\n"); + rval = -EIO; + } else { + rval = (ssize_t)copied; + } + + return rval; +} + +static unsigned int si476x_radio_fops_poll(struct file *file, + struct poll_table_struct *pts) +{ + struct si476x_radio *radio = video_drvdata(file); + unsigned long req_events = poll_requested_events(pts); + unsigned int err = v4l2_ctrl_poll(file, pts); + + if (req_events & (POLLIN | POLLRDNORM)) { + if (atomic_read(&radio->core->is_alive)) + poll_wait(file, &radio->core->rds_read_queue, pts); + + if (!atomic_read(&radio->core->is_alive)) + err = POLLHUP; + + if (!kfifo_is_empty(&radio->core->rds_fifo)) + err = POLLIN | POLLRDNORM; + } + + return err; +} + +static const struct v4l2_file_operations si476x_fops = { + .owner = THIS_MODULE, + .read = si476x_radio_fops_read, + .poll = si476x_radio_fops_poll, + .unlocked_ioctl = video_ioctl2, + .open = si476x_radio_fops_open, + .release = si476x_radio_fops_release, +}; + + +static const struct v4l2_ioctl_ops si4761_ioctl_ops = { + .vidioc_querycap = si476x_radio_querycap, + .vidioc_g_tuner = si476x_radio_g_tuner, + .vidioc_s_tuner = si476x_radio_s_tuner, + + .vidioc_g_frequency = si476x_radio_g_frequency, + .vidioc_s_frequency = si476x_radio_s_frequency, + .vidioc_s_hw_freq_seek = si476x_radio_s_hw_freq_seek, + .vidioc_enum_freq_bands = si476x_radio_enum_freq_bands, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + + .vidioc_g_chip_ident = si476x_radio_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = si476x_radio_g_register, + .vidioc_s_register = si476x_radio_s_register, +#endif +}; + + +static const struct video_device si476x_viddev_template = { + .fops = &si476x_fops, + .name = DRIVER_NAME, + .release = video_device_release_empty, +}; + + + +static ssize_t si476x_radio_read_acf_blob(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + int err; + struct si476x_radio *radio = file->private_data; + struct si476x_acf_status_report report; + + si476x_core_lock(radio->core); + if (radio->ops->acf_status) + err = radio->ops->acf_status(radio->core, &report); + else + err = -ENOENT; + si476x_core_unlock(radio->core); + + if (err < 0) + return err; + + return simple_read_from_buffer(user_buf, count, ppos, &report, + sizeof(report)); +} + +static const struct file_operations radio_acf_fops = { + .open = simple_open, + .llseek = default_llseek, + .read = si476x_radio_read_acf_blob, +}; + +static ssize_t si476x_radio_read_rds_blckcnt_blob(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + int err; + struct si476x_radio *radio = file->private_data; + struct si476x_rds_blockcount_report report; + + si476x_core_lock(radio->core); + if (radio->ops->rds_blckcnt) + err = radio->ops->rds_blckcnt(radio->core, true, + &report); + else + err = -ENOENT; + si476x_core_unlock(radio->core); + + if (err < 0) + return err; + + return simple_read_from_buffer(user_buf, count, ppos, &report, + sizeof(report)); +} + +static const struct file_operations radio_rds_blckcnt_fops = { + .open = simple_open, + .llseek = default_llseek, + .read = si476x_radio_read_rds_blckcnt_blob, +}; + +static ssize_t si476x_radio_read_agc_blob(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + int err; + struct si476x_radio *radio = file->private_data; + struct si476x_agc_status_report report; + + si476x_core_lock(radio->core); + if (radio->ops->rds_blckcnt) + err = radio->ops->agc_status(radio->core, &report); + else + err = -ENOENT; + si476x_core_unlock(radio->core); + + if (err < 0) + return err; + + return simple_read_from_buffer(user_buf, count, ppos, &report, + sizeof(report)); +} + +static const struct file_operations radio_agc_fops = { + .open = simple_open, + .llseek = default_llseek, + .read = si476x_radio_read_agc_blob, +}; + +static ssize_t si476x_radio_read_rsq_blob(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + int err; + struct si476x_radio *radio = file->private_data; + struct si476x_rsq_status_report report; + struct si476x_rsq_status_args args = { + .primary = false, + .rsqack = false, + .attune = false, + .cancel = false, + .stcack = false, + }; + + si476x_core_lock(radio->core); + if (radio->ops->rds_blckcnt) + err = radio->ops->rsq_status(radio->core, &args, &report); + else + err = -ENOENT; + si476x_core_unlock(radio->core); + + if (err < 0) + return err; + + return simple_read_from_buffer(user_buf, count, ppos, &report, + sizeof(report)); +} + +static const struct file_operations radio_rsq_fops = { + .open = simple_open, + .llseek = default_llseek, + .read = si476x_radio_read_rsq_blob, +}; + +static ssize_t si476x_radio_read_rsq_primary_blob(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + int err; + struct si476x_radio *radio = file->private_data; + struct si476x_rsq_status_report report; + struct si476x_rsq_status_args args = { + .primary = true, + .rsqack = false, + .attune = false, + .cancel = false, + .stcack = false, + }; + + si476x_core_lock(radio->core); + if (radio->ops->rds_blckcnt) + err = radio->ops->rsq_status(radio->core, &args, &report); + else + err = -ENOENT; + si476x_core_unlock(radio->core); + + if (err < 0) + return err; + + return simple_read_from_buffer(user_buf, count, ppos, &report, + sizeof(report)); +} + +static const struct file_operations radio_rsq_primary_fops = { + .open = simple_open, + .llseek = default_llseek, + .read = si476x_radio_read_rsq_primary_blob, +}; + + +static int si476x_radio_init_debugfs(struct si476x_radio *radio) +{ + struct dentry *dentry; + int ret; + + dentry = debugfs_create_dir(dev_name(radio->v4l2dev.dev), NULL); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto exit; + } + radio->debugfs = dentry; + + dentry = debugfs_create_file("acf", S_IRUGO, + radio->debugfs, radio, &radio_acf_fops); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto cleanup; + } + + dentry = debugfs_create_file("rds_blckcnt", S_IRUGO, + radio->debugfs, radio, + &radio_rds_blckcnt_fops); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto cleanup; + } + + dentry = debugfs_create_file("agc", S_IRUGO, + radio->debugfs, radio, &radio_agc_fops); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto cleanup; + } + + dentry = debugfs_create_file("rsq", S_IRUGO, + radio->debugfs, radio, &radio_rsq_fops); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto cleanup; + } + + dentry = debugfs_create_file("rsq_primary", S_IRUGO, + radio->debugfs, radio, + &radio_rsq_primary_fops); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto cleanup; + } + + return 0; +cleanup: + debugfs_remove_recursive(radio->debugfs); +exit: + return ret; +} + + +static int si476x_radio_add_new_custom(struct si476x_radio *radio, + enum si476x_ctrl_idx idx) +{ + int rval; + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_new_custom(&radio->ctrl_handler, + &si476x_ctrls[idx], + NULL); + rval = radio->ctrl_handler.error; + if (ctrl == NULL && rval) + dev_err(radio->v4l2dev.dev, + "Could not initialize '%s' control %d\n", + si476x_ctrls[idx].name, rval); + + return rval; +} + +static int si476x_radio_probe(struct platform_device *pdev) +{ + int rval; + struct si476x_radio *radio; + struct v4l2_ctrl *ctrl; + + static atomic_t instance = ATOMIC_INIT(0); + + radio = devm_kzalloc(&pdev->dev, sizeof(*radio), GFP_KERNEL); + if (!radio) + return -ENOMEM; + + radio->core = i2c_mfd_cell_to_core(&pdev->dev); + + v4l2_device_set_name(&radio->v4l2dev, DRIVER_NAME, &instance); + + rval = v4l2_device_register(&pdev->dev, &radio->v4l2dev); + if (rval) { + dev_err(&pdev->dev, "Cannot register v4l2_device.\n"); + return rval; + } + + memcpy(&radio->videodev, &si476x_viddev_template, + sizeof(struct video_device)); + + radio->videodev.v4l2_dev = &radio->v4l2dev; + radio->videodev.ioctl_ops = &si4761_ioctl_ops; + + video_set_drvdata(&radio->videodev, radio); + platform_set_drvdata(pdev, radio); + + set_bit(V4L2_FL_USE_FH_PRIO, &radio->videodev.flags); + + radio->v4l2dev.ctrl_handler = &radio->ctrl_handler; + v4l2_ctrl_handler_init(&radio->ctrl_handler, + 1 + ARRAY_SIZE(si476x_ctrls)); + + if (si476x_core_has_am(radio->core)) { + ctrl = v4l2_ctrl_new_std_menu(&radio->ctrl_handler, + &si476x_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, + 0, 0); + rval = radio->ctrl_handler.error; + if (ctrl == NULL && rval) { + dev_err(&pdev->dev, "Could not initialize V4L2_CID_POWER_LINE_FREQUENCY control %d\n", + rval); + goto exit; + } + + rval = si476x_radio_add_new_custom(radio, + SI476X_IDX_HARMONICS_COUNT); + if (rval < 0) + goto exit; + } + + rval = si476x_radio_add_new_custom(radio, SI476X_IDX_RSSI_THRESHOLD); + if (rval < 0) + goto exit; + + rval = si476x_radio_add_new_custom(radio, SI476X_IDX_SNR_THRESHOLD); + if (rval < 0) + goto exit; + + rval = si476x_radio_add_new_custom(radio, SI476X_IDX_MAX_TUNE_ERROR); + if (rval < 0) + goto exit; + + ctrl = v4l2_ctrl_new_std_menu(&radio->ctrl_handler, + &si476x_ctrl_ops, + V4L2_CID_TUNE_DEEMPHASIS, + V4L2_DEEMPHASIS_75_uS, 0, 0); + rval = radio->ctrl_handler.error; + if (ctrl == NULL && rval) { + dev_err(&pdev->dev, "Could not initialize V4L2_CID_TUNE_DEEMPHASIS control %d\n", + rval); + goto exit; + } + + ctrl = v4l2_ctrl_new_std(&radio->ctrl_handler, &si476x_ctrl_ops, + V4L2_CID_RDS_RECEPTION, + 0, 1, 1, 1); + rval = radio->ctrl_handler.error; + if (ctrl == NULL && rval) { + dev_err(&pdev->dev, "Could not initialize V4L2_CID_RDS_RECEPTION control %d\n", + rval); + goto exit; + } + + if (si476x_core_has_diversity(radio->core)) { + si476x_ctrls[SI476X_IDX_DIVERSITY_MODE].def = + si476x_phase_diversity_mode_to_idx(radio->core->diversity_mode); + si476x_radio_add_new_custom(radio, SI476X_IDX_DIVERSITY_MODE); + if (rval < 0) + goto exit; + + si476x_radio_add_new_custom(radio, SI476X_IDX_INTERCHIP_LINK); + if (rval < 0) + goto exit; + } + + /* register video device */ + rval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, -1); + if (rval < 0) { + dev_err(&pdev->dev, "Could not register video device\n"); + goto exit; + } + + rval = si476x_radio_init_debugfs(radio); + if (rval < 0) { + dev_err(&pdev->dev, "Could not creat debugfs interface\n"); + goto exit; + } + + return 0; +exit: + v4l2_ctrl_handler_free(radio->videodev.ctrl_handler); + return rval; +} + +static int si476x_radio_remove(struct platform_device *pdev) +{ + struct si476x_radio *radio = platform_get_drvdata(pdev); + + v4l2_ctrl_handler_free(radio->videodev.ctrl_handler); + video_unregister_device(&radio->videodev); + v4l2_device_unregister(&radio->v4l2dev); + debugfs_remove_recursive(radio->debugfs); + + return 0; +} + +MODULE_ALIAS("platform:si476x-radio"); + +static struct platform_driver si476x_radio_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = si476x_radio_probe, + .remove = si476x_radio_remove, +}; +module_platform_driver(si476x_radio_driver); + +MODULE_AUTHOR("Andrey Smirnov "); +MODULE_DESCRIPTION("Driver for Si4761/64/68 AM/FM Radio MFD Cell"); +MODULE_LICENSE("GPL"); diff --git a/include/media/si476x.h b/include/media/si476x.h new file mode 100644 index 000000000000..beb6433d6958 --- /dev/null +++ b/include/media/si476x.h @@ -0,0 +1,426 @@ +/* + * include/media/si476x.h -- Common definitions for si476x driver + * + * Copyright (C) 2012 Innovative Converged Devices(ICD) + * Copyright (C) 2013 Andrey Smirnov + * + * Author: Andrey Smirnov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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 SI476X_H +#define SI476X_H + +#include +#include + +struct si476x_device; + +/* It is possible to select one of the four adresses using pins A0 + * and A1 on SI476x */ +#define SI476X_I2C_ADDR_1 0x60 +#define SI476X_I2C_ADDR_2 0x61 +#define SI476X_I2C_ADDR_3 0x62 +#define SI476X_I2C_ADDR_4 0x63 + +enum si476x_iqclk_config { + SI476X_IQCLK_NOOP = 0, + SI476X_IQCLK_TRISTATE = 1, + SI476X_IQCLK_IQ = 21, +}; +enum si476x_iqfs_config { + SI476X_IQFS_NOOP = 0, + SI476X_IQFS_TRISTATE = 1, + SI476X_IQFS_IQ = 21, +}; +enum si476x_iout_config { + SI476X_IOUT_NOOP = 0, + SI476X_IOUT_TRISTATE = 1, + SI476X_IOUT_OUTPUT = 22, +}; +enum si476x_qout_config { + SI476X_QOUT_NOOP = 0, + SI476X_QOUT_TRISTATE = 1, + SI476X_QOUT_OUTPUT = 22, +}; + +enum si476x_dclk_config { + SI476X_DCLK_NOOP = 0, + SI476X_DCLK_TRISTATE = 1, + SI476X_DCLK_DAUDIO = 10, +}; + +enum si476x_dfs_config { + SI476X_DFS_NOOP = 0, + SI476X_DFS_TRISTATE = 1, + SI476X_DFS_DAUDIO = 10, +}; + +enum si476x_dout_config { + SI476X_DOUT_NOOP = 0, + SI476X_DOUT_TRISTATE = 1, + SI476X_DOUT_I2S_OUTPUT = 12, + SI476X_DOUT_I2S_INPUT = 13, +}; + +enum si476x_xout_config { + SI476X_XOUT_NOOP = 0, + SI476X_XOUT_TRISTATE = 1, + SI476X_XOUT_I2S_INPUT = 13, + SI476X_XOUT_MODE_SELECT = 23, +}; + + +enum si476x_icin_config { + SI476X_ICIN_NOOP = 0, + SI476X_ICIN_TRISTATE = 1, + SI476X_ICIN_GPO1_HIGH = 2, + SI476X_ICIN_GPO1_LOW = 3, + SI476X_ICIN_IC_LINK = 30, +}; + +enum si476x_icip_config { + SI476X_ICIP_NOOP = 0, + SI476X_ICIP_TRISTATE = 1, + SI476X_ICIP_GPO2_HIGH = 2, + SI476X_ICIP_GPO2_LOW = 3, + SI476X_ICIP_IC_LINK = 30, +}; + +enum si476x_icon_config { + SI476X_ICON_NOOP = 0, + SI476X_ICON_TRISTATE = 1, + SI476X_ICON_I2S = 10, + SI476X_ICON_IC_LINK = 30, +}; + +enum si476x_icop_config { + SI476X_ICOP_NOOP = 0, + SI476X_ICOP_TRISTATE = 1, + SI476X_ICOP_I2S = 10, + SI476X_ICOP_IC_LINK = 30, +}; + + +enum si476x_lrout_config { + SI476X_LROUT_NOOP = 0, + SI476X_LROUT_TRISTATE = 1, + SI476X_LROUT_AUDIO = 2, + SI476X_LROUT_MPX = 3, +}; + + +enum si476x_intb_config { + SI476X_INTB_NOOP = 0, + SI476X_INTB_TRISTATE = 1, + SI476X_INTB_DAUDIO = 10, + SI476X_INTB_IRQ = 40, +}; + +enum si476x_a1_config { + SI476X_A1_NOOP = 0, + SI476X_A1_TRISTATE = 1, + SI476X_A1_IRQ = 40, +}; + +enum si476x_part_revisions { + SI476X_REVISION_A10 = 0, + SI476X_REVISION_A20 = 1, + SI476X_REVISION_A30 = 2, +}; + +struct si476x_pinmux { + enum si476x_dclk_config dclk; + enum si476x_dfs_config dfs; + enum si476x_dout_config dout; + enum si476x_xout_config xout; + + enum si476x_iqclk_config iqclk; + enum si476x_iqfs_config iqfs; + enum si476x_iout_config iout; + enum si476x_qout_config qout; + + enum si476x_icin_config icin; + enum si476x_icip_config icip; + enum si476x_icon_config icon; + enum si476x_icop_config icop; + + enum si476x_lrout_config lrout; + + enum si476x_intb_config intb; + enum si476x_a1_config a1; +}; + +/** + * enum si476x_phase_diversity_mode - possbile phase diversity modes + * for SI4764/5/6/7 chips. + * + * @SI476X_PHDIV_DISABLED: Phase diversity feature is + * disabled. + * @SI476X_PHDIV_PRIMARY_COMBINING: Tuner works as a primary tuner + * in combination with a + * secondary one. + * @SI476X_PHDIV_PRIMARY_ANTENNA: Tuner works as a primary tuner + * using only its own antenna. + * @SI476X_PHDIV_SECONDARY_ANTENNA: Tuner works as a primary tuner + * usning seconary tuner's antenna. + * @SI476X_PHDIV_SECONDARY_COMBINING: Tuner works as a secondary + * tuner in combination with the + * primary one. + */ +enum si476x_phase_diversity_mode { + SI476X_PHDIV_DISABLED = 0, + SI476X_PHDIV_PRIMARY_COMBINING = 1, + SI476X_PHDIV_PRIMARY_ANTENNA = 2, + SI476X_PHDIV_SECONDARY_ANTENNA = 3, + SI476X_PHDIV_SECONDARY_COMBINING = 5, +}; + +enum si476x_ibias6x { + SI476X_IBIAS6X_OTHER = 0, + SI476X_IBIAS6X_RCVR1_NON_4MHZ_CLK = 1, +}; + +enum si476x_xstart { + SI476X_XSTART_MULTIPLE_TUNER = 0x11, + SI476X_XSTART_NORMAL = 0x77, +}; + +enum si476x_freq { + SI476X_FREQ_4_MHZ = 0, + SI476X_FREQ_37P209375_MHZ = 1, + SI476X_FREQ_36P4_MHZ = 2, + SI476X_FREQ_37P8_MHZ = 3, +}; + +enum si476x_xmode { + SI476X_XMODE_CRYSTAL_RCVR1 = 1, + SI476X_XMODE_EXT_CLOCK = 2, + SI476X_XMODE_CRYSTAL_RCVR2_3 = 3, +}; + +enum si476x_xbiashc { + SI476X_XBIASHC_SINGLE_RECEIVER = 0, + SI476X_XBIASHC_MULTIPLE_RECEIVER = 1, +}; + +enum si476x_xbias { + SI476X_XBIAS_RCVR2_3 = 0, + SI476X_XBIAS_4MHZ_RCVR1 = 3, + SI476X_XBIAS_RCVR1 = 7, +}; + +enum si476x_func { + SI476X_FUNC_BOOTLOADER = 0, + SI476X_FUNC_FM_RECEIVER = 1, + SI476X_FUNC_AM_RECEIVER = 2, + SI476X_FUNC_WB_RECEIVER = 3, +}; + + +/** + * @xcload: Selects the amount of additional on-chip capacitance to + * be connected between XTAL1 and gnd and between XTAL2 and + * GND. One half of the capacitance value shown here is the + * additional load capacitance presented to the xtal. The + * minimum step size is 0.277 pF. Recommended value is 0x28 + * but it will be layout dependent. Range is 0–0x3F i.e. + * (0–16.33 pF) + * @ctsien: enable CTSINT(interrupt request when CTS condition + * arises) when set + * @intsel: when set A1 pin becomes the interrupt pin; otherwise, + * INTB is the interrupt pin + * @func: selects the boot function of the device. I.e. + * SI476X_BOOTLOADER - Boot loader + * SI476X_FM_RECEIVER - FM receiver + * SI476X_AM_RECEIVER - AM receiver + * SI476X_WB_RECEIVER - Weatherband receiver + * @freq: oscillator's crystal frequency: + * SI476X_XTAL_37P209375_MHZ - 37.209375 Mhz + * SI476X_XTAL_36P4_MHZ - 36.4 Mhz + * SI476X_XTAL_37P8_MHZ - 37.8 Mhz + */ +struct si476x_power_up_args { + enum si476x_ibias6x ibias6x; + enum si476x_xstart xstart; + u8 xcload; + bool fastboot; + enum si476x_xbiashc xbiashc; + enum si476x_xbias xbias; + enum si476x_func func; + enum si476x_freq freq; + enum si476x_xmode xmode; +}; + + +enum si476x_ctrl_id { + V4L2_CID_SI476X_RSSI_THRESHOLD = (V4L2_CID_USER_SI476X_BASE + 1), + V4L2_CID_SI476X_SNR_THRESHOLD = (V4L2_CID_USER_SI476X_BASE + 2), + V4L2_CID_SI476X_MAX_TUNE_ERROR = (V4L2_CID_USER_SI476X_BASE + 3), + V4L2_CID_SI476X_HARMONICS_COUNT = (V4L2_CID_USER_SI476X_BASE + 4), + V4L2_CID_SI476X_DIVERSITY_MODE = (V4L2_CID_USER_SI476X_BASE + 5), + V4L2_CID_SI476X_INTERCHIP_LINK = (V4L2_CID_USER_SI476X_BASE + 6), +}; + +/* + * Platform dependent definition + */ +struct si476x_platform_data { + int gpio_reset; /* < 0 if not used */ + + struct si476x_power_up_args power_up_parameters; + enum si476x_phase_diversity_mode diversity_mode; + + struct si476x_pinmux pinmux; +}; + +/** + * struct si476x_rsq_status - structure containing received signal + * quality + * @multhint: Multipath Detect High. + * true - Indicatedes that the value is below + * FM_RSQ_MULTIPATH_HIGH_THRESHOLD + * false - Indicatedes that the value is above + * FM_RSQ_MULTIPATH_HIGH_THRESHOLD + * @multlint: Multipath Detect Low. + * true - Indicatedes that the value is below + * FM_RSQ_MULTIPATH_LOW_THRESHOLD + * false - Indicatedes that the value is above + * FM_RSQ_MULTIPATH_LOW_THRESHOLD + * @snrhint: SNR Detect High. + * true - Indicatedes that the value is below + * FM_RSQ_SNR_HIGH_THRESHOLD + * false - Indicatedes that the value is above + * FM_RSQ_SNR_HIGH_THRESHOLD + * @snrlint: SNR Detect Low. + * true - Indicatedes that the value is below + * FM_RSQ_SNR_LOW_THRESHOLD + * false - Indicatedes that the value is above + * FM_RSQ_SNR_LOW_THRESHOLD + * @rssihint: RSSI Detect High. + * true - Indicatedes that the value is below + * FM_RSQ_RSSI_HIGH_THRESHOLD + * false - Indicatedes that the value is above + * FM_RSQ_RSSI_HIGH_THRESHOLD + * @rssilint: RSSI Detect Low. + * true - Indicatedes that the value is below + * FM_RSQ_RSSI_LOW_THRESHOLD + * false - Indicatedes that the value is above + * FM_RSQ_RSSI_LOW_THRESHOLD + * @bltf: Band Limit. + * Set if seek command hits the band limit or wrapped to + * the original frequency. + * @snr_ready: SNR measurement in progress. + * @rssiready: RSSI measurement in progress. + * @afcrl: Set if FREQOFF >= MAX_TUNE_ERROR + * @valid: Set if the channel is valid + * rssi < FM_VALID_RSSI_THRESHOLD + * snr < FM_VALID_SNR_THRESHOLD + * tune_error < FM_VALID_MAX_TUNE_ERROR + * @readfreq: Current tuned frequency. + * @freqoff: Signed frequency offset. + * @rssi: Received Signal Strength Indicator(dBuV). + * @snr: RF SNR Indicator(dB). + * @lassi: + * @hassi: Low/High side Adjacent(100 kHz) Channel Strength Indicator + * @mult: Multipath indicator + * @dev: Who knows? But values may vary. + * @readantcap: Antenna tuning capacity value. + * @assi: Adjacent Channel(+/- 200kHz) Strength Indicator + * @usn: Ultrasonic Noise Inticator in -DBFS + */ +struct si476x_rsq_status_report { + __u8 multhint, multlint; + __u8 snrhint, snrlint; + __u8 rssihint, rssilint; + __u8 bltf; + __u8 snr_ready; + __u8 rssiready; + __u8 injside; + __u8 afcrl; + __u8 valid; + + __u16 readfreq; + __s8 freqoff; + __s8 rssi; + __s8 snr; + __s8 issi; + __s8 lassi, hassi; + __s8 mult; + __u8 dev; + __u16 readantcap; + __s8 assi; + __s8 usn; + + __u8 pilotdev; + __u8 rdsdev; + __u8 assidev; + __u8 strongdev; + __u16 rdspi; +} __packed; + +/** + * si476x_acf_status_report - ACF report results + * + * @blend_int: If set, indicates that stereo separation has crossed + * below the blend threshold as set by FM_ACF_BLEND_THRESHOLD + * @hblend_int: If set, indicates that HiBlend cutoff frequency is + * lower than threshold as set by FM_ACF_HBLEND_THRESHOLD + * @hicut_int: If set, indicates that HiCut cutoff frequency is lower + * than the threshold set by ACF_ + + */ +struct si476x_acf_status_report { + __u8 blend_int; + __u8 hblend_int; + __u8 hicut_int; + __u8 chbw_int; + __u8 softmute_int; + __u8 smute; + __u8 smattn; + __u8 chbw; + __u8 hicut; + __u8 hiblend; + __u8 pilot; + __u8 stblend; +} __packed; + +enum si476x_fmagc { + SI476X_FMAGC_10K_OHM = 0, + SI476X_FMAGC_800_OHM = 1, + SI476X_FMAGC_400_OHM = 2, + SI476X_FMAGC_200_OHM = 4, + SI476X_FMAGC_100_OHM = 8, + SI476X_FMAGC_50_OHM = 16, + SI476X_FMAGC_25_OHM = 32, + SI476X_FMAGC_12P5_OHM = 64, + SI476X_FMAGC_6P25_OHM = 128, +}; + +struct si476x_agc_status_report { + __u8 mxhi; + __u8 mxlo; + __u8 lnahi; + __u8 lnalo; + __u8 fmagc1; + __u8 fmagc2; + __u8 pgagain; + __u8 fmwblang; +} __packed; + +struct si476x_rds_blockcount_report { + __u16 expected; + __u16 received; + __u16 uncorrectable; +} __packed; + +#endif /* SI476X_H*/ -- cgit v1.2.3 From e5354107e14755991da82e0d2a4791db92908d9d Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 27 Mar 2013 17:29:53 +0200 Subject: mei: bus: Initial MEI Client bus type implementation mei client bus will present some of the mei clients as devices for other standard subsystems Implement the probe, remove, match, device addtion routines, along with the sysfs and uevent ones. mei_cl_device_id is also added to mod_devicetable.h A mei-cleint-bus.txt document describing the rationale and the API usage is also added while ABI/testing/sysfs-bus-mei describeis the modalias ABI. Signed-off-by: Samuel Ortiz Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-mei | 7 + Documentation/misc-devices/mei/mei-client-bus.txt | 135 +++++++++++++++++ drivers/misc/mei/Makefile | 1 + drivers/misc/mei/bus.c | 172 ++++++++++++++++++++++ drivers/misc/mei/mei_dev.h | 26 ++++ include/linux/mei_cl_bus.h | 20 +++ include/linux/mod_devicetable.h | 9 ++ scripts/mod/devicetable-offsets.c | 3 + scripts/mod/file2alias.c | 12 ++ 9 files changed, 385 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-mei create mode 100644 Documentation/misc-devices/mei/mei-client-bus.txt create mode 100644 drivers/misc/mei/bus.c create mode 100644 include/linux/mei_cl_bus.h (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-mei b/Documentation/ABI/testing/sysfs-bus-mei new file mode 100644 index 000000000000..2066f0bbd453 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-mei @@ -0,0 +1,7 @@ +What: /sys/bus/mei/devices/.../modalias +Date: March 2013 +KernelVersion: 3.10 +Contact: Samuel Ortiz + linux-mei@linux.intel.com +Description: Stores the same MODALIAS value emitted by uevent + Format: mei: diff --git a/Documentation/misc-devices/mei/mei-client-bus.txt b/Documentation/misc-devices/mei/mei-client-bus.txt new file mode 100644 index 000000000000..9dc5ebf94eb1 --- /dev/null +++ b/Documentation/misc-devices/mei/mei-client-bus.txt @@ -0,0 +1,135 @@ +Intel(R) Management Engine (ME) Client bus API +=============================================== + + +Rationale +========= +MEI misc character device is useful for dedicated applications to send and receive +data to the many FW appliance found in Intel's ME from the user space. +However for some of the ME functionalities it make sense to leverage existing software +stack and expose them through existing kernel subsystems. + +In order to plug seamlessly into the kernel device driver model we add kernel virtual +bus abstraction on top of the MEI driver. This allows implementing linux kernel drivers +for the various MEI features as a stand alone entities found in their respective subsystem. +Existing device drivers can even potentially be re-used by adding an MEI CL bus layer to +the existing code. + + +MEI CL bus API +=========== +A driver implementation for an MEI Client is very similar to existing bus +based device drivers. The driver registers itself as an MEI CL bus driver through +the mei_cl_driver structure: + +struct mei_cl_driver { + struct device_driver driver; + const char *name; + + const struct mei_cl_device_id *id_table; + + int (*probe)(struct mei_cl_device *dev, const struct mei_cl_id *id); + int (*remove)(struct mei_cl_device *dev); +}; + +struct mei_cl_id { + char name[MEI_NAME_SIZE]; + kernel_ulong_t driver_info; +}; + +The mei_cl_id structure allows the driver to bind itself against a device name. + +To actually register a driver on the ME Client bus one must call the mei_cl_add_driver() +API. This is typically called at module init time. + +Once registered on the ME Client bus, a driver will typically try to do some I/O on +this bus and this should be done through the mei_cl_send() and mei_cl_recv() +routines. The latter is synchronous (blocks and sleeps until data shows up). +In order for drivers to be notified of pending events waiting for them (e.g. +an Rx event) they can register an event handler through the +mei_cl_register_event_cb() routine. Currently only the MEI_EVENT_RX event +will trigger an event handler call and the driver implementation is supposed +to call mei_recv() from the event handler in order to fetch the pending +received buffers. + + +Example +======= +As a theoretical example let's pretend the ME comes with a "contact" NFC IP. +The driver init and exit routines for this device would look like: + +#define CONTACT_DRIVER_NAME "contact" + +static struct mei_cl_device_id contact_mei_cl_tbl[] = { + { CONTACT_DRIVER_NAME, }, + + /* required last entry */ + { } +}; +MODULE_DEVICE_TABLE(mei_cl, contact_mei_cl_tbl); + +static struct mei_cl_driver contact_driver = { + .id_table = contact_mei_tbl, + .name = CONTACT_DRIVER_NAME, + + .probe = contact_probe, + .remove = contact_remove, +}; + +static int contact_init(void) +{ + int r; + + r = mei_cl_driver_register(&contact_driver); + if (r) { + pr_err(CONTACT_DRIVER_NAME ": driver registration failed\n"); + return r; + } + + return 0; +} + +static void __exit contact_exit(void) +{ + mei_cl_driver_unregister(&contact_driver); +} + +module_init(contact_init); +module_exit(contact_exit); + +And the driver's simplified probe routine would look like that: + +int contact_probe(struct mei_cl_device *dev, struct mei_cl_device_id *id) +{ + struct contact_driver *contact; + + [...] + mei_cl_register_event_cb(dev, contact_event_cb, contact); + + return 0; + } + +In the probe routine the driver basically registers an ME bus event handler +which is as close as it can get to registering a threaded IRQ handler. +The handler implementation will typically call some I/O routine depending on +the pending events: + +#define MAX_NFC_PAYLOAD 128 + +static void contact_event_cb(struct mei_cl_device *dev, u32 events, + void *context) +{ + struct contact_driver *contact = context; + + if (events & BIT(MEI_EVENT_RX)) { + u8 payload[MAX_NFC_PAYLOAD]; + int payload_size; + + payload_size = mei_recv(dev, payload, MAX_NFC_PAYLOAD); + if (payload_size <= 0) + return; + + /* Hook to the NFC subsystem */ + nfc_hci_recv_frame(contact->hdev, payload, payload_size); + } +} diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 2c336d087749..1b29f7ccac49 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -10,6 +10,7 @@ mei-objs += client.o mei-objs += main.o mei-objs += amthif.o mei-objs += wd.o +mei-objs += bus.o obj-$(CONFIG_INTEL_MEI_ME) += mei-me.o mei-me-objs := pci-me.o diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c new file mode 100644 index 000000000000..78c876af2676 --- /dev/null +++ b/drivers/misc/mei/bus.c @@ -0,0 +1,172 @@ +/* + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2012-2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mei_dev.h" + +#define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver) +#define to_mei_cl_device(d) container_of(d, struct mei_cl_device, dev) + +static int mei_cl_device_match(struct device *dev, struct device_driver *drv) +{ + struct mei_cl_device *device = to_mei_cl_device(dev); + struct mei_cl_driver *driver = to_mei_cl_driver(drv); + const struct mei_cl_device_id *id; + + if (!device) + return 0; + + if (!driver || !driver->id_table) + return 0; + + id = driver->id_table; + + while (id->name[0]) { + if (!strcmp(dev_name(dev), id->name)) + return 1; + + id++; + } + + return 0; +} + +static int mei_cl_device_probe(struct device *dev) +{ + struct mei_cl_device *device = to_mei_cl_device(dev); + struct mei_cl_driver *driver; + struct mei_cl_device_id id; + + if (!device) + return 0; + + driver = to_mei_cl_driver(dev->driver); + if (!driver || !driver->probe) + return -ENODEV; + + dev_dbg(dev, "Device probe\n"); + + strncpy(id.name, dev_name(dev), MEI_CL_NAME_SIZE); + + return driver->probe(device, &id); +} + +static int mei_cl_device_remove(struct device *dev) +{ + struct mei_cl_device *device = to_mei_cl_device(dev); + struct mei_cl_driver *driver; + + if (!device || !dev->driver) + return 0; + + driver = to_mei_cl_driver(dev->driver); + if (!driver->remove) { + dev->driver = NULL; + + return 0; + } + + return driver->remove(device); +} + +static ssize_t modalias_show(struct device *dev, struct device_attribute *a, + char *buf) +{ + int len; + + len = snprintf(buf, PAGE_SIZE, "mei:%s\n", dev_name(dev)); + + return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; +} + +static struct device_attribute mei_cl_dev_attrs[] = { + __ATTR_RO(modalias), + __ATTR_NULL, +}; + +static int mei_cl_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + if (add_uevent_var(env, "MODALIAS=mei:%s", dev_name(dev))) + return -ENOMEM; + + return 0; +} + +static struct bus_type mei_cl_bus_type = { + .name = "mei", + .dev_attrs = mei_cl_dev_attrs, + .match = mei_cl_device_match, + .probe = mei_cl_device_probe, + .remove = mei_cl_device_remove, + .uevent = mei_cl_uevent, +}; + +static void mei_cl_dev_release(struct device *dev) +{ + kfree(to_mei_cl_device(dev)); +} + +static struct device_type mei_cl_device_type = { + .release = mei_cl_dev_release, +}; + +struct mei_cl_device *mei_cl_add_device(struct mei_device *mei_device, + uuid_le uuid, char *name) +{ + struct mei_cl_device *device; + int status; + + device = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL); + if (!device) + return NULL; + + device->dev.parent = &mei_device->pdev->dev; + device->dev.bus = &mei_cl_bus_type; + device->dev.type = &mei_cl_device_type; + + dev_set_name(&device->dev, "%s", name); + + status = device_register(&device->dev); + if (status) + goto out_err; + + dev_dbg(&device->dev, "client %s registered\n", name); + + return device; + +out_err: + dev_err(device->dev.parent, "Failed to register MEI client\n"); + + kfree(device); + + return NULL; +} +EXPORT_SYMBOL_GPL(mei_cl_add_device); + +void mei_cl_remove_device(struct mei_cl_device *device) +{ + device_unregister(&device->dev); +} +EXPORT_SYMBOL_GPL(mei_cl_remove_device); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index b5d66076de3d..7abb705ddf3f 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -21,6 +21,7 @@ #include #include #include +#include #include "hw.h" #include "hw-me-regs.h" @@ -262,6 +263,31 @@ struct mei_hw_ops { unsigned char *buf, unsigned long len); }; +/* MEI bus API*/ +struct mei_cl_device *mei_cl_add_device(struct mei_device *dev, + uuid_le uuid, char *name); +void mei_cl_remove_device(struct mei_cl_device *device); + +/** + * struct mei_cl_device - MEI device handle + * An mei_cl_device pointer is returned from mei_add_device() + * and links MEI bus clients to their actual ME host client pointer. + * Drivers for MEI devices will get an mei_cl_device pointer + * when being probed and shall use it for doing ME bus I/O. + * + * @dev: linux driver model device pointer + * @uuid: me client uuid + * @cl: mei client + * @priv_data: client private data + */ +struct mei_cl_device { + struct device dev; + + struct mei_cl *cl; + + void *priv_data; +}; + /** * struct mei_device - MEI private device struct diff --git a/include/linux/mei_cl_bus.h b/include/linux/mei_cl_bus.h new file mode 100644 index 000000000000..4e7351de7eca --- /dev/null +++ b/include/linux/mei_cl_bus.h @@ -0,0 +1,20 @@ +#ifndef _LINUX_MEI_CL_BUS_H +#define _LINUX_MEI_CL_BUS_H + +#include +#include + +struct mei_cl_device; + +struct mei_cl_driver { + struct device_driver driver; + const char *name; + + const struct mei_cl_device_id *id_table; + + int (*probe)(struct mei_cl_device *dev, + const struct mei_cl_device_id *id); + int (*remove)(struct mei_cl_device *dev); +}; + +#endif /* _LINUX_MEI_CL_BUS_H */ diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 779cf7c4a3d1..b508016fb76d 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -9,6 +9,7 @@ #ifdef __KERNEL__ #include +#include typedef unsigned long kernel_ulong_t; #endif @@ -568,4 +569,12 @@ struct ipack_device_id { __u32 device; /* Device ID or IPACK_ANY_ID */ }; +#define MEI_CL_MODULE_PREFIX "mei:" +#define MEI_CL_NAME_SIZE 32 + +struct mei_cl_device_id { + char name[MEI_CL_NAME_SIZE]; + kernel_ulong_t driver_info; +}; + #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index b45260bfeaa0..e66d4d258e1a 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c @@ -174,5 +174,8 @@ int main(void) DEVID_FIELD(x86_cpu_id, model); DEVID_FIELD(x86_cpu_id, vendor); + DEVID(mei_cl_device_id); + DEVID_FIELD(mei_cl_device_id, name); + return 0; } diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 771ac17f635d..45f9a3377dcd 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1133,6 +1133,18 @@ static int do_x86cpu_entry(const char *filename, void *symval, } ADD_TO_DEVTABLE("x86cpu", x86_cpu_id, do_x86cpu_entry); +/* Looks like: mei:S */ +static int do_mei_entry(const char *filename, void *symval, + char *alias) +{ + DEF_FIELD_ADDR(symval, mei_cl_device_id, name); + + sprintf(alias, MEI_CL_MODULE_PREFIX "%s", *name); + + return 1; +} +ADD_TO_DEVTABLE("mei", mei_cl_device_id, do_mei_entry); + /* Does namelen bytes of name exactly match the symbol? */ static bool sym_is(const char *name, unsigned namelen, const char *symbol) { -- cgit v1.2.3 From c4fd675f584a17bd8ceb4fcbf29af12be7ac2a57 Mon Sep 17 00:00:00 2001 From: Zhang Yanfei Date: Thu, 28 Mar 2013 16:16:45 +0800 Subject: Documentation: Add ABI entry for crash_notes and crash_notes_size Add an Documentation/ABI entry for /sys/devices/system/cpu/cpu0/crash_notes and /sys/devices/system/cpu/cpu0/crash_notes_size. Cc: "Eric W. Biederman" Cc: Vivek Goyal Signed-off-by: Zhang Yanfei Acked-by: Simon Horman Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-devices-system-cpu | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu index 9c978dcae07d..2447698aed41 100644 --- a/Documentation/ABI/testing/sysfs-devices-system-cpu +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu @@ -173,3 +173,15 @@ Description: Processor frequency boosting control Boosting allows the CPU and the firmware to run at a frequency beyound it's nominal limit. More details can be found in Documentation/cpu-freq/boost.txt + + +What: /sys/devices/system/cpu/cpu#/crash_notes + /sys/devices/system/cpu/cpu#/crash_notes_size +Date: April 2013 +Contact: kexec@lists.infradead.org +Description: address and size of the percpu note. + + crash_notes: the physical address of the memory that holds the + note of cpu#. + + crash_notes_size: size of the note of cpu#. -- cgit v1.2.3 From a068533079a0a1be53c78c89e65adfbd3c687591 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Sat, 30 Mar 2013 12:54:01 +0200 Subject: usb: chipidea: usbmisc: add post handling and errata fix for mx25 This adds a post handling routine which is called after ci13xxx_add_device was called. The first user is the mx25, which has to disable the external-vbus-divider after the udc has started. Signed-off-by: Michael Grzeschik Signed-off-by: Marc Kleine-Budde [Alex: also fixed a signed one-bit bitfield a whitespace error and yet another set of line-too-long and void pointer casting errors] Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/ci13xxx-imx.txt | 2 ++ drivers/usb/chipidea/ci13xxx_imx.c | 12 ++++++++ drivers/usb/chipidea/ci13xxx_imx.h | 3 ++ drivers/usb/chipidea/usbmisc_imx.c | 36 ++++++++++++++++++++++ 4 files changed, 53 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt b/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt index 5778b9c83bd8..1c04a4c9515f 100644 --- a/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt +++ b/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt @@ -11,6 +11,7 @@ Optional properties: that indicate usb controller index - vbus-supply: regulator for vbus - disable-over-current: disable over current detect +- external-vbus-divider: enables off-chip resistor divider for Vbus Examples: usb@02184000 { /* USB OTG */ @@ -20,4 +21,5 @@ usb@02184000 { /* USB OTG */ fsl,usbphy = <&usbphy1>; fsl,usbmisc = <&usbmisc 0>; disable-over-current; + external-vbus-divider; }; diff --git a/drivers/usb/chipidea/ci13xxx_imx.c b/drivers/usb/chipidea/ci13xxx_imx.c index 8c291220be7f..8faec9dbbb84 100644 --- a/drivers/usb/chipidea/ci13xxx_imx.c +++ b/drivers/usb/chipidea/ci13xxx_imx.c @@ -79,6 +79,9 @@ int usbmisc_get_init_data(struct device *dev, struct usbmisc_usb_device *usbdev) if (of_find_property(np, "disable-over-current", NULL)) usbdev->disable_oc = 1; + if (of_find_property(np, "external-vbus-divider", NULL)) + usbdev->evdo = 1; + return 0; } EXPORT_SYMBOL_GPL(usbmisc_get_init_data); @@ -202,6 +205,15 @@ static int ci13xxx_imx_probe(struct platform_device *pdev) goto err; } + if (usbmisc_ops && usbmisc_ops->post) { + ret = usbmisc_ops->post(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, + "usbmisc post failed, ret=%d\n", ret); + goto put_np; + } + } + data->ci_pdev = plat_ci; platform_set_drvdata(pdev, data); diff --git a/drivers/usb/chipidea/ci13xxx_imx.h b/drivers/usb/chipidea/ci13xxx_imx.h index 9cd2e910b1ca..550bfa457620 100644 --- a/drivers/usb/chipidea/ci13xxx_imx.h +++ b/drivers/usb/chipidea/ci13xxx_imx.h @@ -13,6 +13,8 @@ struct usbmisc_ops { /* It's called once when probe a usb device */ int (*init)(struct device *dev); + /* It's called once after adding a usb device */ + int (*post)(struct device *dev); }; struct usbmisc_usb_device { @@ -20,6 +22,7 @@ struct usbmisc_usb_device { int index; unsigned int disable_oc:1; /* over current detect disabled */ + unsigned int evdo:1; /* set external vbus divider option */ }; int usbmisc_set_ops(const struct usbmisc_ops *ops); diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 746013d7d391..714a6bd810ed 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -14,11 +14,15 @@ #include #include #include +#include #include "ci13xxx_imx.h" #define USB_DEV_MAX 4 +#define MX25_USB_PHY_CTRL_OFFSET 0x08 +#define MX25_BM_EXTERNAL_VBUS_DIVIDER BIT(23) + #define MX53_USB_OTG_PHY_CTRL_0_OFFSET 0x08 #define MX53_USB_UH2_CTRL_OFFSET 0x14 #define MX53_USB_UH3_CTRL_OFFSET 0x18 @@ -59,6 +63,30 @@ static struct usbmisc_usb_device *get_usbdev(struct device *dev) return &usbmisc->usbdev[i]; } +static int usbmisc_imx25_post(struct device *dev) +{ + struct usbmisc_usb_device *usbdev; + void __iomem *reg; + unsigned long flags; + u32 val; + + usbdev = get_usbdev(dev); + if (IS_ERR(usbdev)) + return PTR_ERR(usbdev); + + reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET; + + if (usbdev->evdo) { + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(reg); + writel(val | MX25_BM_EXTERNAL_VBUS_DIVIDER, reg); + spin_unlock_irqrestore(&usbmisc->lock, flags); + usleep_range(5000, 10000); /* needed to stabilize voltage */ + } + + return 0; +} + static int usbmisc_imx53_init(struct device *dev) { struct usbmisc_usb_device *usbdev; @@ -120,6 +148,10 @@ static int usbmisc_imx6q_init(struct device *dev) return 0; } +static const struct usbmisc_ops imx25_usbmisc_ops = { + .post = usbmisc_imx25_post, +}; + static const struct usbmisc_ops imx53_usbmisc_ops = { .init = usbmisc_imx53_init, }; @@ -129,6 +161,10 @@ static const struct usbmisc_ops imx6q_usbmisc_ops = { }; static const struct of_device_id usbmisc_imx_dt_ids[] = { + { + .compatible = "fsl,imx25-usbmisc", + .data = &imx25_usbmisc_ops, + }, { .compatible = "fsl,imx53-usbmisc", .data = &imx53_usbmisc_ops, -- cgit v1.2.3 From 4eb06148250f92e1e58bf069c309dac173e8b5f7 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 29 Mar 2013 05:36:29 +0000 Subject: doc: packet: add minimal TPACKET_V3 example code Lost in space for a long time, but it finally came back to us from some ancient code tombs. This patch adds a minimal runnable example of Linux' packet mmap(2) from Chetan Loke's TPACKET_V3. Special thanks to David S. Miller, and also Eric Leblond and Victor Julien! Cc: Eric Leblond Cc: Victor Julien Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- Documentation/networking/packet_mmap.txt | 327 +++++++++++++++++++++++++++++++ 1 file changed, 327 insertions(+) (limited to 'Documentation') diff --git a/Documentation/networking/packet_mmap.txt b/Documentation/networking/packet_mmap.txt index 94444b152fbc..65efb85e49de 100644 --- a/Documentation/networking/packet_mmap.txt +++ b/Documentation/networking/packet_mmap.txt @@ -684,6 +684,333 @@ int main(int argc, char **argp) return 0; } +------------------------------------------------------------------------------- ++ AF_PACKET TPACKET_V3 example +------------------------------------------------------------------------------- + +AF_PACKET's TPACKET_V3 ring buffer can be configured to use non-static frame +sizes by doing it's own memory management. It is based on blocks where polling +works on a per block basis instead of per ring as in TPACKET_V2 and predecessor. + +It is said that TPACKET_V3 brings the following benefits: + *) ~15 - 20% reduction in CPU-usage + *) ~20% increase in packet capture rate + *) ~2x increase in packet density + *) Port aggregation analysis + *) Non static frame size to capture entire packet payload + +So it seems to be a good candidate to be used with packet fanout. + +Minimal example code by Daniel Borkmann based on Chetan Loke's lolpcap (compile +it with gcc -Wall -O2 blob.c, and try things like "./a.out eth0", etc.): + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BLOCK_SIZE (1 << 22) +#define FRAME_SIZE 2048 + +#define NUM_BLOCKS 64 +#define NUM_FRAMES ((BLOCK_SIZE * NUM_BLOCKS) / FRAME_SIZE) + +#define BLOCK_RETIRE_TOV_IN_MS 64 +#define BLOCK_PRIV_AREA_SZ 13 + +#define ALIGN_8(x) (((x) + 8 - 1) & ~(8 - 1)) + +#define BLOCK_STATUS(x) ((x)->h1.block_status) +#define BLOCK_NUM_PKTS(x) ((x)->h1.num_pkts) +#define BLOCK_O2FP(x) ((x)->h1.offset_to_first_pkt) +#define BLOCK_LEN(x) ((x)->h1.blk_len) +#define BLOCK_SNUM(x) ((x)->h1.seq_num) +#define BLOCK_O2PRIV(x) ((x)->offset_to_priv) +#define BLOCK_PRIV(x) ((void *) ((uint8_t *) (x) + BLOCK_O2PRIV(x))) +#define BLOCK_HDR_LEN (ALIGN_8(sizeof(struct block_desc))) +#define BLOCK_PLUS_PRIV(sz_pri) (BLOCK_HDR_LEN + ALIGN_8((sz_pri))) + +#ifndef likely +# define likely(x) __builtin_expect(!!(x), 1) +#endif +#ifndef unlikely +# define unlikely(x) __builtin_expect(!!(x), 0) +#endif + +struct block_desc { + uint32_t version; + uint32_t offset_to_priv; + struct tpacket_hdr_v1 h1; +}; + +struct ring { + struct iovec *rd; + uint8_t *map; + struct tpacket_req3 req; +}; + +static unsigned long packets_total = 0, bytes_total = 0; +static sig_atomic_t sigint = 0; + +void sighandler(int num) +{ + sigint = 1; +} + +static int setup_socket(struct ring *ring, char *netdev) +{ + int err, i, fd, v = TPACKET_V3; + struct sockaddr_ll ll; + + fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (fd < 0) { + perror("socket"); + exit(1); + } + + err = setsockopt(fd, SOL_PACKET, PACKET_VERSION, &v, sizeof(v)); + if (err < 0) { + perror("setsockopt"); + exit(1); + } + + memset(&ring->req, 0, sizeof(ring->req)); + ring->req.tp_block_size = BLOCK_SIZE; + ring->req.tp_frame_size = FRAME_SIZE; + ring->req.tp_block_nr = NUM_BLOCKS; + ring->req.tp_frame_nr = NUM_FRAMES; + ring->req.tp_retire_blk_tov = BLOCK_RETIRE_TOV_IN_MS; + ring->req.tp_sizeof_priv = BLOCK_PRIV_AREA_SZ; + ring->req.tp_feature_req_word |= TP_FT_REQ_FILL_RXHASH; + + err = setsockopt(fd, SOL_PACKET, PACKET_RX_RING, &ring->req, + sizeof(ring->req)); + if (err < 0) { + perror("setsockopt"); + exit(1); + } + + ring->map = mmap(NULL, ring->req.tp_block_size * ring->req.tp_block_nr, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, + fd, 0); + if (ring->map == MAP_FAILED) { + perror("mmap"); + exit(1); + } + + ring->rd = malloc(ring->req.tp_block_nr * sizeof(*ring->rd)); + assert(ring->rd); + for (i = 0; i < ring->req.tp_block_nr; ++i) { + ring->rd[i].iov_base = ring->map + (i * ring->req.tp_block_size); + ring->rd[i].iov_len = ring->req.tp_block_size; + } + + memset(&ll, 0, sizeof(ll)); + ll.sll_family = PF_PACKET; + ll.sll_protocol = htons(ETH_P_ALL); + ll.sll_ifindex = if_nametoindex(netdev); + ll.sll_hatype = 0; + ll.sll_pkttype = 0; + ll.sll_halen = 0; + + err = bind(fd, (struct sockaddr *) &ll, sizeof(ll)); + if (err < 0) { + perror("bind"); + exit(1); + } + + return fd; +} + +#ifdef __checked +static uint64_t prev_block_seq_num = 0; + +void assert_block_seq_num(struct block_desc *pbd) +{ + if (unlikely(prev_block_seq_num + 1 != BLOCK_SNUM(pbd))) { + printf("prev_block_seq_num:%"PRIu64", expected seq:%"PRIu64" != " + "actual seq:%"PRIu64"\n", prev_block_seq_num, + prev_block_seq_num + 1, (uint64_t) BLOCK_SNUM(pbd)); + exit(1); + } + + prev_block_seq_num = BLOCK_SNUM(pbd); +} + +static void assert_block_len(struct block_desc *pbd, uint32_t bytes, int block_num) +{ + if (BLOCK_NUM_PKTS(pbd)) { + if (unlikely(bytes != BLOCK_LEN(pbd))) { + printf("block:%u with %upackets, expected len:%u != actual len:%u\n", + block_num, BLOCK_NUM_PKTS(pbd), bytes, BLOCK_LEN(pbd)); + exit(1); + } + } else { + if (unlikely(BLOCK_LEN(pbd) != BLOCK_PLUS_PRIV(BLOCK_PRIV_AREA_SZ))) { + printf("block:%u, expected len:%lu != actual len:%u\n", + block_num, BLOCK_HDR_LEN, BLOCK_LEN(pbd)); + exit(1); + } + } +} + +static void assert_block_header(struct block_desc *pbd, const int block_num) +{ + uint32_t block_status = BLOCK_STATUS(pbd); + + if (unlikely((block_status & TP_STATUS_USER) == 0)) { + printf("block:%u, not in TP_STATUS_USER\n", block_num); + exit(1); + } + + assert_block_seq_num(pbd); +} +#else +static inline void assert_block_header(struct block_desc *pbd, const int block_num) +{ +} +static void assert_block_len(struct block_desc *pbd, uint32_t bytes, int block_num) +{ +} +#endif + +static void display(struct tpacket3_hdr *ppd) +{ + struct ethhdr *eth = (struct ethhdr *) ((uint8_t *) ppd + ppd->tp_mac); + struct iphdr *ip = (struct iphdr *) ((uint8_t *) eth + ETH_HLEN); + + if (eth->h_proto == htons(ETH_P_IP)) { + struct sockaddr_in ss, sd; + char sbuff[NI_MAXHOST], dbuff[NI_MAXHOST]; + + memset(&ss, 0, sizeof(ss)); + ss.sin_family = PF_INET; + ss.sin_addr.s_addr = ip->saddr; + getnameinfo((struct sockaddr *) &ss, sizeof(ss), + sbuff, sizeof(sbuff), NULL, 0, NI_NUMERICHOST); + + memset(&sd, 0, sizeof(sd)); + sd.sin_family = PF_INET; + sd.sin_addr.s_addr = ip->daddr; + getnameinfo((struct sockaddr *) &sd, sizeof(sd), + dbuff, sizeof(dbuff), NULL, 0, NI_NUMERICHOST); + + printf("%s -> %s, ", sbuff, dbuff); + } + + printf("rxhash: 0x%x\n", ppd->hv1.tp_rxhash); +} + +static void walk_block(struct block_desc *pbd, const int block_num) +{ + int num_pkts = BLOCK_NUM_PKTS(pbd), i; + unsigned long bytes = 0; + unsigned long bytes_with_padding = BLOCK_PLUS_PRIV(BLOCK_PRIV_AREA_SZ); + struct tpacket3_hdr *ppd; + + assert_block_header(pbd, block_num); + + ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd + BLOCK_O2FP(pbd)); + for (i = 0; i < num_pkts; ++i) { + bytes += ppd->tp_snaplen; + if (ppd->tp_next_offset) + bytes_with_padding += ppd->tp_next_offset; + else + bytes_with_padding += ALIGN_8(ppd->tp_snaplen + ppd->tp_mac); + + display(ppd); + + ppd = (struct tpacket3_hdr *) ((uint8_t *) ppd + ppd->tp_next_offset); + __sync_synchronize(); + } + + assert_block_len(pbd, bytes_with_padding, block_num); + + packets_total += num_pkts; + bytes_total += bytes; +} + +void flush_block(struct block_desc *pbd) +{ + BLOCK_STATUS(pbd) = TP_STATUS_KERNEL; + __sync_synchronize(); +} + +static void teardown_socket(struct ring *ring, int fd) +{ + munmap(ring->map, ring->req.tp_block_size * ring->req.tp_block_nr); + free(ring->rd); + close(fd); +} + +int main(int argc, char **argp) +{ + int fd, err; + socklen_t len; + struct ring ring; + struct pollfd pfd; + unsigned int block_num = 0; + struct block_desc *pbd; + struct tpacket_stats_v3 stats; + + if (argc != 2) { + fprintf(stderr, "Usage: %s INTERFACE\n", argp[0]); + return EXIT_FAILURE; + } + + signal(SIGINT, sighandler); + + memset(&ring, 0, sizeof(ring)); + fd = setup_socket(&ring, argp[argc - 1]); + assert(fd > 0); + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fd; + pfd.events = POLLIN | POLLERR; + pfd.revents = 0; + + while (likely(!sigint)) { + pbd = (struct block_desc *) ring.rd[block_num].iov_base; +retry_block: + if ((BLOCK_STATUS(pbd) & TP_STATUS_USER) == 0) { + poll(&pfd, 1, -1); + goto retry_block; + } + + walk_block(pbd, block_num); + flush_block(pbd); + block_num = (block_num + 1) % NUM_BLOCKS; + } + + len = sizeof(stats); + err = getsockopt(fd, SOL_PACKET, PACKET_STATISTICS, &stats, &len); + if (err < 0) { + perror("getsockopt"); + exit(1); + } + + fflush(stdout); + printf("\nReceived %u packets, %lu bytes, %u dropped, freeze_q_cnt: %u\n", + stats.tp_packets, bytes_total, stats.tp_drops, + stats.tp_freeze_q_cnt); + + teardown_socket(&ring, fd); + return 0; +} + ------------------------------------------------------------------------------- + PACKET_TIMESTAMP ------------------------------------------------------------------------------- -- cgit v1.2.3 From 2c0a4f8b870dbeb48e3351899bd0e0cc886d4985 Mon Sep 17 00:00:00 2001 From: Mischa Jonker Date: Sun, 31 Mar 2013 00:25:33 -0700 Subject: Input: arc_ps2 - add support for device tree Add match table for device tree binding and dts binding doc. Signed-off-by: Mischa Jonker Signed-off-by: Dmitry Torokhov --- Documentation/devicetree/bindings/serio/snps-arc_ps2.txt | 16 ++++++++++++++++ drivers/input/serio/arc_ps2.c | 14 ++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/serio/snps-arc_ps2.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/serio/snps-arc_ps2.txt b/Documentation/devicetree/bindings/serio/snps-arc_ps2.txt new file mode 100644 index 000000000000..38c2f21e8044 --- /dev/null +++ b/Documentation/devicetree/bindings/serio/snps-arc_ps2.txt @@ -0,0 +1,16 @@ +* ARC PS/2 driver: PS/2 block used in some ARC FPGA's & nSIM OSCI model + +Required properties: +- compatible : "snps,arc_ps2" +- reg : offset and length (always 0x14) of registers +- interrupts : interrupt +- interrupt-names : name of interrupt, must be "arc_ps2_irq" + +Example: + +serio@c9000400 { + compatible = "snps,arc_ps2"; + reg = <0xc9000400 0x14>; + interrupts = <13>; + interrupt-names = "arc_ps2_irq"; +} diff --git a/drivers/input/serio/arc_ps2.c b/drivers/input/serio/arc_ps2.c index c52e3e589f72..3fb7727c8ea5 100644 --- a/drivers/input/serio/arc_ps2.c +++ b/drivers/input/serio/arc_ps2.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -259,10 +260,19 @@ static int arc_ps2_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id arc_ps2_match[] = { + { .compatible = "snps,arc_ps2" }, + { }, +}; +MODULE_DEVICE_TABLE(of, arc_ps2_match); +#endif + static struct platform_driver arc_ps2_driver = { .driver = { - .name = "arc_ps2", - .owner = THIS_MODULE, + .name = "arc_ps2", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(arc_ps2_match), }, .probe = arc_ps2_probe, .remove = arc_ps2_remove, -- cgit v1.2.3 From 53c5b6c95f1850b0f95b445e0c6f1c133b00bfef Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 11 Sep 2012 12:44:18 -0300 Subject: [media] Add common video interfaces OF bindings documentation This patch adds a document describing common OF bindings for video capture, output and video processing devices. It is curently mainly focused on video capture devices, with data busses defined by standards such as ITU-R BT.656 or MIPI-CSI2. It also documents a method of describing data links between devices. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/video-interfaces.txt | 228 +++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/video-interfaces.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/media/video-interfaces.txt b/Documentation/devicetree/bindings/media/video-interfaces.txt new file mode 100644 index 000000000000..e022d2dc4962 --- /dev/null +++ b/Documentation/devicetree/bindings/media/video-interfaces.txt @@ -0,0 +1,228 @@ +Common bindings for video receiver and transmitter interfaces + +General concept +--------------- + +Video data pipelines usually consist of external devices, e.g. camera sensors, +controlled over an I2C, SPI or UART bus, and SoC internal IP blocks, including +video DMA engines and video data processors. + +SoC internal blocks are described by DT nodes, placed similarly to other SoC +blocks. External devices are represented as child nodes of their respective +bus controller nodes, e.g. I2C. + +Data interfaces on all video devices are described by their child 'port' nodes. +Configuration of a port depends on other devices participating in the data +transfer and is described by 'endpoint' subnodes. + +device { + ... + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + ... + endpoint@0 { ... }; + endpoint@1 { ... }; + }; + port@1 { ... }; + }; +}; + +If a port can be configured to work with more than one remote device on the same +bus, an 'endpoint' child node must be provided for each of them. If more than +one port is present in a device node or there is more than one endpoint at a +port, or port node needs to be associated with a selected hardware interface, +a common scheme using '#address-cells', '#size-cells' and 'reg' properties is +used. + +All 'port' nodes can be grouped under optional 'ports' node, which allows to +specify #address-cells, #size-cells properties independently for the 'port' +and 'endpoint' nodes and any child device nodes a device might have. + +Two 'endpoint' nodes are linked with each other through their 'remote-endpoint' +phandles. An endpoint subnode of a device contains all properties needed for +configuration of this device for data exchange with other device. In most +cases properties at the peer 'endpoint' nodes will be identical, however they +might need to be different when there is any signal modifications on the bus +between two devices, e.g. there are logic signal inverters on the lines. + +It is allowed for multiple endpoints at a port to be active simultaneously, +where supported by a device. For example, in case where a data interface of +a device is partitioned into multiple data busses, e.g. 16-bit input port +divided into two separate ITU-R BT.656 8-bit busses. In such case bus-width +and data-shift properties can be used to assign physical data lines to each +endpoint node (logical bus). + + +Required properties +------------------- + +If there is more than one 'port' or more than one 'endpoint' node or 'reg' +property is present in port and/or endpoint nodes the following properties +are required in a relevant parent node: + + - #address-cells : number of cells required to define port/endpoint + identifier, should be 1. + - #size-cells : should be zero. + +Optional endpoint properties +---------------------------- + +- remote-endpoint: phandle to an 'endpoint' subnode of a remote device node. +- slave-mode: a boolean property indicating that the link is run in slave mode. + The default when this property is not specified is master mode. In the slave + mode horizontal and vertical synchronization signals are provided to the + slave device (data source) by the master device (data sink). In the master + mode the data source device is also the source of the synchronization signals. +- bus-width: number of data lines actively used, valid for the parallel busses. +- data-shift: on the parallel data busses, if bus-width is used to specify the + number of data lines, data-shift can be used to specify which data lines are + used, e.g. "bus-width=<8>; data-shift=<2>;" means, that lines 9:2 are used. +- hsync-active: active state of the HSYNC signal, 0/1 for LOW/HIGH respectively. +- vsync-active: active state of the VSYNC signal, 0/1 for LOW/HIGH respectively. + Note, that if HSYNC and VSYNC polarities are not specified, embedded + synchronization may be required, where supported. +- data-active: similar to HSYNC and VSYNC, specifies data line polarity. +- field-even-active: field signal level during the even field data transmission. +- pclk-sample: sample data on rising (1) or falling (0) edge of the pixel clock + signal. +- data-lanes: an array of physical data lane indexes. Position of an entry + determines the logical lane number, while the value of an entry indicates + physical lane, e.g. for 2-lane MIPI CSI-2 bus we could have + "data-lanes = <1 2>;", assuming the clock lane is on hardware lane 0. + This property is valid for serial busses only (e.g. MIPI CSI-2). +- clock-lanes: an array of physical clock lane indexes. Position of an entry + determines the logical lane number, while the value of an entry indicates + physical lane, e.g. for a MIPI CSI-2 bus we could have "clock-lanes = <0>;", + which places the clock lane on hardware lane 0. This property is valid for + serial busses only (e.g. MIPI CSI-2). Note that for the MIPI CSI-2 bus this + array contains only one entry. +- clock-noncontinuous: a boolean property to allow MIPI CSI-2 non-continuous + clock mode. + + +Example +------- + +The example snippet below describes two data pipelines. ov772x and imx074 are +camera sensors with a parallel and serial (MIPI CSI-2) video bus respectively. +Both sensors are on the I2C control bus corresponding to the i2c0 controller +node. ov772x sensor is linked directly to the ceu0 video host interface. +imx074 is linked to ceu0 through the MIPI CSI-2 receiver (csi2). ceu0 has a +(single) DMA engine writing captured data to memory. ceu0 node has a single +'port' node which may indicate that at any time only one of the following data +pipelines can be active: ov772x -> ceu0 or imx074 -> csi2 -> ceu0. + + ceu0: ceu@0xfe910000 { + compatible = "renesas,sh-mobile-ceu"; + reg = <0xfe910000 0xa0>; + interrupts = <0x880>; + + mclk: master_clock { + compatible = "renesas,ceu-clock"; + #clock-cells = <1>; + clock-frequency = <50000000>; /* Max clock frequency */ + clock-output-names = "mclk"; + }; + + port { + #address-cells = <1>; + #size-cells = <0>; + + /* Parallel bus endpoint */ + ceu0_1: endpoint@1 { + reg = <1>; /* Local endpoint # */ + remote = <&ov772x_1_1>; /* Remote phandle */ + bus-width = <8>; /* Used data lines */ + data-shift = <2>; /* Lines 9:2 are used */ + + /* If hsync-active/vsync-active are missing, + embedded BT.656 sync is used */ + hsync-active = <0>; /* Active low */ + vsync-active = <0>; /* Active low */ + data-active = <1>; /* Active high */ + pclk-sample = <1>; /* Rising */ + }; + + /* MIPI CSI-2 bus endpoint */ + ceu0_0: endpoint@0 { + reg = <0>; + remote = <&csi2_2>; + }; + }; + }; + + i2c0: i2c@0xfff20000 { + ... + ov772x_1: camera@0x21 { + compatible = "omnivision,ov772x"; + reg = <0x21>; + vddio-supply = <®ulator1>; + vddcore-supply = <®ulator2>; + + clock-frequency = <20000000>; + clocks = <&mclk 0>; + clock-names = "xclk"; + + port { + /* With 1 endpoint per port no need for addresses. */ + ov772x_1_1: endpoint { + bus-width = <8>; + remote-endpoint = <&ceu0_1>; + hsync-active = <1>; + vsync-active = <0>; /* Who came up with an + inverter here ?... */ + data-active = <1>; + pclk-sample = <1>; + }; + }; + }; + + imx074: camera@0x1a { + compatible = "sony,imx074"; + reg = <0x1a>; + vddio-supply = <®ulator1>; + vddcore-supply = <®ulator2>; + + clock-frequency = <30000000>; /* Shared clock with ov772x_1 */ + clocks = <&mclk 0>; + clock-names = "sysclk"; /* Assuming this is the + name in the datasheet */ + port { + imx074_1: endpoint { + clock-lanes = <0>; + data-lanes = <1 2>; + remote-endpoint = <&csi2_1>; + }; + }; + }; + }; + + csi2: csi2@0xffc90000 { + compatible = "renesas,sh-mobile-csi2"; + reg = <0xffc90000 0x1000>; + interrupts = <0x17a0>; + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + compatible = "renesas,csi2c"; /* One of CSI2I and CSI2C. */ + reg = <1>; /* CSI-2 PHY #1 of 2: PHY_S, + PHY_M has port address 0, + is unused. */ + csi2_1: endpoint { + clock-lanes = <0>; + data-lanes = <2 1>; + remote-endpoint = <&imx074_1>; + }; + }; + port@2 { + reg = <2>; /* port 2: link to the CEU */ + + csi2_2: endpoint { + remote-endpoint = <&ceu0_0>; + }; + }; + }; -- cgit v1.2.3 From 02399e35e6bb716ce9636eba006b792362270034 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 26 Mar 2013 08:20:30 -0300 Subject: [media] s5p-csis: Add device tree support This patch support for binding the driver to the MIPI-CSIS devices instantiated from device tree and parsing the SoC and board specific properties. The MIPI CSI-2 channel is determined by the value of reg property placed in csis' port subnode. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/samsung-mipi-csis.txt | 81 +++++++++++ drivers/media/platform/s5p-fimc/mipi-csis.c | 155 ++++++++++++++++----- drivers/media/platform/s5p-fimc/mipi-csis.h | 1 + include/media/s5p_fimc.h | 13 ++ 4 files changed, 215 insertions(+), 35 deletions(-) create mode 100644 Documentation/devicetree/bindings/media/samsung-mipi-csis.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/media/samsung-mipi-csis.txt b/Documentation/devicetree/bindings/media/samsung-mipi-csis.txt new file mode 100644 index 000000000000..5f8e28e2484f --- /dev/null +++ b/Documentation/devicetree/bindings/media/samsung-mipi-csis.txt @@ -0,0 +1,81 @@ +Samsung S5P/EXYNOS SoC series MIPI CSI-2 receiver (MIPI CSIS) +------------------------------------------------------------- + +Required properties: + +- compatible : "samsung,s5pv210-csis" for S5PV210 (S5PC110), + "samsung,exynos4210-csis" for Exynos4210 (S5PC210), + "samsung,exynos4212-csis" for Exynos4212/Exynos4412 + SoC series; +- reg : offset and length of the register set for the device; +- interrupts : should contain MIPI CSIS interrupt; the format of the + interrupt specifier depends on the interrupt controller; +- bus-width : maximum number of data lanes supported (SoC specific); +- vddio-supply : MIPI CSIS I/O and PLL voltage supply (e.g. 1.8V); +- vddcore-supply : MIPI CSIS Core voltage supply (e.g. 1.1V); +- clocks : list of clock specifiers, corresponding to entries in + clock-names property; +- clock-names : must contain "csis", "sclk_csis" entries, matching entries + in the clocks property. + +Optional properties: + +- clock-frequency : The IP's main (system bus) clock frequency in Hz, default + value when this property is not specified is 166 MHz; +- samsung,csis-wclk : CSI-2 wrapper clock selection. If this property is present + external clock from CMU will be used, or the bus clock if + if it's not specified. + +The device node should contain one 'port' child node with one child 'endpoint' +node, according to the bindings defined in Documentation/devicetree/bindings/ +media/video-interfaces.txt. The following are properties specific to those nodes. + +port node +--------- + +- reg : (required) must be 3 for camera C input (CSIS0) or 4 for + camera D input (CSIS1); + +endpoint node +------------- + +- data-lanes : (required) an array specifying active physical MIPI-CSI2 + data input lanes and their mapping to logical lanes; the + array's content is unused, only its length is meaningful; + +- samsung,csis-hs-settle : (optional) differential receiver (HS-RX) settle time; + + +Example: + + reg0: regulator@0 { + }; + + reg1: regulator@1 { + }; + +/* SoC properties */ + + csis_0: csis@11880000 { + compatible = "samsung,exynos4210-csis"; + reg = <0x11880000 0x1000>; + interrupts = <0 78 0>; + #address-cells = <1>; + #size-cells = <0>; + }; + +/* Board properties */ + + csis_0: csis@11880000 { + clock-frequency = <166000000>; + vddio-supply = <®0>; + vddcore-supply = <®1>; + port { + reg = <3>; /* 3 - CSIS0, 4 - CSIS1 */ + csis0_ep: endpoint { + remote-endpoint = <...>; + data-lanes = <1>, <2>; + samsung,csis-hs-settle = <12>; + }; + }; + }; diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index 981863d05aaa..8636bcddde1b 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -19,14 +19,18 @@ #include #include #include +#include +#include #include #include #include #include #include #include +#include +#include #include -#include + #include "mipi-csis.h" static int debug; @@ -113,6 +117,7 @@ static char *csi_clock_name[] = { [CSIS_CLK_GATE] = "csis", }; #define NUM_CSIS_CLOCKS ARRAY_SIZE(csi_clock_name) +#define DEFAULT_SCLK_CSIS_FREQ 166000000UL static const char * const csis_supply_name[] = { "vddcore", /* CSIS Core (1.0V, 1.1V or 1.2V) suppply */ @@ -167,6 +172,11 @@ struct csis_pktbuf { * @clock: CSIS clocks * @irq: requested s5p-mipi-csis irq number * @flags: the state variable for power and streaming control + * @clock_frequency: device bus clock frequency + * @hs_settle: HS-RX settle time + * @num_lanes: number of MIPI-CSI data lanes used + * @max_num_lanes: maximum number of MIPI-CSI data lanes supported + * @wclk_ext: CSI wrapper clock: 0 - bus clock, 1 - external SCLK_CAM * @csis_fmt: current CSIS pixel format * @format: common media bus format for the source and sink pad * @slock: spinlock protecting structure members below @@ -184,6 +194,13 @@ struct csis_state { struct clk *clock[NUM_CSIS_CLOCKS]; int irq; u32 flags; + + u32 clk_frequency; + u32 hs_settle; + u32 num_lanes; + u32 max_num_lanes; + u8 wclk_ext; + const struct csis_pix_format *csis_fmt; struct v4l2_mbus_framefmt format; @@ -273,7 +290,6 @@ static void s5pcsis_reset(struct csis_state *state) static void s5pcsis_system_enable(struct csis_state *state, int on) { - struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data; u32 val, mask; val = s5pcsis_read(state, S5PCSIS_CTRL); @@ -286,7 +302,7 @@ static void s5pcsis_system_enable(struct csis_state *state, int on) val = s5pcsis_read(state, S5PCSIS_DPHYCTRL); val &= ~S5PCSIS_DPHYCTRL_ENABLE; if (on) { - mask = (1 << (pdata->lanes + 1)) - 1; + mask = (1 << (state->num_lanes + 1)) - 1; val |= (mask & S5PCSIS_DPHYCTRL_ENABLE); } s5pcsis_write(state, S5PCSIS_DPHYCTRL, val); @@ -321,15 +337,14 @@ static void s5pcsis_set_hsync_settle(struct csis_state *state, int settle) static void s5pcsis_set_params(struct csis_state *state) { - struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data; u32 val; val = s5pcsis_read(state, S5PCSIS_CONFIG); - val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (pdata->lanes - 1); + val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (state->num_lanes - 1); s5pcsis_write(state, S5PCSIS_CONFIG, val); __s5pcsis_set_format(state); - s5pcsis_set_hsync_settle(state, pdata->hs_settle); + s5pcsis_set_hsync_settle(state, state->hs_settle); val = s5pcsis_read(state, S5PCSIS_CTRL); if (state->csis_fmt->data_alignment == 32) @@ -338,7 +353,7 @@ static void s5pcsis_set_params(struct csis_state *state) val &= ~S5PCSIS_CTRL_ALIGN_32BIT; val &= ~S5PCSIS_CTRL_WCLK_EXTCLK; - if (pdata->wclk_source) + if (state->wclk_ext) val |= S5PCSIS_CTRL_WCLK_EXTCLK; s5pcsis_write(state, S5PCSIS_CTRL, val); @@ -701,52 +716,111 @@ static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +static int s5pcsis_get_platform_data(struct platform_device *pdev, + struct csis_state *state) +{ + struct s5p_platform_mipi_csis *pdata = pdev->dev.platform_data; + + if (pdata == NULL) { + dev_err(&pdev->dev, "Platform data not specified\n"); + return -EINVAL; + } + + state->clk_frequency = pdata->clk_rate; + state->num_lanes = pdata->lanes; + state->hs_settle = pdata->hs_settle; + state->index = max(0, pdev->id); + state->max_num_lanes = state->index ? CSIS1_MAX_LANES : + CSIS0_MAX_LANES; + return 0; +} + +#ifdef CONFIG_OF +static int s5pcsis_parse_dt(struct platform_device *pdev, + struct csis_state *state) +{ + struct device_node *node = pdev->dev.of_node; + struct v4l2_of_endpoint endpoint; + + if (of_property_read_u32(node, "clock-frequency", + &state->clk_frequency)) + state->clk_frequency = DEFAULT_SCLK_CSIS_FREQ; + if (of_property_read_u32(node, "bus-width", + &state->max_num_lanes)) + return -EINVAL; + + node = v4l2_of_get_next_endpoint(node, NULL); + if (!node) { + dev_err(&pdev->dev, "No port node at %s\n", + node->full_name); + return -EINVAL; + } + /* Get port node and validate MIPI-CSI channel id. */ + v4l2_of_parse_endpoint(node, &endpoint); + + state->index = endpoint.port - FIMC_INPUT_MIPI_CSI2_0; + if (state->index < 0 || state->index >= CSIS_MAX_ENTITIES) + return -ENXIO; + + /* Get MIPI CSI-2 bus configration from the endpoint node. */ + of_property_read_u32(node, "samsung,csis-hs-settle", + &state->hs_settle); + state->wclk_ext = of_property_read_bool(node, + "samsung,csis-wclk"); + + state->num_lanes = endpoint.bus.mipi_csi2.num_data_lanes; + + of_node_put(node); + return 0; +} +#else +#define s5pcsis_parse_dt(pdev, state) (-ENOSYS) +#endif + static int s5pcsis_probe(struct platform_device *pdev) { - struct s5p_platform_mipi_csis *pdata; + struct device *dev = &pdev->dev; struct resource *mem_res; struct csis_state *state; int ret = -ENOMEM; int i; - state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL); + state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); if (!state) return -ENOMEM; mutex_init(&state->lock); spin_lock_init(&state->slock); - state->pdev = pdev; - state->index = max(0, pdev->id); - pdata = pdev->dev.platform_data; - if (pdata == NULL) { - dev_err(&pdev->dev, "Platform data not fully specified\n"); - return -EINVAL; - } + if (dev->of_node) + ret = s5pcsis_parse_dt(pdev, state); + else + ret = s5pcsis_get_platform_data(pdev, state); + if (ret < 0) + return ret; - if ((state->index == 1 && pdata->lanes > CSIS1_MAX_LANES) || - pdata->lanes > CSIS0_MAX_LANES) { - dev_err(&pdev->dev, "Unsupported number of data lanes: %d\n", - pdata->lanes); + if (state->num_lanes == 0 || state->num_lanes > state->max_num_lanes) { + dev_err(dev, "Unsupported number of data lanes: %d (max. %d)\n", + state->num_lanes, state->max_num_lanes); return -EINVAL; } mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - state->regs = devm_ioremap_resource(&pdev->dev, mem_res); + state->regs = devm_ioremap_resource(dev, mem_res); if (IS_ERR(state->regs)) return PTR_ERR(state->regs); state->irq = platform_get_irq(pdev, 0); if (state->irq < 0) { - dev_err(&pdev->dev, "Failed to get irq\n"); + dev_err(dev, "Failed to get irq\n"); return state->irq; } for (i = 0; i < CSIS_NUM_SUPPLIES; i++) state->supplies[i].supply = csis_supply_name[i]; - ret = devm_regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES, + ret = devm_regulator_bulk_get(dev, CSIS_NUM_SUPPLIES, state->supplies); if (ret) return ret; @@ -755,11 +829,11 @@ static int s5pcsis_probe(struct platform_device *pdev) if (ret < 0) return ret; - if (pdata->clk_rate) + if (state->clk_frequency) ret = clk_set_rate(state->clock[CSIS_CLK_MUX], - pdata->clk_rate); + state->clk_frequency); else - dev_WARN(&pdev->dev, "No clock frequency specified!\n"); + dev_WARN(dev, "No clock frequency specified!\n"); if (ret < 0) goto e_clkput; @@ -767,16 +841,17 @@ static int s5pcsis_probe(struct platform_device *pdev) if (ret < 0) goto e_clkput; - ret = devm_request_irq(&pdev->dev, state->irq, s5pcsis_irq_handler, - 0, dev_name(&pdev->dev), state); + ret = devm_request_irq(dev, state->irq, s5pcsis_irq_handler, + 0, dev_name(dev), state); if (ret) { - dev_err(&pdev->dev, "Interrupt request failed\n"); + dev_err(dev, "Interrupt request failed\n"); goto e_clkdis; } v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops); state->sd.owner = THIS_MODULE; - strlcpy(state->sd.name, dev_name(&pdev->dev), sizeof(state->sd.name)); + snprintf(state->sd.name, sizeof(state->sd.name), "%s.%d", + CSIS_SUBDEV_NAME, state->index); state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; state->csis_fmt = &s5pcsis_formats[0]; @@ -796,10 +871,12 @@ static int s5pcsis_probe(struct platform_device *pdev) /* .. and a pointer to the subdev. */ platform_set_drvdata(pdev, &state->sd); - memcpy(state->events, s5pcsis_events, sizeof(state->events)); + pm_runtime_enable(dev); - pm_runtime_enable(&pdev->dev); + dev_info(&pdev->dev, "lanes: %d, hs_settle: %d, wclk: %d, freq: %u\n", + state->num_lanes, state->hs_settle, state->wclk_ext, + state->clk_frequency); return 0; e_clkdis: @@ -923,13 +1000,21 @@ static const struct dev_pm_ops s5pcsis_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume) }; +static const struct of_device_id s5pcsis_of_match[] = { + { .compatible = "samsung,s5pv210-csis" }, + { .compatible = "samsung,exynos4210-csis" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, s5pcsis_of_match); + static struct platform_driver s5pcsis_driver = { .probe = s5pcsis_probe, .remove = s5pcsis_remove, .driver = { - .name = CSIS_DRIVER_NAME, - .owner = THIS_MODULE, - .pm = &s5pcsis_pm_ops, + .of_match_table = s5pcsis_of_match, + .name = CSIS_DRIVER_NAME, + .owner = THIS_MODULE, + .pm = &s5pcsis_pm_ops, }, }; diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.h b/drivers/media/platform/s5p-fimc/mipi-csis.h index 2709286396e1..28c11c4085d8 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.h +++ b/drivers/media/platform/s5p-fimc/mipi-csis.h @@ -11,6 +11,7 @@ #define S5P_MIPI_CSIS_H_ #define CSIS_DRIVER_NAME "s5p-mipi-csis" +#define CSIS_SUBDEV_NAME CSIS_DRIVER_NAME #define CSIS_MAX_ENTITIES 2 #define CSIS0_MAX_LANES 4 #define CSIS1_MAX_LANES 2 diff --git a/include/media/s5p_fimc.h b/include/media/s5p_fimc.h index 28f3590aa031..d6dbb791f423 100644 --- a/include/media/s5p_fimc.h +++ b/include/media/s5p_fimc.h @@ -14,6 +14,19 @@ #include +/* + * Enumeration of data inputs to the camera subsystem. + */ +enum fimc_input { + FIMC_INPUT_PARALLEL_0 = 1, + FIMC_INPUT_PARALLEL_1, + FIMC_INPUT_MIPI_CSI2_0 = 3, + FIMC_INPUT_MIPI_CSI2_1, + FIMC_INPUT_WRITEBACK_A = 5, + FIMC_INPUT_WRITEBACK_B, + FIMC_INPUT_WRITEBACK_ISP = 5, +}; + /* * Enumeration of the FIMC data bus types. */ -- cgit v1.2.3 From e80cb1fae55ceb01b6e886e5499461c07b69c454 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 26 Mar 2013 08:22:21 -0300 Subject: [media] s5p-fimc: Add device tree support for FIMC device driver This patch adds device tree support for FIMC driver on S5PV210 and Exynos4 SoCs. The FIMC IP block's features and quirks encoded statically in the driver are now parsed from the device tree. Once all relevant platforms are converted to device tree based booting the FIMC variant data structures will all be removed from the driver. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/samsung-fimc.txt | 101 +++++++++ drivers/media/platform/s5p-fimc/fimc-capture.c | 6 +- drivers/media/platform/s5p-fimc/fimc-core.c | 238 ++++++++++++--------- drivers/media/platform/s5p-fimc/fimc-core.h | 21 +- drivers/media/platform/s5p-fimc/fimc-m2m.c | 2 +- drivers/media/platform/s5p-fimc/fimc-reg.c | 6 +- 6 files changed, 260 insertions(+), 114 deletions(-) create mode 100644 Documentation/devicetree/bindings/media/samsung-fimc.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt new file mode 100644 index 000000000000..22e2889162c3 --- /dev/null +++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt @@ -0,0 +1,101 @@ +Samsung S5P/EXYNOS SoC Camera Subsystem (FIMC) +---------------------------------------------- + +The S5P/Exynos SoC Camera subsystem comprises of multiple sub-devices +represented by separate device tree nodes. Currently this includes: FIMC (in +the S5P SoCs series known as CAMIF), MIPI CSIS, FIMC-LITE and FIMC-IS (ISP). + +The sub-subdevices are defined as child nodes of the common 'camera' node which +also includes common properties of the whole subsystem not really specific to +any single sub-device, like common camera port pins or the CAMCLK clock outputs +for external image sensors attached to an SoC. + +Common 'camera' node +-------------------- + +Required properties: + +- compatible : must be "samsung,fimc", "simple-bus" +- clocks : list of clock specifiers, corresponding to entries in + clock-names property; +- clock-names : must contain "fimc", "sclk_fimc" entries, matching entries + in the clocks property. + +The 'camera' node must include at least one 'fimc' child node. + + +'fimc' device nodes +------------------- + +Required properties: + +- compatible: "samsung,s5pv210-fimc" for S5PV210, "samsung,exynos4210-fimc" + for Exynos4210 and "samsung,exynos4212-fimc" for Exynos4x12 SoCs; +- reg: physical base address and length of the registers set for the device; +- interrupts: should contain FIMC interrupt; +- clocks: list of clock specifiers, must contain an entry for each required + entry in clock-names; +- clock-names: must include "fimc", "sclk_fimc", "mux" entries and optionally + "parent" entry. +- samsung,pix-limits: an array of maximum supported image sizes in pixels, for + details refer to Table 2-1 in the S5PV210 SoC User Manual; The meaning of + each cell is as follows: + 0 - scaler input horizontal size, + 1 - input horizontal size for the scaler bypassed, + 2 - REAL_WIDTH without input rotation, + 3 - REAL_HEIGHT with input rotation, +- samsung,sysreg: a phandle to the SYSREG node. + +Each FIMC device should have an alias in the aliases node, in the form of +fimc, where is an integer specifying the IP block instance. + +Optional properties: + +- clock-frequency: maximum FIMC local clock (LCLK) frequency; +- samsung,min-pix-sizes: an array specyfing minimum image size in pixels at + the FIMC input and output DMA, in the first and second cell respectively. + Default value when this property is not present is <16 16>; +- samsung,min-pix-alignment: minimum supported image height alignment (first + cell) and the horizontal image offset (second cell). The values are in pixels + and default to <2 1> when this property is not present; +- samsung,mainscaler-ext: a boolean property indicating whether the FIMC IP + supports extended image size and has CIEXTEN register; +- samsung,rotators: a bitmask specifying whether this IP has the input and + the output rotator. Bits 4 and 0 correspond to input and output rotator + respectively. If a rotator is present its corresponding bit should be set. + Default value when this property is not specified is 0x11. +- samsung,cam-if: a bolean property indicating whether the IP block includes + the camera input interface. +- samsung,isp-wb: this property must be present if the IP block has the ISP + writeback input. +- samsung,lcd-wb: this property must be present if the IP block has the LCD + writeback input. + + +Example: + + aliases { + fimc0 = &fimc_0; + }; + + camera { + compatible = "samsung,fimc", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + status = "okay"; + + fimc_0: fimc@11800000 { + compatible = "samsung,exynos4210-fimc"; + reg = <0x11800000 0x1000>; + interrupts = <0 85 0>; + status = "okay"; + }; + + csis_0: csis@11880000 { + compatible = "samsung,exynos4210-csis"; + reg = <0x11880000 0x1000>; + interrupts = <0 78 0>; + }; + }; + +The MIPI-CSIS device binding is defined in samsung-mipi-csis.txt. diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index e9928cda6387..724cd8b4933d 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -65,7 +65,7 @@ static int fimc_capture_hw_init(struct fimc_dev *fimc) fimc_hw_set_effect(ctx); fimc_hw_set_output_path(ctx); fimc_hw_set_out_dma(ctx); - if (fimc->variant->has_alpha) + if (fimc->drv_data->alpha_color) fimc_hw_set_rgb_alpha(ctx); clear_bit(ST_CAPT_APPLY_CFG, &fimc->state); } @@ -168,7 +168,7 @@ static int fimc_capture_config_update(struct fimc_ctx *ctx) fimc_hw_set_effect(ctx); fimc_prepare_dma_offset(ctx, &ctx->d_frame); fimc_hw_set_out_dma(ctx); - if (fimc->variant->has_alpha) + if (fimc->drv_data->alpha_color) fimc_hw_set_rgb_alpha(ctx); clear_bit(ST_CAPT_APPLY_CFG, &fimc->state); @@ -1802,7 +1802,7 @@ int fimc_initialize_capture_subdev(struct fimc_dev *fimc) v4l2_subdev_init(sd, &fimc_subdev_ops); sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; - snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->pdev->id); + snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->id); fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; diff --git a/drivers/media/platform/s5p-fimc/fimc-core.c b/drivers/media/platform/s5p-fimc/fimc-core.c index e3916bde45cf..d39e47abb229 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.c +++ b/drivers/media/platform/s5p-fimc/fimc-core.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -440,14 +442,14 @@ void fimc_set_yuv_order(struct fimc_ctx *ctx) void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) { - const struct fimc_variant *variant = ctx->fimc_dev->variant; + bool pix_hoff = ctx->fimc_dev->drv_data->dma_pix_hoff; u32 i, depth = 0; for (i = 0; i < f->fmt->colplanes; i++) depth += f->fmt->depth[i]; f->dma_offset.y_h = f->offs_h; - if (!variant->pix_hoff) + if (!pix_hoff) f->dma_offset.y_h *= (depth >> 3); f->dma_offset.y_v = f->offs_v; @@ -458,7 +460,7 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) f->dma_offset.cr_h = f->offs_h; f->dma_offset.cr_v = f->offs_v; - if (!variant->pix_hoff) { + if (!pix_hoff) { if (f->fmt->colplanes == 3) { f->dma_offset.cb_h >>= 1; f->dma_offset.cr_h >>= 1; @@ -589,7 +591,6 @@ static const struct v4l2_ctrl_ops fimc_ctrl_ops = { int fimc_ctrls_create(struct fimc_ctx *ctx) { - const struct fimc_variant *variant = ctx->fimc_dev->variant; unsigned int max_alpha = fimc_get_alpha_mask(ctx->d_frame.fmt); struct fimc_ctrls *ctrls = &ctx->ctrls; struct v4l2_ctrl_handler *handler = &ctrls->handler; @@ -606,7 +607,7 @@ int fimc_ctrls_create(struct fimc_ctx *ctx) ctrls->vflip = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); - if (variant->has_alpha) + if (ctx->fimc_dev->drv_data->alpha_color) ctrls->alpha = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, 0, max_alpha, 1, 0); @@ -677,7 +678,7 @@ void fimc_alpha_ctrl_update(struct fimc_ctx *ctx) struct fimc_dev *fimc = ctx->fimc_dev; struct v4l2_ctrl *ctrl = ctx->ctrls.alpha; - if (ctrl == NULL || !fimc->variant->has_alpha) + if (ctrl == NULL || !fimc->drv_data->alpha_color) return; v4l2_ctrl_lock(ctrl); @@ -863,43 +864,109 @@ static int fimc_m2m_resume(struct fimc_dev *fimc) return 0; } +static const struct of_device_id fimc_of_match[]; + +static int fimc_parse_dt(struct fimc_dev *fimc, u32 *clk_freq) +{ + struct device *dev = &fimc->pdev->dev; + struct device_node *node = dev->of_node; + const struct of_device_id *of_id; + struct fimc_variant *v; + struct fimc_pix_limit *lim; + u32 args[FIMC_PIX_LIMITS_MAX]; + int ret; + + if (of_property_read_bool(node, "samsung,lcd-wb")) + return -ENODEV; + + v = devm_kzalloc(dev, sizeof(*v) + sizeof(*lim), GFP_KERNEL); + if (!v) + return -ENOMEM; + + of_id = of_match_node(fimc_of_match, node); + if (!of_id) + return -EINVAL; + fimc->drv_data = of_id->data; + ret = of_property_read_u32_array(node, "samsung,pix-limits", + args, FIMC_PIX_LIMITS_MAX); + if (ret < 0) + return ret; + + lim = (struct fimc_pix_limit *)&v[1]; + + lim->scaler_en_w = args[0]; + lim->scaler_dis_w = args[1]; + lim->out_rot_en_w = args[2]; + lim->out_rot_dis_w = args[3]; + v->pix_limit = lim; + + ret = of_property_read_u32_array(node, "samsung,min-pix-sizes", + args, 2); + v->min_inp_pixsize = ret ? FIMC_DEF_MIN_SIZE : args[0]; + v->min_out_pixsize = ret ? FIMC_DEF_MIN_SIZE : args[1]; + ret = of_property_read_u32_array(node, "samsung,min-pix-alignment", + args, 2); + v->min_vsize_align = ret ? FIMC_DEF_HEIGHT_ALIGN : args[0]; + v->hor_offs_align = ret ? FIMC_DEF_HOR_OFFS_ALIGN : args[1]; + + ret = of_property_read_u32(node, "samsung,rotators", &args[1]); + v->has_inp_rot = ret ? 1 : args[1] & 0x01; + v->has_out_rot = ret ? 1 : args[1] & 0x10; + v->has_mainscaler_ext = of_property_read_bool(node, + "samsung,mainscaler-ext"); + + v->has_isp_wb = of_property_read_bool(node, "samsung,isp-wb"); + v->has_cam_if = of_property_read_bool(node, "samsung,cam-if"); + of_property_read_u32(node, "clock-frequency", clk_freq); + fimc->id = of_alias_get_id(node, "fimc"); + + fimc->variant = v; + return 0; +} + static int fimc_probe(struct platform_device *pdev) { - const struct fimc_drvdata *drv_data = fimc_get_drvdata(pdev); - struct s5p_platform_fimc *pdata; + struct device *dev = &pdev->dev; + u32 lclk_freq = 0; struct fimc_dev *fimc; struct resource *res; int ret = 0; - if (pdev->id >= drv_data->num_entities) { - dev_err(&pdev->dev, "Invalid platform device id: %d\n", - pdev->id); - return -EINVAL; - } - - fimc = devm_kzalloc(&pdev->dev, sizeof(*fimc), GFP_KERNEL); + fimc = devm_kzalloc(dev, sizeof(*fimc), GFP_KERNEL); if (!fimc) return -ENOMEM; - fimc->id = pdev->id; - - fimc->variant = drv_data->variant[fimc->id]; fimc->pdev = pdev; - pdata = pdev->dev.platform_data; - fimc->pdata = pdata; + + if (dev->of_node) { + ret = fimc_parse_dt(fimc, &lclk_freq); + if (ret < 0) + return ret; + } else { + fimc->drv_data = fimc_get_drvdata(pdev); + fimc->id = pdev->id; + } + if (!fimc->drv_data || fimc->id >= fimc->drv_data->num_entities || + fimc->id < 0) { + dev_err(dev, "Invalid driver data or device id (%d/%d)\n", + fimc->id, fimc->drv_data->num_entities); + return -EINVAL; + } + if (!dev->of_node) + fimc->variant = fimc->drv_data->variant[fimc->id]; init_waitqueue_head(&fimc->irq_queue); spin_lock_init(&fimc->slock); mutex_init(&fimc->lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - fimc->regs = devm_ioremap_resource(&pdev->dev, res); + fimc->regs = devm_ioremap_resource(dev, res); if (IS_ERR(fimc->regs)) return PTR_ERR(fimc->regs); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { - dev_err(&pdev->dev, "Failed to get IRQ resource\n"); + dev_err(dev, "Failed to get IRQ resource\n"); return -ENXIO; } @@ -907,7 +974,10 @@ static int fimc_probe(struct platform_device *pdev) if (ret) return ret; - ret = clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency); + if (lclk_freq == 0) + lclk_freq = fimc->drv_data->lclk_frequency; + + ret = clk_set_rate(fimc->clock[CLK_BUS], lclk_freq); if (ret < 0) return ret; @@ -915,10 +985,10 @@ static int fimc_probe(struct platform_device *pdev) if (ret < 0) return ret; - ret = devm_request_irq(&pdev->dev, res->start, fimc_irq_handler, - 0, dev_name(&pdev->dev), fimc); + ret = devm_request_irq(dev, res->start, fimc_irq_handler, + 0, dev_name(dev), fimc); if (ret) { - dev_err(&pdev->dev, "failed to install irq (%d)\n", ret); + dev_err(dev, "failed to install irq (%d)\n", ret); goto err_clk; } @@ -927,23 +997,23 @@ static int fimc_probe(struct platform_device *pdev) goto err_clk; platform_set_drvdata(pdev, fimc); - pm_runtime_enable(&pdev->dev); - ret = pm_runtime_get_sync(&pdev->dev); + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); if (ret < 0) goto err_sd; /* Initialize contiguous memory allocator */ - fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev); if (IS_ERR(fimc->alloc_ctx)) { ret = PTR_ERR(fimc->alloc_ctx); goto err_pm; } - dev_dbg(&pdev->dev, "FIMC.%d registered successfully\n", fimc->id); + dev_dbg(dev, "FIMC.%d registered successfully\n", fimc->id); - pm_runtime_put(&pdev->dev); + pm_runtime_put(dev); return 0; err_pm: - pm_runtime_put(&pdev->dev); + pm_runtime_put(dev); err_sd: fimc_unregister_capture_subdev(fimc); err_clk: @@ -1046,24 +1116,18 @@ static const struct fimc_pix_limit s5p_pix_limit[4] = { [0] = { .scaler_en_w = 3264, .scaler_dis_w = 8192, - .in_rot_en_h = 1920, - .in_rot_dis_w = 8192, .out_rot_en_w = 1920, .out_rot_dis_w = 4224, }, [1] = { .scaler_en_w = 4224, .scaler_dis_w = 8192, - .in_rot_en_h = 1920, - .in_rot_dis_w = 8192, .out_rot_en_w = 1920, .out_rot_dis_w = 4224, }, [2] = { .scaler_en_w = 1920, .scaler_dis_w = 8192, - .in_rot_en_h = 1280, - .in_rot_dis_w = 8192, .out_rot_en_w = 1280, .out_rot_dis_w = 1920, }, @@ -1085,7 +1149,6 @@ static const struct fimc_variant fimc0_variant_s5p = { .min_out_pixsize = 16, .hor_offs_align = 8, .min_vsize_align = 16, - .out_buf_count = 4, .pix_limit = &s5p_pix_limit[0], }; @@ -1095,12 +1158,10 @@ static const struct fimc_variant fimc2_variant_s5p = { .min_out_pixsize = 16, .hor_offs_align = 8, .min_vsize_align = 16, - .out_buf_count = 4, .pix_limit = &s5p_pix_limit[1], }; static const struct fimc_variant fimc0_variant_s5pv210 = { - .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, .has_cam_if = 1, @@ -1108,12 +1169,10 @@ static const struct fimc_variant fimc0_variant_s5pv210 = { .min_out_pixsize = 16, .hor_offs_align = 8, .min_vsize_align = 16, - .out_buf_count = 4, .pix_limit = &s5p_pix_limit[1], }; static const struct fimc_variant fimc1_variant_s5pv210 = { - .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, .has_cam_if = 1, @@ -1122,80 +1181,39 @@ static const struct fimc_variant fimc1_variant_s5pv210 = { .min_out_pixsize = 16, .hor_offs_align = 1, .min_vsize_align = 1, - .out_buf_count = 4, .pix_limit = &s5p_pix_limit[2], }; static const struct fimc_variant fimc2_variant_s5pv210 = { .has_cam_if = 1, - .pix_hoff = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, .hor_offs_align = 8, .min_vsize_align = 16, - .out_buf_count = 4, .pix_limit = &s5p_pix_limit[2], }; static const struct fimc_variant fimc0_variant_exynos4210 = { - .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, .has_cam_if = 1, - .has_cistatus2 = 1, .has_mainscaler_ext = 1, - .has_alpha = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, .hor_offs_align = 2, .min_vsize_align = 1, - .out_buf_count = 32, .pix_limit = &s5p_pix_limit[1], }; static const struct fimc_variant fimc3_variant_exynos4210 = { - .pix_hoff = 1, - .has_cistatus2 = 1, .has_mainscaler_ext = 1, - .has_alpha = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, .hor_offs_align = 2, .min_vsize_align = 1, - .out_buf_count = 32, .pix_limit = &s5p_pix_limit[3], }; -static const struct fimc_variant fimc0_variant_exynos4x12 = { - .pix_hoff = 1, - .has_inp_rot = 1, - .has_out_rot = 1, - .has_cam_if = 1, - .has_isp_wb = 1, - .has_cistatus2 = 1, - .has_mainscaler_ext = 1, - .has_alpha = 1, - .min_inp_pixsize = 16, - .min_out_pixsize = 16, - .hor_offs_align = 2, - .min_vsize_align = 1, - .out_buf_count = 32, - .pix_limit = &s5p_pix_limit[1], -}; - -static const struct fimc_variant fimc3_variant_exynos4x12 = { - .pix_hoff = 1, - .has_cistatus2 = 1, - .has_mainscaler_ext = 1, - .has_alpha = 1, - .min_inp_pixsize = 16, - .min_out_pixsize = 16, - .hor_offs_align = 2, - .min_vsize_align = 1, - .out_buf_count = 32, - .pix_limit = &s5p_pix_limit[3], -}; - /* S5PC100 */ static const struct fimc_drvdata fimc_drvdata_s5p = { .variant = { @@ -1203,8 +1221,9 @@ static const struct fimc_drvdata fimc_drvdata_s5p = { [1] = &fimc0_variant_s5p, [2] = &fimc2_variant_s5p, }, - .num_entities = 3, + .num_entities = 3, .lclk_frequency = 133000000UL, + .out_buf_count = 4, }; /* S5PV210, S5PC110 */ @@ -1214,8 +1233,10 @@ static const struct fimc_drvdata fimc_drvdata_s5pv210 = { [1] = &fimc1_variant_s5pv210, [2] = &fimc2_variant_s5pv210, }, - .num_entities = 3, - .lclk_frequency = 166000000UL, + .num_entities = 3, + .lclk_frequency = 166000000UL, + .out_buf_count = 4, + .dma_pix_hoff = 1, }; /* EXYNOS4210, S5PV310, S5PC210 */ @@ -1226,20 +1247,22 @@ static const struct fimc_drvdata fimc_drvdata_exynos4210 = { [2] = &fimc0_variant_exynos4210, [3] = &fimc3_variant_exynos4210, }, - .num_entities = 4, + .num_entities = 4, .lclk_frequency = 166000000UL, + .dma_pix_hoff = 1, + .cistatus2 = 1, + .alpha_color = 1, + .out_buf_count = 32, }; /* EXYNOS4212, EXYNOS4412 */ static const struct fimc_drvdata fimc_drvdata_exynos4x12 = { - .variant = { - [0] = &fimc0_variant_exynos4x12, - [1] = &fimc0_variant_exynos4x12, - [2] = &fimc0_variant_exynos4x12, - [3] = &fimc3_variant_exynos4x12, - }, - .num_entities = 4, - .lclk_frequency = 166000000UL, + .num_entities = 4, + .lclk_frequency = 166000000UL, + .dma_pix_hoff = 1, + .cistatus2 = 1, + .alpha_color = 1, + .out_buf_count = 32, }; static const struct platform_device_id fimc_driver_ids[] = { @@ -1256,10 +1279,24 @@ static const struct platform_device_id fimc_driver_ids[] = { .name = "exynos4x12-fimc", .driver_data = (unsigned long)&fimc_drvdata_exynos4x12, }, - {}, + { }, }; MODULE_DEVICE_TABLE(platform, fimc_driver_ids); +static const struct of_device_id fimc_of_match[] = { + { + .compatible = "samsung,s5pv210-fimc", + .data = &fimc_drvdata_s5pv210, + }, { + .compatible = "samsung,exynos4210-fimc", + .data = &fimc_drvdata_exynos4210, + }, { + .compatible = "samsung,exynos4212-fimc", + .data = &fimc_drvdata_exynos4x12, + }, + { /* sentinel */ }, +}; + static const struct dev_pm_ops fimc_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(fimc_suspend, fimc_resume) SET_RUNTIME_PM_OPS(fimc_runtime_suspend, fimc_runtime_resume, NULL) @@ -1270,9 +1307,10 @@ static struct platform_driver fimc_driver = { .remove = fimc_remove, .id_table = fimc_driver_ids, .driver = { - .name = FIMC_MODULE_NAME, - .owner = THIS_MODULE, - .pm = &fimc_pm_ops, + .of_match_table = fimc_of_match, + .name = FIMC_MODULE_NAME, + .owner = THIS_MODULE, + .pm = &fimc_pm_ops, } }; diff --git a/drivers/media/platform/s5p-fimc/fimc-core.h b/drivers/media/platform/s5p-fimc/fimc-core.h index 412d50708f75..145b8cc77af0 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.h +++ b/drivers/media/platform/s5p-fimc/fimc-core.h @@ -42,6 +42,10 @@ #define FIMC_CAMIF_MAX_HEIGHT 0x2000 #define FIMC_MAX_JPEG_BUF_SIZE (10 * SZ_1M) #define FIMC_MAX_PLANES 3 +#define FIMC_PIX_LIMITS_MAX 4 +#define FIMC_DEF_MIN_SIZE 16 +#define FIMC_DEF_HEIGHT_ALIGN 2 +#define FIMC_DEF_HOR_OFFS_ALIGN 1 /* indices to the clocks array */ enum { @@ -365,10 +369,8 @@ struct fimc_pix_limit { /** * struct fimc_variant - FIMC device variant information - * @pix_hoff: indicate whether horizontal offset is in pixels or in bytes * @has_inp_rot: set if has input rotator * @has_out_rot: set if has output rotator - * @has_cistatus2: 1 if CISTATUS2 register is present in this IP revision * @has_mainscaler_ext: 1 if extended mainscaler ratios in CIEXTEN register * are present in this IP revision * @has_cam_if: set if this instance has a camera input interface @@ -378,23 +380,18 @@ struct fimc_pix_limit { * @min_out_pixsize: minimum output pixel size * @hor_offs_align: horizontal pixel offset aligment * @min_vsize_align: minimum vertical pixel size alignment - * @out_buf_count: the number of buffers in output DMA sequence */ struct fimc_variant { - unsigned int pix_hoff:1; unsigned int has_inp_rot:1; unsigned int has_out_rot:1; - unsigned int has_cistatus2:1; unsigned int has_mainscaler_ext:1; unsigned int has_cam_if:1; unsigned int has_isp_wb:1; - unsigned int has_alpha:1; const struct fimc_pix_limit *pix_limit; u16 min_inp_pixsize; u16 min_out_pixsize; u16 hor_offs_align; u16 min_vsize_align; - u16 out_buf_count; }; /** @@ -402,11 +399,20 @@ struct fimc_variant { * @variant: variant information for this device * @num_entities: number of fimc instances available in a SoC * @lclk_frequency: local bus clock frequency + * @cistatus2: 1 if the FIMC IPs have CISTATUS2 register + * @dma_pix_hoff: the horizontal DMA offset unit: 1 - pixels, 0 - bytes + * @alpha_color: 1 if alpha color component is supported + * @out_buf_count: maximum number of output DMA buffers supported */ struct fimc_drvdata { const struct fimc_variant *variant[FIMC_MAX_DEVS]; int num_entities; unsigned long lclk_frequency; + /* Fields common to all FIMC IP instances */ + u8 cistatus2; + u8 dma_pix_hoff; + u8 alpha_color; + u8 out_buf_count; }; #define fimc_get_drvdata(_pdev) \ @@ -438,6 +444,7 @@ struct fimc_dev { struct platform_device *pdev; struct s5p_platform_fimc *pdata; const struct fimc_variant *variant; + const struct fimc_drvdata *drv_data; u16 id; struct clk *clock[MAX_FIMC_CLOCKS]; void __iomem *regs; diff --git a/drivers/media/platform/s5p-fimc/fimc-m2m.c b/drivers/media/platform/s5p-fimc/fimc-m2m.c index c8509dd1776d..a224dac36b3a 100644 --- a/drivers/media/platform/s5p-fimc/fimc-m2m.c +++ b/drivers/media/platform/s5p-fimc/fimc-m2m.c @@ -152,7 +152,7 @@ static void fimc_device_run(void *priv) fimc_hw_set_rotation(ctx); fimc_hw_set_effect(ctx); fimc_hw_set_out_dma(ctx); - if (fimc->variant->has_alpha) + if (fimc->drv_data->alpha_color) fimc_hw_set_rgb_alpha(ctx); fimc_hw_set_output_path(ctx); } diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.c b/drivers/media/platform/s5p-fimc/fimc-reg.c index 50b97c75b956..4d2fc69d2cba 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.c +++ b/drivers/media/platform/s5p-fimc/fimc-reg.c @@ -35,7 +35,7 @@ void fimc_hw_reset(struct fimc_dev *dev) cfg &= ~FIMC_REG_CIGCTRL_SWRST; writel(cfg, dev->regs + FIMC_REG_CIGCTRL); - if (dev->variant->out_buf_count > 4) + if (dev->drv_data->out_buf_count > 4) fimc_hw_set_dma_seq(dev, 0xF); } @@ -747,7 +747,7 @@ s32 fimc_hw_get_frame_index(struct fimc_dev *dev) { s32 reg; - if (dev->variant->has_cistatus2) { + if (dev->drv_data->cistatus2) { reg = readl(dev->regs + FIMC_REG_CISTATUS2) & 0x3f; return reg - 1; } @@ -763,7 +763,7 @@ s32 fimc_hw_get_prev_frame_index(struct fimc_dev *dev) { s32 reg; - if (!dev->variant->has_cistatus2) + if (!dev->drv_data->cistatus2) return -1; reg = readl(dev->regs + FIMC_REG_CISTATUS2); -- cgit v1.2.3 From eb62d9e9d6327322a5fd3894bfecb3a3aa9391ba Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 26 Mar 2013 09:33:54 -0300 Subject: [media] s5p-fimc: Add device tree support for FIMC-LITE device driver This patch adds the device tree support for FIMC-LITE device driver. The bindings include compatible property for the Exynos5 SoC series, however the actual implementation for these SoCs will be added in a separate patch. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/exynos-fimc-lite.txt | 14 +++++ drivers/media/platform/s5p-fimc/fimc-lite.c | 63 +++++++++++++++------- 2 files changed, 59 insertions(+), 18 deletions(-) create mode 100644 Documentation/devicetree/bindings/media/exynos-fimc-lite.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/media/exynos-fimc-lite.txt b/Documentation/devicetree/bindings/media/exynos-fimc-lite.txt new file mode 100644 index 000000000000..3f62adfb3e0b --- /dev/null +++ b/Documentation/devicetree/bindings/media/exynos-fimc-lite.txt @@ -0,0 +1,14 @@ +Exynos4x12/Exynos5 SoC series camera host interface (FIMC-LITE) + +Required properties: + +- compatible : should be "samsung,exynos4212-fimc" for Exynos4212 and + Exynos4412 SoCs; +- reg : physical base address and size of the device memory mapped + registers; +- interrupts : should contain FIMC-LITE interrupt; +- clocks : FIMC LITE gate clock should be specified in this property. +- clock-names : should contain "flite" entry. + +Each FIMC device should have an alias in the aliases node, in the form of +fimc-lite, where is an integer specifying the IP block instance. diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/s5p-fimc/fimc-lite.c index b64297ba35d3..65082fe2578c 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1400,18 +1401,34 @@ static int fimc_lite_clk_get(struct fimc_lite *fimc) return ret; } +static const struct of_device_id flite_of_match[]; + static int fimc_lite_probe(struct platform_device *pdev) { - struct flite_drvdata *drv_data = fimc_lite_get_drvdata(pdev); + struct flite_drvdata *drv_data = NULL; + struct device *dev = &pdev->dev; + const struct of_device_id *of_id; struct fimc_lite *fimc; struct resource *res; int ret; - fimc = devm_kzalloc(&pdev->dev, sizeof(*fimc), GFP_KERNEL); + fimc = devm_kzalloc(dev, sizeof(*fimc), GFP_KERNEL); if (!fimc) return -ENOMEM; - fimc->index = pdev->id; + if (dev->of_node) { + of_id = of_match_node(flite_of_match, dev->of_node); + if (of_id) + drv_data = (struct flite_drvdata *)of_id->data; + fimc->index = of_alias_get_id(dev->of_node, "fimc-lite"); + } else { + drv_data = fimc_lite_get_drvdata(pdev); + fimc->index = pdev->id; + } + + if (!drv_data || fimc->index < 0 || fimc->index >= FIMC_LITE_MAX_DEVS) + return -EINVAL; + fimc->variant = drv_data->variant[fimc->index]; fimc->pdev = pdev; @@ -1420,13 +1437,13 @@ static int fimc_lite_probe(struct platform_device *pdev) mutex_init(&fimc->lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - fimc->regs = devm_ioremap_resource(&pdev->dev, res); + fimc->regs = devm_ioremap_resource(dev, res); if (IS_ERR(fimc->regs)) return PTR_ERR(fimc->regs); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { - dev_err(&pdev->dev, "Failed to get IRQ resource\n"); + dev_err(dev, "Failed to get IRQ resource\n"); return -ENXIO; } @@ -1434,10 +1451,10 @@ static int fimc_lite_probe(struct platform_device *pdev) if (ret) return ret; - ret = devm_request_irq(&pdev->dev, res->start, flite_irq_handler, - 0, dev_name(&pdev->dev), fimc); + ret = devm_request_irq(dev, res->start, flite_irq_handler, + 0, dev_name(dev), fimc); if (ret) { - dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret); + dev_err(dev, "Failed to install irq (%d)\n", ret); goto err_clk; } @@ -1447,23 +1464,23 @@ static int fimc_lite_probe(struct platform_device *pdev) goto err_clk; platform_set_drvdata(pdev, fimc); - pm_runtime_enable(&pdev->dev); - ret = pm_runtime_get_sync(&pdev->dev); + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); if (ret < 0) goto err_sd; - fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev); if (IS_ERR(fimc->alloc_ctx)) { ret = PTR_ERR(fimc->alloc_ctx); goto err_pm; } - pm_runtime_put(&pdev->dev); + pm_runtime_put(dev); - dev_dbg(&pdev->dev, "FIMC-LITE.%d registered successfully\n", + dev_dbg(dev, "FIMC-LITE.%d registered successfully\n", fimc->index); return 0; err_pm: - pm_runtime_put(&pdev->dev); + pm_runtime_put(dev); err_sd: fimc_lite_unregister_capture_subdev(fimc); err_clk: @@ -1554,6 +1571,12 @@ static int fimc_lite_remove(struct platform_device *pdev) return 0; } +static const struct dev_pm_ops fimc_lite_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fimc_lite_suspend, fimc_lite_resume) + SET_RUNTIME_PM_OPS(fimc_lite_runtime_suspend, fimc_lite_runtime_resume, + NULL) +}; + static struct flite_variant fimc_lite0_variant_exynos4 = { .max_width = 8192, .max_height = 8192, @@ -1579,17 +1602,21 @@ static struct platform_device_id fimc_lite_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, fimc_lite_driver_ids); -static const struct dev_pm_ops fimc_lite_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(fimc_lite_suspend, fimc_lite_resume) - SET_RUNTIME_PM_OPS(fimc_lite_runtime_suspend, fimc_lite_runtime_resume, - NULL) +static const struct of_device_id flite_of_match[] = { + { + .compatible = "samsung,exynos4212-fimc-lite", + .data = &fimc_lite_drvdata_exynos4, + }, + { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, flite_of_match); static struct platform_driver fimc_lite_driver = { .probe = fimc_lite_probe, .remove = fimc_lite_remove, .id_table = fimc_lite_driver_ids, .driver = { + .of_match_table = flite_of_match, .name = FIMC_LITE_DRV_NAME, .owner = THIS_MODULE, .pm = &fimc_lite_pm_ops, -- cgit v1.2.3 From 2b13f7d4e3822ed4de37b73b009ff81932e884bb Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 29 Mar 2013 14:12:39 -0300 Subject: [media] s5p-fimc: Add device tree based sensors registration The sensor (I2C and/or SPI client) devices are instantiated by their corresponding control bus drivers. Since the I2C client's master clock is often provided by a video bus receiver (host interface) or other than I2C/SPI controller device, the drivers of those client devices are not accessing hardware in their driver's probe() callback. Instead, after enabling clock, the host driver calls back into a sub-device when it wants to activate them. This pattern is used by some in-tree drivers and this patch also uses it for DT case. This patch is intended as a first step for adding device tree support to the S5P/Exynos SoC camera drivers. The second one is adding support for asynchronous sub-devices registration and clock control from sub-device driver level. The bindings shall not change when asynchronous probing support is added. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/samsung-fimc.txt | 86 ++++++++ drivers/media/platform/s5p-fimc/fimc-mdevice.c | 240 ++++++++++++++++++--- include/media/s5p_fimc.h | 3 + 3 files changed, 303 insertions(+), 26 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt index 22e2889162c3..bcb0de7462a2 100644 --- a/Documentation/devicetree/bindings/media/samsung-fimc.txt +++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt @@ -72,18 +72,95 @@ Optional properties: writeback input. +'parallel-ports' node +--------------------- + +This node should contain child 'port' nodes specifying active parallel video +input ports. It includes camera A and camera B inputs. 'reg' property in the +port nodes specifies data input - 0, 1 indicates input A, B respectively. + +Optional properties + +- samsung,camclk-out : specifies clock output for remote sensor, + 0 - CAM_A_CLKOUT, 1 - CAM_B_CLKOUT; + +Image sensor nodes +------------------ + +The sensor device nodes should be added to their control bus controller (e.g. +I2C0) nodes and linked to a port node in the csis or the parallel-ports node, +using the common video interfaces bindings, defined in video-interfaces.txt. +The implementation of this bindings requires clock-frequency property to be +present in the sensor device nodes. + Example: aliases { fimc0 = &fimc_0; }; + /* Parallel bus IF sensor */ + i2c_0: i2c@13860000 { + s5k6aa: sensor@3c { + compatible = "samsung,s5k6aafx"; + reg = <0x3c>; + vddio-supply = <...>; + + clock-frequency = <24000000>; + clocks = <...>; + clock-names = "mclk"; + + port { + s5k6aa_ep: endpoint { + remote-endpoint = <&fimc0_ep>; + bus-width = <8>; + hsync-active = <0>; + vsync-active = <1>; + pclk-sample = <1>; + }; + }; + }; + }; + + /* MIPI CSI-2 bus IF sensor */ + s5c73m3: sensor@0x1a { + compatible = "samsung,s5c73m3"; + reg = <0x1a>; + vddio-supply = <...>; + + clock-frequency = <24000000>; + clocks = <...>; + clock-names = "mclk"; + + port { + s5c73m3_1: endpoint { + data-lanes = <1 2 3 4>; + remote-endpoint = <&csis0_ep>; + }; + }; + }; + camera { compatible = "samsung,fimc", "simple-bus"; #address-cells = <1>; #size-cells = <1>; status = "okay"; + /* parallel camera ports */ + parallel-ports { + /* camera A input */ + port@0 { + reg = <0>; + fimc0_ep: endpoint { + remote-endpoint = <&s5k6aa_ep>; + bus-width = <8>; + hsync-active = <0>; + vsync-active = <1>; + pclk-sample = <1>; + }; + }; + }; + fimc_0: fimc@11800000 { compatible = "samsung,exynos4210-fimc"; reg = <0x11800000 0x1000>; @@ -95,6 +172,15 @@ Example: compatible = "samsung,exynos4210-csis"; reg = <0x11880000 0x1000>; interrupts = <0 78 0>; + /* camera C input */ + port@3 { + reg = <3>; + csis0_ep: endpoint { + remote-endpoint = <&s5c73m3_ep>; + data-lanes = <1 2 3 4>; + samsung,csis-hs-settle = <12>; + }; + }; }; }; diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index d34106c66427..bfa02abd94c8 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -251,7 +251,7 @@ static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd, sd->grp_id = GRP_ID_SENSOR; v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n", - s_info->pdata.board_info->type); + sd->name); return sd; } @@ -263,13 +263,191 @@ static void fimc_md_unregister_sensor(struct v4l2_subdev *sd) if (!client) return; v4l2_device_unregister_subdev(sd); - adapter = client->adapter; - i2c_unregister_device(client); - if (adapter) - i2c_put_adapter(adapter); + + if (!client->dev.of_node) { + adapter = client->adapter; + i2c_unregister_device(client); + if (adapter) + i2c_put_adapter(adapter); + } } #ifdef CONFIG_OF +/* Register I2C client subdev associated with @node. */ +static int fimc_md_of_add_sensor(struct fimc_md *fmd, + struct device_node *node, int index) +{ + struct fimc_sensor_info *si; + struct i2c_client *client; + struct v4l2_subdev *sd; + int ret; + + if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor))) + return -EINVAL; + si = &fmd->sensor[index]; + + client = of_find_i2c_device_by_node(node); + if (!client) + return -EPROBE_DEFER; + + device_lock(&client->dev); + + if (!client->driver || + !try_module_get(client->driver->driver.owner)) { + ret = -EPROBE_DEFER; + v4l2_info(&fmd->v4l2_dev, "No driver found for %s\n", + node->full_name); + goto dev_put; + } + + /* Enable sensor's master clock */ + ret = __fimc_md_set_camclk(fmd, si, true); + if (ret < 0) + goto mod_put; + sd = i2c_get_clientdata(client); + + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); + __fimc_md_set_camclk(fmd, si, false); + if (ret < 0) + goto mod_put; + + v4l2_set_subdev_hostdata(sd, si); + sd->grp_id = GRP_ID_SENSOR; + si->subdev = sd; + v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n", + sd->name, fmd->num_sensors); + fmd->num_sensors++; + +mod_put: + module_put(client->driver->driver.owner); +dev_put: + device_unlock(&client->dev); + put_device(&client->dev); + return ret; +} + +/* Parse port node and register as a sub-device any sensor specified there. */ +static int fimc_md_parse_port_node(struct fimc_md *fmd, + struct device_node *port, + unsigned int index) +{ + struct device_node *rem, *ep, *np; + struct fimc_source_info *pd; + struct v4l2_of_endpoint endpoint; + int ret; + u32 val; + + pd = &fmd->sensor[index].pdata; + + /* Assume here a port node can have only one endpoint node. */ + ep = of_get_next_child(port, NULL); + if (!ep) + return 0; + + v4l2_of_parse_endpoint(ep, &endpoint); + if (WARN_ON(endpoint.port == 0) || index >= FIMC_MAX_SENSORS) + return -EINVAL; + + pd->mux_id = (endpoint.port - 1) & 0x1; + + rem = v4l2_of_get_remote_port_parent(ep); + of_node_put(ep); + if (rem == NULL) { + v4l2_info(&fmd->v4l2_dev, "Remote device at %s not found\n", + ep->full_name); + return 0; + } + if (!of_property_read_u32(rem, "samsung,camclk-out", &val)) + pd->clk_id = val; + + if (!of_property_read_u32(rem, "clock-frequency", &val)) + pd->clk_frequency = val; + + if (pd->clk_frequency == 0) { + v4l2_err(&fmd->v4l2_dev, "Wrong clock frequency at node %s\n", + rem->full_name); + of_node_put(rem); + return -EINVAL; + } + + if (fimc_input_is_parallel(endpoint.port)) { + if (endpoint.bus_type == V4L2_MBUS_PARALLEL) + pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_601; + else + pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_656; + pd->flags = endpoint.bus.parallel.flags; + } else if (fimc_input_is_mipi_csi(endpoint.port)) { + /* + * MIPI CSI-2: only input mux selection and + * the sensor's clock frequency is needed. + */ + pd->sensor_bus_type = FIMC_BUS_TYPE_MIPI_CSI2; + } else { + v4l2_err(&fmd->v4l2_dev, "Wrong port id (%u) at node %s\n", + endpoint.port, rem->full_name); + } + /* + * For FIMC-IS handled sensors, that are placed under i2c-isp device + * node, FIMC is connected to the FIMC-IS through its ISP Writeback + * input. Sensors are attached to the FIMC-LITE hostdata interface + * directly or through MIPI-CSIS, depending on the external media bus + * used. This needs to be handled in a more reliable way, not by just + * checking parent's node name. + */ + np = of_get_parent(rem); + + if (np && !of_node_cmp(np->name, "i2c-isp")) + pd->fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK; + else + pd->fimc_bus_type = pd->sensor_bus_type; + + ret = fimc_md_of_add_sensor(fmd, rem, index); + of_node_put(rem); + + return ret; +} + +/* Register all SoC external sub-devices */ +static int fimc_md_of_sensors_register(struct fimc_md *fmd, + struct device_node *np) +{ + struct device_node *parent = fmd->pdev->dev.of_node; + struct device_node *node, *ports; + int index = 0; + int ret; + + /* Attach sensors linked to MIPI CSI-2 receivers */ + for_each_available_child_of_node(parent, node) { + struct device_node *port; + + if (of_node_cmp(node->name, "csis")) + continue; + /* The csis node can have only port subnode. */ + port = of_get_next_child(node, NULL); + if (!port) + continue; + + ret = fimc_md_parse_port_node(fmd, port, index); + if (ret < 0) + return ret; + index++; + } + + /* Attach sensors listed in the parallel-ports node */ + ports = of_get_child_by_name(parent, "parallel-ports"); + if (!ports) + return 0; + + for_each_child_of_node(ports, node) { + ret = fimc_md_parse_port_node(fmd, node, index); + if (ret < 0) + break; + index++; + } + + return 0; +} + static int __of_get_csis_id(struct device_node *np) { u32 reg = 0; @@ -281,14 +459,17 @@ static int __of_get_csis_id(struct device_node *np) return reg - FIMC_INPUT_MIPI_CSI2_0; } #else +#define fimc_md_of_sensors_register(fmd, np) (-ENOSYS) #define __of_get_csis_id(np) (-ENOSYS) #endif static int fimc_md_register_sensor_entities(struct fimc_md *fmd) { struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data; + struct device_node *of_node = fmd->pdev->dev.of_node; struct fimc_dev *fd = NULL; - int num_clients, ret, i; + int num_clients = 0; + int ret, i; /* * Runtime resume one of the FIMC entities to make sure @@ -299,34 +480,41 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) fd = fmd->fimc[i]; if (!fd) return -ENXIO; + ret = pm_runtime_get_sync(&fd->pdev->dev); if (ret < 0) return ret; - WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor)); - num_clients = min_t(u32, pdata->num_clients, ARRAY_SIZE(fmd->sensor)); + if (of_node) { + fmd->num_sensors = 0; + ret = fimc_md_of_sensors_register(fmd, of_node); + } else if (pdata) { + WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor)); + num_clients = min_t(u32, pdata->num_clients, + ARRAY_SIZE(fmd->sensor)); + fmd->num_sensors = num_clients; - fmd->num_sensors = num_clients; - for (i = 0; i < num_clients; i++) { - struct v4l2_subdev *sd; + for (i = 0; i < num_clients; i++) { + struct v4l2_subdev *sd; - fmd->sensor[i].pdata = pdata->source_info[i]; - ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true); - if (ret) - break; - sd = fimc_md_register_sensor(fmd, &fmd->sensor[i]); - ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false); - - if (!IS_ERR(sd)) { + fmd->sensor[i].pdata = pdata->source_info[i]; + ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true); + if (ret) + break; + sd = fimc_md_register_sensor(fmd, &fmd->sensor[i]); + ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false); + + if (IS_ERR(sd)) { + fmd->sensor[i].subdev = NULL; + ret = PTR_ERR(sd); + break; + } fmd->sensor[i].subdev = sd; - } else { - fmd->sensor[i].subdev = NULL; - ret = PTR_ERR(sd); - break; + if (ret) + break; } - if (ret) - break; } + pm_runtime_put(&fd->pdev->dev); return ret; } @@ -1037,7 +1225,7 @@ static int fimc_md_probe(struct platform_device *pdev) if (ret) goto err_unlock; - if (dev->platform_data) { + if (dev->platform_data || dev->of_node) { ret = fimc_md_register_sensor_entities(fmd); if (ret) goto err_unlock; diff --git a/include/media/s5p_fimc.h b/include/media/s5p_fimc.h index e2c598962698..e2434bb0b308 100644 --- a/include/media/s5p_fimc.h +++ b/include/media/s5p_fimc.h @@ -45,6 +45,9 @@ enum fimc_bus_type { FIMC_BUS_TYPE_ISP_WRITEBACK = FIMC_BUS_TYPE_LCD_WRITEBACK_B, }; +#define fimc_input_is_parallel(x) ((x) == 1 || (x) == 2) +#define fimc_input_is_mipi_csi(x) ((x) == 3 || (x) == 4) + struct i2c_board_info; /** -- cgit v1.2.3 From 4163851f7b997e24602cad8e0eae96d31a252548 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 8 Mar 2013 10:41:47 -0300 Subject: [media] s5p-fimc: Use pinctrl API for camera ports configuration Before the camera ports can be used the pinmux needs to be configured properly. This patch adds a function to set the camera ports pinctrl to a default state within the media driver's probe(). The camera port(s) are then configured for the video bus operation. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- .../devicetree/bindings/media/samsung-fimc.txt | 12 ++++++++++ drivers/media/platform/s5p-fimc/fimc-mdevice.c | 26 ++++++++++++++++++++++ drivers/media/platform/s5p-fimc/fimc-mdevice.h | 11 +++++++++ 3 files changed, 49 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt index bcb0de7462a2..2a63ddd23d16 100644 --- a/Documentation/devicetree/bindings/media/samsung-fimc.txt +++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt @@ -21,6 +21,15 @@ Required properties: - clock-names : must contain "fimc", "sclk_fimc" entries, matching entries in the clocks property. +The pinctrl bindings defined in ../pinctrl/pinctrl-bindings.txt must be used +to define a required pinctrl state named "default" and optional pinctrl states: +"idle", "active-a", active-b". These optional states can be used to switch the +camera port pinmux at runtime. The "idle" state should configure both the camera +ports A and B into high impedance state, especially the CAMCLK clock output +should be inactive. For the "active-a" state the camera port A must be activated +and the port B deactivated and for the state "active-b" it should be the other +way around. + The 'camera' node must include at least one 'fimc' child node. @@ -146,6 +155,9 @@ Example: #size-cells = <1>; status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&cam_port_a_clk_active>; + /* parallel camera ports */ parallel-ports { /* camera A input */ diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index bfa02abd94c8..5e96775fca8d 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -1174,6 +1174,25 @@ static ssize_t fimc_md_sysfs_store(struct device *dev, static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO, fimc_md_sysfs_show, fimc_md_sysfs_store); +static int fimc_md_get_pinctrl(struct fimc_md *fmd) +{ + struct device *dev = &fmd->pdev->dev; + struct fimc_pinctrl *pctl = &fmd->pinctl; + + pctl->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(pctl->pinctrl)) + return PTR_ERR(pctl->pinctrl); + + pctl->state_default = pinctrl_lookup_state(pctl->pinctrl, + PINCTRL_STATE_DEFAULT); + if (IS_ERR(pctl->state_default)) + return PTR_ERR(pctl->state_default); + + pctl->state_idle = pinctrl_lookup_state(pctl->pinctrl, + PINCTRL_STATE_IDLE); + return 0; +} + static int fimc_md_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1217,6 +1236,13 @@ static int fimc_md_probe(struct platform_device *pdev) /* Protect the media graph while we're registering entities */ mutex_lock(&fmd->media_dev.graph_mutex); + ret = fimc_md_get_pinctrl(fmd); + if (ret < 0) { + if (ret != EPROBE_DEFER) + dev_err(dev, "Failed to get pinctrl: %d\n", ret); + goto err_unlock; + } + if (dev->of_node) ret = fimc_md_register_of_platform_entities(fmd, dev->of_node); else diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.h b/drivers/media/platform/s5p-fimc/fimc-mdevice.h index b6ceb5984664..5d6146e76802 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.h +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,8 @@ #define FIMC_IS_OF_NODE_NAME "fimc-is" #define CSIS_OF_NODE_NAME "csis" +#define PINCTRL_STATE_IDLE "idle" + /* Group IDs of sensor, MIPI-CSIS, FIMC-LITE and the writeback subdevs. */ #define GRP_ID_SENSOR (1 << 8) #define GRP_ID_FIMC_IS_SENSOR (1 << 9) @@ -73,6 +76,9 @@ struct fimc_sensor_info { * @media_dev: top level media device * @v4l2_dev: top level v4l2_device holding up the subdevs * @pdev: platform device this media device is hooked up into + * @pinctrl: camera port pinctrl handle + * @state_default: pinctrl default state handle + * @state_idle: pinctrl idle state handle * @user_subdev_api: true if subdevs are not configured by the host driver * @slock: spinlock protecting @sensor array */ @@ -86,6 +92,11 @@ struct fimc_md { struct media_device media_dev; struct v4l2_device v4l2_dev; struct platform_device *pdev; + struct fimc_pinctrl { + struct pinctrl *pinctrl; + struct pinctrl_state *state_default; + struct pinctrl_state *state_idle; + } pinctl; bool user_subdev_api; spinlock_t slock; }; -- cgit v1.2.3 From fc39f46b54b600f053bf9bab757023344e97925e Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 14 Mar 2013 07:01:24 -0300 Subject: [media] V4L: Add MATRIX option to V4L2_CID_EXPOSURE_METERING control This patch adds a menu option to the V4L2_CID_EXPOSURE_METERING control for multi-zone metering. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 7 +++++++ drivers/media/v4l2-core/v4l2-ctrls.c | 1 + include/uapi/linux/v4l2-controls.h | 1 + 3 files changed, 9 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index c8eb6c222274..8d7a77928d49 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -3142,6 +3142,13 @@ giving priority to the center of the metered area. V4L2_EXPOSURE_METERING_SPOT  Measure only very small area at the center of the frame. + + V4L2_EXPOSURE_METERING_MATRIX  + A multi-zone metering. The light intensity is measured +in several points of the frame and the the results are combined. The +algorithm of the zones selection and their significance in calculating the +final value is device dependant. + diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index ec89fd16361c..ebb8e48619a2 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -234,6 +234,7 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Average", "Center Weighted", "Spot", + "Matrix", NULL }; static const char * const camera_auto_focus_range[] = { diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 7da22cec30cd..69bd5bb0d5af 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -658,6 +658,7 @@ enum v4l2_exposure_metering { V4L2_EXPOSURE_METERING_AVERAGE = 0, V4L2_EXPOSURE_METERING_CENTER_WEIGHTED = 1, V4L2_EXPOSURE_METERING_SPOT = 2, + V4L2_EXPOSURE_METERING_MATRIX = 3, }; #define V4L2_CID_SCENE_MODE (V4L2_CID_CAMERA_CLASS_BASE+26) -- cgit v1.2.3 From 7af1c0568d33339318c6710381921a3a3d40eebb Mon Sep 17 00:00:00 2001 From: Stratos Karafotis Date: Tue, 5 Mar 2013 22:06:29 +0000 Subject: cpufreq: conservative: Fix sampling_down_factor functionality sampling_down_factor tunable is unused since commit 8e677ce83bf41ba9c74e5b6d9ee60b07d4e5ed93 (4 years ago). This patch restores the original functionality and documents the tunable. Signed-off-by: Stratos Karafotis Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- Documentation/cpu-freq/governors.txt | 6 ++++++ drivers/cpufreq/cpufreq_conservative.c | 11 ++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index c7a2eb8450c2..4dfed30b7fda 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -191,6 +191,12 @@ governor but for the opposite direction. For example when set to its default value of '20' it means that if the CPU usage needs to be below 20% between samples to have the frequency decreased. +sampling_down_factor: similar functionality as in "ondemand" governor. +But in "conservative", it controls the rate at which the kernel makes +a decision on when to decrease the frequency while running in any +speed. Load for frequency increase is still evaluated every +sampling rate. + 3. The Governor Interface in the CPUfreq Core ============================================= diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 52f76b19a883..d746d6abbca8 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -36,9 +36,9 @@ static DEFINE_PER_CPU(struct cs_cpu_dbs_info_s, cs_cpu_dbs_info); /* * Every sampling_rate, we check, if current idle time is less than 20% - * (default), then we try to increase frequency Every sampling_rate * - * sampling_down_factor, we check, if current idle time is more than 80%, then - * we try to decrease frequency + * (default), then we try to increase frequency. Every sampling_rate * + * sampling_down_factor, we check, if current idle time is more than 80% + * (default), then we try to decrease frequency * * Any frequency increase takes it to the maximum frequency. Frequency reduction * happens at minimum steps of 5% (default) of maximum frequency @@ -81,6 +81,11 @@ static void cs_check_cpu(int cpu, unsigned int load) return; } + /* if sampling_down_factor is active break out early */ + if (++dbs_info->down_skip < cs_tuners->sampling_down_factor) + return; + dbs_info->down_skip = 0; + /* * The optimal frequency is the frequency that is the lowest that can * support the current CPU usage without triggering the up policy. To be -- cgit v1.2.3 From 6b24c9cb7d57a41f2ff193bcfcc2e1976bb4bbac Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Thu, 21 Mar 2013 16:33:04 -0400 Subject: tps65090: Update bindings for subnode This updates the DT documentation to reflect that the tps65090-charger should be represented as a child node of the tps65090 device itself. Signed-off-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- .../devicetree/bindings/power_supply/tps65090.txt | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/power_supply/tps65090.txt b/Documentation/devicetree/bindings/power_supply/tps65090.txt index 56370c724e35..8e5e0d3910df 100644 --- a/Documentation/devicetree/bindings/power_supply/tps65090.txt +++ b/Documentation/devicetree/bindings/power_supply/tps65090.txt @@ -1,23 +1,17 @@ TPS65090 Frontend PMU with Switchmode Charger Required Properties: --compatible: "ti,tps65090" --reg: I2C slave address --interrupts: the interrupt output to which this device connects +-compatible: "ti,tps65090-charger" Optional Properties: -ti,enable-low-current-chrg: Enables charging when a low current is detected while the default logic is to stop charging. -Example: +This node is a subnode of the tps65090 PMIC. - tps65090@48 { - compatible = "ti,tps65090"; - reg = <0x48>; - interrupts = <0 88 0x4>; +Example: + tps65090-charger { + compatible = "ti,tps65090-charger"; ti,enable-low-current-chrg; - - regulators { - ... - }; + }; -- cgit v1.2.3 From d55262c4d164759a8debe772da6c9b16059dec47 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 1 Apr 2013 11:23:38 -0700 Subject: workqueue: update sysfs interface to reflect NUMA awareness and a kernel param to disable NUMA affinity Unbound workqueues are now NUMA aware. Let's add some control knobs and update sysfs interface accordingly. * Add kernel param workqueue.numa_disable which disables NUMA affinity globally. * Replace sysfs file "pool_id" with "pool_ids" which contain node:pool_id pairs. This change is userland-visible but "pool_id" hasn't seen a release yet, so this is okay. * Add a new sysf files "numa" which can toggle NUMA affinity on individual workqueues. This is implemented as attrs->no_numa whichn is special in that it isn't part of a pool's attributes. It only affects how apply_workqueue_attrs() picks which pools to use. After "pool_ids" change, first_pwq() doesn't have any user left. Removed. Signed-off-by: Tejun Heo Reviewed-by: Lai Jiangshan --- Documentation/kernel-parameters.txt | 9 ++++ include/linux/workqueue.h | 5 +++ kernel/workqueue.c | 82 ++++++++++++++++++++++++++----------- 3 files changed, 73 insertions(+), 23 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 4609e81dbc37..c75ea0b8ec59 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -3222,6 +3222,15 @@ bytes respectively. Such letter suffixes can also be entirely omitted. or other driver-specific files in the Documentation/watchdog/ directory. + workqueue.disable_numa + By default, all work items queued to unbound + workqueues are affine to the NUMA nodes they're + issued on, which results in better behavior in + general. If NUMA affinity needs to be disabled for + whatever reason, this option can be used. Note + that this also can be controlled per-workqueue for + workqueues visible under /sys/bus/workqueue/. + x2apic_phys [X86-64,APIC] Use x2apic physical mode instead of default x2apic cluster mode on platforms supporting x2apic. diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 835d12b76960..717975639378 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -119,10 +119,15 @@ struct delayed_work { /* * A struct for workqueue attributes. This can be used to change * attributes of an unbound workqueue. + * + * Unlike other fields, ->no_numa isn't a property of a worker_pool. It + * only modifies how apply_workqueue_attrs() select pools and thus doesn't + * participate in pool hash calculations or equality comparisons. */ struct workqueue_attrs { int nice; /* nice level */ cpumask_var_t cpumask; /* allowed CPUs */ + bool no_numa; /* disable NUMA affinity */ }; static inline struct delayed_work *to_delayed_work(struct work_struct *work) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 57cd77de4a4f..729ac6a44860 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -268,6 +268,9 @@ static int wq_numa_tbl_len; /* highest possible NUMA node id + 1 */ static cpumask_var_t *wq_numa_possible_cpumask; /* possible CPUs of each node */ +static bool wq_disable_numa; +module_param_named(disable_numa, wq_disable_numa, bool, 0444); + static bool wq_numa_enabled; /* unbound NUMA affinity enabled */ /* buf for wq_update_unbound_numa_attrs(), protected by CPU hotplug exclusion */ @@ -516,21 +519,6 @@ static int worker_pool_assign_id(struct worker_pool *pool) return ret; } -/** - * first_pwq - return the first pool_workqueue of the specified workqueue - * @wq: the target workqueue - * - * This must be called either with wq->mutex held or sched RCU read locked. - * If the pwq needs to be used beyond the locking in effect, the caller is - * responsible for guaranteeing that the pwq stays online. - */ -static struct pool_workqueue *first_pwq(struct workqueue_struct *wq) -{ - assert_rcu_or_wq_mutex(wq); - return list_first_or_null_rcu(&wq->pwqs, struct pool_workqueue, - pwqs_node); -} - /** * unbound_pwq_by_node - return the unbound pool_workqueue for the given node * @wq: the target workqueue @@ -3114,16 +3102,21 @@ static struct device_attribute wq_sysfs_attrs[] = { __ATTR_NULL, }; -static ssize_t wq_pool_id_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t wq_pool_ids_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct workqueue_struct *wq = dev_to_wq(dev); - struct worker_pool *pool; - int written; + const char *delim = ""; + int node, written = 0; rcu_read_lock_sched(); - pool = first_pwq(wq)->pool; - written = scnprintf(buf, PAGE_SIZE, "%d\n", pool->id); + for_each_node(node) { + written += scnprintf(buf + written, PAGE_SIZE - written, + "%s%d:%d", delim, node, + unbound_pwq_by_node(wq, node)->pool->id); + delim = " "; + } + written += scnprintf(buf + written, PAGE_SIZE - written, "\n"); rcu_read_unlock_sched(); return written; @@ -3212,10 +3205,46 @@ static ssize_t wq_cpumask_store(struct device *dev, return ret ?: count; } +static ssize_t wq_numa_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct workqueue_struct *wq = dev_to_wq(dev); + int written; + + mutex_lock(&wq->mutex); + written = scnprintf(buf, PAGE_SIZE, "%d\n", + !wq->unbound_attrs->no_numa); + mutex_unlock(&wq->mutex); + + return written; +} + +static ssize_t wq_numa_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct workqueue_struct *wq = dev_to_wq(dev); + struct workqueue_attrs *attrs; + int v, ret; + + attrs = wq_sysfs_prep_attrs(wq); + if (!attrs) + return -ENOMEM; + + ret = -EINVAL; + if (sscanf(buf, "%d", &v) == 1) { + attrs->no_numa = !v; + ret = apply_workqueue_attrs(wq, attrs); + } + + free_workqueue_attrs(attrs); + return ret ?: count; +} + static struct device_attribute wq_sysfs_unbound_attrs[] = { - __ATTR(pool_id, 0444, wq_pool_id_show, NULL), + __ATTR(pool_ids, 0444, wq_pool_ids_show, NULL), __ATTR(nice, 0644, wq_nice_show, wq_nice_store), __ATTR(cpumask, 0644, wq_cpumask_show, wq_cpumask_store), + __ATTR(numa, 0644, wq_numa_show, wq_numa_store), __ATTR_NULL, }; @@ -3750,7 +3779,7 @@ static void free_unbound_pwq(struct pool_workqueue *pwq) static bool wq_calc_node_cpumask(const struct workqueue_attrs *attrs, int node, int cpu_going_down, cpumask_t *cpumask) { - if (!wq_numa_enabled) + if (!wq_numa_enabled || attrs->no_numa) goto use_dfl; /* does @node have any online CPUs @attrs wants? */ @@ -3951,6 +3980,8 @@ static void wq_update_unbound_numa(struct workqueue_struct *wq, int cpu, cpumask = target_attrs->cpumask; mutex_lock(&wq->mutex); + if (wq->unbound_attrs->no_numa) + goto out_unlock; copy_workqueue_attrs(target_attrs, wq->unbound_attrs); pwq = unbound_pwq_by_node(wq, node); @@ -4763,6 +4794,11 @@ static void __init wq_numa_init(void) if (num_possible_nodes() <= 1) return; + if (wq_disable_numa) { + pr_info("workqueue: NUMA affinity support disabled\n"); + return; + } + wq_update_unbound_numa_attrs_buf = alloc_workqueue_attrs(GFP_KERNEL); BUG_ON(!wq_update_unbound_numa_attrs_buf); -- cgit v1.2.3 From b2a63431b49f4ef4df1ea5ddb799af48cfa349fc Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 1 Apr 2013 12:57:41 +0000 Subject: cpufreq: cpu0: Fix mistake in Documentation example "clock-latency" is incorrectly written as "transition-latency" in an example present in Documentation of cpufreq-cpu0. Fix it. Signed-off-by: Viresh Kumar Acked-by: Shawn Guo Signed-off-by: Rafael J. Wysocki --- Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt index 4416ccc33472..051f764bedb8 100644 --- a/Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-cpu0.txt @@ -32,7 +32,7 @@ cpus { 396000 950000 198000 850000 >; - transition-latency = <61036>; /* two CLK32 periods */ + clock-latency = <61036>; /* two CLK32 periods */ }; cpu@1 { -- cgit v1.2.3 From 3a7818e62751a4dda40d6ea0bdb0022d56aee3c1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 1 Apr 2013 12:57:42 +0000 Subject: cpufreq: Documentation: Fix cpufreq_frequency_table name At few places in documentation cpufreq_frequency_table is written as cpufreq_freq_table. Fix these. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- Documentation/cpu-freq/cpu-drivers.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/cpu-freq/cpu-drivers.txt b/Documentation/cpu-freq/cpu-drivers.txt index 72f70b16d299..7c7721d042ff 100644 --- a/Documentation/cpu-freq/cpu-drivers.txt +++ b/Documentation/cpu-freq/cpu-drivers.txt @@ -184,10 +184,10 @@ the reference implementation in drivers/cpufreq/longrun.c As most cpufreq processors only allow for being set to a few specific frequencies, a "frequency table" with some functions might assist in some work of the processor driver. Such a "frequency table" consists -of an array of struct cpufreq_freq_table entries, with any value in +of an array of struct cpufreq_frequency_table entries, with any value in "index" you want to use, and the corresponding frequency in "frequency". At the end of the table, you need to add a -cpufreq_freq_table entry with frequency set to CPUFREQ_TABLE_END. And +cpufreq_frequency_table entry with frequency set to CPUFREQ_TABLE_END. And if you want to skip one entry in the table, set the frequency to CPUFREQ_ENTRY_INVALID. The entries don't need to be in ascending order. -- cgit v1.2.3 From eb2f50ff93f08c8001d0d7e1148948ba80989aaf Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 1 Apr 2013 12:57:48 +0000 Subject: cpufreq: drivers: Remove unnecessary assignments of policy-> members Some assignments of policy-> min/max/cur/cpuinfo.min_freq/cpuinfo.max_freq aren't required as part of it is done by cpufreq driver or cpufreq core. Remove them. At some places we merge multiple lines together too. Signed-off-by: Viresh Kumar Acked-by: Sekhar Nori Signed-off-by: Rafael J. Wysocki --- Documentation/cpu-freq/cpu-drivers.txt | 5 +++-- arch/arm/mach-davinci/cpufreq.c | 21 ++++++++------------- arch/arm/mach-imx/cpufreq.c | 3 --- arch/sh/kernel/cpufreq.c | 9 +++------ drivers/cpufreq/cpufreq-nforce2.c | 6 ++---- drivers/cpufreq/omap-cpufreq.c | 4 +--- 6 files changed, 17 insertions(+), 31 deletions(-) (limited to 'Documentation') diff --git a/Documentation/cpu-freq/cpu-drivers.txt b/Documentation/cpu-freq/cpu-drivers.txt index 7c7721d042ff..a3585eac83b6 100644 --- a/Documentation/cpu-freq/cpu-drivers.txt +++ b/Documentation/cpu-freq/cpu-drivers.txt @@ -108,8 +108,9 @@ policy->governor must contain the "default policy" for cpufreq_driver.target is called with these values. -For setting some of these values, the frequency table helpers might be -helpful. See the section 2 for more information on them. +For setting some of these values (cpuinfo.min[max]_freq, policy->min[max]), the +frequency table helpers might be helpful. See the section 2 for more information +on them. SMP systems normally have same clock source for a group of cpus. For these the .init() would be called only once for the first online cpu. Here the .init() diff --git a/arch/arm/mach-davinci/cpufreq.c b/arch/arm/mach-davinci/cpufreq.c index 8fb0c2ac227e..7c2e943c5500 100644 --- a/arch/arm/mach-davinci/cpufreq.c +++ b/arch/arm/mach-davinci/cpufreq.c @@ -137,21 +137,16 @@ static int davinci_cpu_init(struct cpufreq_policy *policy) return result; } - policy->cur = policy->min = policy->max = davinci_getspeed(0); - - if (freq_table) { - result = cpufreq_frequency_table_cpuinfo(policy, freq_table); - if (!result) - cpufreq_frequency_table_get_attr(freq_table, - policy->cpu); - } else { - policy->cpuinfo.min_freq = policy->min; - policy->cpuinfo.max_freq = policy->max; + policy->cur = davinci_getspeed(0); + + result = cpufreq_frequency_table_cpuinfo(policy, freq_table); + if (result) { + pr_err("%s: cpufreq_frequency_table_cpuinfo() failed", + __func__); + return result; } - policy->min = policy->cpuinfo.min_freq; - policy->max = policy->cpuinfo.max_freq; - policy->cur = davinci_getspeed(0); + cpufreq_frequency_table_get_attr(freq_table, policy->cpu); /* * Time measurement across the target() function yields ~1500-1800us diff --git a/arch/arm/mach-imx/cpufreq.c b/arch/arm/mach-imx/cpufreq.c index cfce5e3f67f5..387dc4cceca2 100644 --- a/arch/arm/mach-imx/cpufreq.c +++ b/arch/arm/mach-imx/cpufreq.c @@ -144,14 +144,11 @@ static int mxc_cpufreq_init(struct cpufreq_policy *policy) imx_freq_table[i].frequency = CPUFREQ_TABLE_END; policy->cur = clk_get_rate(cpu_clk) / 1000; - policy->min = policy->cpuinfo.min_freq = cpu_freq_khz_min; - policy->max = policy->cpuinfo.max_freq = cpu_freq_khz_max; /* Manual states, that PLL stabilizes in two CLK32 periods */ policy->cpuinfo.transition_latency = 2 * NANOSECOND / CLK32_FREQ; ret = cpufreq_frequency_table_cpuinfo(policy, imx_freq_table); - if (ret < 0) { printk(KERN_ERR "%s: failed to register i.MXC CPUfreq with error code %d\n", __func__, ret); diff --git a/arch/sh/kernel/cpufreq.c b/arch/sh/kernel/cpufreq.c index 0fdf64b759c8..88c8feedf785 100644 --- a/arch/sh/kernel/cpufreq.c +++ b/arch/sh/kernel/cpufreq.c @@ -116,7 +116,7 @@ static int sh_cpufreq_cpu_init(struct cpufreq_policy *policy) return PTR_ERR(cpuclk); } - policy->cur = policy->min = policy->max = sh_cpufreq_get(cpu); + policy->cur = sh_cpufreq_get(cpu); freq_table = cpuclk->nr_freqs ? cpuclk->freq_table : NULL; if (freq_table) { @@ -129,15 +129,12 @@ static int sh_cpufreq_cpu_init(struct cpufreq_policy *policy) dev_notice(dev, "no frequency table found, falling back " "to rate rounding.\n"); - policy->cpuinfo.min_freq = + policy->min = policy->cpuinfo.min_freq = (clk_round_rate(cpuclk, 1) + 500) / 1000; - policy->cpuinfo.max_freq = + policy->max = policy->cpuinfo.max_freq = (clk_round_rate(cpuclk, ~0UL) + 500) / 1000; } - policy->min = policy->cpuinfo.min_freq; - policy->max = policy->cpuinfo.max_freq; - policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; dev_info(dev, "CPU Frequencies - Minimum %u.%03u MHz, " diff --git a/drivers/cpufreq/cpufreq-nforce2.c b/drivers/cpufreq/cpufreq-nforce2.c index 224a4787ecc2..af1542d41440 100644 --- a/drivers/cpufreq/cpufreq-nforce2.c +++ b/drivers/cpufreq/cpufreq-nforce2.c @@ -359,12 +359,10 @@ static int nforce2_cpu_init(struct cpufreq_policy *policy) min_fsb = NFORCE2_MIN_FSB; /* cpuinfo and default policy values */ - policy->cpuinfo.min_freq = min_fsb * fid * 100; - policy->cpuinfo.max_freq = max_fsb * fid * 100; + policy->min = policy->cpuinfo.min_freq = min_fsb * fid * 100; + policy->max = policy->cpuinfo.max_freq = max_fsb * fid * 100; policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; policy->cur = nforce2_get(policy->cpu); - policy->min = policy->cpuinfo.min_freq; - policy->max = policy->cpuinfo.max_freq; return 0; } diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index b610edd820b1..ad7549c13ed2 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -177,7 +177,7 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) goto fail_ck; } - policy->cur = policy->min = policy->max = omap_getspeed(policy->cpu); + policy->cur = omap_getspeed(policy->cpu); if (!freq_table) result = opp_init_cpufreq_table(mpu_dev, &freq_table); @@ -196,8 +196,6 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) cpufreq_frequency_table_get_attr(freq_table, policy->cpu); - policy->min = policy->cpuinfo.min_freq; - policy->max = policy->cpuinfo.max_freq; policy->cur = omap_getspeed(policy->cpu); /* -- cgit v1.2.3 From 8a67f0ef2b684b34162d39e8f436fe39e9635a19 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 1 Apr 2013 12:57:49 +0000 Subject: cpufreq: ARM big LITTLE: Add generic cpufreq driver and its DT glue big LITTLE is ARM's new Architecture focussing power/performance needs of modern world. More information about big LITTLE can be found here: http://www.arm.com/products/processors/technologies/biglittleprocessing.php http://lwn.net/Articles/481055/ In order to keep cpufreq support for all big LITTLE platforms simple/generic, this patch tries to add a generic cpufreq driver layer for all big LITTLE platforms. The driver is divided into two parts: - Core driver: Generic and shared across all big LITTLE SoC's - Glue drivers: Per platform drivers providing ops to the core driver This patch adds in a generic glue driver which would extract information from Device Tree. Future SoC's can either reuse the DT glue or write their own depending on the need. Signed-off-by: Sudeep KarkadaNagesha Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- .../bindings/cpufreq/arm_big_little_dt.txt | 65 +++++ MAINTAINERS | 11 + drivers/cpufreq/Kconfig.arm | 12 + drivers/cpufreq/Makefile | 5 + drivers/cpufreq/arm_big_little.c | 282 +++++++++++++++++++++ drivers/cpufreq/arm_big_little.h | 40 +++ drivers/cpufreq/arm_big_little_dt.c | 92 +++++++ 7 files changed, 507 insertions(+) create mode 100644 Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt create mode 100644 drivers/cpufreq/arm_big_little.c create mode 100644 drivers/cpufreq/arm_big_little.h create mode 100644 drivers/cpufreq/arm_big_little_dt.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt b/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt new file mode 100644 index 000000000000..0715695e94a9 --- /dev/null +++ b/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt @@ -0,0 +1,65 @@ +Generic ARM big LITTLE cpufreq driver's DT glue +----------------------------------------------- + +This is DT specific glue layer for generic cpufreq driver for big LITTLE +systems. + +Both required and optional properties listed below must be defined +under node /cpus/cpu@x. Where x is the first cpu inside a cluster. + +FIXME: Cpus should boot in the order specified in DT and all cpus for a cluster +must be present contiguously. Generic DT driver will check only node 'x' for +cpu:x. + +Required properties: +- operating-points: Refer to Documentation/devicetree/bindings/power/opp.txt + for details + +Optional properties: +- clock-latency: Specify the possible maximum transition latency for clock, + in unit of nanoseconds. + +Examples: + +cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "arm,cortex-a15"; + reg = <0>; + next-level-cache = <&L2>; + operating-points = < + /* kHz uV */ + 792000 1100000 + 396000 950000 + 198000 850000 + >; + clock-latency = <61036>; /* two CLK32 periods */ + }; + + cpu@1 { + compatible = "arm,cortex-a15"; + reg = <1>; + next-level-cache = <&L2>; + }; + + cpu@100 { + compatible = "arm,cortex-a7"; + reg = <100>; + next-level-cache = <&L2>; + operating-points = < + /* kHz uV */ + 792000 950000 + 396000 750000 + 198000 450000 + >; + clock-latency = <61036>; /* two CLK32 periods */ + }; + + cpu@101 { + compatible = "arm,cortex-a7"; + reg = <101>; + next-level-cache = <&L2>; + }; +}; diff --git a/MAINTAINERS b/MAINTAINERS index 74e58a4d035b..be739837d6c3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2206,6 +2206,17 @@ S: Maintained F: drivers/cpufreq/ F: include/linux/cpufreq.h +CPU FREQUENCY DRIVERS - ARM BIG LITTLE +M: Viresh Kumar +M: Sudeep KarkadaNagesha +L: cpufreq@vger.kernel.org +L: linux-pm@vger.kernel.org +W: http://www.arm.com/products/processors/technologies/biglittleprocessing.php +S: Maintained +F: drivers/cpufreq/arm_big_little.h +F: drivers/cpufreq/arm_big_little.c +F: drivers/cpufreq/arm_big_little_dt.c + CPUID/MSR DRIVER M: "H. Peter Anvin" S: Maintained diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 030ddf6dd3f1..87b7e48698d0 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -2,6 +2,18 @@ # ARM CPU Frequency scaling drivers # +config ARM_BIG_LITTLE_CPUFREQ + tristate + depends on ARM_CPU_TOPOLOGY + +config ARM_DT_BL_CPUFREQ + tristate "Generic ARM big LITTLE CPUfreq driver probed via DT" + select ARM_BIG_LITTLE_CPUFREQ + depends on OF && HAVE_CLK + help + This enables the Generic CPUfreq driver for ARM big.LITTLE platform. + This gets frequency tables from DT. + config ARM_OMAP2PLUS_CPUFREQ bool "TI OMAP2+" depends on ARCH_OMAP2PLUS diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 863fd1865d45..ba9a3e1b1e69 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -44,6 +44,11 @@ obj-$(CONFIG_X86_INTEL_PSTATE) += intel_pstate.o ################################################################################## # ARM SoC drivers +obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ) += arm_big_little.o +# big LITTLE per platform glues. Keep DT_BL_CPUFREQ as the last entry in all big +# LITTLE drivers, so that it is probed last. +obj-$(CONFIG_ARM_DT_BL_CPUFREQ) += arm_big_little_dt.o + obj-$(CONFIG_UX500_SOC_DB8500) += dbx500-cpufreq.o obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c new file mode 100644 index 000000000000..1d29d1acbda7 --- /dev/null +++ b/drivers/cpufreq/arm_big_little.c @@ -0,0 +1,282 @@ +/* + * ARM big.LITTLE Platforms CPUFreq support + * + * Copyright (C) 2013 ARM Ltd. + * Sudeep KarkadaNagesha + * + * Copyright (C) 2013 Linaro. + * Viresh Kumar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "arm_big_little.h" + +/* Currently we support only two clusters */ +#define MAX_CLUSTERS 2 + +static struct cpufreq_arm_bL_ops *arm_bL_ops; +static struct clk *clk[MAX_CLUSTERS]; +static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS]; +static atomic_t cluster_usage[MAX_CLUSTERS] = {ATOMIC_INIT(0), ATOMIC_INIT(0)}; + +static int cpu_to_cluster(int cpu) +{ + return topology_physical_package_id(cpu); +} + +static unsigned int bL_cpufreq_get(unsigned int cpu) +{ + u32 cur_cluster = cpu_to_cluster(cpu); + + return clk_get_rate(clk[cur_cluster]) / 1000; +} + +/* Validate policy frequency range */ +static int bL_cpufreq_verify_policy(struct cpufreq_policy *policy) +{ + u32 cur_cluster = cpu_to_cluster(policy->cpu); + + return cpufreq_frequency_table_verify(policy, freq_table[cur_cluster]); +} + +/* Set clock frequency */ +static int bL_cpufreq_set_target(struct cpufreq_policy *policy, + unsigned int target_freq, unsigned int relation) +{ + struct cpufreq_freqs freqs; + u32 cpu = policy->cpu, freq_tab_idx, cur_cluster; + int ret = 0; + + cur_cluster = cpu_to_cluster(policy->cpu); + + freqs.old = bL_cpufreq_get(policy->cpu); + + /* Determine valid target frequency using freq_table */ + cpufreq_frequency_table_target(policy, freq_table[cur_cluster], + target_freq, relation, &freq_tab_idx); + freqs.new = freq_table[cur_cluster][freq_tab_idx].frequency; + + freqs.cpu = policy->cpu; + + pr_debug("%s: cpu: %d, cluster: %d, oldfreq: %d, target freq: %d, new freq: %d\n", + __func__, cpu, cur_cluster, freqs.old, target_freq, + freqs.new); + + if (freqs.old == freqs.new) + return 0; + + for_each_cpu(freqs.cpu, policy->cpus) + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + ret = clk_set_rate(clk[cur_cluster], freqs.new * 1000); + if (ret) { + pr_err("clk_set_rate failed: %d\n", ret); + return ret; + } + + policy->cur = freqs.new; + + for_each_cpu(freqs.cpu, policy->cpus) + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return ret; +} + +static void put_cluster_clk_and_freq_table(struct device *cpu_dev) +{ + u32 cluster = cpu_to_cluster(cpu_dev->id); + + if (!atomic_dec_return(&cluster_usage[cluster])) { + clk_put(clk[cluster]); + opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]); + dev_dbg(cpu_dev, "%s: cluster: %d\n", __func__, cluster); + } +} + +static int get_cluster_clk_and_freq_table(struct device *cpu_dev) +{ + u32 cluster = cpu_to_cluster(cpu_dev->id); + char name[14] = "cpu-cluster."; + int ret; + + if (atomic_inc_return(&cluster_usage[cluster]) != 1) + return 0; + + ret = arm_bL_ops->init_opp_table(cpu_dev); + if (ret) { + dev_err(cpu_dev, "%s: init_opp_table failed, cpu: %d, err: %d\n", + __func__, cpu_dev->id, ret); + goto atomic_dec; + } + + ret = opp_init_cpufreq_table(cpu_dev, &freq_table[cluster]); + if (ret) { + dev_err(cpu_dev, "%s: failed to init cpufreq table, cpu: %d, err: %d\n", + __func__, cpu_dev->id, ret); + goto atomic_dec; + } + + name[12] = cluster + '0'; + clk[cluster] = clk_get_sys(name, NULL); + if (!IS_ERR(clk[cluster])) { + dev_dbg(cpu_dev, "%s: clk: %p & freq table: %p, cluster: %d\n", + __func__, clk[cluster], freq_table[cluster], + cluster); + return 0; + } + + dev_err(cpu_dev, "%s: Failed to get clk for cpu: %d, cluster: %d\n", + __func__, cpu_dev->id, cluster); + ret = PTR_ERR(clk[cluster]); + opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]); + +atomic_dec: + atomic_dec(&cluster_usage[cluster]); + dev_err(cpu_dev, "%s: Failed to get data for cluster: %d\n", __func__, + cluster); + return ret; +} + +/* Per-CPU initialization */ +static int bL_cpufreq_init(struct cpufreq_policy *policy) +{ + u32 cur_cluster = cpu_to_cluster(policy->cpu); + struct device *cpu_dev; + int ret; + + cpu_dev = get_cpu_device(policy->cpu); + if (!cpu_dev) { + pr_err("%s: failed to get cpu%d device\n", __func__, + policy->cpu); + return -ENODEV; + } + + ret = get_cluster_clk_and_freq_table(cpu_dev); + if (ret) + return ret; + + ret = cpufreq_frequency_table_cpuinfo(policy, freq_table[cur_cluster]); + if (ret) { + dev_err(cpu_dev, "CPU %d, cluster: %d invalid freq table\n", + policy->cpu, cur_cluster); + put_cluster_clk_and_freq_table(cpu_dev); + return ret; + } + + cpufreq_frequency_table_get_attr(freq_table[cur_cluster], policy->cpu); + + if (arm_bL_ops->get_transition_latency) + policy->cpuinfo.transition_latency = + arm_bL_ops->get_transition_latency(cpu_dev); + else + policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; + + policy->cur = bL_cpufreq_get(policy->cpu); + + cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu)); + + dev_info(cpu_dev, "CPU %d initialized\n", policy->cpu); + return 0; +} + +static int bL_cpufreq_exit(struct cpufreq_policy *policy) +{ + struct device *cpu_dev; + + cpu_dev = get_cpu_device(policy->cpu); + if (!cpu_dev) { + pr_err("%s: failed to get cpu%d device\n", __func__, + policy->cpu); + return -ENODEV; + } + + put_cluster_clk_and_freq_table(cpu_dev); + dev_dbg(cpu_dev, "%s: Exited, cpu: %d\n", __func__, policy->cpu); + + return 0; +} + +/* Export freq_table to sysfs */ +static struct freq_attr *bL_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver bL_cpufreq_driver = { + .name = "arm-big-little", + .flags = CPUFREQ_STICKY, + .verify = bL_cpufreq_verify_policy, + .target = bL_cpufreq_set_target, + .get = bL_cpufreq_get, + .init = bL_cpufreq_init, + .exit = bL_cpufreq_exit, + .have_multiple_policies = true, + .attr = bL_cpufreq_attr, +}; + +int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops) +{ + int ret; + + if (arm_bL_ops) { + pr_debug("%s: Already registered: %s, exiting\n", __func__, + arm_bL_ops->name); + return -EBUSY; + } + + if (!ops || !strlen(ops->name) || !ops->init_opp_table) { + pr_err("%s: Invalid arm_bL_ops, exiting\n", __func__); + return -ENODEV; + } + + arm_bL_ops = ops; + + ret = cpufreq_register_driver(&bL_cpufreq_driver); + if (ret) { + pr_info("%s: Failed registering platform driver: %s, err: %d\n", + __func__, ops->name, ret); + arm_bL_ops = NULL; + } else { + pr_info("%s: Registered platform driver: %s\n", __func__, + ops->name); + } + + return ret; +} +EXPORT_SYMBOL_GPL(bL_cpufreq_register); + +void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops) +{ + if (arm_bL_ops != ops) { + pr_err("%s: Registered with: %s, can't unregister, exiting\n", + __func__, arm_bL_ops->name); + return; + } + + cpufreq_unregister_driver(&bL_cpufreq_driver); + pr_info("%s: Un-registered platform driver: %s\n", __func__, + arm_bL_ops->name); + arm_bL_ops = NULL; +} +EXPORT_SYMBOL_GPL(bL_cpufreq_unregister); diff --git a/drivers/cpufreq/arm_big_little.h b/drivers/cpufreq/arm_big_little.h new file mode 100644 index 000000000000..70f18fc12d4a --- /dev/null +++ b/drivers/cpufreq/arm_big_little.h @@ -0,0 +1,40 @@ +/* + * ARM big.LITTLE platform's CPUFreq header file + * + * Copyright (C) 2013 ARM Ltd. + * Sudeep KarkadaNagesha + * + * Copyright (C) 2013 Linaro. + * Viresh Kumar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef CPUFREQ_ARM_BIG_LITTLE_H +#define CPUFREQ_ARM_BIG_LITTLE_H + +#include +#include +#include + +struct cpufreq_arm_bL_ops { + char name[CPUFREQ_NAME_LEN]; + int (*get_transition_latency)(struct device *cpu_dev); + + /* + * This must set opp table for cpu_dev in a similar way as done by + * of_init_opp_table(). + */ + int (*init_opp_table)(struct device *cpu_dev); +}; + +int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops); +void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops); + +#endif /* CPUFREQ_ARM_BIG_LITTLE_H */ diff --git a/drivers/cpufreq/arm_big_little_dt.c b/drivers/cpufreq/arm_big_little_dt.c new file mode 100644 index 000000000000..452ff46f20b7 --- /dev/null +++ b/drivers/cpufreq/arm_big_little_dt.c @@ -0,0 +1,92 @@ +/* + * Generic big.LITTLE CPUFreq Interface driver + * + * It provides necessary ops to arm_big_little cpufreq driver and gets + * Frequency information from Device Tree. Freq table in DT must be in KHz. + * + * Copyright (C) 2013 Linaro. + * Viresh Kumar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include "arm_big_little.h" + +static int dt_init_opp_table(struct device *cpu_dev) +{ + struct device_node *np = NULL; + int count = 0, ret; + + for_each_child_of_node(of_find_node_by_path("/cpus"), np) { + if (count++ != cpu_dev->id) + continue; + if (!of_get_property(np, "operating-points", NULL)) + return -ENODATA; + + cpu_dev->of_node = np; + + ret = of_init_opp_table(cpu_dev); + if (ret) + return ret; + + return 0; + } + + return -ENODEV; +} + +static int dt_get_transition_latency(struct device *cpu_dev) +{ + struct device_node *np = NULL; + u32 transition_latency = CPUFREQ_ETERNAL; + int count = 0; + + for_each_child_of_node(of_find_node_by_path("/cpus"), np) { + if (count++ != cpu_dev->id) + continue; + + of_property_read_u32(np, "clock-latency", &transition_latency); + return 0; + } + + return -ENODEV; +} + +static struct cpufreq_arm_bL_ops dt_bL_ops = { + .name = "dt-bl", + .get_transition_latency = dt_get_transition_latency, + .init_opp_table = dt_init_opp_table, +}; + +static int generic_bL_init(void) +{ + return bL_cpufreq_register(&dt_bL_ops); +} +module_init(generic_bL_init); + +static void generic_bL_exit(void) +{ + return bL_cpufreq_unregister(&dt_bL_ops); +} +module_exit(generic_bL_exit); + +MODULE_AUTHOR("Viresh Kumar "); +MODULE_DESCRIPTION("Generic ARM big LITTLE cpufreq driver via DT"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 83d435dd6e24b17955f91480f633bb39f0a7c2b4 Mon Sep 17 00:00:00 2001 From: Xie XiuQi Date: Fri, 29 Mar 2013 09:43:47 +0800 Subject: Fix example error_injection_tool I got a "sched_setaffinity:: Invalid argument" error when using err_injection_tool to inject error on a system with over 32 cpus. Error information when injecting an error on a system with over 32 cpus: $ ./err_injection_tool -i /sys/devices/system/cpu/cpu0/err_inject//err_type_info Begine at Tue Mar 26 11:20:08 2013 Configurations: On cpu32: loop=10, interval=5(s) err_type_info=4101,err_struct_info=95 Error sched_setaffinity:: Invalid argument All done This because there is overflow when calculating the cpumask: the type of (1< 31, (1< Signed-off-by: Tony Luck --- Documentation/ia64/err_inject.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/ia64/err_inject.txt b/Documentation/ia64/err_inject.txt index 223e4f0582d0..9f651c181429 100644 --- a/Documentation/ia64/err_inject.txt +++ b/Documentation/ia64/err_inject.txt @@ -882,7 +882,7 @@ int err_inj() cpu=parameters[i].cpu; k = cpu%64; j = cpu/64; - mask[j]=1< Date: Mon, 25 Mar 2013 19:14:16 +0800 Subject: Add size restriction to the kdump documentation In efi_init() memory aligns in IA64_GRANULE_SIZE(16M). If set "crashkernel=1024M-:600M" and use sparse memory model, when crash kernel booting it changes [128M-728M] to [128M-720M]. But initrd memory is in [709M-727M], and virt_addr_valid() *can not* check the invalid pages when freeing initrd memory, because there are some pages missed at the end of the section, and this causes error. ... Unpacking initramfs... Freeing initrd memory: 19648kB freed BUG: Bad page state in process swapper pfn:02d00 page:e0000000102dd800 flags:(null) count:0 mapcount:1 mapping:(null) index:0 Call Trace: [] show_stack+0x80/0xa0 sp=e000000021e8fbd0 bsp=e000000021e81360 [] dump_stack+0x30/0x50 sp=e000000021e8fda0 bsp=e000000021e81348 [] bad_page+0x280/0x380 sp=e000000021e8fda0 bsp=e000000021e81308 [] free_hot_cold_page+0x3a0/0x5c0 sp=e000000021e8fda0 bsp=e000000021e812a0 [] free_hot_page+0x30/0x60 sp=e000000021e8fda0 bsp=e000000021e81280 [] __free_pages+0xb0/0xe0 sp=e000000021e8fda0 bsp=e000000021e81258 [] free_pages+0xa0/0xc0 sp=e000000021e8fda0 bsp=e000000021e81230 [] free_initrd_mem+0x230/0x290 sp=e000000021e8fda0 bsp=e000000021e811d8 [] populate_rootfs+0x1c0/0x280 sp=e000000021e8fdb0 bsp=e000000021e811a0 [] do_one_initcall+0x3b0/0x3e0 sp=e000000021e8fdb0 bsp=e000000021e81158 [] kernel_init+0x3f0/0x4b0 sp=e000000021e8fdb0 bsp=e000000021e81108 [] kernel_thread_helper+0xd0/0x100 sp=e000000021e8fe30 bsp=e000000021e810e0 [] start_kernel_thread+0x20/0x40 sp=e000000021e8fe30 bsp=e000000021e810e0 ... In "http://marc.info/?l=linux-arch&m=136147092429314&w=2" Tony said: "Perhaps in Documentation/kdump/kdump.txt (which the crashkernel entry in kernel-parameters.txt points at). The ia64 section of kdump.txt notes that the start address will be rounded up to a GRANULE boundary, but doesn't talk about restrictions on the size." This patch add size restriction to the documentation of kdump. Signed-off-by: Xishi Qiu Signed-off-by: Tony Luck --- Documentation/kdump/kdump.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/kdump/kdump.txt b/Documentation/kdump/kdump.txt index 13f1aa09b938..9c7fd988e299 100644 --- a/Documentation/kdump/kdump.txt +++ b/Documentation/kdump/kdump.txt @@ -297,6 +297,7 @@ Boot into System Kernel On ia64, 256M@256M is a generous value that typically works. The region may be automatically placed on ia64, see the dump-capture kernel config option notes above. + If use sparse memory, the size should be rounded to GRANULE boundaries. On s390x, typically use "crashkernel=xxM". The value of xx is dependent on the memory consumption of the kdump system. In general this is not -- cgit v1.2.3 From 47be16b6683b86653545bf98f6f57019bb99969c Mon Sep 17 00:00:00 2001 From: Ludovic Desroches Date: Fri, 29 Mar 2013 14:54:00 +0000 Subject: iio: at91_adc: add low and high res support at91 adc offers the choice between two resolutions: low and high. The low and high resolution values depends on adc IP version, as many IP properties have been exposed through device tree, these settings have also been added to the dt bindings. Signed-off-by: Ludovic Desroches Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/arm/atmel-adc.txt | 11 ++++ drivers/iio/adc/at91_adc.c | 74 ++++++++++++++++++++-- 2 files changed, 81 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/atmel-adc.txt b/Documentation/devicetree/bindings/arm/atmel-adc.txt index c63097d6afeb..fd2d69ee2876 100644 --- a/Documentation/devicetree/bindings/arm/atmel-adc.txt +++ b/Documentation/devicetree/bindings/arm/atmel-adc.txt @@ -14,9 +14,17 @@ Required properties: - atmel,adc-status-register: Offset of the Interrupt Status Register - atmel,adc-trigger-register: Offset of the Trigger Register - atmel,adc-vref: Reference voltage in millivolts for the conversions + - atmel,adc-res: List of resolution in bits supported by the ADC. List size + must be two at least. + - atmel,adc-res-names: Contains one identifier string for each resolution + in atmel,adc-res property. "lowres" and "highres" + identifiers are required. Optional properties: - atmel,adc-use-external: Boolean to enable of external triggers + - atmel,adc-use-res: String corresponding to an identifier from + atmel,adc-res-names property. If not specified, the highest + resolution will be used. Optional trigger Nodes: - Required properties: @@ -40,6 +48,9 @@ adc0: adc@fffb0000 { atmel,adc-trigger-register = <0x08>; atmel,adc-use-external; atmel,adc-vref = <3300>; + atmel,adc-res = <8 10>; + atmel,adc-res-names = "lowres", "highres"; + atmel,adc-use-res = "lowres"; trigger@0 { trigger-name = "external-rising"; diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 6fc43c15f028..3fb3fe48a98c 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -57,6 +57,8 @@ struct at91_adc_state { u32 trigger_number; bool use_external; u32 vref_mv; + u32 res; /* resolution used for convertions */ + bool low_res; /* the resolution corresponds to the lowest one */ wait_queue_head_t wq_data_avail; }; @@ -138,7 +140,7 @@ static int at91_adc_channel_init(struct iio_dev *idev) chan->channel = bit; chan->scan_index = idx; chan->scan_type.sign = 'u'; - chan->scan_type.realbits = 10; + chan->scan_type.realbits = st->res; chan->scan_type.storagebits = 16; chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); @@ -372,6 +374,59 @@ static int at91_adc_read_raw(struct iio_dev *idev, return -EINVAL; } +static int at91_adc_of_get_resolution(struct at91_adc_state *st, + struct platform_device *pdev) +{ + struct iio_dev *idev = iio_priv_to_dev(st); + struct device_node *np = pdev->dev.of_node; + int count, i, ret = 0; + char *res_name, *s; + u32 *resolutions; + + count = of_property_count_strings(np, "atmel,adc-res-names"); + if (count < 2) { + dev_err(&idev->dev, "You must specified at least two resolution names for " + "adc-res-names property in the DT\n"); + return count; + } + + resolutions = kmalloc(count * sizeof(*resolutions), GFP_KERNEL); + if (!resolutions) + return -ENOMEM; + + if (of_property_read_u32_array(np, "atmel,adc-res", resolutions, count)) { + dev_err(&idev->dev, "Missing adc-res property in the DT.\n"); + ret = -ENODEV; + goto ret; + } + + if (of_property_read_string(np, "atmel,adc-use-res", (const char **)&res_name)) + res_name = "highres"; + + for (i = 0; i < count; i++) { + if (of_property_read_string_index(np, "atmel,adc-res-names", i, (const char **)&s)) + continue; + + if (strcmp(res_name, s)) + continue; + + st->res = resolutions[i]; + if (!strcmp(res_name, "lowres")) + st->low_res = true; + else + st->low_res = false; + + dev_info(&idev->dev, "Resolution used: %u bits\n", st->res); + goto ret; + } + + dev_err(&idev->dev, "There is no resolution for %s\n", res_name); + +ret: + kfree(resolutions); + return ret; +} + static int at91_adc_probe_dt(struct at91_adc_state *st, struct platform_device *pdev) { @@ -415,6 +470,10 @@ static int at91_adc_probe_dt(struct at91_adc_state *st, } st->vref_mv = prop; + ret = at91_adc_of_get_resolution(st, pdev); + if (ret) + goto error_ret; + st->registers = devm_kzalloc(&idev->dev, sizeof(struct at91_adc_reg_desc), GFP_KERNEL); @@ -628,9 +687,16 @@ static int at91_adc_probe(struct platform_device *pdev) */ ticks = round_up((st->startup_time * adc_clk / 1000000) - 1, 8) / 8; - at91_adc_writel(st, AT91_ADC_MR, - (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | - (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); + + if (st->low_res) + at91_adc_writel(st, AT91_ADC_MR, + AT91_ADC_LOWRES | + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); + else + at91_adc_writel(st, AT91_ADC_MR, + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); /* Setup the ADC channels available on the board */ ret = at91_adc_channel_init(idev); -- cgit v1.2.3 From e748783c55f074046134d2ef15a6e04dd467ecfc Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 29 Mar 2013 14:54:00 +0000 Subject: iio: at91_adc: add sleep mode support The sleep mode will allow to put the adc in sleep between conversion. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Ludovic Desroches Acked-by: Maxime Ripard Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/arm/atmel-adc.txt | 1 + drivers/iio/adc/at91_adc.c | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/atmel-adc.txt b/Documentation/devicetree/bindings/arm/atmel-adc.txt index fd2d69ee2876..3a05492acdaf 100644 --- a/Documentation/devicetree/bindings/arm/atmel-adc.txt +++ b/Documentation/devicetree/bindings/arm/atmel-adc.txt @@ -25,6 +25,7 @@ Optional properties: - atmel,adc-use-res: String corresponding to an identifier from atmel,adc-res-names property. If not specified, the highest resolution will be used. + - atmel,adc-sleep-mode: Boolean to enable sleep mode when no conversion Optional trigger Nodes: - Required properties: diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 3fb3fe48a98c..7295bc5280bd 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -52,6 +52,7 @@ struct at91_adc_state { void __iomem *reg_base; struct at91_adc_reg_desc *registers; u8 startup_time; + bool sleep_mode; struct iio_trigger **trig; struct at91_adc_trigger *trigger_list; u32 trigger_number; @@ -455,6 +456,8 @@ static int at91_adc_probe_dt(struct at91_adc_state *st, } st->num_channels = prop; + st->sleep_mode = of_property_read_bool(node, "atmel,adc-sleep-mode"); + if (of_property_read_u32(node, "atmel,adc-startup-time", &prop)) { dev_err(&idev->dev, "Missing adc-startup-time property in the DT.\n"); ret = -EINVAL; @@ -580,6 +583,7 @@ static int at91_adc_probe(struct platform_device *pdev) struct iio_dev *idev; struct at91_adc_state *st; struct resource *res; + u32 reg; idev = iio_device_alloc(sizeof(struct at91_adc_state)); if (idev == NULL) { @@ -687,16 +691,13 @@ static int at91_adc_probe(struct platform_device *pdev) */ ticks = round_up((st->startup_time * adc_clk / 1000000) - 1, 8) / 8; - + reg = AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL; + reg |= AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP; if (st->low_res) - at91_adc_writel(st, AT91_ADC_MR, - AT91_ADC_LOWRES | - (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | - (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); - else - at91_adc_writel(st, AT91_ADC_MR, - (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | - (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); + reg |= AT91_ADC_LOWRES; + if (st->sleep_mode) + reg |= AT91_ADC_SLEEP; + at91_adc_writel(st, AT91_ADC_MR, reg); /* Setup the ADC channels available on the board */ ret = at91_adc_channel_init(idev); -- cgit v1.2.3 From beca9e767e27f756d6aed9a62665e3f73546aabb Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 29 Mar 2013 14:54:00 +0000 Subject: iio: at91_adc: fix missing Sample and Hold time On the at91_adc a minimal Sample and Hold Time is necessary for the ADC to guarantee the best converted final value between two channels selection. This time has to be programmed through the bitfield SHTIM in the Mode Register ADC_MR. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Ludovic Desroches Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/arm/atmel-adc.txt | 1 + drivers/iio/adc/at91_adc.c | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/atmel-adc.txt b/Documentation/devicetree/bindings/arm/atmel-adc.txt index 3a05492acdaf..16769d9cedd6 100644 --- a/Documentation/devicetree/bindings/arm/atmel-adc.txt +++ b/Documentation/devicetree/bindings/arm/atmel-adc.txt @@ -26,6 +26,7 @@ Optional properties: atmel,adc-res-names property. If not specified, the highest resolution will be used. - atmel,adc-sleep-mode: Boolean to enable sleep mode when no conversion + - atmel,adc-sample-hold-time: Sample and Hold Time in microseconds Optional trigger Nodes: - Required properties: diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 7295bc5280bd..e5b88d5d3b59 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -52,6 +52,7 @@ struct at91_adc_state { void __iomem *reg_base; struct at91_adc_reg_desc *registers; u8 startup_time; + u8 sample_hold_time; bool sleep_mode; struct iio_trigger **trig; struct at91_adc_trigger *trigger_list; @@ -465,6 +466,9 @@ static int at91_adc_probe_dt(struct at91_adc_state *st, } st->startup_time = prop; + prop = 0; + of_property_read_u32(node, "atmel,adc-sample-hold-time", &prop); + st->sample_hold_time = prop; if (of_property_read_u32(node, "atmel,adc-vref", &prop)) { dev_err(&idev->dev, "Missing adc-vref property in the DT.\n"); @@ -578,7 +582,7 @@ static const struct iio_info at91_adc_info = { static int at91_adc_probe(struct platform_device *pdev) { - unsigned int prsc, mstrclk, ticks, adc_clk; + unsigned int prsc, mstrclk, ticks, adc_clk, shtim; int ret; struct iio_dev *idev; struct at91_adc_state *st; @@ -691,12 +695,21 @@ static int at91_adc_probe(struct platform_device *pdev) */ ticks = round_up((st->startup_time * adc_clk / 1000000) - 1, 8) / 8; + /* + * a minimal Sample and Hold Time is necessary for the ADC to guarantee + * the best converted final value between two channels selection + * The formula thus is : Sample and Hold Time = (shtim + 1) / ADCClock + */ + shtim = round_up((st->sample_hold_time * adc_clk / + 1000000) - 1, 1); + reg = AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL; reg |= AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP; if (st->low_res) reg |= AT91_ADC_LOWRES; if (st->sleep_mode) reg |= AT91_ADC_SLEEP; + reg |= AT91_ADC_SHTIM_(shtim) & AT91_ADC_SHTIM; at91_adc_writel(st, AT91_ADC_MR, reg); /* Setup the ADC channels available on the board */ -- cgit v1.2.3 From aca91bfc67fe356544eb1cfc483c09b27ce49fa9 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 4 Mar 2013 21:16:18 +0100 Subject: x86-64, docs, mm: Add vsyscall range to virtual address space layout Add the end of the virtual address space to its layout documentation. Signed-off-by: Borislav Petkov Link: http://lkml.kernel.org/r/1362428180-8865-4-git-send-email-bp@alien8.de Signed-off-by: H. Peter Anvin --- Documentation/x86/x86_64/mm.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/x86/x86_64/mm.txt b/Documentation/x86/x86_64/mm.txt index d6498e3cd713..881582f75c9c 100644 --- a/Documentation/x86/x86_64/mm.txt +++ b/Documentation/x86/x86_64/mm.txt @@ -13,7 +13,9 @@ ffffe90000000000 - ffffe9ffffffffff (=40 bits) hole ffffea0000000000 - ffffeaffffffffff (=40 bits) virtual memory map (1TB) ... unused hole ... ffffffff80000000 - ffffffffa0000000 (=512 MB) kernel text mapping, from phys 0 -ffffffffa0000000 - fffffffffff00000 (=1536 MB) module mapping space +ffffffffa0000000 - ffffffffff5fffff (=1525 MB) module mapping space +ffffffffff600000 - ffffffffffdfffff (=8 MB) vsyscalls +ffffffffffe00000 - ffffffffffffffff (=2 MB) unused hole The direct mapping covers all memory in the system up to the highest memory address (this means in some cases it can also include PCI memory -- cgit v1.2.3 From 5f03dc2002f5dc85ce87e69caff7f28f17f5c9b2 Mon Sep 17 00:00:00 2001 From: Christian Daudt Date: Wed, 13 Mar 2013 14:27:28 -0700 Subject: ARM: bcm281xx: Add timer driver (DT portion) This adds support for the Broadcom timer, used in the following SoCs: BCM11130, BCM11140, BCM11351, BCM28145, BCM28155 Updates from V6: - Split DT portion into a separate patch Updates from V5: - Rebase to latest arm-soc/for-next Updates from V4: - Switch code to use CLOCKSOURCE_OF_DECLARE Updates from V3: - Migrate to 3.9 timer framework updates Updates from V2: - prepend static fns + fields with kona_ Updates from V1: - Rename bcm_timer.c to bcm_kona_timer.c - Pull .h into bcm_kona_timer.c - Make timers static - Clean up comment block - Switched to using clockevents_config_and_register - Added an error to the get_timer loop if it repeats too much - Added to Documentation/devicetree/bindings/arm/bcm/bcm,kona-timer.txt - Added missing readl to timer_disable_and_clear Note: bcm,kona-timer was kept as the 'compatible' field to make it specific enough for when there are multiple bcm timers (bcm,timer is too generic). Signed-off-by: Christian Daudt Acked-by: Arnd Bergmann Acked-by: John Stultz Reviewed-by: Stephen Warren Signed-off-by: Olof Johansson --- .../devicetree/bindings/arm/bcm/bcm,kona-timer.txt | 19 +++++++++++++++++++ arch/arm/boot/dts/bcm11351.dtsi | 8 ++++++++ 2 files changed, 27 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/bcm/bcm,kona-timer.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/bcm/bcm,kona-timer.txt b/Documentation/devicetree/bindings/arm/bcm/bcm,kona-timer.txt new file mode 100644 index 000000000000..59fa6e68d4f6 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/bcm/bcm,kona-timer.txt @@ -0,0 +1,19 @@ +Broadcom Kona Family timer +----------------------------------------------------- +This timer is used in the following Broadcom SoCs: + BCM11130, BCM11140, BCM11351, BCM28145, BCM28155 + +Required properties: +- compatible : "bcm,kona-timer" +- reg : Register range for the timer +- interrupts : interrupt for the timer +- clock-frequency: frequency that the clock operates + +Example: + timer@35006000 { + compatible = "bcm,kona-timer"; + reg = <0x35006000 0x1000>; + interrupts = <0x0 7 0x4>; + clock-frequency = <32768>; + }; + diff --git a/arch/arm/boot/dts/bcm11351.dtsi b/arch/arm/boot/dts/bcm11351.dtsi index ad135885bd2a..8f71f40722b9 100644 --- a/arch/arm/boot/dts/bcm11351.dtsi +++ b/arch/arm/boot/dts/bcm11351.dtsi @@ -47,4 +47,12 @@ cache-unified; cache-level = <2>; }; + + timer@35006000 { + compatible = "bcm,kona-timer"; + reg = <0x35006000 0x1000>; + interrupts = <0x0 7 0x4>; + clock-frequency = <32768>; + }; + }; -- cgit v1.2.3 From 7021d1220584ab1e6efd3d59da47b65674d9896a Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Wed, 3 Apr 2013 19:31:27 +0800 Subject: ARM: tegra: add clock source of PMC to device trees Adding the bindings of the clock source of PMC in DT. Signed-off-by: Joseph Lo Signed-off-by: Stephen Warren --- .../bindings/arm/tegra/nvidia,tegra20-pmc.txt | 29 +++++++++++++++++++++- arch/arm/boot/dts/tegra114-dalmore.dts | 13 ++++++++++ arch/arm/boot/dts/tegra114-pluto.dts | 13 ++++++++++ arch/arm/boot/dts/tegra114.dtsi | 2 ++ arch/arm/boot/dts/tegra20-colibri-512.dtsi | 13 ++++++++++ arch/arm/boot/dts/tegra20-harmony.dts | 13 ++++++++++ arch/arm/boot/dts/tegra20-paz00.dts | 13 ++++++++++ arch/arm/boot/dts/tegra20-seaboard.dts | 13 ++++++++++ arch/arm/boot/dts/tegra20-tamonten.dtsi | 13 ++++++++++ arch/arm/boot/dts/tegra20-trimslice.dts | 13 ++++++++++ arch/arm/boot/dts/tegra20-ventana.dts | 13 ++++++++++ arch/arm/boot/dts/tegra20-whistler.dts | 13 ++++++++++ arch/arm/boot/dts/tegra20.dtsi | 2 ++ arch/arm/boot/dts/tegra30-beaver.dts | 13 ++++++++++ arch/arm/boot/dts/tegra30-cardhu.dtsi | 13 ++++++++++ arch/arm/boot/dts/tegra30.dtsi | 2 ++ 16 files changed, 190 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt index b5846e21cc2e..ee529b17cb9f 100644 --- a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt +++ b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt @@ -1,9 +1,15 @@ NVIDIA Tegra Power Management Controller (PMC) -Properties: +Required properties: - name : Should be pmc - compatible : Should contain "nvidia,tegra-pmc". - reg : Offset and length of the register set for the device +- clocks : Must contain an entry for each entry in clock-names. +- clock-names : Must include the following entries: + "pclk" (The Tegra clock of that name), + "clk32k_in" (The 32KHz clock input to Tegra). + +Optional properties: - nvidia,invert-interrupt : If present, inverts the PMU interrupt signal. The PMU is an external Power Management Unit, whose interrupt output signal is fed into the PMC. This signal is optionally inverted, and then @@ -12,8 +18,29 @@ Properties: Example: +/ SoC dts including file pmc@7000f400 { compatible = "nvidia,tegra20-pmc"; reg = <0x7000e400 0x400>; + clocks = <&tegra_car 110>, <&clk32k_in>; + clock-names = "pclk", "clk32k_in"; nvidia,invert-interrupt; }; + +/ Tegra board dts file +{ + ... + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + ... +}; diff --git a/arch/arm/boot/dts/tegra114-dalmore.dts b/arch/arm/boot/dts/tegra114-dalmore.dts index a30aca62658a..6ebc1b704190 100644 --- a/arch/arm/boot/dts/tegra114-dalmore.dts +++ b/arch/arm/boot/dts/tegra114-dalmore.dts @@ -18,4 +18,17 @@ pmc { nvidia,invert-interrupt; }; + + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; }; diff --git a/arch/arm/boot/dts/tegra114-pluto.dts b/arch/arm/boot/dts/tegra114-pluto.dts index 9bea8f57aa47..5deb8692b350 100644 --- a/arch/arm/boot/dts/tegra114-pluto.dts +++ b/arch/arm/boot/dts/tegra114-pluto.dts @@ -18,4 +18,17 @@ pmc { nvidia,invert-interrupt; }; + + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; }; diff --git a/arch/arm/boot/dts/tegra114.dtsi b/arch/arm/boot/dts/tegra114.dtsi index e4ddeddcd437..c0b527d15fda 100644 --- a/arch/arm/boot/dts/tegra114.dtsi +++ b/arch/arm/boot/dts/tegra114.dtsi @@ -101,6 +101,8 @@ pmc { compatible = "nvidia,tegra114-pmc"; reg = <0x7000e400 0x400>; + clocks = <&tegra_car 261>, <&clk32k_in>; + clock-names = "pclk", "clk32k_in"; }; iommu { diff --git a/arch/arm/boot/dts/tegra20-colibri-512.dtsi b/arch/arm/boot/dts/tegra20-colibri-512.dtsi index cb73e62d61a9..4e3afdef28a8 100644 --- a/arch/arm/boot/dts/tegra20-colibri-512.dtsi +++ b/arch/arm/boot/dts/tegra20-colibri-512.dtsi @@ -447,6 +447,19 @@ cd-gpios = <&gpio 23 1>; /* gpio PC7 */ }; + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + sound { compatible = "nvidia,tegra-audio-wm9712-colibri_t20", "nvidia,tegra-audio-wm9712"; diff --git a/arch/arm/boot/dts/tegra20-harmony.dts b/arch/arm/boot/dts/tegra20-harmony.dts index 1f79c0debb05..ae9d5a20834e 100644 --- a/arch/arm/boot/dts/tegra20-harmony.dts +++ b/arch/arm/boot/dts/tegra20-harmony.dts @@ -451,6 +451,19 @@ bus-width = <8>; }; + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + kbc { status = "okay"; nvidia,debounce-delay-ms = <2>; diff --git a/arch/arm/boot/dts/tegra20-paz00.dts b/arch/arm/boot/dts/tegra20-paz00.dts index 9db36da8e023..fd60940e4063 100644 --- a/arch/arm/boot/dts/tegra20-paz00.dts +++ b/arch/arm/boot/dts/tegra20-paz00.dts @@ -447,6 +447,19 @@ bus-width = <8>; }; + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + gpio-keys { compatible = "gpio-keys"; diff --git a/arch/arm/boot/dts/tegra20-seaboard.dts b/arch/arm/boot/dts/tegra20-seaboard.dts index 715a8b8dd9cd..4ee700a33ca5 100644 --- a/arch/arm/boot/dts/tegra20-seaboard.dts +++ b/arch/arm/boot/dts/tegra20-seaboard.dts @@ -595,6 +595,19 @@ bus-width = <8>; }; + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + gpio-keys { compatible = "gpio-keys"; diff --git a/arch/arm/boot/dts/tegra20-tamonten.dtsi b/arch/arm/boot/dts/tegra20-tamonten.dtsi index 6e9d91fc6195..c19025725918 100644 --- a/arch/arm/boot/dts/tegra20-tamonten.dtsi +++ b/arch/arm/boot/dts/tegra20-tamonten.dtsi @@ -471,6 +471,19 @@ status = "okay"; }; + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + regulators { compatible = "simple-bus"; diff --git a/arch/arm/boot/dts/tegra20-trimslice.dts b/arch/arm/boot/dts/tegra20-trimslice.dts index 98f3e44f2a51..a9f3f06580f5 100644 --- a/arch/arm/boot/dts/tegra20-trimslice.dts +++ b/arch/arm/boot/dts/tegra20-trimslice.dts @@ -330,6 +330,19 @@ bus-width = <4>; }; + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + poweroff { compatible = "gpio-poweroff"; gpios = <&gpio 191 1>; /* gpio PX7, active low */ diff --git a/arch/arm/boot/dts/tegra20-ventana.dts b/arch/arm/boot/dts/tegra20-ventana.dts index 4aef56f2d96a..f544806e9618 100644 --- a/arch/arm/boot/dts/tegra20-ventana.dts +++ b/arch/arm/boot/dts/tegra20-ventana.dts @@ -531,6 +531,19 @@ bus-width = <8>; }; + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + regulators { compatible = "simple-bus"; #address-cells = <1>; diff --git a/arch/arm/boot/dts/tegra20-whistler.dts b/arch/arm/boot/dts/tegra20-whistler.dts index 5762188c60ad..258cf945f515 100644 --- a/arch/arm/boot/dts/tegra20-whistler.dts +++ b/arch/arm/boot/dts/tegra20-whistler.dts @@ -520,6 +520,19 @@ bus-width = <8>; }; + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + kbc { status = "okay"; nvidia,debounce-delay-ms = <20>; diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi index 37701d8727d8..8adaa3576c35 100644 --- a/arch/arm/boot/dts/tegra20.dtsi +++ b/arch/arm/boot/dts/tegra20.dtsi @@ -417,6 +417,8 @@ pmc { compatible = "nvidia,tegra20-pmc"; reg = <0x7000e400 0x400>; + clocks = <&tegra_car 110>, <&clk32k_in>; + clock-names = "pclk", "clk32k_in"; }; memory-controller@7000f000 { diff --git a/arch/arm/boot/dts/tegra30-beaver.dts b/arch/arm/boot/dts/tegra30-beaver.dts index 0a2cd24df853..6248b2445b32 100644 --- a/arch/arm/boot/dts/tegra30-beaver.dts +++ b/arch/arm/boot/dts/tegra30-beaver.dts @@ -268,6 +268,19 @@ bus-width = <8>; }; + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + regulators { compatible = "simple-bus"; #address-cells = <1>; diff --git a/arch/arm/boot/dts/tegra30-cardhu.dtsi b/arch/arm/boot/dts/tegra30-cardhu.dtsi index 3e2d21018a5b..65bf2b63174e 100644 --- a/arch/arm/boot/dts/tegra30-cardhu.dtsi +++ b/arch/arm/boot/dts/tegra30-cardhu.dtsi @@ -322,6 +322,19 @@ bus-width = <8>; }; + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk32k_in: clock { + compatible = "fixed-clock"; + reg=<0>; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + regulators { compatible = "simple-bus"; #address-cells = <1>; diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi index d376959b7731..94013404808a 100644 --- a/arch/arm/boot/dts/tegra30.dtsi +++ b/arch/arm/boot/dts/tegra30.dtsi @@ -426,6 +426,8 @@ pmc { compatible = "nvidia,tegra30-pmc"; reg = <0x7000e400 0x400>; + clocks = <&tegra_car 218>, <&clk32k_in>; + clock-names = "pclk", "clk32k_in"; }; memory-controller { -- cgit v1.2.3 From 4b51ccbc469facb7b589a71c2a4ae47d3e425d02 Mon Sep 17 00:00:00 2001 From: Joseph Lo Date: Wed, 3 Apr 2013 19:31:46 +0800 Subject: ARM: dt: tegra: add bindings of power management configurations for PMC The PMC mostly controls the entry and exit of the system from different sleep modes. Different platform or system may have different configurations. The power management configurations of PMC is represented as some properties. The system needs to define the properties when the system supports deep sleep mode (i.e. suspend). Cc: Grant Likely Cc: Rob Herring Cc: devicetree-discuss@lists.ozlabs.org Signed-off-by: Joseph Lo Signed-off-by: Stephen Warren --- .../bindings/arm/tegra/nvidia,tegra20-pmc.txt | 38 ++++++++++ arch/arm/mach-tegra/pmc.c | 83 ++++++++++++++++++++++ arch/arm/mach-tegra/pmc.h | 8 +++ 3 files changed, 129 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt index ee529b17cb9f..1608a54e90e1 100644 --- a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt +++ b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.txt @@ -1,5 +1,9 @@ NVIDIA Tegra Power Management Controller (PMC) +The PMC block interacts with an external Power Management Unit. The PMC +mostly controls the entry and exit of the system from different sleep +modes. It provides power-gating controllers for SoC and CPU power-islands. + Required properties: - name : Should be pmc - compatible : Should contain "nvidia,tegra-pmc". @@ -15,6 +19,32 @@ Optional properties: signal is fed into the PMC. This signal is optionally inverted, and then fed into the ARM GIC. The PMC is not involved in the detection or handling of this interrupt signal, merely its inversion. +- nvidia,suspend-mode : The suspend mode that the platform should use. + Valid values are 0, 1 and 2: + 0 (LP0): CPU + Core voltage off and DRAM in self-refresh + 1 (LP1): CPU voltage off and DRAM in self-refresh + 2 (LP2): CPU voltage off +- nvidia,core-power-req-active-high : Boolean, core power request active-high +- nvidia,sys-clock-req-active-high : Boolean, system clock request active-high +- nvidia,combined-power-req : Boolean, combined power request for CPU & Core +- nvidia,cpu-pwr-good-en : Boolean, CPU power good signal (from PMIC to PMC) + is enabled. + +Required properties when nvidia,suspend-mode is specified: +- nvidia,cpu-pwr-good-time : CPU power good time in uS. +- nvidia,cpu-pwr-off-time : CPU power off time in uS. +- nvidia,core-pwr-good-time : + Core power good time in uS. +- nvidia,core-pwr-off-time : Core power off time in uS. + +Required properties when nvidia,suspend-mode=<0>: +- nvidia,lp0-vec : Starting address and length of LP0 vector + The LP0 vector contains the warm boot code that is executed by AVP when + resuming from the LP0 state. The AVP (Audio-Video Processor) is an ARM7 + processor and always being the first boot processor when chip is power on + or resume from deep sleep mode. When the system is resumed from the deep + sleep mode, the warm boot code will restore some PLLs, clocks and then + bring up CPU0 for resuming the system. Example: @@ -25,6 +55,14 @@ pmc@7000f400 { clocks = <&tegra_car 110>, <&clk32k_in>; clock-names = "pclk", "clk32k_in"; nvidia,invert-interrupt; + nvidia,suspend-mode = <1>; + nvidia,cpu-pwr-good-time = <2000>; + nvidia,cpu-pwr-off-time = <100>; + nvidia,core-pwr-good-time = <3845 3845>; + nvidia,core-pwr-off-time = <458>; + nvidia,core-power-req-active-high; + nvidia,sys-clock-req-active-high; + nvidia,lp0-vec = <0xbdffd000 0x2000>; }; / Tegra board dts file diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c index faa33e8f937d..e896826d7d0f 100644 --- a/arch/arm/mach-tegra/pmc.c +++ b/arch/arm/mach-tegra/pmc.c @@ -21,6 +21,8 @@ #include #include +#include "pmc.h" + #define PMC_CTRL 0x0 #define PMC_CTRL_INTR_LOW (1 << 17) #define PMC_PWRGATE_TOGGLE 0x30 @@ -49,6 +51,22 @@ static void __iomem *tegra_pmc_base; static bool tegra_pmc_invert_interrupt; static struct clk *tegra_pclk; +struct pmc_pm_data { + u32 cpu_good_time; /* CPU power good time in uS */ + u32 cpu_off_time; /* CPU power off time in uS */ + u32 core_osc_time; /* Core power good osc time in uS */ + u32 core_pmu_time; /* Core power good pmu time in uS */ + u32 core_off_time; /* Core power off time in uS */ + bool corereq_high; /* Core power request active-high */ + bool sysclkreq_high; /* System clock request active-high */ + bool combined_req; /* Combined pwr req for CPU & Core */ + bool cpu_pwr_good_en; /* CPU power good signal is enabled */ + u32 lp0_vec_phy_addr; /* The phy addr of LP0 warm boot code */ + u32 lp0_vec_size; /* The size of LP0 warm boot code */ + enum tegra_suspend_mode suspend_mode; +}; +static struct pmc_pm_data pmc_pm_data; + static inline u32 tegra_pmc_readl(u32 reg) { return readl(tegra_pmc_base + reg); @@ -176,6 +194,10 @@ static const struct of_device_id matches[] __initconst = { static void tegra_pmc_parse_dt(void) { struct device_node *np; + u32 prop; + enum tegra_suspend_mode suspend_mode; + u32 core_good_time[2] = {0, 0}; + u32 lp0_vec[2] = {0, 0}; np = of_find_matching_node(NULL, matches); BUG_ON(!np); @@ -186,6 +208,67 @@ static void tegra_pmc_parse_dt(void) "nvidia,invert-interrupt"); tegra_pclk = of_clk_get_by_name(np, "pclk"); WARN_ON(IS_ERR(tegra_pclk)); + + /* Grabbing the power management configurations */ + if (of_property_read_u32(np, "nvidia,suspend-mode", &prop)) { + suspend_mode = TEGRA_SUSPEND_NONE; + } else { + switch (prop) { + case 0: + suspend_mode = TEGRA_SUSPEND_LP0; + break; + case 1: + suspend_mode = TEGRA_SUSPEND_LP1; + break; + case 2: + suspend_mode = TEGRA_SUSPEND_LP2; + break; + default: + suspend_mode = TEGRA_SUSPEND_NONE; + break; + } + } + + if (of_property_read_u32(np, "nvidia,cpu-pwr-good-time", &prop)) + suspend_mode = TEGRA_SUSPEND_NONE; + pmc_pm_data.cpu_good_time = prop; + + if (of_property_read_u32(np, "nvidia,cpu-pwr-off-time", &prop)) + suspend_mode = TEGRA_SUSPEND_NONE; + pmc_pm_data.cpu_off_time = prop; + + if (of_property_read_u32_array(np, "nvidia,core-pwr-good-time", + core_good_time, ARRAY_SIZE(core_good_time))) + suspend_mode = TEGRA_SUSPEND_NONE; + pmc_pm_data.core_osc_time = core_good_time[0]; + pmc_pm_data.core_pmu_time = core_good_time[1]; + + if (of_property_read_u32(np, "nvidia,core-pwr-off-time", + &prop)) + suspend_mode = TEGRA_SUSPEND_NONE; + pmc_pm_data.core_off_time = prop; + + pmc_pm_data.corereq_high = of_property_read_bool(np, + "nvidia,core-power-req-active-high"); + + pmc_pm_data.sysclkreq_high = of_property_read_bool(np, + "nvidia,sys-clock-req-active-high"); + + pmc_pm_data.combined_req = of_property_read_bool(np, + "nvidia,combined-power-req"); + + pmc_pm_data.cpu_pwr_good_en = of_property_read_bool(np, + "nvidia,cpu-pwr-good-en"); + + if (of_property_read_u32_array(np, "nvidia,lp0-vec", lp0_vec, + ARRAY_SIZE(lp0_vec))) + if (suspend_mode == TEGRA_SUSPEND_LP0) + suspend_mode = TEGRA_SUSPEND_LP1; + + pmc_pm_data.lp0_vec_phy_addr = lp0_vec[0]; + pmc_pm_data.lp0_vec_size = lp0_vec[1]; + + pmc_pm_data.suspend_mode = suspend_mode; } void __init tegra_pmc_init(void) diff --git a/arch/arm/mach-tegra/pmc.h b/arch/arm/mach-tegra/pmc.h index 22f16c9dd44d..6bc0fc095269 100644 --- a/arch/arm/mach-tegra/pmc.h +++ b/arch/arm/mach-tegra/pmc.h @@ -18,6 +18,14 @@ #ifndef __MACH_TEGRA_PMC_H #define __MACH_TEGRA_PMC_H +enum tegra_suspend_mode { + TEGRA_SUSPEND_NONE = 0, + TEGRA_SUSPEND_LP2, /* CPU voltage off */ + TEGRA_SUSPEND_LP1, /* CPU voltage off, DRAM self-refresh */ + TEGRA_SUSPEND_LP0, /* CPU + core voltage off, DRAM self-refresh */ + TEGRA_MAX_SUSPEND_MODE, +}; + #ifdef CONFIG_PM_SLEEP void set_power_timers(unsigned long us_on, unsigned long us_off); #endif -- cgit v1.2.3 From 1ae65ae92d77542f81c68269bc2f15bcf1ee4d6a Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Wed, 3 Apr 2013 20:32:04 +0300 Subject: cgroups: Documentation/cgroup/cgroup.txt - a trivial fix. This trivial patch removes a word which appears twice in Documentation/cgroup/cgroup.txt. Signed-off-by: Rami Rosen Signed-off-by: Tejun Heo --- Documentation/cgroups/cgroups.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/cgroups/cgroups.txt b/Documentation/cgroups/cgroups.txt index 0028e888828c..638bf17ff869 100644 --- a/Documentation/cgroups/cgroups.txt +++ b/Documentation/cgroups/cgroups.txt @@ -442,7 +442,7 @@ You can attach the current shell task by echoing 0: You can use the cgroup.procs file instead of the tasks file to move all threads in a threadgroup at once. Echoing the PID of any task in a threadgroup to cgroup.procs causes all tasks in that threadgroup to be -be attached to the cgroup. Writing 0 to cgroup.procs moves all tasks +attached to the cgroup. Writing 0 to cgroup.procs moves all tasks in the writing task's threadgroup. Note: Since every task is always a member of exactly one cgroup in each -- cgit v1.2.3 From 7ab0a48420c95dc4099d1777a5a24dffad102cf1 Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Wed, 3 Apr 2013 07:20:38 +1300 Subject: video: fb: vt8500: Convert framebuffer drivers to standardized binding Now that a display timing binding is available, convert our almost identical binding to use the standard binding. This patch converts the vt8500 and wm8505 framebuffer drivers and associated dts/dtsi files to use the standard binding as defined in bindings/video/display-timing.txt. There are two side-effects of making this conversion: 1) The fb node should now be in the board file, rather than the soc file as the display-timing node is a child of the fb node. 2) We still require a bits per pixel property to initialize the framebuffer for the different lcd panels. Rather than including this as part of the display timing, it is moved into the framebuffer node. I have also taken the opportunity to alphabetise the includes of each driver to avoid double-ups. Signed-off-by: Tony Prisk Signed-off-by: Tomi Valkeinen --- .../devicetree/bindings/video/via,vt8500-fb.txt | 48 ++++------------ .../devicetree/bindings/video/wm,wm8505-fb.txt | 32 +++++++---- arch/arm/boot/dts/vt8500-bv07.dts | 34 +++++------ arch/arm/boot/dts/vt8500.dtsi | 4 +- arch/arm/boot/dts/wm8505-ref.dts | 34 +++++------ arch/arm/boot/dts/wm8505.dtsi | 4 +- arch/arm/boot/dts/wm8650-mid.dts | 36 ++++++------ arch/arm/boot/dts/wm8650.dtsi | 4 +- arch/arm/boot/dts/wm8850-w70v2.dts | 40 ++++++------- arch/arm/boot/dts/wm8850.dtsi | 4 +- drivers/video/Kconfig | 6 ++ drivers/video/vt8500lcdfb.c | 53 +++++++---------- drivers/video/wm8505fb.c | 67 +++++++++------------- 13 files changed, 154 insertions(+), 212 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/video/via,vt8500-fb.txt b/Documentation/devicetree/bindings/video/via,vt8500-fb.txt index c870b6478ec8..2871e218a0fb 100644 --- a/Documentation/devicetree/bindings/video/via,vt8500-fb.txt +++ b/Documentation/devicetree/bindings/video/via,vt8500-fb.txt @@ -5,58 +5,32 @@ Required properties: - compatible : "via,vt8500-fb" - reg : Should contain 1 register ranges(address and length) - interrupts : framebuffer controller interrupt -- display: a phandle pointing to the display node +- bits-per-pixel : bit depth of framebuffer (16 or 32) -Required nodes: -- display: a display node is required to initialize the lcd panel - This should be in the board dts. -- default-mode: a videomode within the display with timing parameters - as specified below. +Required subnodes: +- display-timings: see display-timing.txt for information Example: - fb@d800e400 { + fb@d8050800 { compatible = "via,vt8500-fb"; reg = <0xd800e400 0x400>; interrupts = <12>; - display = <&display>; - default-mode = <&mode0>; - }; - -VIA VT8500 Display ------------------------------------------------------ -Required properties (as per of_videomode_helper): - - - hactive, vactive: Display resolution - - hfront-porch, hback-porch, hsync-len: Horizontal Display timing parameters - in pixels - vfront-porch, vback-porch, vsync-len: Vertical display timing parameters in - lines - - clock: displayclock in Hz - - bpp: lcd panel bit-depth. - <16> for RGB565, <32> for RGB888 - -Optional properties (as per of_videomode_helper): - - width-mm, height-mm: Display dimensions in mm - - hsync-active-high (bool): Hsync pulse is active high - - vsync-active-high (bool): Vsync pulse is active high - - interlaced (bool): This is an interlaced mode - - doublescan (bool): This is a doublescan mode + bits-per-pixel = <16>; -Example: - display: display@0 { - modes { - mode0: mode@0 { + display-timings { + native-mode = <&timing0>; + timing0: 800x480 { + clock-frequency = <0>; /* unused but required */ hactive = <800>; vactive = <480>; - hback-porch = <88>; hfront-porch = <40>; + hback-porch = <88>; hsync-len = <0>; vback-porch = <32>; vfront-porch = <11>; vsync-len = <1>; - clock = <0>; /* unused but required */ - bpp = <16>; /* non-standard but required */ }; }; }; + diff --git a/Documentation/devicetree/bindings/video/wm,wm8505-fb.txt b/Documentation/devicetree/bindings/video/wm,wm8505-fb.txt index 3d325e1d11ee..0bcadb2840a5 100644 --- a/Documentation/devicetree/bindings/video/wm,wm8505-fb.txt +++ b/Documentation/devicetree/bindings/video/wm,wm8505-fb.txt @@ -4,20 +4,30 @@ Wondermedia WM8505 Framebuffer Required properties: - compatible : "wm,wm8505-fb" - reg : Should contain 1 register ranges(address and length) -- via,display: a phandle pointing to the display node +- bits-per-pixel : bit depth of framebuffer (16 or 32) -Required nodes: -- display: a display node is required to initialize the lcd panel - This should be in the board dts. See definition in - Documentation/devicetree/bindings/video/via,vt8500-fb.txt -- default-mode: a videomode node as specified in - Documentation/devicetree/bindings/video/via,vt8500-fb.txt +Required subnodes: +- display-timings: see display-timing.txt for information Example: - fb@d8050800 { + fb@d8051700 { compatible = "wm,wm8505-fb"; - reg = <0xd8050800 0x200>; - display = <&display>; - default-mode = <&mode0>; + reg = <0xd8051700 0x200>; + bits-per-pixel = <16>; + + display-timings { + native-mode = <&timing0>; + timing0: 800x480 { + clock-frequency = <0>; /* unused but required */ + hactive = <800>; + vactive = <480>; + hfront-porch = <40>; + hback-porch = <88>; + hsync-len = <0>; + vback-porch = <32>; + vfront-porch = <11>; + vsync-len = <1>; + }; + }; }; diff --git a/arch/arm/boot/dts/vt8500-bv07.dts b/arch/arm/boot/dts/vt8500-bv07.dts index 567cf4e8ab84..877b33afa7ed 100644 --- a/arch/arm/boot/dts/vt8500-bv07.dts +++ b/arch/arm/boot/dts/vt8500-bv07.dts @@ -11,26 +11,22 @@ / { model = "Benign BV07 Netbook"; +}; - /* - * Display node is based on Sascha Hauer's patch on dri-devel. - * Added a bpp property to calculate the size of the framebuffer - * until the binding is formalized. - */ - display: display@0 { - modes { - mode0: mode@0 { - hactive = <800>; - vactive = <480>; - hback-porch = <88>; - hfront-porch = <40>; - hsync-len = <0>; - vback-porch = <32>; - vfront-porch = <11>; - vsync-len = <1>; - clock = <0>; /* unused but required */ - bpp = <16>; /* non-standard but required */ - }; +&fb { + bits-per-pixel = <16>; + display-timings { + native-mode = <&timing0>; + timing0: 800x480 { + clock-frequency = <0>; /* unused but required */ + hactive = <800>; + vactive = <480>; + hfront-porch = <40>; + hback-porch = <88>; + hsync-len = <0>; + vback-porch = <32>; + vfront-porch = <11>; + vsync-len = <1>; }; }; }; diff --git a/arch/arm/boot/dts/vt8500.dtsi b/arch/arm/boot/dts/vt8500.dtsi index cf31ced46602..68c8dc644383 100644 --- a/arch/arm/boot/dts/vt8500.dtsi +++ b/arch/arm/boot/dts/vt8500.dtsi @@ -98,12 +98,10 @@ interrupts = <43>; }; - fb@d800e400 { + fb: fb@d8050800 { compatible = "via,vt8500-fb"; reg = <0xd800e400 0x400>; interrupts = <12>; - display = <&display>; - default-mode = <&mode0>; }; ge_rops@d8050400 { diff --git a/arch/arm/boot/dts/wm8505-ref.dts b/arch/arm/boot/dts/wm8505-ref.dts index fd4e248074c6..edd2cec3d37f 100644 --- a/arch/arm/boot/dts/wm8505-ref.dts +++ b/arch/arm/boot/dts/wm8505-ref.dts @@ -11,26 +11,22 @@ / { model = "Wondermedia WM8505 Netbook"; +}; - /* - * Display node is based on Sascha Hauer's patch on dri-devel. - * Added a bpp property to calculate the size of the framebuffer - * until the binding is formalized. - */ - display: display@0 { - modes { - mode0: mode@0 { - hactive = <800>; - vactive = <480>; - hback-porch = <88>; - hfront-porch = <40>; - hsync-len = <0>; - vback-porch = <32>; - vfront-porch = <11>; - vsync-len = <1>; - clock = <0>; /* unused but required */ - bpp = <32>; /* non-standard but required */ - }; +&fb { + bits-per-pixel = <32>; + display-timings { + native-mode = <&timing0>; + timing0: 800x480 { + clock-frequency = <0>; /* unused but required */ + hactive = <800>; + vactive = <480>; + hfront-porch = <40>; + hback-porch = <88>; + hsync-len = <0>; + vback-porch = <32>; + vfront-porch = <11>; + vsync-len = <1>; }; }; }; diff --git a/arch/arm/boot/dts/wm8505.dtsi b/arch/arm/boot/dts/wm8505.dtsi index e74a1c0fb9a2..bcf668d31b28 100644 --- a/arch/arm/boot/dts/wm8505.dtsi +++ b/arch/arm/boot/dts/wm8505.dtsi @@ -128,11 +128,9 @@ interrupts = <0>; }; - fb@d8050800 { + fb: fb@d8050800 { compatible = "wm,wm8505-fb"; reg = <0xd8050800 0x200>; - display = <&display>; - default-mode = <&mode0>; }; ge_rops@d8050400 { diff --git a/arch/arm/boot/dts/wm8650-mid.dts b/arch/arm/boot/dts/wm8650-mid.dts index cefd938f842f..61671a0d9ede 100644 --- a/arch/arm/boot/dts/wm8650-mid.dts +++ b/arch/arm/boot/dts/wm8650-mid.dts @@ -11,26 +11,24 @@ / { model = "Wondermedia WM8650-MID Tablet"; +}; + +&fb { + bits-per-pixel = <16>; - /* - * Display node is based on Sascha Hauer's patch on dri-devel. - * Added a bpp property to calculate the size of the framebuffer - * until the binding is formalized. - */ - display: display@0 { - modes { - mode0: mode@0 { - hactive = <800>; - vactive = <480>; - hback-porch = <88>; - hfront-porch = <40>; - hsync-len = <0>; - vback-porch = <32>; - vfront-porch = <11>; - vsync-len = <1>; - clock = <0>; /* unused but required */ - bpp = <16>; /* non-standard but required */ - }; + display-timings { + native-mode = <&timing0>; + timing0: 800x480 { + clock-frequency = <0>; /* unused but required */ + hactive = <800>; + vactive = <480>; + hfront-porch = <40>; + hback-porch = <88>; + hsync-len = <0>; + vback-porch = <32>; + vfront-porch = <11>; + vsync-len = <1>; }; }; }; + diff --git a/arch/arm/boot/dts/wm8650.dtsi b/arch/arm/boot/dts/wm8650.dtsi index db3c0a12e052..9313407bbc30 100644 --- a/arch/arm/boot/dts/wm8650.dtsi +++ b/arch/arm/boot/dts/wm8650.dtsi @@ -128,11 +128,9 @@ interrupts = <43>; }; - fb@d8050800 { + fb: fb@d8050800 { compatible = "wm,wm8505-fb"; reg = <0xd8050800 0x200>; - display = <&display>; - default-mode = <&mode0>; }; ge_rops@d8050400 { diff --git a/arch/arm/boot/dts/wm8850-w70v2.dts b/arch/arm/boot/dts/wm8850-w70v2.dts index fcc660c89540..32d22532cd6c 100644 --- a/arch/arm/boot/dts/wm8850-w70v2.dts +++ b/arch/arm/boot/dts/wm8850-w70v2.dts @@ -15,28 +15,6 @@ / { model = "Wondermedia WM8850-W70v2 Tablet"; - /* - * Display node is based on Sascha Hauer's patch on dri-devel. - * Added a bpp property to calculate the size of the framebuffer - * until the binding is formalized. - */ - display: display@0 { - modes { - mode0: mode@0 { - hactive = <800>; - vactive = <480>; - hback-porch = <88>; - hfront-porch = <40>; - hsync-len = <0>; - vback-porch = <32>; - vfront-porch = <11>; - vsync-len = <1>; - clock = <0>; /* unused but required */ - bpp = <16>; /* non-standard but required */ - }; - }; - }; - backlight { compatible = "pwm-backlight"; pwms = <&pwm 0 50000 1>; /* duty inverted */ @@ -45,3 +23,21 @@ default-brightness-level = <5>; }; }; + +&fb { + bits-per-pixel = <16>; + display-timings { + native-mode = <&timing0>; + timing0: 800x480 { + clock-frequency = <0>; /* unused but required */ + hactive = <800>; + vactive = <480>; + hfront-porch = <40>; + hback-porch = <88>; + hsync-len = <0>; + vback-porch = <32>; + vfront-porch = <11>; + vsync-len = <1>; + }; + }; +}; diff --git a/arch/arm/boot/dts/wm8850.dtsi b/arch/arm/boot/dts/wm8850.dtsi index e8cbfdc87bba..7149cd13e3b9 100644 --- a/arch/arm/boot/dts/wm8850.dtsi +++ b/arch/arm/boot/dts/wm8850.dtsi @@ -135,11 +135,9 @@ }; }; - fb@d8051700 { + fb: fb@d8051700 { compatible = "wm,wm8505-fb"; reg = <0xd8051700 0x200>; - display = <&display>; - default-mode = <&mode0>; }; ge_rops@d8050400 { diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index ad762ed2c5aa..d0c932a86115 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1794,6 +1794,9 @@ config FB_VT8500 select FB_SYS_FILLRECT if (!FB_WMT_GE_ROPS) select FB_SYS_COPYAREA if (!FB_WMT_GE_ROPS) select FB_SYS_IMAGEBLIT + select FB_MODE_HELPERS + select OF_DISPLAY_TIMING + select OF_VIDEOMODE help This is the framebuffer driver for VIA VT8500 integrated LCD controller. @@ -1804,6 +1807,9 @@ config FB_WM8505 select FB_SYS_FILLRECT if (!FB_WMT_GE_ROPS) select FB_SYS_COPYAREA if (!FB_WMT_GE_ROPS) select FB_SYS_IMAGEBLIT + select FB_MODE_HELPERS + select OF_DISPLAY_TIMING + select OF_VIDEOMODE help This is the framebuffer driver for WonderMedia WM8xxx-series integrated LCD controller. This driver covers the WM8505, WM8650 diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c index 2ff2312a16ac..9547e1831e03 100644 --- a/drivers/video/vt8500lcdfb.c +++ b/drivers/video/vt8500lcdfb.c @@ -15,20 +15,21 @@ * GNU General Public License for more details. */ -#include -#include -#include -#include -#include -#include #include +#include +#include #include #include #include #include -#include +#include +#include +#include #include +#include +#include #include +#include