From c51ce403d3d9e3d54339c4563f17e958f3bc64df Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 25 Dec 2012 20:36:00 +0530 Subject: regulator: tps51632: add DT support Add DT support for the TI TPS51632. Add device binding document also. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- .../bindings/regulator/tps51632-regulator.txt | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/tps51632-regulator.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/regulator/tps51632-regulator.txt b/Documentation/devicetree/bindings/regulator/tps51632-regulator.txt new file mode 100644 index 000000000000..2f7e44a96414 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/tps51632-regulator.txt @@ -0,0 +1,27 @@ +TPS51632 Voltage regulators + +Required properties: +- compatible: Must be "ti,tps51632" +- reg: I2C slave address + +Optional properties: +- ti,enable-pwm-dvfs: Enable the DVFS voltage control through the PWM interface. +- ti,dvfs-step-20mV: The 20mV step voltage when PWM DVFS enabled. Missing this + will set 10mV step voltage in PWM DVFS mode. In normal mode, the voltage + step is 10mV as per datasheet. + +Any property defined as part of the core regulator binding, defined in +regulator.txt, can also be used. + +Example: + + tps51632 { + compatible = "ti,tps51632"; + reg = <0x43>; + regulator-name = "tps51632-vout"; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1500000>; + regulator-boot-on; + ti,enable-pwm-dvfs; + ti,dvfs-step-20mV; + }; -- cgit v1.2.3-59-g8ed1b From 9a57247f31e361f80508c40363366222dbbb6aa5 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 27 Dec 2012 23:49:39 +0000 Subject: rtnl: expose carrier value with possibility to set it Signed-off-by: Jiri Pirko Acked-by: Flavio Leitner Signed-off-by: David S. Miller --- Documentation/networking/operstates.txt | 4 ++++ include/uapi/linux/if_link.h | 1 + net/core/rtnetlink.c | 10 ++++++++++ 3 files changed, 15 insertions(+) (limited to 'Documentation') diff --git a/Documentation/networking/operstates.txt b/Documentation/networking/operstates.txt index 1a77a3cfae54..97694572338b 100644 --- a/Documentation/networking/operstates.txt +++ b/Documentation/networking/operstates.txt @@ -88,6 +88,10 @@ set this flag. On netif_carrier_off(), the scheduler stops sending packets. The name 'carrier' and the inversion are historical, think of it as lower layer. +Note that for certain kind of soft-devices, which are not managing any +real hardware, there is possible to set this bit from userpsace. +One should use TVL IFLA_CARRIER to do so. + netif_carrier_ok() can be used to query that bit. __LINK_STATE_DORMANT, maps to IFF_DORMANT: diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 60f3b6b90602..c4edfe11f1f7 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -142,6 +142,7 @@ enum { #define IFLA_PROMISCUITY IFLA_PROMISCUITY IFLA_NUM_TX_QUEUES, IFLA_NUM_RX_QUEUES, + IFLA_CARRIER, __IFLA_MAX }; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 1868625af25e..2ef7a56ba117 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -780,6 +780,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, + nla_total_size(4) /* IFLA_MTU */ + nla_total_size(4) /* IFLA_LINK */ + nla_total_size(4) /* IFLA_MASTER */ + + nla_total_size(1) /* IFLA_CARRIER */ + nla_total_size(4) /* IFLA_PROMISCUITY */ + nla_total_size(4) /* IFLA_NUM_TX_QUEUES */ + nla_total_size(4) /* IFLA_NUM_RX_QUEUES */ @@ -909,6 +910,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, nla_put_u32(skb, IFLA_LINK, dev->iflink)) || (dev->master && nla_put_u32(skb, IFLA_MASTER, dev->master->ifindex)) || + nla_put_u8(skb, IFLA_CARRIER, netif_carrier_ok(dev)) || (dev->qdisc && nla_put_string(skb, IFLA_QDISC, dev->qdisc->ops->id)) || (dev->ifalias && @@ -1108,6 +1110,7 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_MTU] = { .type = NLA_U32 }, [IFLA_LINK] = { .type = NLA_U32 }, [IFLA_MASTER] = { .type = NLA_U32 }, + [IFLA_CARRIER] = { .type = NLA_U8 }, [IFLA_TXQLEN] = { .type = NLA_U32 }, [IFLA_WEIGHT] = { .type = NLA_U32 }, [IFLA_OPERSTATE] = { .type = NLA_U8 }, @@ -1438,6 +1441,13 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, modified = 1; } + if (tb[IFLA_CARRIER]) { + err = dev_change_carrier(dev, nla_get_u8(tb[IFLA_CARRIER])); + if (err) + goto errout; + modified = 1; + } + if (tb[IFLA_TXQLEN]) dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]); -- cgit v1.2.3-59-g8ed1b From 0e14235e6cfbc9b7a546d38b51c4e7ffdab41045 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Thu, 3 Jan 2013 12:25:46 -0800 Subject: Input: imx_keypad - add device tree support This patch adds device tree support for imx keypad driver. Signed-off-by: Liu Ying Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/imx-keypad.txt | 53 ++++++++++++++++++++++ drivers/input/keyboard/imx_keypad.c | 43 ++++++++++-------- 2 files changed, 78 insertions(+), 18 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/imx-keypad.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/imx-keypad.txt b/Documentation/devicetree/bindings/input/imx-keypad.txt new file mode 100644 index 000000000000..2ebaf7d26843 --- /dev/null +++ b/Documentation/devicetree/bindings/input/imx-keypad.txt @@ -0,0 +1,53 @@ +* Freescale i.MX Keypad Port(KPP) device tree bindings + +The KPP is designed to interface with a keypad matrix with 2-point contact +or 3-point contact keys. The KPP is designed to simplify the software task +of scanning a keypad matrix. The KPP is capable of detecting, debouncing, +and decoding one or multiple keys pressed simultaneously on a keypad. + +Required SoC Specific Properties: +- compatible: Should be "fsl,-kpp". + +- reg: Physical base address of the KPP and length of memory mapped + region. + +- interrupts: The KPP interrupt number to the CPU(s). + +- clocks: The clock provided by the SoC to the KPP. Some SoCs use dummy +clock(The clock for the KPP is provided by the SoCs automatically). + +Required Board Specific Properties: +- pinctrl-names: The definition can be found at +pinctrl/pinctrl-bindings.txt. + +- pinctrl-0: The definition can be found at +pinctrl/pinctrl-bindings.txt. + +- linux,keymap: The definition can be found at +bindings/input/matrix-keymap.txt. + +Example: +kpp: kpp@73f94000 { + compatible = "fsl,imx51-kpp", "fsl,imx21-kpp"; + reg = <0x73f94000 0x4000>; + interrupts = <60>; + clocks = <&clks 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_kpp_1>; + linux,keymap = <0x00000067 /* KEY_UP */ + 0x0001006c /* KEY_DOWN */ + 0x00020072 /* KEY_VOLUMEDOWN */ + 0x00030066 /* KEY_HOME */ + 0x0100006a /* KEY_RIGHT */ + 0x01010069 /* KEY_LEFT */ + 0x0102001c /* KEY_ENTER */ + 0x01030073 /* KEY_VOLUMEUP */ + 0x02000040 /* KEY_F6 */ + 0x02010042 /* KEY_F8 */ + 0x02020043 /* KEY_F9 */ + 0x02030044 /* KEY_F10 */ + 0x0300003b /* KEY_F1 */ + 0x0301003c /* KEY_F2 */ + 0x0302003d /* KEY_F3 */ + 0x03030074>; /* KEY_POWER */ +}; diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index 6d150e3e1f55..98f9113251d2 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -414,15 +415,23 @@ open_err: return -EIO; } +#ifdef CONFIG_OF +static struct of_device_id imx_keypad_of_match[] = { + { .compatible = "fsl,imx21-kpp", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_keypad_of_match); +#endif + static int imx_keypad_probe(struct platform_device *pdev) { const struct matrix_keymap_data *keymap_data = pdev->dev.platform_data; struct imx_keypad *keypad; struct input_dev *input_dev; struct resource *res; - int irq, error, i; + int irq, error, i, row, col; - if (keymap_data == NULL) { + if (!keymap_data && !pdev->dev.of_node) { dev_err(&pdev->dev, "no keymap defined\n"); return -EINVAL; } @@ -480,22 +489,6 @@ static int imx_keypad_probe(struct platform_device *pdev) goto failed_unmap; } - /* Search for rows and cols enabled */ - for (i = 0; i < keymap_data->keymap_size; i++) { - keypad->rows_en_mask |= 1 << KEY_ROW(keymap_data->keymap[i]); - keypad->cols_en_mask |= 1 << KEY_COL(keymap_data->keymap[i]); - } - - if (keypad->rows_en_mask > ((1 << MAX_MATRIX_KEY_ROWS) - 1) || - keypad->cols_en_mask > ((1 << MAX_MATRIX_KEY_COLS) - 1)) { - dev_err(&pdev->dev, - "invalid key data (too many rows or colums)\n"); - error = -EINVAL; - goto failed_clock_put; - } - dev_dbg(&pdev->dev, "enabled rows mask: %x\n", keypad->rows_en_mask); - dev_dbg(&pdev->dev, "enabled cols mask: %x\n", keypad->cols_en_mask); - /* Init the Input device */ input_dev->name = pdev->name; input_dev->id.bustype = BUS_HOST; @@ -512,6 +505,19 @@ static int imx_keypad_probe(struct platform_device *pdev) goto failed_clock_put; } + /* Search for rows and cols enabled */ + for (row = 0; row < MAX_MATRIX_KEY_ROWS; row++) { + for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) { + i = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT); + if (keypad->keycodes[i] != KEY_RESERVED) { + keypad->rows_en_mask |= 1 << row; + keypad->cols_en_mask |= 1 << col; + } + } + } + dev_dbg(&pdev->dev, "enabled rows mask: %x\n", keypad->rows_en_mask); + dev_dbg(&pdev->dev, "enabled cols mask: %x\n", keypad->cols_en_mask); + __set_bit(EV_REP, input_dev->evbit); input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_set_drvdata(input_dev, keypad); @@ -631,6 +637,7 @@ static struct platform_driver imx_keypad_driver = { .name = "imx-keypad", .owner = THIS_MODULE, .pm = &imx_kbd_pm_ops, + .of_match_table = of_match_ptr(imx_keypad_of_match), }, .probe = imx_keypad_probe, .remove = imx_keypad_remove, -- cgit v1.2.3-59-g8ed1b From e8fc721a9ab8dd6723063a92f5c5fdb5eaffbd6e Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Fri, 28 Dec 2012 13:25:09 +0100 Subject: power/reset: Add a new driver to turn QNAP board power off The QNAP NAS boxes have a microcontroller attached to the SoCs second serial port. By sending it a simple command, it will turn the power for the board off. This driver registers a function for pm_power_off to send such a command. Signed-off-by: Andrew Lunn Signed-off-by: Anton Vorontsov --- .../bindings/power_supply/qnap-poweroff.txt | 13 +++ drivers/power/reset/Kconfig | 9 ++ drivers/power/reset/Makefile | 1 + drivers/power/reset/qnap-poweroff.c | 116 +++++++++++++++++++++ 4 files changed, 139 insertions(+) create mode 100644 Documentation/devicetree/bindings/power_supply/qnap-poweroff.txt create mode 100644 drivers/power/reset/qnap-poweroff.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/power_supply/qnap-poweroff.txt b/Documentation/devicetree/bindings/power_supply/qnap-poweroff.txt new file mode 100644 index 000000000000..9a599d27bd75 --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/qnap-poweroff.txt @@ -0,0 +1,13 @@ +* QNAP Power Off + +QNAP NAS devices have a microcontroller controlling the main power +supply. This microcontroller is connected to UART1 of the Kirkwood and +Orion5x SoCs. Sending the charactor 'A', at 19200 baud, tells the +microcontroller to turn the power off. This driver adds a handler to +pm_power_off which is called to turn the power off. + +Required Properties: +- compatible: Should be "qnap,power-off" + +- reg: Address and length of the register set for UART1 +- clocks: tclk clock diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 6461b489fb09..6453aac701c0 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -13,3 +13,12 @@ config POWER_RESET_GPIO This driver supports turning off your board via a GPIO line. If your board needs a GPIO high/low to power down, say Y and create a binding in your devicetree. + +config POWER_RESET_QNAP + bool "QNAP power-off driver" + depends on OF_GPIO && POWER_RESET && PLAT_ORION + help + This driver supports turning off QNAP NAS devices by sending + commands to the microcontroller which controls the main power. + + Say Y if you have a QNAP NAS. diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 751488a4a0c5..c4d3b2d11b9e 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o +obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o diff --git a/drivers/power/reset/qnap-poweroff.c b/drivers/power/reset/qnap-poweroff.c new file mode 100644 index 000000000000..cecb317cf2fe --- /dev/null +++ b/drivers/power/reset/qnap-poweroff.c @@ -0,0 +1,116 @@ +/* + * QNAP Turbo NAS Board power off + * + * Copyright (C) 2012 Andrew Lunn + * + * Based on the code from: + * + * Copyright (C) 2009 Martin Michlmayr + * Copyright (C) 2008 Byron Bradley + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#define UART1_REG(x) (base + ((UART_##x) << 2)) + +static void __iomem *base; +static unsigned long tclk; + +static void qnap_power_off(void) +{ + /* 19200 baud divisor */ + const unsigned divisor = ((tclk + (8 * 19200)) / (16 * 19200)); + + pr_err("%s: triggering power-off...\n", __func__); + + /* hijack UART1 and reset into sane state (19200,8n1) */ + writel(0x83, UART1_REG(LCR)); + writel(divisor & 0xff, UART1_REG(DLL)); + writel((divisor >> 8) & 0xff, UART1_REG(DLM)); + writel(0x03, UART1_REG(LCR)); + writel(0x00, UART1_REG(IER)); + writel(0x00, UART1_REG(FCR)); + writel(0x00, UART1_REG(MCR)); + + /* send the power-off command 'A' to PIC */ + writel('A', UART1_REG(TX)); +} + +static int qnap_power_off_probe(struct platform_device *pdev) +{ + struct resource *res; + struct clk *clk; + char symname[KSYM_NAME_LEN]; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Missing resource"); + return -EINVAL; + } + + base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!base) { + dev_err(&pdev->dev, "Unable to map resource"); + return -EINVAL; + } + + /* We need to know tclk in order to calculate the UART divisor */ + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "Clk missing"); + return PTR_ERR(clk); + } + + tclk = clk_get_rate(clk); + + /* Check that nothing else has already setup a handler */ + if (pm_power_off) { + lookup_symbol_name((ulong)pm_power_off, symname); + dev_err(&pdev->dev, + "pm_power_off already claimed %p %s", + pm_power_off, symname); + return -EBUSY; + } + pm_power_off = qnap_power_off; + + return 0; +} + +static int qnap_power_off_remove(struct platform_device *pdev) +{ + pm_power_off = NULL; + return 0; +} + +static const struct of_device_id qnap_power_off_of_match_table[] = { + { .compatible = "qnap,power-off", }, + {} +}; +MODULE_DEVICE_TABLE(of, qnap_power_off_of_match_table); + +static struct platform_driver qnap_power_off_driver = { + .probe = qnap_power_off_probe, + .remove = qnap_power_off_remove, + .driver = { + .owner = THIS_MODULE, + .name = "qnap_power_off", + .of_match_table = of_match_ptr(qnap_power_off_of_match_table), + }, +}; +module_platform_driver(qnap_power_off_driver); + +MODULE_AUTHOR("Andrew Lunn "); +MODULE_DESCRIPTION("QNAP Power off driver"); +MODULE_LICENSE("GPLv2+"); -- cgit v1.2.3-59-g8ed1b From ffd8f9a727f27270584c7472ca56cce1e4736888 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Fri, 28 Dec 2012 13:25:11 +0100 Subject: power/reset: Add a new driver implementing 'power off by restarting' Some devices, Buffalo Linkstation LS-XHL and LS-CHLv2 for example, power-off by restarting to letting u-boot hold the SoC until the user presses a key. Add a generic driver to implement this. It binds a function to pm_power_off, which calls arm_pm_restart. Signed-off-by: Andrew Lunn Tested-by: Michael Walle Signed-off-by: Anton Vorontsov --- .../bindings/power_supply/restart-poweroff.txt | 8 +++ drivers/power/reset/Kconfig | 8 +++ drivers/power/reset/Makefile | 1 + drivers/power/reset/restart-poweroff.c | 65 ++++++++++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 Documentation/devicetree/bindings/power_supply/restart-poweroff.txt create mode 100644 drivers/power/reset/restart-poweroff.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/power_supply/restart-poweroff.txt b/Documentation/devicetree/bindings/power_supply/restart-poweroff.txt new file mode 100644 index 000000000000..5776e684afda --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/restart-poweroff.txt @@ -0,0 +1,8 @@ +* Restart Power Off + +Buffalo Linkstation LS-XHL and LS-CHLv2, and other devices power off +by restarting and letting u-boot keep hold of the machine until the +user presses a button. + +Required Properties: +- compatible: Should be "restart-poweroff" diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 6453aac701c0..1ae65b822864 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -22,3 +22,11 @@ config POWER_RESET_QNAP commands to the microcontroller which controls the main power. Say Y if you have a QNAP NAS. + +config POWER_RESET_RESTART + bool "Restart power-off driver" + depends on ARM + help + Some boards don't actually have the ability to power off. + Instead they restart, and u-boot holds the SoC until the + user presses a key. u-boot then boots into Linux. diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index c4d3b2d11b9e..0f317f50c56f 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o +obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o \ No newline at end of file diff --git a/drivers/power/reset/restart-poweroff.c b/drivers/power/reset/restart-poweroff.c new file mode 100644 index 000000000000..b11b9e887174 --- /dev/null +++ b/drivers/power/reset/restart-poweroff.c @@ -0,0 +1,65 @@ +/* + * Power off by restarting and let u-boot keep hold of the machine + * until the user presses a button for example. + * + * Andrew Lunn + * + * Copyright (C) 2012 Andrew Lunn + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include + +static void restart_poweroff_do_poweroff(void) +{ + arm_pm_restart('h', NULL); +} + +static int __devinit restart_poweroff_probe(struct platform_device *pdev) +{ + /* If a pm_power_off function has already been added, leave it alone */ + if (pm_power_off != NULL) { + dev_err(&pdev->dev, + "pm_power_off function already registered"); + return -EBUSY; + } + + pm_power_off = &restart_poweroff_do_poweroff; + return 0; +} + +static int __devexit restart_poweroff_remove(struct platform_device *pdev) +{ + if (pm_power_off == &restart_poweroff_do_poweroff) + pm_power_off = NULL; + + return 0; +} + +static const struct of_device_id of_restart_poweroff_match[] = { + { .compatible = "restart-poweroff", }, + {}, +}; + +static struct platform_driver restart_poweroff_driver = { + .probe = restart_poweroff_probe, + .remove = __devexit_p(restart_poweroff_remove), + .driver = { + .name = "poweroff-restart", + .owner = THIS_MODULE, + .of_match_table = of_restart_poweroff_match, + }, +}; +module_platform_driver(restart_poweroff_driver); + +MODULE_AUTHOR("Andrew Lunn Date: Fri, 4 Jan 2013 13:05:17 -0800 Subject: cgroups: move cgroup_event_listener.c to tools/cgroup Move the cgroup_event_listener.c tool from Documentation into the new tools/cgroup directory. This change involves wiring cgroup_event_listener.c into the tools/ make system so that is can be built with: $ make tools/cgroup Signed-off-by: Greg Thelen Signed-off-by: Tejun Heo --- Documentation/cgroups/00-INDEX | 2 - Documentation/cgroups/cgroup_event_listener.c | 110 -------------------------- Documentation/cgroups/memcg_test.txt | 3 +- tools/Makefile | 19 ++--- tools/cgroup/.gitignore | 1 + tools/cgroup/Makefile | 11 +++ tools/cgroup/cgroup_event_listener.c | 110 ++++++++++++++++++++++++++ 7 files changed, 133 insertions(+), 123 deletions(-) delete mode 100644 Documentation/cgroups/cgroup_event_listener.c create mode 100644 tools/cgroup/.gitignore create mode 100644 tools/cgroup/Makefile create mode 100644 tools/cgroup/cgroup_event_listener.c (limited to 'Documentation') diff --git a/Documentation/cgroups/00-INDEX b/Documentation/cgroups/00-INDEX index f78b90a35ad0..f5635a09c3f6 100644 --- a/Documentation/cgroups/00-INDEX +++ b/Documentation/cgroups/00-INDEX @@ -4,8 +4,6 @@ blkio-controller.txt - Description for Block IO Controller, implementation and usage details. cgroups.txt - Control Groups definition, implementation details, examples and API. -cgroup_event_listener.c - - A user program for cgroup listener. cpuacct.txt - CPU Accounting Controller; account CPU usage for groups of tasks. cpusets.txt diff --git a/Documentation/cgroups/cgroup_event_listener.c b/Documentation/cgroups/cgroup_event_listener.c deleted file mode 100644 index 3e082f96dc12..000000000000 --- a/Documentation/cgroups/cgroup_event_listener.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * cgroup_event_listener.c - Simple listener of cgroup events - * - * Copyright (C) Kirill A. Shutemov - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define USAGE_STR "Usage: cgroup_event_listener \n" - -int main(int argc, char **argv) -{ - int efd = -1; - int cfd = -1; - int event_control = -1; - char event_control_path[PATH_MAX]; - char line[LINE_MAX]; - int ret; - - if (argc != 3) { - fputs(USAGE_STR, stderr); - return 1; - } - - cfd = open(argv[1], O_RDONLY); - if (cfd == -1) { - fprintf(stderr, "Cannot open %s: %s\n", argv[1], - strerror(errno)); - goto out; - } - - ret = snprintf(event_control_path, PATH_MAX, "%s/cgroup.event_control", - dirname(argv[1])); - if (ret >= PATH_MAX) { - fputs("Path to cgroup.event_control is too long\n", stderr); - goto out; - } - - event_control = open(event_control_path, O_WRONLY); - if (event_control == -1) { - fprintf(stderr, "Cannot open %s: %s\n", event_control_path, - strerror(errno)); - goto out; - } - - efd = eventfd(0, 0); - if (efd == -1) { - perror("eventfd() failed"); - goto out; - } - - ret = snprintf(line, LINE_MAX, "%d %d %s", efd, cfd, argv[2]); - if (ret >= LINE_MAX) { - fputs("Arguments string is too long\n", stderr); - goto out; - } - - ret = write(event_control, line, strlen(line) + 1); - if (ret == -1) { - perror("Cannot write to cgroup.event_control"); - goto out; - } - - while (1) { - uint64_t result; - - ret = read(efd, &result, sizeof(result)); - if (ret == -1) { - if (errno == EINTR) - continue; - perror("Cannot read from eventfd"); - break; - } - assert(ret == sizeof(result)); - - ret = access(event_control_path, W_OK); - if ((ret == -1) && (errno == ENOENT)) { - puts("The cgroup seems to have removed."); - ret = 0; - break; - } - - if (ret == -1) { - perror("cgroup.event_control " - "is not accessible any more"); - break; - } - - printf("%s %s: crossed\n", argv[1], argv[2]); - } - -out: - if (efd >= 0) - close(efd); - if (event_control >= 0) - close(event_control); - if (cfd >= 0) - close(cfd); - - return (ret != 0); -} diff --git a/Documentation/cgroups/memcg_test.txt b/Documentation/cgroups/memcg_test.txt index fc8fa97a09ac..ce94a83a7d9a 100644 --- a/Documentation/cgroups/memcg_test.txt +++ b/Documentation/cgroups/memcg_test.txt @@ -399,8 +399,7 @@ Under below explanation, we assume CONFIG_MEM_RES_CTRL_SWAP=y. 9.10 Memory thresholds Memory controller implements memory thresholds using cgroups notification - API. You can use Documentation/cgroups/cgroup_event_listener.c to test - it. + API. You can use tools/cgroup/cgroup_event_listener.c to test it. (Shell-A) Create cgroup and run event listener # mkdir /cgroup/A diff --git a/tools/Makefile b/tools/Makefile index 1f9a529fe544..fc57a28abca1 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -3,6 +3,7 @@ include scripts/Makefile.include help: @echo 'Possible targets:' @echo '' + @echo ' cgroup - cgroup tools' @echo ' cpupower - a tool for all things x86 CPU power' @echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer' @echo ' lguest - a minimal 32-bit x86 hypervisor' @@ -33,7 +34,7 @@ help: cpupower: FORCE $(call descend,power/$@) -firewire lguest perf usb virtio vm: FORCE +cgroup firewire lguest perf usb virtio vm: FORCE $(call descend,$@) selftests: FORCE @@ -45,7 +46,7 @@ turbostat x86_energy_perf_policy: FORCE cpupower_install: $(call descend,power/$(@:_install=),install) -firewire_install lguest_install perf_install usb_install virtio_install vm_install: +cgroup_install firewire_install lguest_install perf_install usb_install virtio_install vm_install: $(call descend,$(@:_install=),install) selftests_install: @@ -54,14 +55,14 @@ selftests_install: turbostat_install x86_energy_perf_policy_install: $(call descend,power/x86/$(@:_install=),install) -install: cpupower_install firewire_install lguest_install perf_install \ - selftests_install turbostat_install usb_install virtio_install \ - vm_install x86_energy_perf_policy_install +install: cgroup_install cpupower_install firewire_install lguest_install \ + perf_install selftests_install turbostat_install usb_install \ + virtio_install vm_install x86_energy_perf_policy_install cpupower_clean: $(call descend,power/cpupower,clean) -firewire_clean lguest_clean perf_clean usb_clean virtio_clean vm_clean: +cgroup_clean firewire_clean lguest_clean perf_clean usb_clean virtio_clean vm_clean: $(call descend,$(@:_clean=),clean) selftests_clean: @@ -70,8 +71,8 @@ selftests_clean: turbostat_clean x86_energy_perf_policy_clean: $(call descend,power/x86/$(@:_clean=),clean) -clean: cpupower_clean firewire_clean lguest_clean perf_clean selftests_clean \ - turbostat_clean usb_clean virtio_clean vm_clean \ - x86_energy_perf_policy_clean +clean: cgroup_clean cpupower_clean firewire_clean lguest_clean perf_clean \ + selftests_clean turbostat_clean usb_clean virtio_clean \ + vm_clean x86_energy_perf_policy_clean .PHONY: FORCE diff --git a/tools/cgroup/.gitignore b/tools/cgroup/.gitignore new file mode 100644 index 000000000000..633cd9b874f9 --- /dev/null +++ b/tools/cgroup/.gitignore @@ -0,0 +1 @@ +cgroup_event_listener diff --git a/tools/cgroup/Makefile b/tools/cgroup/Makefile new file mode 100644 index 000000000000..b4286196b763 --- /dev/null +++ b/tools/cgroup/Makefile @@ -0,0 +1,11 @@ +# Makefile for cgroup tools + +CC = $(CROSS_COMPILE)gcc +CFLAGS = -Wall -Wextra + +all: cgroup_event_listener +%: %.c + $(CC) $(CFLAGS) -o $@ $^ + +clean: + $(RM) cgroup_event_listener diff --git a/tools/cgroup/cgroup_event_listener.c b/tools/cgroup/cgroup_event_listener.c new file mode 100644 index 000000000000..3e082f96dc12 --- /dev/null +++ b/tools/cgroup/cgroup_event_listener.c @@ -0,0 +1,110 @@ +/* + * cgroup_event_listener.c - Simple listener of cgroup events + * + * Copyright (C) Kirill A. Shutemov + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define USAGE_STR "Usage: cgroup_event_listener \n" + +int main(int argc, char **argv) +{ + int efd = -1; + int cfd = -1; + int event_control = -1; + char event_control_path[PATH_MAX]; + char line[LINE_MAX]; + int ret; + + if (argc != 3) { + fputs(USAGE_STR, stderr); + return 1; + } + + cfd = open(argv[1], O_RDONLY); + if (cfd == -1) { + fprintf(stderr, "Cannot open %s: %s\n", argv[1], + strerror(errno)); + goto out; + } + + ret = snprintf(event_control_path, PATH_MAX, "%s/cgroup.event_control", + dirname(argv[1])); + if (ret >= PATH_MAX) { + fputs("Path to cgroup.event_control is too long\n", stderr); + goto out; + } + + event_control = open(event_control_path, O_WRONLY); + if (event_control == -1) { + fprintf(stderr, "Cannot open %s: %s\n", event_control_path, + strerror(errno)); + goto out; + } + + efd = eventfd(0, 0); + if (efd == -1) { + perror("eventfd() failed"); + goto out; + } + + ret = snprintf(line, LINE_MAX, "%d %d %s", efd, cfd, argv[2]); + if (ret >= LINE_MAX) { + fputs("Arguments string is too long\n", stderr); + goto out; + } + + ret = write(event_control, line, strlen(line) + 1); + if (ret == -1) { + perror("Cannot write to cgroup.event_control"); + goto out; + } + + while (1) { + uint64_t result; + + ret = read(efd, &result, sizeof(result)); + if (ret == -1) { + if (errno == EINTR) + continue; + perror("Cannot read from eventfd"); + break; + } + assert(ret == sizeof(result)); + + ret = access(event_control_path, W_OK); + if ((ret == -1) && (errno == ENOENT)) { + puts("The cgroup seems to have removed."); + ret = 0; + break; + } + + if (ret == -1) { + perror("cgroup.event_control " + "is not accessible any more"); + break; + } + + printf("%s %s: crossed\n", argv[1], argv[2]); + } + +out: + if (efd >= 0) + close(efd); + if (event_control >= 0) + close(event_control); + if (cfd >= 0) + close(cfd); + + return (ret != 0); +} -- cgit v1.2.3-59-g8ed1b From 7e8b1e78ea028cbd32337e2aea574a8466c796bb Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 13 Dec 2012 11:07:32 +0100 Subject: Documentation: Memory barrier semantics of atomic_xchg() Add atomic_xchg() to documentation for atomic operations and memory barriers. Signed-off-by: Richard Braun Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett --- Documentation/atomic_ops.txt | 2 ++ Documentation/memory-barriers.txt | 1 + 2 files changed, 3 insertions(+) (limited to 'Documentation') diff --git a/Documentation/atomic_ops.txt b/Documentation/atomic_ops.txt index 27f2b21a9d5c..d9ca5be9b471 100644 --- a/Documentation/atomic_ops.txt +++ b/Documentation/atomic_ops.txt @@ -253,6 +253,8 @@ This performs an atomic exchange operation on the atomic variable v, setting the given new value. It returns the old value that the atomic variable v had just before the operation. +atomic_xchg requires explicit memory barriers around the operation. + int atomic_cmpxchg(atomic_t *v, int old, int new); This performs an atomic compare exchange operation on the atomic value v, diff --git a/Documentation/memory-barriers.txt b/Documentation/memory-barriers.txt index 3c4e1b3b80a1..fa5d8a9ae205 100644 --- a/Documentation/memory-barriers.txt +++ b/Documentation/memory-barriers.txt @@ -1685,6 +1685,7 @@ explicit lock operations, described later). These include: xchg(); cmpxchg(); + atomic_xchg(); atomic_cmpxchg(); atomic_inc_return(); atomic_dec_return(); -- cgit v1.2.3-59-g8ed1b From 7265a6bbdd28c37e567162308a09807cb22cdb75 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Mon, 7 Jan 2013 20:52:42 +0000 Subject: netconsole: add IPv6 example in doc Update the netconsole document as well. Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- Documentation/networking/netconsole.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/networking/netconsole.txt b/Documentation/networking/netconsole.txt index 2e9e0ae2cd45..a5d574a9ae09 100644 --- a/Documentation/networking/netconsole.txt +++ b/Documentation/networking/netconsole.txt @@ -1,9 +1,10 @@ started by Ingo Molnar , 2001.09.17 2.6 port and netpoll api by Matt Mackall , Sep 9 2003 +IPv6 support by Cong Wang , Jan 1 2013 Please send bug reports to Matt Mackall -and Satyam Sharma +Satyam Sharma , and Cong Wang Introduction: ============= @@ -41,6 +42,10 @@ Examples: insmod netconsole netconsole=@/,@10.0.0.2/ + or using IPv6 + + insmod netconsole netconsole=@/,@fd00:1:2:3::1/ + It also supports logging to multiple remote agents by specifying parameters for the multiple agents separated by semicolons and the complete string enclosed in "quotes", thusly: -- cgit v1.2.3-59-g8ed1b From 2ad554a502facd705ce6eb362d0f1ac3ca426508 Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Tue, 18 Dec 2012 17:35:02 +0100 Subject: NFC: update HCI documentation Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- Documentation/nfc/nfc-hci.txt | 129 +++++++++++++++++++++++++++++++++--------- 1 file changed, 103 insertions(+), 26 deletions(-) (limited to 'Documentation') diff --git a/Documentation/nfc/nfc-hci.txt b/Documentation/nfc/nfc-hci.txt index 89a339c9b079..0686c9e211c2 100644 --- a/Documentation/nfc/nfc-hci.txt +++ b/Documentation/nfc/nfc-hci.txt @@ -17,10 +17,12 @@ HCI HCI registers as an nfc device with NFC Core. Requests coming from userspace are routed through netlink sockets to NFC Core and then to HCI. From this point, they are translated in a sequence of HCI commands sent to the HCI layer in the -host controller (the chip). The sending context blocks while waiting for the -response to arrive. +host controller (the chip). Commands can be executed synchronously (the sending +context blocks waiting for response) or asynchronously (the response is returned +from HCI Rx context). HCI events can also be received from the host controller. They will be handled -and a translation will be forwarded to NFC Core as needed. +and a translation will be forwarded to NFC Core as needed. There are hooks to +let the HCI driver handle proprietary events or override standard behavior. HCI uses 2 execution contexts: - one for executing commands : nfc_hci_msg_tx_work(). Only one command can be executing at any given moment. @@ -33,6 +35,8 @@ The Session initialization is an HCI standard which must unfortunately support proprietary gates. This is the reason why the driver will pass a list of proprietary gates that must be part of the session. HCI will ensure all those gates have pipes connected when the hci device is set up. +In case the chip supports pre-opened gates and pseudo-static pipes, the driver +can pass that information to HCI core. HCI Gates and Pipes ------------------- @@ -46,6 +50,13 @@ without knowing the pipe connected to it. Driver interface ---------------- +A driver is generally written in two parts : the physical link management and +the HCI management. This makes it easier to maintain a driver for a chip that +can be connected using various phy (i2c, spi, ...) + +HCI Management +-------------- + A driver would normally register itself with HCI and provide the following entry points: @@ -53,58 +64,113 @@ struct nfc_hci_ops { int (*open)(struct nfc_hci_dev *hdev); void (*close)(struct nfc_hci_dev *hdev); int (*hci_ready) (struct nfc_hci_dev *hdev); - int (*xmit)(struct nfc_hci_dev *hdev, struct sk_buff *skb); - int (*start_poll)(struct nfc_hci_dev *hdev, u32 protocols); - int (*target_from_gate)(struct nfc_hci_dev *hdev, u8 gate, - struct nfc_target *target); + int (*xmit) (struct nfc_hci_dev *hdev, struct sk_buff *skb); + int (*start_poll) (struct nfc_hci_dev *hdev, + u32 im_protocols, u32 tm_protocols); + int (*dep_link_up)(struct nfc_hci_dev *hdev, struct nfc_target *target, + u8 comm_mode, u8 *gb, size_t gb_len); + int (*dep_link_down)(struct nfc_hci_dev *hdev); + int (*target_from_gate) (struct nfc_hci_dev *hdev, u8 gate, + struct nfc_target *target); int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate, struct nfc_target *target); - int (*data_exchange) (struct nfc_hci_dev *hdev, - struct nfc_target *target, - struct sk_buff *skb, struct sk_buff **res_skb); + int (*im_transceive) (struct nfc_hci_dev *hdev, + struct nfc_target *target, struct sk_buff *skb, + data_exchange_cb_t cb, void *cb_context); + int (*tm_send)(struct nfc_hci_dev *hdev, struct sk_buff *skb); int (*check_presence)(struct nfc_hci_dev *hdev, struct nfc_target *target); + int (*event_received)(struct nfc_hci_dev *hdev, u8 gate, u8 event, + struct sk_buff *skb); }; - open() and close() shall turn the hardware on and off. - hci_ready() is an optional entry point that is called right after the hci session has been set up. The driver can use it to do additional initialization that must be performed using HCI commands. -- xmit() shall simply write a frame to the chip. +- xmit() shall simply write a frame to the physical link. - start_poll() is an optional entrypoint that shall set the hardware in polling mode. This must be implemented only if the hardware uses proprietary gates or a mechanism slightly different from the HCI standard. +- dep_link_up() is called after a p2p target has been detected, to finish +the p2p connection setup with hardware parameters that need to be passed back +to nfc core. +- dep_link_down() is called to bring the p2p link down. - target_from_gate() is an optional entrypoint to return the nfc protocols corresponding to a proprietary gate. - complete_target_discovered() is an optional entry point to let the driver perform additional proprietary processing necessary to auto activate the discovered target. -- data_exchange() must be implemented by the driver if proprietary HCI commands +- im_transceive() must be implemented by the driver if proprietary HCI commands are required to send data to the tag. Some tag types will require custom commands, others can be written to using the standard HCI commands. The driver can check the tag type and either do proprietary processing, or return 1 to ask -for standard processing. +for standard processing. The data exchange command itself must be sent +asynchronously. +- tm_send() is called to send data in the case of a p2p connection - check_presence() is an optional entry point that will be called regularly by the core to check that an activated tag is still in the field. If this is not implemented, the core will not be able to push tag_lost events to the user space +- event_received() is called to handle an event coming from the chip. Driver +can handle the event or return 1 to let HCI attempt standard processing. On the rx path, the driver is responsible to push incoming HCP frames to HCI using nfc_hci_recv_frame(). HCI will take care of re-aggregation and handling This must be done from a context that can sleep. -SHDLC ------ +PHY Management +-------------- + +The physical link (i2c, ...) management is defined by the following struture: + +struct nfc_phy_ops { + int (*write)(void *dev_id, struct sk_buff *skb); + int (*enable)(void *dev_id); + void (*disable)(void *dev_id); +}; + +enable(): turn the phy on (power on), make it ready to transfer data +disable(): turn the phy off +write(): Send a data frame to the chip. Note that to enable higher +layers such as an llc to store the frame for re-emission, this function must +not alter the skb. It must also not return a positive result (return 0 for +success, negative for failure). + +Data coming from the chip shall be sent directly to nfc_hci_recv_frame(). + +LLC +--- + +Communication between the CPU and the chip often requires some link layer +protocol. Those are isolated as modules managed by the HCI layer. There are +currently two modules : nop (raw transfert) and shdlc. +A new llc must implement the following functions: + +struct nfc_llc_ops { + void *(*init) (struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv, + rcv_to_hci_t rcv_to_hci, int tx_headroom, + int tx_tailroom, int *rx_headroom, int *rx_tailroom, + llc_failure_t llc_failure); + void (*deinit) (struct nfc_llc *llc); + int (*start) (struct nfc_llc *llc); + int (*stop) (struct nfc_llc *llc); + void (*rcv_from_drv) (struct nfc_llc *llc, struct sk_buff *skb); + int (*xmit_from_hci) (struct nfc_llc *llc, struct sk_buff *skb); +}; + +- init() : allocate and init your private storage +- deinit() : cleanup +- start() : establish the logical connection +- stop () : terminate the logical connection +- rcv_from_drv() : handle data coming from the chip, going to HCI +- xmit_from_hci() : handle data sent by HCI, going to the chip -Most chips use shdlc to ensure integrity and delivery ordering of the HCP -frames between the host controller (the chip) and hosts (entities connected -to the chip, like the cpu). In order to simplify writing the driver, an shdlc -layer is available for use by the driver. -When used, the driver actually registers with shdlc, and shdlc will register -with HCI. HCI sees shdlc as the driver and thus send its HCP frames -through shdlc->xmit. -SHDLC adds a new execution context (nfc_shdlc_sm_work()) to run its state -machine and handle both its rx and tx path. +The llc must be registered with nfc before it can be used. Do that by +calling nfc_llc_register(const char *name, struct nfc_llc_ops *ops); + +Again, note that the llc does not handle the physical link. It is thus very +easy to mix any physical link with any llc for a given chip driver. Included Drivers ---------------- @@ -117,10 +183,12 @@ Execution Contexts The execution contexts are the following: - IRQ handler (IRQH): -fast, cannot sleep. stores incoming frames into an shdlc rx queue +fast, cannot sleep. sends incoming frames to HCI where they are passed to +the current llc. In case of shdlc, the frame is queued in shdlc rx queue. - SHDLC State Machine worker (SMW) -handles shdlc rx & tx queues. Dispatches HCI cmd responses. +Only when llc_shdlc is used: handles shdlc rx & tx queues. +Dispatches HCI cmd responses. - HCI Tx Cmd worker (MSGTXWQ) Serializes execution of HCI commands. Completes execution in case of response @@ -166,6 +234,15 @@ waiting command execution. Response processing involves invoking the completion callback that was provided by nfc_hci_msg_tx_work() when it sent the command. The completion callback will then wake the syscall context. +It is also possible to execute the command asynchronously using this API: + +static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd, + const u8 *param, size_t param_len, + data_exchange_cb_t cb, void *cb_context) + +The workflow is the same, except that the API call returns immediately, and +the callback will be called with the result from the SMW context. + Workflow receiving an HCI event or command ------------------------------------------ -- cgit v1.2.3-59-g8ed1b From 1353a10c474d1c6895051d33eefeb93542015008 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Thu, 10 Jan 2013 01:25:04 +0100 Subject: NFC: Update pn544 documentation The pn544 driver no longer has a /dev/pn544 interface nor a sysfs one. Reported-by: Marcel Holtmann Signed-off-by: Samuel Ortiz --- Documentation/nfc/nfc-pn544.txt | 84 +---------------------------------------- 1 file changed, 1 insertion(+), 83 deletions(-) (limited to 'Documentation') diff --git a/Documentation/nfc/nfc-pn544.txt b/Documentation/nfc/nfc-pn544.txt index 2fcac9f5996e..b36ca14ca2d6 100644 --- a/Documentation/nfc/nfc-pn544.txt +++ b/Documentation/nfc/nfc-pn544.txt @@ -1,32 +1,15 @@ Kernel driver for the NXP Semiconductors PN544 Near Field Communication chip -Author: Jari Vanhala -Contact: Matti Aaltonen (matti.j.aaltonen at nokia.com) - General ------- The PN544 is an integrated transmission module for contactless communication. The driver goes under drives/nfc/ and is compiled as a -module named "pn544". It registers a misc device and creates a device -file named "/dev/pn544". +module named "pn544". Host Interfaces: I2C, SPI and HSU, this driver supports currently only I2C. -The Interface -------------- - -The driver offers a sysfs interface for a hardware test and an IOCTL -interface for selecting between two operating modes. There are read, -write and poll functions for transferring messages. The two operating -modes are the normal (HCI) mode and the firmware update mode. - -PN544 is controlled by sending messages from the userspace to the -chip. The main function of the driver is just to pass those messages -without caring about the message content. - - Protocols --------- @@ -47,68 +30,3 @@ and third (LSB) bytes of the message. The maximum FW message length is For the ETSI HCI specification see http://www.etsi.org/WebSite/Technologies/ProtocolSpecification.aspx - -The Hardware Test ------------------ - -The idea of the test is that it can performed by reading from the -corresponding sysfs file. The test is implemented in the board file -and it should test that PN544 can be put into the firmware update -mode. If the test is not implemented the sysfs file does not get -created. - -Example: -> cat /sys/module/pn544/drivers/i2c\:pn544/3-002b/nfc_test -1 - -Normal Operation ----------------- - -PN544 is powered up when the device file is opened, otherwise it's -turned off. Only one instance can use the device at a time. - -Userspace applications control PN544 with HCI messages. The hardware -sends an interrupt when data is available for reading. Data is -physically read when the read function is called by a userspace -application. Poll() checks the read interrupt state. Configuration and -self testing are also done from the userspace using read and write. - -Example platform data: - -static int rx71_pn544_nfc_request_resources(struct i2c_client *client) -{ - /* Get and setup the HW resources for the device */ -} - -static void rx71_pn544_nfc_free_resources(void) -{ - /* Release the HW resources */ -} - -static void rx71_pn544_nfc_enable(int fw) -{ - /* Turn the device on */ -} - -static int rx71_pn544_nfc_test(void) -{ - /* - * Put the device into the FW update mode - * and then back to the normal mode. - * Check the behavior and return one on success, - * zero on failure. - */ -} - -static void rx71_pn544_nfc_disable(void) -{ - /* turn the power off */ -} - -static struct pn544_nfc_platform_data rx71_nfc_data = { - .request_resources = rx71_pn544_nfc_request_resources, - .free_resources = rx71_pn544_nfc_free_resources, - .enable = rx71_pn544_nfc_enable, - .test = rx71_pn544_nfc_test, - .disable = rx71_pn544_nfc_disable, -}; -- cgit v1.2.3-59-g8ed1b From 65397edfa1bba622a8159bed4c8efcc4ddb8ed16 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 11 Dec 2012 17:28:27 +0000 Subject: ARM: psci: add devicetree binding for describing PSCI firmware This patch adds a new devicetree binding for describing PSCI firmware to Linux. Acked-by: Arnd Bergmann Acked-by: Nicolas Pitre Signed-off-by: Will Deacon --- Documentation/devicetree/bindings/arm/psci.txt | 55 ++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/psci.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/psci.txt b/Documentation/devicetree/bindings/arm/psci.txt new file mode 100644 index 000000000000..433afe9cb590 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/psci.txt @@ -0,0 +1,55 @@ +* Power State Coordination Interface (PSCI) + +Firmware implementing the PSCI functions described in ARM document number +ARM DEN 0022A ("Power State Coordination Interface System Software on ARM +processors") can be used by Linux to initiate various CPU-centric power +operations. + +Issue A of the specification describes functions for CPU suspend, hotplug +and migration of secure software. + +Functions are invoked by trapping to the privilege level of the PSCI +firmware (specified as part of the binding below) and passing arguments +in a manner similar to that specified by AAPCS: + + r0 => 32-bit Function ID / return value + {r1 - r3} => Parameters + +Note that the immediate field of the trapping instruction must be set +to #0. + + +Main node required properties: + + - compatible : Must be "arm,psci" + + - method : The method of calling the PSCI firmware. Permitted + values are: + + "smc" : SMC #0, with the register assignments specified + in this binding. + + "hvc" : HVC #0, with the register assignments specified + in this binding. + +Main node optional properties: + + - cpu_suspend : Function ID for CPU_SUSPEND operation + + - cpu_off : Function ID for CPU_OFF operation + + - cpu_on : Function ID for CPU_ON operation + + - migrate : Function ID for MIGRATE operation + + +Example: + + psci { + compatible = "arm,psci"; + method = "smc"; + cpu_suspend = <0x95c10000>; + cpu_off = <0x95c10001>; + cpu_on = <0x95c10002>; + migrate = <0x95c10003>; + }; -- cgit v1.2.3-59-g8ed1b From 3d55b323709661df34d93e4cdcc5337620e34dfd Mon Sep 17 00:00:00 2001 From: Vijay Subramanian Date: Wed, 9 Jan 2013 12:21:30 +0000 Subject: doc: Clarify behavior when sysctl tcp_ecn = 1 Recent commit (commit 7e3a2dc52953 doc: make the description of how tcp_ecn works more explicit and clear ) clarified the behavior of tcp_ecn sysctl variable but description is inconsistent. When requested by incoming conections, ECN is enabled with not just tcp_ecn = 2 but also with tcp_ecn = 1. This patch makes it clear that with tcp_ecn = 1, ECN is enabled when requested by incoming connections. Also fix spelling of 'incoming'. Signed-off-by: Vijay Subramanian Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index dd52d516cb89..51dcd52a91c4 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -214,8 +214,9 @@ tcp_ecn - INTEGER congestion before having to drop packets. Possible values are: 0 Disable ECN. Neither initiate nor accept ECN. - 1 Always request ECN on outgoing connection attempts. - 2 Enable ECN when requested by incomming connections + 1 Enable ECN when requested by incoming connections and + also request ECN on outgoing connection attempts. + 2 Enable ECN when requested by incoming connections but do not request ECN on outgoing connections. Default: 2 -- cgit v1.2.3-59-g8ed1b From e32af889458837d11a1ec5ec98934a1e711b049d Mon Sep 17 00:00:00 2001 From: Gabriel Fernandez Date: Mon, 17 Dec 2012 15:53:24 +0100 Subject: pinctrl/nomadik: add device tree support This implements pin multiplexing and pin configuration for the Nomadik pin controller using the device tree. Signed-off-by: Gabriel Fernandez Reviewed-by: Stephen Warren Reviewed-by: Philippe Langlais Signed-off-by: Linus Walleij --- .../devicetree/bindings/pinctrl/ste,nomadik.txt | 140 +++++++++++ .../devicetree/bindings/vendor-prefixes.txt | 1 + arch/arm/mach-ux500/cpu-db8500.c | 2 +- drivers/pinctrl/pinctrl-nomadik.c | 277 ++++++++++++++++++++- 4 files changed, 418 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/pinctrl/ste,nomadik.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pinctrl/ste,nomadik.txt b/Documentation/devicetree/bindings/pinctrl/ste,nomadik.txt new file mode 100644 index 000000000000..9a2f3f420526 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/ste,nomadik.txt @@ -0,0 +1,140 @@ +ST Ericsson Nomadik pinmux controller + +Required properties: +- compatible: "stericsson,nmk-pinctrl", "stericsson,nmk-pinctrl-db8540", + "stericsson,nmk-pinctrl-stn8815" +- reg: Should contain the register physical address and length of the PRCMU. + +Please refer to pinctrl-bindings.txt in this directory for details of the +common pinctrl bindings used by client devices, including the meaning of the +phrase "pin configuration node". + +ST Ericsson's pin configuration nodes act as a container for an arbitrary number of +subnodes. Each of these subnodes represents some desired configuration for a +pin, a group, or a list of pins or groups. This configuration can include the +mux function to select on those pin(s)/group(s), and various pin configuration +parameters, such as input, output, pull up, pull down... + +The name of each subnode is not important; all subnodes should be enumerated +and processed purely based on their content. + +Required subnode-properties: +- ste,pins : An array of strings. Each string contains the name of a pin or + group. + +Optional subnode-properties: +- ste,function: A string containing the name of the function to mux to the + pin or group. + +- ste,config: Handle of pin configuration node (e.g. ste,config = <&slpm_in_wkup_pdis>) + +- ste,input : <0/1/2> + 0: input with no pull + 1: input with pull up, + 2: input with pull down, + +- ste,output: <0/1/2> + 0: output low, + 1: output high, + 2: output (value is not specified). + +- ste,sleep: <0/1> + 0: sleep mode disable, + 1: sleep mode enable. + +- ste,sleep-input: <0/1/2/3> + 0: sleep input with no pull, + 1: sleep input with pull up, + 2: sleep input with pull down. + 3: sleep input and keep last input configuration (no pull, pull up or pull down). + +- ste,sleep-output: <0/1/2> + 0: sleep output low, + 1: sleep output high, + 2: sleep output (value is not specified). + +- ste,sleep-gpio: <0/1> + 0: disable sleep gpio mode, + 1: enable sleep gpio mode. + +- ste,sleep-wakeup: <0/1> + 0: wake-up detection enabled, + 1: wake-up detection disabled. + +- ste,sleep-pull-disable: <0/1> + 0: GPIO pull-up or pull-down resistor is enabled, when pin is an input, + 1: GPIO pull-up and pull-down resistor are disabled. + +Example board file extract: + + pinctrl@80157000 { + compatible = "stericsson,nmk-pinctrl"; + reg = <0x80157000 0x2000>; + + pinctrl-names = "default"; + + slpm_in_wkup_pdis: slpm_in_wkup_pdis { + ste,sleep = <1>; + ste,sleep-input = <3>; + ste,sleep-wakeup = <1>; + ste,sleep-pull-disable = <0>; + }; + + slpm_out_hi_wkup_pdis: slpm_out_hi_wkup_pdis { + ste,sleep = <1>; + ste,sleep-output = <1>; + ste,sleep-wakeup = <1>; + ste,sleep-pull-disable = <0>; + }; + + slpm_out_wkup_pdis: slpm_out_wkup_pdis { + ste,sleep = <1>; + ste,sleep-output = <2>; + ste,sleep-wakeup = <1>; + ste,sleep-pull-disable = <0>; + }; + + uart0 { + uart0_default_mux: uart0_mux { + u0_default_mux { + ste,function = "u0"; + ste,pins = "u0_a_1"; + }; + }; + uart0_default_mode: uart0_default { + uart0_default_cfg1 { + ste,pins = "GPIO0", "GPIO2"; + ste,input = <1>; + }; + + uart0_default_cfg2 { + ste,pins = "GPIO1", "GPIO3"; + ste,output = <1>; + }; + }; + uart0_sleep_mode: uart0_sleep { + uart0_sleep_cfg1 { + ste,pins = "GPIO0", "GPIO2"; + ste,config = <&slpm_in_wkup_pdis>; + }; + uart0_sleep_cfg2 { + ste,pins = "GPIO1"; + ste,config = <&slpm_out_hi_wkup_pdis>; + }; + uart0_sleep_cfg3 { + ste,pins = "GPIO3"; + ste,config = <&slpm_out_wkup_pdis>; + }; + }; + }; + }; + + uart@80120000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x80120000 0x1000>; + interrupts = <0 11 0x4>; + + pinctrl-names = "default","sleep"; + pinctrl-0 = <&uart0_default_mux>, <&uart0_default_mode>; + pinctrl-1 = <&uart0_sleep_mode>; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 902b1b1f568e..15321373ec8d 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -50,6 +50,7 @@ simtek sirf SiRF Technology, Inc. snps Synopsys, Inc. st STMicroelectronics +ste ST-Ericsson stericsson ST-Ericsson ti Texas Instruments via VIA Technologies, Inc. diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c index 5b286e06474c..b80ad9610e97 100644 --- a/arch/arm/mach-ux500/cpu-db8500.c +++ b/arch/arm/mach-ux500/cpu-db8500.c @@ -285,7 +285,7 @@ static struct of_dev_auxdata u8500_auxdata_lookup[] __initdata = { OF_DEV_AUXDATA("st,nomadik-i2c", 0x80110000, "nmk-i2c.3", NULL), OF_DEV_AUXDATA("st,nomadik-i2c", 0x8012a000, "nmk-i2c.4", NULL), /* Requires device name bindings. */ - OF_DEV_AUXDATA("stericsson,nmk_pinctrl", U8500_PRCMU_BASE, + OF_DEV_AUXDATA("stericsson,nmk-pinctrl", U8500_PRCMU_BASE, "pinctrl-db8500", NULL), /* Requires clock name and DMA bindings. */ OF_DEV_AUXDATA("stericsson,ux500-msp-i2s", 0x80123000, diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c index f404e644a16e..ec3a5742e413 100644 --- a/drivers/pinctrl/pinctrl-nomadik.c +++ b/drivers/pinctrl/pinctrl-nomadik.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -1490,11 +1491,285 @@ static void nmk_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, nmk_gpio_dbg_show_one(s, pctldev, chip, offset - chip->base, offset); } +static void nmk_pinctrl_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps) +{ + int i; + + for (i = 0; i < num_maps; i++) + if (map[i].type == PIN_MAP_TYPE_CONFIGS_PIN) + kfree(map[i].data.configs.configs); + kfree(map); +} + +static int nmk_dt_reserve_map(struct pinctrl_map **map, unsigned *reserved_maps, + unsigned *num_maps, unsigned reserve) +{ + unsigned old_num = *reserved_maps; + unsigned new_num = *num_maps + reserve; + struct pinctrl_map *new_map; + + if (old_num >= new_num) + return 0; + + new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL); + if (!new_map) + return -ENOMEM; + + memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map)); + + *map = new_map; + *reserved_maps = new_num; + + return 0; +} + +static int nmk_dt_add_map_mux(struct pinctrl_map **map, unsigned *reserved_maps, + unsigned *num_maps, const char *group, + const char *function) +{ + if (*num_maps == *reserved_maps) + return -ENOSPC; + + (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP; + (*map)[*num_maps].data.mux.group = group; + (*map)[*num_maps].data.mux.function = function; + (*num_maps)++; + + return 0; +} + +static int nmk_dt_add_map_configs(struct pinctrl_map **map, + unsigned *reserved_maps, + unsigned *num_maps, const char *group, + unsigned long *configs, unsigned num_configs) +{ + unsigned long *dup_configs; + + if (*num_maps == *reserved_maps) + return -ENOSPC; + + dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs), + GFP_KERNEL); + if (!dup_configs) + return -ENOMEM; + + (*map)[*num_maps].type = PIN_MAP_TYPE_CONFIGS_PIN; + + (*map)[*num_maps].data.configs.group_or_pin = group; + (*map)[*num_maps].data.configs.configs = dup_configs; + (*map)[*num_maps].data.configs.num_configs = num_configs; + (*num_maps)++; + + return 0; +} + +#define NMK_CONFIG_PIN(x,y) { .property = x, .config = y, } +#define NMK_CONFIG_PIN_ARRAY(x,y) { .property = x, .choice = y, \ + .size = ARRAY_SIZE(y), } + +static const unsigned long nmk_pin_input_modes[] = { + PIN_INPUT_NOPULL, + PIN_INPUT_PULLUP, + PIN_INPUT_PULLDOWN, +}; + +static const unsigned long nmk_pin_output_modes[] = { + PIN_OUTPUT_LOW, + PIN_OUTPUT_HIGH, + PIN_DIR_OUTPUT, +}; + +static const unsigned long nmk_pin_sleep_modes[] = { + PIN_SLEEPMODE_DISABLED, + PIN_SLEEPMODE_ENABLED, +}; + +static const unsigned long nmk_pin_sleep_input_modes[] = { + PIN_SLPM_INPUT_NOPULL, + PIN_SLPM_INPUT_PULLUP, + PIN_SLPM_INPUT_PULLDOWN, + PIN_SLPM_DIR_INPUT, +}; + +static const unsigned long nmk_pin_sleep_output_modes[] = { + PIN_SLPM_OUTPUT_LOW, + PIN_SLPM_OUTPUT_HIGH, + PIN_SLPM_DIR_OUTPUT, +}; + +static const unsigned long nmk_pin_sleep_wakeup_modes[] = { + PIN_SLPM_WAKEUP_DISABLE, + PIN_SLPM_WAKEUP_ENABLE, +}; + +static const unsigned long nmk_pin_gpio_modes[] = { + PIN_GPIOMODE_DISABLED, + PIN_GPIOMODE_ENABLED, +}; + +static const unsigned long nmk_pin_sleep_pdis_modes[] = { + PIN_SLPM_PDIS_DISABLED, + PIN_SLPM_PDIS_ENABLED, +}; + +struct nmk_cfg_param { + const char *property; + unsigned long config; + const unsigned long *choice; + int size; +}; + +static const struct nmk_cfg_param nmk_cfg_params[] = { + NMK_CONFIG_PIN_ARRAY("ste,input", nmk_pin_input_modes), + NMK_CONFIG_PIN_ARRAY("ste,output", nmk_pin_output_modes), + NMK_CONFIG_PIN_ARRAY("ste,sleep", nmk_pin_sleep_modes), + NMK_CONFIG_PIN_ARRAY("ste,sleep-input", nmk_pin_sleep_input_modes), + NMK_CONFIG_PIN_ARRAY("ste,sleep-output", nmk_pin_sleep_output_modes), + NMK_CONFIG_PIN_ARRAY("ste,sleep-wakeup", nmk_pin_sleep_wakeup_modes), + NMK_CONFIG_PIN_ARRAY("ste,gpio", nmk_pin_gpio_modes), + NMK_CONFIG_PIN_ARRAY("ste,sleep-pull-disable", nmk_pin_sleep_pdis_modes), +}; + +static int nmk_dt_pin_config(int index, int val, unsigned long *config) +{ + int ret = 0; + + if (nmk_cfg_params[index].choice == NULL) + *config = nmk_cfg_params[index].config; + else { + /* test if out of range */ + if (val < nmk_cfg_params[index].size) { + *config = nmk_cfg_params[index].config | + nmk_cfg_params[index].choice[val]; + } + } + return ret; +} + +static const char *nmk_find_pin_name(struct pinctrl_dev *pctldev, const char *pin_name) +{ + int i, pin_number; + struct nmk_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev); + + if (sscanf((char *)pin_name, "GPIO%d", &pin_number) == 1) + for (i = 0; i < npct->soc->npins; i++) + if (npct->soc->pins[i].number == pin_number) + return npct->soc->pins[i].name; + return NULL; +} + +static bool nmk_pinctrl_dt_get_config(struct device_node *np, + unsigned long *configs) +{ + bool has_config = 0; + unsigned long cfg = 0; + int i, val, ret; + + for (i = 0; i < ARRAY_SIZE(nmk_cfg_params); i++) { + ret = of_property_read_u32(np, + nmk_cfg_params[i].property, &val); + if (ret != -EINVAL) { + if (nmk_dt_pin_config(i, val, &cfg) == 0) { + *configs |= cfg; + has_config = 1; + } + } + } + + return has_config; +} + +int nmk_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, + unsigned *reserved_maps, + unsigned *num_maps) +{ + int ret; + const char *function = NULL; + unsigned long configs = 0; + bool has_config = 0; + unsigned reserve = 0; + struct property *prop; + const char *group, *gpio_name; + struct device_node *np_config; + + ret = of_property_read_string(np, "ste,function", &function); + if (ret >= 0) + reserve = 1; + + has_config = nmk_pinctrl_dt_get_config(np, &configs); + + np_config = of_parse_phandle(np, "ste,config", 0); + if (np_config) + has_config |= nmk_pinctrl_dt_get_config(np_config, &configs); + + ret = of_property_count_strings(np, "ste,pins"); + if (ret < 0) + goto exit; + + if (has_config) + reserve++; + + reserve *= ret; + + ret = nmk_dt_reserve_map(map, reserved_maps, num_maps, reserve); + if (ret < 0) + goto exit; + + of_property_for_each_string(np, "ste,pins", prop, group) { + if (function) { + ret = nmk_dt_add_map_mux(map, reserved_maps, num_maps, + group, function); + if (ret < 0) + goto exit; + } + if (has_config) { + gpio_name = nmk_find_pin_name(pctldev, group); + + ret = nmk_dt_add_map_configs(map, reserved_maps, num_maps, + gpio_name, &configs, 1); + if (ret < 0) + goto exit; + } + + } +exit: + return ret; +} + +int nmk_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np_config, + struct pinctrl_map **map, unsigned *num_maps) +{ + unsigned reserved_maps; + struct device_node *np; + int ret; + + reserved_maps = 0; + *map = NULL; + *num_maps = 0; + + for_each_child_of_node(np_config, np) { + ret = nmk_pinctrl_dt_subnode_to_map(pctldev, np, map, + &reserved_maps, num_maps); + if (ret < 0) { + nmk_pinctrl_dt_free_map(pctldev, *map, *num_maps); + return ret; + } + } + + return 0; +} + static struct pinctrl_ops nmk_pinctrl_ops = { .get_groups_count = nmk_get_groups_cnt, .get_group_name = nmk_get_group_name, .get_group_pins = nmk_get_group_pins, .pin_dbg_show = nmk_pin_dbg_show, + .dt_node_to_map = nmk_pinctrl_dt_node_to_map, + .dt_free_map = nmk_pinctrl_dt_free_map, }; static int nmk_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev) @@ -1828,7 +2103,7 @@ static struct pinctrl_desc nmk_pinctrl_desc = { static const struct of_device_id nmk_pinctrl_match[] = { { - .compatible = "stericsson,nmk_pinctrl", + .compatible = "stericsson,nmk-pinctrl", .data = (void *)PINCTRL_NMK_DB8500, }, {}, -- cgit v1.2.3-59-g8ed1b From 00494be4546432a11d62ebfeca363256ff9822b5 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Fri, 11 Jan 2013 14:45:58 +0000 Subject: networking/cs89x0.txt: delete stale information about hand patching Output of a git grep happened to make me look into this file, and I found instructions about how to hand patch (without using patch) the driver into the kernel tree. Since the driver has been a part of the mainline kernel for years, we can dump this whole section. Fortunately it doesn't even cause a renumbering of the sections to do so. Signed-off-by: Paul Gortmaker Signed-off-by: David S. Miller --- Documentation/networking/cs89x0.txt | 79 ------------------------------------- 1 file changed, 79 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/cs89x0.txt b/Documentation/networking/cs89x0.txt index c725d33b316f..0e190180eec8 100644 --- a/Documentation/networking/cs89x0.txt +++ b/Documentation/networking/cs89x0.txt @@ -36,7 +36,6 @@ TABLE OF CONTENTS 4.1 Compiling the Driver as a Loadable Module 4.2 Compiling the driver to support memory mode 4.3 Compiling the driver to support Rx DMA - 4.4 Compiling the Driver into the Kernel 5.0 TESTING AND TROUBLESHOOTING 5.1 Known Defects and Limitations @@ -364,84 +363,6 @@ The compile-time optionality for DMA was removed in the 2.3 kernel series. DMA support is now unconditionally part of the driver. It is enabled by the 'use_dma=1' module option. -4.4 COMPILING THE DRIVER INTO THE KERNEL - -If your Linux distribution already has support for the cs89x0 driver -then simply copy the source file to the /usr/src/linux/drivers/net -directory to replace the original ones and run the make utility to -rebuild the kernel. See Step 3 for rebuilding the kernel. - -If your Linux does not include the cs89x0 driver, you need to edit three -configuration files, copy the source file to the /usr/src/linux/drivers/net -directory, and then run the make utility to rebuild the kernel. - -1. Edit the following configuration files by adding the statements as -indicated. (When possible, try to locate the added text to the section of the -file containing similar statements). - - -a.) In /usr/src/linux/drivers/net/Config.in, add: - -tristate 'CS89x0 support' CONFIG_CS89x0 - -Example: - - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'ICL EtherTeam 16i/32 support' CONFIG_ETH16I - fi - - tristate 'CS89x0 support' CONFIG_CS89x0 - - tristate 'NE2000/NE1000 support' CONFIG_NE2000 - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'NI5210 support' CONFIG_NI52 - - -b.) In /usr/src/linux/drivers/net/Makefile, add the following lines: - -ifeq ($(CONFIG_CS89x0),y) -L_OBJS += cs89x0.o -else - ifeq ($(CONFIG_CS89x0),m) - M_OBJS += cs89x0.o - endif -endif - - -c.) In /linux/drivers/net/Space.c file, add the line: - -extern int cs89x0_probe(struct device *dev); - - -Example: - - extern int ultra_probe(struct device *dev); - extern int wd_probe(struct device *dev); - extern int el2_probe(struct device *dev); - - extern int cs89x0_probe(struct device *dev); - - extern int ne_probe(struct device *dev); - extern int hp_probe(struct device *dev); - extern int hp_plus_probe(struct device *dev); - - -Also add: - - #ifdef CONFIG_CS89x0 - { cs89x0_probe,0 }, - #endif - - -2.) Copy the driver source files (cs89x0.c and cs89x0.h) -into the /usr/src/linux/drivers/net directory. - - -3.) Go to /usr/src/linux directory and run 'make config' followed by 'make' -(or make bzImage) to rebuild the kernel. - -4.) Use the DOS 'setup' utility to disable plug and play on the NIC. - 5.0 TESTING AND TROUBLESHOOTING =============================================================================== -- cgit v1.2.3-59-g8ed1b From f9a8f83b04e0c362a2fc660dbad980d24af209fc Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 14 Jan 2013 00:52:52 +0000 Subject: net: phy: remove flags argument from phy_{attach, connect, connect_direct} The flags argument of the phy_{attach,connect,connect_direct} functions is then used to assign a struct phy_device dev_flags with its value. All callers but the tg3 driver pass the flag 0, which results in the underlying PHY drivers in drivers/net/phy/ not being able to actually use any of the flags they would set in dev_flags. This patch gets rid of the flags argument, and passes phydev->dev_flags to the internal PHY library call phy_attach_direct() such that drivers which actually modify a phy device dev_flags get the value preserved for use by the underlying phy driver. Acked-by: Kosta Zertsekel Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/networking/phy.txt | 11 ++++++----- drivers/net/ethernet/8390/ax88796.c | 2 +- drivers/net/ethernet/adi/bfin_mac.c | 4 ++-- drivers/net/ethernet/aeroflex/greth.c | 4 +--- drivers/net/ethernet/amd/au1000_eth.c | 4 ++-- drivers/net/ethernet/broadcom/bcm63xx_enet.c | 2 +- drivers/net/ethernet/broadcom/sb1250-mac.c | 2 +- drivers/net/ethernet/broadcom/tg3.c | 4 ++-- drivers/net/ethernet/cadence/macb.c | 2 +- drivers/net/ethernet/dnet.c | 4 ++-- drivers/net/ethernet/ethoc.c | 4 ++-- drivers/net/ethernet/faraday/ftgmac100.c | 3 +-- drivers/net/ethernet/freescale/fec.c | 2 +- drivers/net/ethernet/lantiq_etop.c | 4 ++-- drivers/net/ethernet/marvell/mv643xx_eth.c | 2 +- drivers/net/ethernet/marvell/pxa168_eth.c | 2 +- drivers/net/ethernet/nxp/lpc_eth.c | 2 +- drivers/net/ethernet/rdc/r6040.c | 2 +- drivers/net/ethernet/renesas/sh_eth.c | 2 +- drivers/net/ethernet/s6gmac.c | 2 +- drivers/net/ethernet/smsc/smsc911x.c | 5 ++--- drivers/net/ethernet/smsc/smsc9420.c | 2 +- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 +-- drivers/net/ethernet/ti/cpmac.c | 4 ++-- drivers/net/ethernet/ti/cpsw.c | 2 +- drivers/net/ethernet/ti/davinci_emac.c | 2 +- drivers/net/ethernet/toshiba/tc35815.c | 5 ++--- drivers/net/ethernet/xscale/ixp4xx_eth.c | 2 +- drivers/net/phy/phy_device.c | 15 ++++++--------- drivers/net/usb/ax88172a.c | 2 +- drivers/of/of_mdio.c | 4 ++-- drivers/staging/et131x/et131x.c | 2 +- include/linux/phy.h | 6 +++--- net/dsa/slave.c | 2 +- 34 files changed, 56 insertions(+), 64 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/phy.txt b/Documentation/networking/phy.txt index 95e5f5985a2a..d5b1a3935245 100644 --- a/Documentation/networking/phy.txt +++ b/Documentation/networking/phy.txt @@ -103,7 +103,7 @@ Letting the PHY Abstraction Layer do Everything Now, to connect, just call this function: - phydev = phy_connect(dev, phy_name, &adjust_link, flags, interface); + phydev = phy_connect(dev, phy_name, &adjust_link, interface); phydev is a pointer to the phy_device structure which represents the PHY. If phy_connect is successful, it will return the pointer. dev, here, is the @@ -113,7 +113,9 @@ Letting the PHY Abstraction Layer do Everything current state, though the PHY will not yet be truly operational at this point. - flags is a u32 which can optionally contain phy-specific flags. + PHY-specific flags should be set in phydev->dev_flags prior to the call + to phy_connect() such that the underlying PHY driver can check for flags + and perform specific operations based on them. This is useful if the system has put hardware restrictions on the PHY/controller, of which the PHY needs to be aware. @@ -185,11 +187,10 @@ Doing it all yourself start, or disables then frees them for stop. struct phy_device * phy_attach(struct net_device *dev, const char *phy_id, - u32 flags, phy_interface_t interface); + phy_interface_t interface); Attaches a network device to a particular PHY, binding the PHY to a generic - driver if none was found during bus initialization. Passes in - any phy-specific flags as needed. + driver if none was found during bus initialization. int phy_start_aneg(struct phy_device *phydev); diff --git a/drivers/net/ethernet/8390/ax88796.c b/drivers/net/ethernet/8390/ax88796.c index 7eeddf01307f..cab306a9888e 100644 --- a/drivers/net/ethernet/8390/ax88796.c +++ b/drivers/net/ethernet/8390/ax88796.c @@ -358,7 +358,7 @@ static int ax_mii_probe(struct net_device *dev) return -ENODEV; } - ret = phy_connect_direct(dev, phy_dev, ax_handle_link_change, 0, + ret = phy_connect_direct(dev, phy_dev, ax_handle_link_change, PHY_INTERFACE_MODE_MII); if (ret) { netdev_err(dev, "Could not attach to PHY\n"); diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c index c7a83f6f2382..a175d0be1ae1 100644 --- a/drivers/net/ethernet/adi/bfin_mac.c +++ b/drivers/net/ethernet/adi/bfin_mac.c @@ -425,8 +425,8 @@ static int mii_probe(struct net_device *dev, int phy_mode) return -EINVAL; } - phydev = phy_connect(dev, dev_name(&phydev->dev), &bfin_mac_adjust_link, - 0, phy_mode); + phydev = phy_connect(dev, dev_name(&phydev->dev), + &bfin_mac_adjust_link, phy_mode); if (IS_ERR(phydev)) { netdev_err(dev, "could not attach PHY\n"); diff --git a/drivers/net/ethernet/aeroflex/greth.c b/drivers/net/ethernet/aeroflex/greth.c index 480662ba5227..0be2195e5034 100644 --- a/drivers/net/ethernet/aeroflex/greth.c +++ b/drivers/net/ethernet/aeroflex/greth.c @@ -1288,9 +1288,7 @@ static int greth_mdio_probe(struct net_device *dev) } ret = phy_connect_direct(dev, phy, &greth_link_change, - 0, greth->gbit_mac ? - PHY_INTERFACE_MODE_GMII : - PHY_INTERFACE_MODE_MII); + greth->gbit_mac ? PHY_INTERFACE_MODE_GMII : PHY_INTERFACE_MODE_MII); if (ret) { if (netif_msg_ifup(greth)) dev_err(&dev->dev, "could not attach to PHY\n"); diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c index 65b865a0cc78..de774d419144 100644 --- a/drivers/net/ethernet/amd/au1000_eth.c +++ b/drivers/net/ethernet/amd/au1000_eth.c @@ -437,8 +437,8 @@ static int au1000_mii_probe(struct net_device *dev) /* now we are supposed to have a proper phydev, to attach to... */ BUG_ON(phydev->attached_dev); - phydev = phy_connect(dev, dev_name(&phydev->dev), &au1000_adjust_link, - 0, PHY_INTERFACE_MODE_MII); + phydev = phy_connect(dev, dev_name(&phydev->dev), + &au1000_adjust_link, PHY_INTERFACE_MODE_MII); if (IS_ERR(phydev)) { netdev_err(dev, "Could not attach to PHY\n"); diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index d8a151046728..f5b6b4715d45 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -799,7 +799,7 @@ static int bcm_enet_open(struct net_device *dev) snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, priv->mii_bus->id, priv->phy_id); - phydev = phy_connect(dev, phy_id, bcm_enet_adjust_phy_link, 0, + phydev = phy_connect(dev, phy_id, bcm_enet_adjust_phy_link, PHY_INTERFACE_MODE_MII); if (IS_ERR(phydev)) { diff --git a/drivers/net/ethernet/broadcom/sb1250-mac.c b/drivers/net/ethernet/broadcom/sb1250-mac.c index 3a1c8a3cf7c9..e9b35da375cb 100644 --- a/drivers/net/ethernet/broadcom/sb1250-mac.c +++ b/drivers/net/ethernet/broadcom/sb1250-mac.c @@ -2385,7 +2385,7 @@ static int sbmac_mii_probe(struct net_device *dev) return -ENXIO; } - phy_dev = phy_connect(dev, dev_name(&phy_dev->dev), &sbmac_mii_poll, 0, + phy_dev = phy_connect(dev, dev_name(&phy_dev->dev), &sbmac_mii_poll, PHY_INTERFACE_MODE_GMII); if (IS_ERR(phy_dev)) { printk(KERN_ERR "%s: could not attach to PHY\n", dev->name); diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 88f2d41c009b..227749107789 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -2004,8 +2004,8 @@ static int tg3_phy_init(struct tg3 *tp) phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; /* Attach the MAC to the PHY. */ - phydev = phy_connect(tp->dev, dev_name(&phydev->dev), tg3_adjust_link, - phydev->dev_flags, phydev->interface); + phydev = phy_connect(tp->dev, dev_name(&phydev->dev), + tg3_adjust_link, phydev->interface); if (IS_ERR(phydev)) { dev_err(&tp->pdev->dev, "Could not attach to PHY\n"); return PTR_ERR(phydev); diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index a9b0830fb39d..352190b9ebe7 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -287,7 +287,7 @@ static int macb_mii_probe(struct net_device *dev) } /* attach the mac to the phy */ - ret = phy_connect_direct(dev, phydev, &macb_handle_link_change, 0, + ret = phy_connect_direct(dev, phydev, &macb_handle_link_change, bp->phy_interface); if (ret) { netdev_err(dev, "Could not attach to PHY\n"); diff --git a/drivers/net/ethernet/dnet.c b/drivers/net/ethernet/dnet.c index 2c177b329c8b..f3d60eb13c3a 100644 --- a/drivers/net/ethernet/dnet.c +++ b/drivers/net/ethernet/dnet.c @@ -281,11 +281,11 @@ static int dnet_mii_probe(struct net_device *dev) /* attach the mac to the phy */ if (bp->capabilities & DNET_HAS_RMII) { phydev = phy_connect(dev, dev_name(&phydev->dev), - &dnet_handle_link_change, 0, + &dnet_handle_link_change, PHY_INTERFACE_MODE_RMII); } else { phydev = phy_connect(dev, dev_name(&phydev->dev), - &dnet_handle_link_change, 0, + &dnet_handle_link_change, PHY_INTERFACE_MODE_MII); } diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c index b51c81ac0b6f..aa47ef9689a8 100644 --- a/drivers/net/ethernet/ethoc.c +++ b/drivers/net/ethernet/ethoc.c @@ -682,8 +682,8 @@ static int ethoc_mdio_probe(struct net_device *dev) return -ENXIO; } - err = phy_connect_direct(dev, phy, ethoc_mdio_poll, 0, - PHY_INTERFACE_MODE_GMII); + err = phy_connect_direct(dev, phy, ethoc_mdio_poll, + PHY_INTERFACE_MODE_GMII); if (err) { dev_err(&dev->dev, "could not attach to PHY\n"); return err; diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 96454b5fca63..7c361d1db94c 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -858,8 +858,7 @@ static int ftgmac100_mii_probe(struct ftgmac100 *priv) } phydev = phy_connect(netdev, dev_name(&phydev->dev), - &ftgmac100_adjust_link, 0, - PHY_INTERFACE_MODE_GMII); + &ftgmac100_adjust_link, PHY_INTERFACE_MODE_GMII); if (IS_ERR(phydev)) { netdev_err(netdev, "%s: Could not attach to PHY\n", netdev->name); diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c index 5f2b4acf4836..1b7684a8851e 100644 --- a/drivers/net/ethernet/freescale/fec.c +++ b/drivers/net/ethernet/freescale/fec.c @@ -1008,7 +1008,7 @@ static int fec_enet_mii_probe(struct net_device *ndev) } snprintf(phy_name, sizeof(phy_name), PHY_ID_FMT, mdio_bus_id, phy_id); - phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 0, + phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, fep->phy_interface); if (IS_ERR(phy_dev)) { printk(KERN_ERR "%s: could not attach to PHY\n", ndev->name); diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c index 8ead46adc21e..6a2127489af7 100644 --- a/drivers/net/ethernet/lantiq_etop.c +++ b/drivers/net/ethernet/lantiq_etop.c @@ -393,8 +393,8 @@ ltq_etop_mdio_probe(struct net_device *dev) return -ENODEV; } - phydev = phy_connect(dev, dev_name(&phydev->dev), <q_etop_mdio_link, - 0, priv->pldata->mii_mode); + phydev = phy_connect(dev, dev_name(&phydev->dev), + <q_etop_mdio_link, priv->pldata->mii_mode); if (IS_ERR(phydev)) { netdev_err(dev, "Could not attach to PHY\n"); diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 84c13263c514..c27b23d8f4fc 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -2789,7 +2789,7 @@ static void phy_init(struct mv643xx_eth_private *mp, int speed, int duplex) phy_reset(mp); - phy_attach(mp->dev, dev_name(&phy->dev), 0, PHY_INTERFACE_MODE_GMII); + phy_attach(mp->dev, dev_name(&phy->dev), PHY_INTERFACE_MODE_GMII); if (speed == 0) { phy->autoneg = AUTONEG_ENABLE; diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index c7f2fa60fe6f..037ed866c22f 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -1390,7 +1390,7 @@ static void phy_init(struct pxa168_eth_private *pep, int speed, int duplex) struct phy_device *phy = pep->phy; ethernet_phy_reset(pep); - phy_attach(pep->dev, dev_name(&phy->dev), 0, PHY_INTERFACE_MODE_MII); + phy_attach(pep->dev, dev_name(&phy->dev), PHY_INTERFACE_MODE_MII); if (speed == 0) { phy->autoneg = AUTONEG_ENABLE; diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c index 6fda51ebcc76..c4122c86f829 100644 --- a/drivers/net/ethernet/nxp/lpc_eth.c +++ b/drivers/net/ethernet/nxp/lpc_eth.c @@ -800,7 +800,7 @@ static int lpc_mii_probe(struct net_device *ndev) else netdev_info(ndev, "using RMII interface\n"); phydev = phy_connect(ndev, dev_name(&phydev->dev), - &lpc_handle_link_change, 0, + &lpc_handle_link_change, lpc_phy_interface_mode(&pldat->pdev->dev)); if (IS_ERR(phydev)) { diff --git a/drivers/net/ethernet/rdc/r6040.c b/drivers/net/ethernet/rdc/r6040.c index be3616d060d9..34f76e99dc8a 100644 --- a/drivers/net/ethernet/rdc/r6040.c +++ b/drivers/net/ethernet/rdc/r6040.c @@ -1042,7 +1042,7 @@ static int r6040_mii_probe(struct net_device *dev) } phydev = phy_connect(dev, dev_name(&phydev->dev), &r6040_adjust_link, - 0, PHY_INTERFACE_MODE_MII); + PHY_INTERFACE_MODE_MII); if (IS_ERR(phydev)) { dev_err(&lp->pdev->dev, "could not attach to PHY\n"); diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 3d705862bd7d..e195c1e89d61 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -1422,7 +1422,7 @@ static int sh_eth_phy_init(struct net_device *ndev) /* Try connect to PHY */ phydev = phy_connect(ndev, phy_id, sh_eth_adjust_link, - 0, mdp->phy_interface); + mdp->phy_interface); if (IS_ERR(phydev)) { dev_err(&ndev->dev, "phy_connect failed\n"); return PTR_ERR(phydev); diff --git a/drivers/net/ethernet/s6gmac.c b/drivers/net/ethernet/s6gmac.c index 72fc57dd084d..21683e2b1ff4 100644 --- a/drivers/net/ethernet/s6gmac.c +++ b/drivers/net/ethernet/s6gmac.c @@ -795,7 +795,7 @@ static inline int s6gmac_phy_start(struct net_device *dev) struct phy_device *p = NULL; while ((i < PHY_MAX_ADDR) && (!(p = pd->mii.bus->phy_map[i]))) i++; - p = phy_connect(dev, dev_name(&p->dev), &s6gmac_adjust_link, 0, + p = phy_connect(dev, dev_name(&p->dev), &s6gmac_adjust_link, PHY_INTERFACE_MODE_RGMII); if (IS_ERR(p)) { printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name); diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c index 04ff63cb6544..da5cc9a3b34c 100644 --- a/drivers/net/ethernet/smsc/smsc911x.c +++ b/drivers/net/ethernet/smsc/smsc911x.c @@ -997,9 +997,8 @@ static int smsc911x_mii_probe(struct net_device *dev) SMSC_TRACE(pdata, probe, "PHY: addr %d, phy_id 0x%08X", phydev->addr, phydev->phy_id); - ret = phy_connect_direct(dev, phydev, - &smsc911x_phy_adjust_link, 0, - pdata->config.phy_interface); + ret = phy_connect_direct(dev, phydev, &smsc911x_phy_adjust_link, + pdata->config.phy_interface); if (ret) { netdev_err(dev, "Could not attach to PHY\n"); diff --git a/drivers/net/ethernet/smsc/smsc9420.c b/drivers/net/ethernet/smsc/smsc9420.c index 3c586585e1b3..ecfb43614d7b 100644 --- a/drivers/net/ethernet/smsc/smsc9420.c +++ b/drivers/net/ethernet/smsc/smsc9420.c @@ -1179,7 +1179,7 @@ static int smsc9420_mii_probe(struct net_device *dev) phydev->phy_id); phydev = phy_connect(dev, dev_name(&phydev->dev), - smsc9420_phy_adjust_link, 0, PHY_INTERFACE_MODE_MII); + smsc9420_phy_adjust_link, PHY_INTERFACE_MODE_MII); if (IS_ERR(phydev)) { pr_err("%s: Could not attach to PHY\n", dev->name); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index f07c0612abf6..8c657294ce56 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -428,8 +428,7 @@ static int stmmac_init_phy(struct net_device *dev) priv->plat->phy_addr); pr_debug("stmmac_init_phy: trying to attach to %s\n", phy_id_fmt); - phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link, 0, - interface); + phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link, interface); if (IS_ERR(phydev)) { pr_err("%s: Could not attach to PHY\n", dev->name); diff --git a/drivers/net/ethernet/ti/cpmac.c b/drivers/net/ethernet/ti/cpmac.c index 70d1920cac97..31bbbca341a7 100644 --- a/drivers/net/ethernet/ti/cpmac.c +++ b/drivers/net/ethernet/ti/cpmac.c @@ -1172,8 +1172,8 @@ static int cpmac_probe(struct platform_device *pdev) snprintf(priv->phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT, mdio_bus_id, phy_id); - priv->phy = phy_connect(dev, priv->phy_name, cpmac_adjust_link, 0, - PHY_INTERFACE_MODE_MII); + priv->phy = phy_connect(dev, priv->phy_name, cpmac_adjust_link, + PHY_INTERFACE_MODE_MII); if (IS_ERR(priv->phy)) { if (netif_msg_drv(priv)) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index bea736b8c3ec..3772804fb697 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -592,7 +592,7 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) 1 << slave_port, 0, ALE_MCAST_FWD_2); slave->phy = phy_connect(priv->ndev, slave->data->phy_id, - &cpsw_adjust_link, 0, slave->data->phy_if); + &cpsw_adjust_link, slave->data->phy_if); if (IS_ERR(slave->phy)) { dev_err(priv->dev, "phy %s not found on slave %d\n", slave->data->phy_id, slave->slave_num); diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 6621ae3a98d9..8478d98c1092 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1599,7 +1599,7 @@ static int emac_dev_open(struct net_device *ndev) if (priv->phy_id && *priv->phy_id) { priv->phydev = phy_connect(ndev, priv->phy_id, - &emac_adjust_link, 0, + &emac_adjust_link, PHY_INTERFACE_MODE_MII); if (IS_ERR(priv->phydev)) { diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c index f16410e599f4..fe256094db35 100644 --- a/drivers/net/ethernet/toshiba/tc35815.c +++ b/drivers/net/ethernet/toshiba/tc35815.c @@ -633,9 +633,8 @@ static int tc_mii_probe(struct net_device *dev) /* attach the mac to the phy */ phydev = phy_connect(dev, dev_name(&phydev->dev), - &tc_handle_link_change, 0, - lp->chiptype == TC35815_TX4939 ? - PHY_INTERFACE_MODE_RMII : PHY_INTERFACE_MODE_MII); + &tc_handle_link_change, + lp->chiptype == TC35815_TX4939 ? PHY_INTERFACE_MODE_RMII : PHY_INTERFACE_MODE_MII); if (IS_ERR(phydev)) { printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name); return PTR_ERR(phydev); diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c index a4be1ad886c5..6958a5e87703 100644 --- a/drivers/net/ethernet/xscale/ixp4xx_eth.c +++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c @@ -1451,7 +1451,7 @@ static int eth_init_one(struct platform_device *pdev) snprintf(phy_id, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, mdio_bus->id, plat->phy); - port->phydev = phy_connect(dev, phy_id, &ixp4xx_adjust_link, 0, + port->phydev = phy_connect(dev, phy_id, &ixp4xx_adjust_link, PHY_INTERFACE_MODE_MII); if (IS_ERR(port->phydev)) { err = PTR_ERR(port->phydev); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 8af46e88a181..9930f9999561 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -416,16 +416,15 @@ static void phy_prepare_link(struct phy_device *phydev, * @dev: the network device to connect * @phydev: the pointer to the phy device * @handler: callback function for state change notifications - * @flags: PHY device's dev_flags * @interface: PHY device's interface */ int phy_connect_direct(struct net_device *dev, struct phy_device *phydev, - void (*handler)(struct net_device *), u32 flags, + void (*handler)(struct net_device *), phy_interface_t interface) { int rc; - rc = phy_attach_direct(dev, phydev, flags, interface); + rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface); if (rc) return rc; @@ -443,7 +442,6 @@ EXPORT_SYMBOL(phy_connect_direct); * @dev: the network device to connect * @bus_id: the id string of the PHY device to connect * @handler: callback function for state change notifications - * @flags: PHY device's dev_flags * @interface: PHY device's interface * * Description: Convenience function for connecting ethernet @@ -455,7 +453,7 @@ EXPORT_SYMBOL(phy_connect_direct); * the desired functionality. */ struct phy_device * phy_connect(struct net_device *dev, const char *bus_id, - void (*handler)(struct net_device *), u32 flags, + void (*handler)(struct net_device *), phy_interface_t interface) { struct phy_device *phydev; @@ -471,7 +469,7 @@ struct phy_device * phy_connect(struct net_device *dev, const char *bus_id, } phydev = to_phy_device(d); - rc = phy_connect_direct(dev, phydev, handler, flags, interface); + rc = phy_connect_direct(dev, phydev, handler, interface); if (rc) return ERR_PTR(rc); @@ -576,14 +574,13 @@ static int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, * phy_attach - attach a network device to a particular PHY device * @dev: network device to attach * @bus_id: Bus ID of PHY device to attach - * @flags: PHY device's dev_flags * @interface: PHY device's interface * * Description: Same as phy_attach_direct() except that a PHY bus_id * string is passed instead of a pointer to a struct phy_device. */ struct phy_device *phy_attach(struct net_device *dev, - const char *bus_id, u32 flags, phy_interface_t interface) + const char *bus_id, phy_interface_t interface) { struct bus_type *bus = &mdio_bus_type; struct phy_device *phydev; @@ -599,7 +596,7 @@ struct phy_device *phy_attach(struct net_device *dev, } phydev = to_phy_device(d); - rc = phy_attach_direct(dev, phydev, flags, interface); + rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface); if (rc) return ERR_PTR(rc); diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c index c8e0aa85fb8e..fdbab72926bd 100644 --- a/drivers/net/usb/ax88172a.c +++ b/drivers/net/usb/ax88172a.c @@ -377,7 +377,7 @@ static int ax88172a_reset(struct usbnet *dev) priv->phydev = phy_connect(dev->net, priv->phy_name, &ax88172a_adjust_link, - 0, PHY_INTERFACE_MODE_MII); + PHY_INTERFACE_MODE_MII); if (IS_ERR(priv->phydev)) { netdev_err(dev->net, "Could not connect to PHY device %s\n", priv->phy_name); diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index 83ca06f4312b..e3a8b22ef9dd 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -157,7 +157,7 @@ struct phy_device *of_phy_connect(struct net_device *dev, if (!phy) return NULL; - return phy_connect_direct(dev, phy, hndlr, flags, iface) ? NULL : phy; + return phy_connect_direct(dev, phy, hndlr, iface) ? NULL : phy; } EXPORT_SYMBOL(of_phy_connect); @@ -194,7 +194,7 @@ struct phy_device *of_phy_connect_fixed_link(struct net_device *dev, sprintf(bus_id, PHY_ID_FMT, "fixed-0", be32_to_cpu(phy_id[0])); - phy = phy_connect(dev, bus_id, hndlr, 0, iface); + phy = phy_connect(dev, bus_id, hndlr, iface); return IS_ERR(phy) ? NULL : phy; } EXPORT_SYMBOL(of_phy_connect_fixed_link); diff --git a/drivers/staging/et131x/et131x.c b/drivers/staging/et131x/et131x.c index f15059ca3781..a0a30b3f2dcd 100644 --- a/drivers/staging/et131x/et131x.c +++ b/drivers/staging/et131x/et131x.c @@ -3917,7 +3917,7 @@ static int et131x_mii_probe(struct net_device *netdev) } phydev = phy_connect(netdev, dev_name(&phydev->dev), - &et131x_adjust_link, 0, PHY_INTERFACE_MODE_MII); + &et131x_adjust_link, PHY_INTERFACE_MODE_MII); if (IS_ERR(phydev)) { dev_err(&adapter->pdev->dev, "Could not attach to PHY\n"); diff --git a/include/linux/phy.h b/include/linux/phy.h index 93b3cf77f564..33999adbf8c8 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -506,13 +506,13 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45); int phy_device_register(struct phy_device *phy); int phy_init_hw(struct phy_device *phydev); struct phy_device * phy_attach(struct net_device *dev, - const char *bus_id, u32 flags, phy_interface_t interface); + const char *bus_id, phy_interface_t interface); struct phy_device *phy_find_first(struct mii_bus *bus); int phy_connect_direct(struct net_device *dev, struct phy_device *phydev, - void (*handler)(struct net_device *), u32 flags, + void (*handler)(struct net_device *), phy_interface_t interface); struct phy_device * phy_connect(struct net_device *dev, const char *bus_id, - void (*handler)(struct net_device *), u32 flags, + void (*handler)(struct net_device *), phy_interface_t interface); void phy_disconnect(struct phy_device *phydev); void phy_detach(struct phy_device *phydev); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index f795b0ca7ee6..f4345582a6b9 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -391,7 +391,7 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent, if (p->phy != NULL) { phy_attach(slave_dev, dev_name(&p->phy->dev), - 0, PHY_INTERFACE_MODE_GMII); + PHY_INTERFACE_MODE_GMII); p->phy->autoneg = AUTONEG_ENABLE; p->phy->speed = 0; -- cgit v1.2.3-59-g8ed1b From d59577b6ffd313d0ab3be39cb1ab47e29bdc9182 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Wed, 16 Jan 2013 22:55:49 +0100 Subject: sk-filter: Add ability to lock a socket filter program While a privileged program can open a raw socket, attach some restrictive filter and drop its privileges (or send the socket to an unprivileged program through some Unix socket), the filter can still be removed or modified by the unprivileged program. This commit adds a socket option to lock the filter (SO_LOCK_FILTER) preventing any modification of a socket filter program. This is similar to OpenBSD BIOCLOCK ioctl on bpf sockets, except even root is not allowed change/drop the filter. The state of the lock can be read with getsockopt(). No error is triggered if the state is not changed. -EPERM is returned when a user tries to remove the lock or to change/remove the filter while the lock is active. The check is done directly in sk_attach_filter() and sk_detach_filter() and does not affect only setsockopt() syscall. Signed-off-by: Vincent Bernat Signed-off-by: David S. Miller --- Documentation/networking/filter.txt | 11 +++++++++-- arch/alpha/include/uapi/asm/socket.h | 1 + arch/avr32/include/uapi/asm/socket.h | 2 ++ arch/cris/include/uapi/asm/socket.h | 2 ++ arch/frv/include/uapi/asm/socket.h | 2 ++ arch/h8300/include/uapi/asm/socket.h | 2 ++ arch/ia64/include/uapi/asm/socket.h | 2 ++ arch/m32r/include/uapi/asm/socket.h | 2 ++ arch/mips/include/uapi/asm/socket.h | 1 + arch/mn10300/include/uapi/asm/socket.h | 2 ++ arch/parisc/include/uapi/asm/socket.h | 1 + arch/powerpc/include/uapi/asm/socket.h | 2 ++ arch/s390/include/uapi/asm/socket.h | 2 ++ arch/sparc/include/uapi/asm/socket.h | 1 + arch/xtensa/include/uapi/asm/socket.h | 2 ++ include/net/sock.h | 1 + include/uapi/asm-generic/socket.h | 2 ++ net/core/filter.c | 6 ++++++ net/core/sock.c | 11 +++++++++++ 19 files changed, 53 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/filter.txt b/Documentation/networking/filter.txt index bbf2005270b5..cdb3e40b9d14 100644 --- a/Documentation/networking/filter.txt +++ b/Documentation/networking/filter.txt @@ -17,12 +17,12 @@ creating filters. LSF is much simpler than BPF. One does not have to worry about devices or anything like that. You simply create your filter -code, send it to the kernel via the SO_ATTACH_FILTER ioctl and +code, send it to the kernel via the SO_ATTACH_FILTER option and if your filter code passes the kernel check on it, you then immediately begin filtering data on that socket. You can also detach filters from your socket via the -SO_DETACH_FILTER ioctl. This will probably not be used much +SO_DETACH_FILTER option. This will probably not be used much since when you close a socket that has a filter on it the filter is automagically removed. The other less common case may be adding a different filter on the same socket where you had another @@ -31,12 +31,19 @@ the old one and placing your new one in its place, assuming your filter has passed the checks, otherwise if it fails the old filter will remain on that socket. +SO_LOCK_FILTER option allows to lock the filter attached to a +socket. Once set, a filter cannot be removed or changed. This allows +one process to setup a socket, attach a filter, lock it then drop +privileges and be assured that the filter will be kept until the +socket is closed. + Examples ======== Ioctls- setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &Filter, sizeof(Filter)); setsockopt(sockfd, SOL_SOCKET, SO_DETACH_FILTER, &value, sizeof(value)); +setsockopt(sockfd, SOL_SOCKET, SO_LOCK_FILTER, &value, sizeof(value)); See the BSD bpf.4 manpage and the BSD Packet Filter paper written by Steven McCanne and Van Jacobson of Lawrence Berkeley Laboratory. diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h index 097c1577735a..755702eefd9c 100644 --- a/arch/alpha/include/uapi/asm/socket.h +++ b/arch/alpha/include/uapi/asm/socket.h @@ -77,5 +77,6 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/avr32/include/uapi/asm/socket.h b/arch/avr32/include/uapi/asm/socket.h index 486df68abeec..f3f38a0e2ef9 100644 --- a/arch/avr32/include/uapi/asm/socket.h +++ b/arch/avr32/include/uapi/asm/socket.h @@ -70,4 +70,6 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* __ASM_AVR32_SOCKET_H */ diff --git a/arch/cris/include/uapi/asm/socket.h b/arch/cris/include/uapi/asm/socket.h index b681b043f6c8..406b5838defd 100644 --- a/arch/cris/include/uapi/asm/socket.h +++ b/arch/cris/include/uapi/asm/socket.h @@ -72,6 +72,8 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/frv/include/uapi/asm/socket.h b/arch/frv/include/uapi/asm/socket.h index 871f89b7fbda..d8e1132a1ab6 100644 --- a/arch/frv/include/uapi/asm/socket.h +++ b/arch/frv/include/uapi/asm/socket.h @@ -70,5 +70,7 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/h8300/include/uapi/asm/socket.h b/arch/h8300/include/uapi/asm/socket.h index 90a2e573c7e6..c8b87a828206 100644 --- a/arch/h8300/include/uapi/asm/socket.h +++ b/arch/h8300/include/uapi/asm/socket.h @@ -70,4 +70,6 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/ia64/include/uapi/asm/socket.h b/arch/ia64/include/uapi/asm/socket.h index 23d6759bb57b..f390896c3104 100644 --- a/arch/ia64/include/uapi/asm/socket.h +++ b/arch/ia64/include/uapi/asm/socket.h @@ -79,4 +79,6 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* _ASM_IA64_SOCKET_H */ diff --git a/arch/m32r/include/uapi/asm/socket.h b/arch/m32r/include/uapi/asm/socket.h index 5e7088a26726..6a895155e7a3 100644 --- a/arch/m32r/include/uapi/asm/socket.h +++ b/arch/m32r/include/uapi/asm/socket.h @@ -70,4 +70,6 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* _ASM_M32R_SOCKET_H */ diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h index 17307ab90474..9d11a7713923 100644 --- a/arch/mips/include/uapi/asm/socket.h +++ b/arch/mips/include/uapi/asm/socket.h @@ -90,5 +90,6 @@ To add: #define SO_REUSEPORT 0x0200 /* Allow local address and port reuse. */ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/mn10300/include/uapi/asm/socket.h b/arch/mn10300/include/uapi/asm/socket.h index af5366bbfe62..ab702c40b30e 100644 --- a/arch/mn10300/include/uapi/asm/socket.h +++ b/arch/mn10300/include/uapi/asm/socket.h @@ -70,4 +70,6 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h index d9ff4731253b..da2c8d3c209e 100644 --- a/arch/parisc/include/uapi/asm/socket.h +++ b/arch/parisc/include/uapi/asm/socket.h @@ -69,6 +69,7 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 0x4024 +#define SO_LOCK_FILTER 0x4025 /* O_NONBLOCK clashes with the bits used for socket types. Therefore we * have to define SOCK_NONBLOCK to a different value here. diff --git a/arch/powerpc/include/uapi/asm/socket.h b/arch/powerpc/include/uapi/asm/socket.h index eb0b1864d400..e6ca31816cc9 100644 --- a/arch/powerpc/include/uapi/asm/socket.h +++ b/arch/powerpc/include/uapi/asm/socket.h @@ -77,4 +77,6 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* _ASM_POWERPC_SOCKET_H */ diff --git a/arch/s390/include/uapi/asm/socket.h b/arch/s390/include/uapi/asm/socket.h index 436d07c23be8..9ce60b68f070 100644 --- a/arch/s390/include/uapi/asm/socket.h +++ b/arch/s390/include/uapi/asm/socket.h @@ -76,4 +76,6 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h index c83a937ead00..fbbba57547d1 100644 --- a/arch/sparc/include/uapi/asm/socket.h +++ b/arch/sparc/include/uapi/asm/socket.h @@ -66,6 +66,7 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 0x0027 +#define SO_LOCK_FILTER 0x0028 /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 0x5001 diff --git a/arch/xtensa/include/uapi/asm/socket.h b/arch/xtensa/include/uapi/asm/socket.h index 38079be1cf1e..dbf316487b51 100644 --- a/arch/xtensa/include/uapi/asm/socket.h +++ b/arch/xtensa/include/uapi/asm/socket.h @@ -81,4 +81,6 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* _XTENSA_SOCKET_H */ diff --git a/include/net/sock.h b/include/net/sock.h index 182ca99405ad..5a34e2f03657 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -664,6 +664,7 @@ enum sock_flags { * Will use last 4 bytes of packet sent from * user-space instead. */ + SOCK_FILTER_LOCKED, /* Filter cannot be changed anymore */ }; static inline void sock_copy_flags(struct sock *nsk, struct sock *osk) diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h index 2d32d073a6f9..3f6a99201410 100644 --- a/include/uapi/asm-generic/socket.h +++ b/include/uapi/asm-generic/socket.h @@ -73,4 +73,6 @@ /* Instruct lower device to use last 4-bytes of skb data as FCS */ #define SO_NOFCS 43 +#define SO_LOCK_FILTER 44 + #endif /* __ASM_GENERIC_SOCKET_H */ diff --git a/net/core/filter.c b/net/core/filter.c index 2ead2a9b1859..2e20b55a7830 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -721,6 +721,9 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) unsigned int fsize = sizeof(struct sock_filter) * fprog->len; int err; + if (sock_flag(sk, SOCK_FILTER_LOCKED)) + return -EPERM; + /* Make sure new filter is there and in the right amounts. */ if (fprog->filter == NULL) return -EINVAL; @@ -757,6 +760,9 @@ int sk_detach_filter(struct sock *sk) int ret = -ENOENT; struct sk_filter *filter; + if (sock_flag(sk, SOCK_FILTER_LOCKED)) + return -EPERM; + filter = rcu_dereference_protected(sk->sk_filter, sock_owned_by_user(sk)); if (filter) { diff --git a/net/core/sock.c b/net/core/sock.c index bc131d419683..8258fb741e9a 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -861,6 +861,13 @@ set_rcvbuf: ret = sk_detach_filter(sk); break; + case SO_LOCK_FILTER: + if (sock_flag(sk, SOCK_FILTER_LOCKED) && !valbool) + ret = -EPERM; + else + sock_valbool_flag(sk, SOCK_FILTER_LOCKED, valbool); + break; + case SO_PASSSEC: if (valbool) set_bit(SOCK_PASSSEC, &sock->flags); @@ -1140,6 +1147,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname, goto lenout; + case SO_LOCK_FILTER: + v.val = sock_flag(sk, SOCK_FILTER_LOCKED); + break; + default: return -ENOPROTOOPT; } -- cgit v1.2.3-59-g8ed1b From 88390243a9937f9831aeba5113a8b59bb1d4411a Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Sun, 6 Jan 2013 18:32:22 -0800 Subject: Input: tegra-kbc - add support for rows/columns configuration from dt The NVIDIA's Tegra KBC has maximum 24 pins to make matrix keypad. Any pin can be configured as row or column. The maximum column pin can be 8 and maximum row pin can be 16. Remove the assumption that all first 16 pins will be used as row and remaining as columns and Add the property for configuring pins to either row or column from DT. Update the devicetree binding document accordingly. Signed-off-by: Laxman Dewangan Signed-off-by: Dmitry Torokhov --- .../bindings/input/nvidia,tegra20-kbc.txt | 22 ++++++ drivers/input/keyboard/tegra-kbc.c | 82 +++++++++++++++++----- 2 files changed, 85 insertions(+), 19 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt b/Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt index 72683be6de35..2995fae7ee47 100644 --- a/Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt +++ b/Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt @@ -1,7 +1,18 @@ * Tegra keyboard controller +The key controller has maximum 24 pins to make matrix keypad. Any pin +can be configured as row or column. The maximum column pin can be 8 +and maximum row pins can be 16 for Tegra20/Tegra30. Required properties: - compatible: "nvidia,tegra20-kbc" +- reg: Register base address of KBC. +- interrupts: Interrupt number for the KBC. +- nvidia,kbc-row-pins: The KBC pins which are configured as row. This is an + array of pin numbers which is used as rows. +- nvidia,kbc-col-pins: The KBC pins which are configured as column. This is an + array of pin numbers which is used as column. +- linux,keymap: The keymap for keys as described in the binding document + devicetree/bindings/input/matrix-keymap.txt. Optional properties, in addition to those specified by the shared matrix-keyboard bindings: @@ -19,5 +30,16 @@ Example: keyboard: keyboard { compatible = "nvidia,tegra20-kbc"; reg = <0x7000e200 0x100>; + interrupts = <0 85 0x04>; nvidia,ghost-filter; + nvidia,debounce-delay-ms = <640>; + nvidia,kbc-row-pins = <0 1 2>; /* pin 0, 1, 2 as rows */ + nvidia,kbc-col-pins = <11 12 13>; /* pin 11, 12, 13 as columns */ + linux,keymap = <0x00000074 + 0x00010067 + 0x00020066 + 0x01010068 + 0x02000069 + 0x02010070 + 0x02020071>; }; diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index f79993679070..39ef663bb38f 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -614,13 +614,21 @@ static struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata( struct device_node *np = pdev->dev.of_node; u32 prop; int i; - - if (!np) - return NULL; + u32 num_rows = 0; + u32 num_cols = 0; + u32 cols_cfg[KBC_MAX_GPIO]; + u32 rows_cfg[KBC_MAX_GPIO]; + int proplen; + int ret; + + if (!np) { + dev_err(&pdev->dev, "device tree data is missing\n"); + return ERR_PTR(-ENOENT); + } pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) - return NULL; + return ERR_PTR(-ENOMEM); if (!of_property_read_u32(np, "nvidia,debounce-delay-ms", &prop)) pdata->debounce_cnt = prop; @@ -634,18 +642,55 @@ static struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata( if (of_find_property(np, "nvidia,wakeup-source", NULL)) pdata->wakeup = true; - /* - * All currently known keymaps with device tree support use the same - * pin_cfg, so set it up here. - */ - for (i = 0; i < KBC_MAX_ROW; i++) { - pdata->pin_cfg[i].num = i; - pdata->pin_cfg[i].type = PIN_CFG_ROW; + if (!of_get_property(np, "nvidia,kbc-row-pins", &proplen)) { + dev_err(&pdev->dev, "property nvidia,kbc-row-pins not found\n"); + return ERR_PTR(-ENOENT); + } + num_rows = proplen / sizeof(u32); + + if (!of_get_property(np, "nvidia,kbc-col-pins", &proplen)) { + dev_err(&pdev->dev, "property nvidia,kbc-col-pins not found\n"); + return ERR_PTR(-ENOENT); + } + num_cols = proplen / sizeof(u32); + + if (!of_get_property(np, "linux,keymap", &proplen)) { + dev_err(&pdev->dev, "property linux,keymap not found\n"); + return ERR_PTR(-ENOENT); } - for (i = 0; i < KBC_MAX_COL; i++) { - pdata->pin_cfg[KBC_MAX_ROW + i].num = i; - pdata->pin_cfg[KBC_MAX_ROW + i].type = PIN_CFG_COL; + if (!num_rows || !num_cols || ((num_rows + num_cols) > KBC_MAX_GPIO)) { + dev_err(&pdev->dev, + "keypad rows/columns not porperly specified\n"); + return ERR_PTR(-EINVAL); + } + + /* Set all pins as non-configured */ + for (i = 0; i < KBC_MAX_GPIO; i++) + pdata->pin_cfg[i].type = PIN_CFG_IGNORE; + + ret = of_property_read_u32_array(np, "nvidia,kbc-row-pins", + rows_cfg, num_rows); + if (ret < 0) { + dev_err(&pdev->dev, "Rows configurations are not proper\n"); + return ERR_PTR(-EINVAL); + } + + ret = of_property_read_u32_array(np, "nvidia,kbc-col-pins", + cols_cfg, num_cols); + if (ret < 0) { + dev_err(&pdev->dev, "Cols configurations are not proper\n"); + return ERR_PTR(-EINVAL); + } + + for (i = 0; i < num_rows; i++) { + pdata->pin_cfg[rows_cfg[i]].type = PIN_CFG_ROW; + pdata->pin_cfg[rows_cfg[i]].num = i; + } + + for (i = 0; i < num_cols; i++) { + pdata->pin_cfg[cols_cfg[i]].type = PIN_CFG_COL; + pdata->pin_cfg[cols_cfg[i]].num = i; } return pdata; @@ -654,7 +699,8 @@ static struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata( static inline struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata( struct platform_device *pdev) { - return NULL; + dev_err(&pdev->dev, "platform data is missing\n"); + return ERR_PTR(-EINVAL); } #endif @@ -700,10 +746,8 @@ static int tegra_kbc_probe(struct platform_device *pdev) if (!pdata) pdata = tegra_kbc_dt_parse_pdata(pdev); - if (!pdata) { - dev_err(&pdev->dev, "Platform data missing\n"); - return -EINVAL; - } + if (IS_ERR(pdata)) + return PTR_ERR(pdata); if (!tegra_kbc_check_pin_cfg(pdata, &pdev->dev, &num_rows)) return -EINVAL; -- cgit v1.2.3-59-g8ed1b From b6ae7a26535bb37c6c023c6f25b7f98904a8ab7c Mon Sep 17 00:00:00 2001 From: Pritesh Raithatha Date: Tue, 8 Jan 2013 13:02:37 +0530 Subject: pinctrl: tegra114: add pinctrl driver for NVIDIA's Tegra114 SoC This adds a driver for the Tegra114 pinmux, and required parameterization data for Tegra114. The driver uses the common Tegra pincontrol driver utility functions to implement the majority of the driver. This driver is not compatible with the earlier NVIDIA's SoCs, hence add new compatibile as "nvidia,tegra114-pinmux". Originally written by Pritesh. ldewangan: - cleanup the patches, - remove non-require tables. - Use module_platform_driver() for driver registartion. Signed-off-by: Pritesh Raithatha Signed-off-by: Laxman Dewangan Reviewed-by: Stephen Warren Signed-off-by: Linus Walleij --- .../bindings/pinctrl/nvidia,tegra114-pinmux.txt | 120 + drivers/pinctrl/Kconfig | 4 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-tegra114.c | 2757 ++++++++++++++++++++ 4 files changed, 2882 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/nvidia,tegra114-pinmux.txt create mode 100644 drivers/pinctrl/pinctrl-tegra114.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra114-pinmux.txt b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra114-pinmux.txt new file mode 100644 index 000000000000..e204d009f16c --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra114-pinmux.txt @@ -0,0 +1,120 @@ +NVIDIA Tegra114 pinmux controller + +The Tegra114 pinctrl binding is very similar to the Tegra20 and Tegra30 +pinctrl binding, as described in nvidia,tegra20-pinmux.txt and +nvidia,tegra30-pinmux.txt. In fact, this document assumes that binding as +a baseline, and only documents the differences between the two bindings. + +Required properties: +- compatible: "nvidia,tegra114-pinmux" +- reg: Should contain the register physical address and length for each of + the pad control and mux registers. The first bank of address must be the + driver strength pad control register address and second bank address must + be pinmux register address. + +Tegra114 adds the following optional properties for pin configuration subnodes: +- nvidia,enable-input: Integer. Enable the pin's input path. 0: no, 1: yes. +- nvidia,open-drain: Integer. Enable open drain mode. 0: no, 1: yes. +- nvidia,lock: Integer. Lock the pin configuration against further changes + until reset. 0: no, 1: yes. +- nvidia,io-reset: Integer. Reset the IO path. 0: no, 1: yes. +- nvidia,rcv-sel: Integer. Select VIL/VIH receivers. 0: normal, 1: high. +- nvidia,drive-type: Integer. Valid range 0...3. + +As with Tegra20 and Terga30, see the Tegra TRM for complete details regarding +which groups support which functionality. + +Valid values for pin and group names are: + + per-pin mux groups: + + These all support nvidia,function, nvidia,tristate, nvidia,pull, + nvidia,enable-input, nvidia,lock. Some support nvidia,open-drain, + nvidia,io-reset and nvidia,rcv-sel. + + ulpi_data0_po1, ulpi_data1_po2, ulpi_data2_po3, ulpi_data3_po4, + ulpi_data4_po5, ulpi_data5_po6, ulpi_data6_po7, ulpi_data7_po0, + ulpi_clk_py0, ulpi_dir_py1, ulpi_nxt_py2, ulpi_stp_py3, dap3_fs_pp0, + dap3_din_pp1, dap3_dout_pp2, dap3_sclk_pp3, pv0, pv1, sdmmc1_clk_pz0, + sdmmc1_cmd_pz1, sdmmc1_dat3_py4, sdmmc1_dat2_py5, sdmmc1_dat1_py6, + sdmmc1_dat0_py7, clk2_out_pw5, clk2_req_pcc5, hdmi_int_pn7, ddc_scl_pv4, + ddc_sda_pv5, uart2_rxd_pc3, uart2_txd_pc2, uart2_rts_n_pj6, + uart2_cts_n_pj5, uart3_txd_pw6, uart3_rxd_pw7, uart3_cts_n_pa1, + uart3_rts_n_pc0, pu0, pu1, pu2, pu3, pu4, pu5, pu6, gen1_i2c_sda_pc5, + gen1_i2c_scl_pc4, dap4_fs_pp4, dap4_din_pp5, dap4_dout_pp6, dap4_sclk_pp7, + clk3_out_pee0, clk3_req_pee1, gmi_wp_n_pc7, gmi_iordy_pi5, gmi_wait_pi7, + gmi_adv_n_pk0, gmi_clk_pk1, gmi_cs0_n_pj0, gmi_cs1_n_pj2, gmi_cs2_n_pk3, + gmi_cs3_n_pk4, gmi_cs4_n_pk2, gmi_cs6_n_pi3, gmi_cs7_n_pi6, gmi_ad0_pg0, + gmi_ad1_pg1, gmi_ad2_pg2, gmi_ad3_pg3, gmi_ad4_pg4, gmi_ad5_pg5, + gmi_ad6_pg6, gmi_ad7_pg7, gmi_ad8_ph0, gmi_ad9_ph1, gmi_ad10_ph2, + gmi_ad11_ph3, gmi_ad12_ph4, gmi_ad13_ph5, gmi_ad14_ph6, gmi_ad15_ph7, + gmi_a16_pj7, gmi_a17_pb0, gmi_a18_pb1, gmi_a19_pk7, gmi_wr_n_pi0, + gmi_oe_n_pi1, gmi_dqs_p_pj3, gmi_rst_n_pi4, gen2_i2c_scl_pt5, + gen2_i2c_sda_pt6, sdmmc4_clk_pcc4, sdmmc4_cmd_pt7, sdmmc4_dat0_paa0, + sdmmc4_dat1_paa1, sdmmc4_dat2_paa2, sdmmc4_dat3_paa3, sdmmc4_dat4_paa4, + sdmmc4_dat5_paa5, sdmmc4_dat6_paa6, sdmmc4_dat7_paa7, cam_mclk_pcc0, + pcc1, pbb0, cam_i2c_scl_pbb1, cam_i2c_sda_pbb2, pbb3, pbb4, pbb5, pbb6, + pbb7, pcc2, pwr_i2c_scl_pz6, pwr_i2c_sda_pz7, kb_row0_pr0, kb_row1_pr1, + kb_row2_pr2, kb_row3_pr3, kb_row4_pr4, kb_row5_pr5, kb_row6_pr6, + kb_row7_pr7, kb_row8_ps0, kb_row9_ps1, kb_row10_ps2, kb_col0_pq0, + kb_col1_pq1, kb_col2_pq2, kb_col3_pq3, kb_col4_pq4, kb_col5_pq5, + kb_col6_pq6, kb_col7_pq7, clk_32k_out_pa0, sys_clk_req_pz5, core_pwr_req, + cpu_pwr_req, pwr_int_n, owr, dap1_fs_pn0, dap1_din_pn1, dap1_dout_pn2, + dap1_sclk_pn3, clk1_req_pee2, clk1_out_pw4, spdif_in_pk6, spdif_out_pk5, + dap2_fs_pa2, dap2_din_pa4, dap2_dout_pa5, dap2_sclk_pa3, dvfs_pwm_px0, + gpio_x1_aud_px1, gpio_x3_aud_px3, dvfs_clk_px2, gpio_x4_aud_px4, + gpio_x5_aud_px5, gpio_x6_aud_px6, gpio_x7_aud_px7, sdmmc3_clk_pa6, + sdmmc3_cmd_pa7, sdmmc3_dat0_pb7, sdmmc3_dat1_pb6, sdmmc3_dat2_pb5, + sdmmc3_dat3_pb4, hdmi_cec_pee3, sdmmc1_wp_n_pv3, sdmmc3_cd_n_pv2, + gpio_w2_aud_pw2, gpio_w3_aud_pw3, usb_vbus_en0_pn4, usb_vbus_en1_pn5, + sdmmc3_clk_lb_in_pee5, sdmmc3_clk_lb_out_pee4, reset_out_n. + + drive groups: + + These all support nvidia,pull-down-strength, nvidia,pull-up-strength, + nvidia,slew-rate-rising, nvidia,slew-rate-falling. Most but not all + support nvidia,high-speed-mode, nvidia,schmitt, nvidia,low-power-mode + and nvidia,drive-type. + + ao1, ao2, at1, at2, at3, at4, at5, cdev1, cdev2, dap1, dap2, dap3, dap4, + dbg, sdio3, spi, uaa, uab, uart2, uart3, sdio1, ddc, gma, gme, gmf, gmg, + gmh, owr, uda. + +Example: + + pinmux: pinmux { + compatible = "nvidia,tegra114-pinmux"; + reg = <0x70000868 0x148 /* Pad control registers */ + 0x70003000 0x40c>; /* PinMux registers */ + }; + +Example board file extract: + + pinctrl { + sdmmc4_default: pinmux { + sdmmc4_clk_pcc4 { + nvidia,pins = "sdmmc4_clk_pcc4", + nvidia,function = "sdmmc4"; + nvidia,pull = <0>; + nvidia,tristate = <0>; + }; + sdmmc4_dat0_paa0 { + nvidia,pins = "sdmmc4_dat0_paa0", + "sdmmc4_dat1_paa1", + "sdmmc4_dat2_paa2", + "sdmmc4_dat3_paa3", + "sdmmc4_dat4_paa4", + "sdmmc4_dat5_paa5", + "sdmmc4_dat6_paa6", + "sdmmc4_dat7_paa7"; + nvidia,function = "sdmmc4"; + nvidia,pull = <2>; + nvidia,tristate = <0>; + }; + }; + }; + + sdhci@78000400 { + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc4_default>; + }; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index c31aeb01bb00..c9c165d08d8b 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -164,6 +164,10 @@ config PINCTRL_TEGRA30 bool select PINCTRL_TEGRA +config PINCTRL_TEGRA114 + bool + select PINCTRL_TEGRA + config PINCTRL_U300 bool "U300 pin controller driver" depends on ARCH_U300 diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index fc4606f27dc7..e5de64fd32ca 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_PINCTRL_SIRF) += pinctrl-sirf.o obj-$(CONFIG_PINCTRL_TEGRA) += pinctrl-tegra.o obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o +obj-$(CONFIG_PINCTRL_TEGRA114) += pinctrl-tegra114.o obj-$(CONFIG_PINCTRL_U300) += pinctrl-u300.o obj-$(CONFIG_PINCTRL_COH901) += pinctrl-coh901.o obj-$(CONFIG_PINCTRL_SAMSUNG) += pinctrl-samsung.o diff --git a/drivers/pinctrl/pinctrl-tegra114.c b/drivers/pinctrl/pinctrl-tegra114.c new file mode 100644 index 000000000000..053a8b1b12f1 --- /dev/null +++ b/drivers/pinctrl/pinctrl-tegra114.c @@ -0,0 +1,2757 @@ +/* + * Pinctrl data and driver for the NVIDIA Tegra114 pinmux + * + * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved. + * + * Arthur: Pritesh Raithatha + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "pinctrl-tegra.h" + +/* + * Most pins affected by the pinmux can also be GPIOs. Define these first. + * These must match how the GPIO driver names/numbers its pins. + */ +#define _GPIO(offset) (offset) + +#define TEGRA_PIN_CLK_32K_OUT_PA0 _GPIO(0) +#define TEGRA_PIN_UART3_CTS_N_PA1 _GPIO(1) +#define TEGRA_PIN_DAP2_FS_PA2 _GPIO(2) +#define TEGRA_PIN_DAP2_SCLK_PA3 _GPIO(3) +#define TEGRA_PIN_DAP2_DIN_PA4 _GPIO(4) +#define TEGRA_PIN_DAP2_DOUT_PA5 _GPIO(5) +#define TEGRA_PIN_SDMMC3_CLK_PA6 _GPIO(6) +#define TEGRA_PIN_SDMMC3_CMD_PA7 _GPIO(7) +#define TEGRA_PIN_GMI_A17_PB0 _GPIO(8) +#define TEGRA_PIN_GMI_A18_PB1 _GPIO(9) +#define TEGRA_PIN_SDMMC3_DAT3_PB4 _GPIO(12) +#define TEGRA_PIN_SDMMC3_DAT2_PB5 _GPIO(13) +#define TEGRA_PIN_SDMMC3_DAT1_PB6 _GPIO(14) +#define TEGRA_PIN_SDMMC3_DAT0_PB7 _GPIO(15) +#define TEGRA_PIN_UART3_RTS_N_PC0 _GPIO(16) +#define TEGRA_PIN_UART2_TXD_PC2 _GPIO(18) +#define TEGRA_PIN_UART2_RXD_PC3 _GPIO(19) +#define TEGRA_PIN_GEN1_I2C_SCL_PC4 _GPIO(20) +#define TEGRA_PIN_GEN1_I2C_SDA_PC5 _GPIO(21) +#define TEGRA_PIN_GMI_WP_N_PC7 _GPIO(23) +#define TEGRA_PIN_GMI_AD0_PG0 _GPIO(48) +#define TEGRA_PIN_GMI_AD1_PG1 _GPIO(49) +#define TEGRA_PIN_GMI_AD2_PG2 _GPIO(50) +#define TEGRA_PIN_GMI_AD3_PG3 _GPIO(51) +#define TEGRA_PIN_GMI_AD4_PG4 _GPIO(52) +#define TEGRA_PIN_GMI_AD5_PG5 _GPIO(53) +#define TEGRA_PIN_GMI_AD6_PG6 _GPIO(54) +#define TEGRA_PIN_GMI_AD7_PG7 _GPIO(55) +#define TEGRA_PIN_GMI_AD8_PH0 _GPIO(56) +#define TEGRA_PIN_GMI_AD9_PH1 _GPIO(57) +#define TEGRA_PIN_GMI_AD10_PH2 _GPIO(58) +#define TEGRA_PIN_GMI_AD11_PH3 _GPIO(59) +#define TEGRA_PIN_GMI_AD12_PH4 _GPIO(60) +#define TEGRA_PIN_GMI_AD13_PH5 _GPIO(61) +#define TEGRA_PIN_GMI_AD14_PH6 _GPIO(62) +#define TEGRA_PIN_GMI_AD15_PH7 _GPIO(63) +#define TEGRA_PIN_GMI_WR_N_PI0 _GPIO(64) +#define TEGRA_PIN_GMI_OE_N_PI1 _GPIO(65) +#define TEGRA_PIN_GMI_CS6_N_PI3 _GPIO(67) +#define TEGRA_PIN_GMI_RST_N_PI4 _GPIO(68) +#define TEGRA_PIN_GMI_IORDY_PI5 _GPIO(69) +#define TEGRA_PIN_GMI_CS7_N_PI6 _GPIO(70) +#define TEGRA_PIN_GMI_WAIT_PI7 _GPIO(71) +#define TEGRA_PIN_GMI_CS0_N_PJ0 _GPIO(72) +#define TEGRA_PIN_GMI_CS1_N_PJ2 _GPIO(74) +#define TEGRA_PIN_GMI_DQS_P_PJ3 _GPIO(75) +#define TEGRA_PIN_UART2_CTS_N_PJ5 _GPIO(77) +#define TEGRA_PIN_UART2_RTS_N_PJ6 _GPIO(78) +#define TEGRA_PIN_GMI_A16_PJ7 _GPIO(79) +#define TEGRA_PIN_GMI_ADV_N_PK0 _GPIO(80) +#define TEGRA_PIN_GMI_CLK_PK1 _GPIO(81) +#define TEGRA_PIN_GMI_CS4_N_PK2 _GPIO(82) +#define TEGRA_PIN_GMI_CS2_N_PK3 _GPIO(83) +#define TEGRA_PIN_GMI_CS3_N_PK4 _GPIO(84) +#define TEGRA_PIN_SPDIF_OUT_PK5 _GPIO(85) +#define TEGRA_PIN_SPDIF_IN_PK6 _GPIO(86) +#define TEGRA_PIN_GMI_A19_PK7 _GPIO(87) +#define TEGRA_PIN_DAP1_FS_PN0 _GPIO(104) +#define TEGRA_PIN_DAP1_DIN_PN1 _GPIO(105) +#define TEGRA_PIN_DAP1_DOUT_PN2 _GPIO(106) +#define TEGRA_PIN_DAP1_SCLK_PN3 _GPIO(107) +#define TEGRA_PIN_USB_VBUS_EN0_PN4 _GPIO(108) +#define TEGRA_PIN_USB_VBUS_EN1_PN5 _GPIO(109) +#define TEGRA_PIN_HDMI_INT_PN7 _GPIO(111) +#define TEGRA_PIN_ULPI_DATA7_PO0 _GPIO(112) +#define TEGRA_PIN_ULPI_DATA0_PO1 _GPIO(113) +#define TEGRA_PIN_ULPI_DATA1_PO2 _GPIO(114) +#define TEGRA_PIN_ULPI_DATA2_PO3 _GPIO(115) +#define TEGRA_PIN_ULPI_DATA3_PO4 _GPIO(116) +#define TEGRA_PIN_ULPI_DATA4_PO5 _GPIO(117) +#define TEGRA_PIN_ULPI_DATA5_PO6 _GPIO(118) +#define TEGRA_PIN_ULPI_DATA6_PO7 _GPIO(119) +#define TEGRA_PIN_DAP3_FS_PP0 _GPIO(120) +#define TEGRA_PIN_DAP3_DIN_PP1 _GPIO(121) +#define TEGRA_PIN_DAP3_DOUT_PP2 _GPIO(122) +#define TEGRA_PIN_DAP3_SCLK_PP3 _GPIO(123) +#define TEGRA_PIN_DAP4_FS_PP4 _GPIO(124) +#define TEGRA_PIN_DAP4_DIN_PP5 _GPIO(125) +#define TEGRA_PIN_DAP4_DOUT_PP6 _GPIO(126) +#define TEGRA_PIN_DAP4_SCLK_PP7 _GPIO(127) +#define TEGRA_PIN_KB_COL0_PQ0 _GPIO(128) +#define TEGRA_PIN_KB_COL1_PQ1 _GPIO(129) +#define TEGRA_PIN_KB_COL2_PQ2 _GPIO(130) +#define TEGRA_PIN_KB_COL3_PQ3 _GPIO(131) +#define TEGRA_PIN_KB_COL4_PQ4 _GPIO(132) +#define TEGRA_PIN_KB_COL5_PQ5 _GPIO(133) +#define TEGRA_PIN_KB_COL6_PQ6 _GPIO(134) +#define TEGRA_PIN_KB_COL7_PQ7 _GPIO(135) +#define TEGRA_PIN_KB_ROW0_PR0 _GPIO(136) +#define TEGRA_PIN_KB_ROW1_PR1 _GPIO(137) +#define TEGRA_PIN_KB_ROW2_PR2 _GPIO(138) +#define TEGRA_PIN_KB_ROW3_PR3 _GPIO(139) +#define TEGRA_PIN_KB_ROW4_PR4 _GPIO(140) +#define TEGRA_PIN_KB_ROW5_PR5 _GPIO(141) +#define TEGRA_PIN_KB_ROW6_PR6 _GPIO(142) +#define TEGRA_PIN_KB_ROW7_PR7 _GPIO(143) +#define TEGRA_PIN_KB_ROW8_PS0 _GPIO(144) +#define TEGRA_PIN_KB_ROW9_PS1 _GPIO(145) +#define TEGRA_PIN_KB_ROW10_PS2 _GPIO(146) +#define TEGRA_PIN_GEN2_I2C_SCL_PT5 _GPIO(157) +#define TEGRA_PIN_GEN2_I2C_SDA_PT6 _GPIO(158) +#define TEGRA_PIN_SDMMC4_CMD_PT7 _GPIO(159) +#define TEGRA_PIN_PU0 _GPIO(160) +#define TEGRA_PIN_PU1 _GPIO(161) +#define TEGRA_PIN_PU2 _GPIO(162) +#define TEGRA_PIN_PU3 _GPIO(163) +#define TEGRA_PIN_PU4 _GPIO(164) +#define TEGRA_PIN_PU5 _GPIO(165) +#define TEGRA_PIN_PU6 _GPIO(166) +#define TEGRA_PIN_PV0 _GPIO(168) +#define TEGRA_PIN_PV1 _GPIO(169) +#define TEGRA_PIN_SDMMC3_CD_N_PV2 _GPIO(170) +#define TEGRA_PIN_SDMMC1_WP_N_PV3 _GPIO(171) +#define TEGRA_PIN_DDC_SCL_PV4 _GPIO(172) +#define TEGRA_PIN_DDC_SDA_PV5 _GPIO(173) +#define TEGRA_PIN_GPIO_W2_AUD_PW2 _GPIO(178) +#define TEGRA_PIN_GPIO_W3_AUD_PW3 _GPIO(179) +#define TEGRA_PIN_CLK1_OUT_PW4 _GPIO(180) +#define TEGRA_PIN_CLK2_OUT_PW5 _GPIO(181) +#define TEGRA_PIN_UART3_TXD_PW6 _GPIO(182) +#define TEGRA_PIN_UART3_RXD_PW7 _GPIO(183) +#define TEGRA_PIN_DVFS_PWM_PX0 _GPIO(184) +#define TEGRA_PIN_GPIO_X1_AUD_PX1 _GPIO(185) +#define TEGRA_PIN_DVFS_CLK_PX2 _GPIO(186) +#define TEGRA_PIN_GPIO_X3_AUD_PX3 _GPIO(187) +#define TEGRA_PIN_GPIO_X4_AUD_PX4 _GPIO(188) +#define TEGRA_PIN_GPIO_X5_AUD_PX5 _GPIO(189) +#define TEGRA_PIN_GPIO_X6_AUD_PX6 _GPIO(190) +#define TEGRA_PIN_GPIO_X7_AUD_PX7 _GPIO(191) +#define TEGRA_PIN_ULPI_CLK_PY0 _GPIO(192) +#define TEGRA_PIN_ULPI_DIR_PY1 _GPIO(193) +#define TEGRA_PIN_ULPI_NXT_PY2 _GPIO(194) +#define TEGRA_PIN_ULPI_STP_PY3 _GPIO(195) +#define TEGRA_PIN_SDMMC1_DAT3_PY4 _GPIO(196) +#define TEGRA_PIN_SDMMC1_DAT2_PY5 _GPIO(197) +#define TEGRA_PIN_SDMMC1_DAT1_PY6 _GPIO(198) +#define TEGRA_PIN_SDMMC1_DAT0_PY7 _GPIO(199) +#define TEGRA_PIN_SDMMC1_CLK_PZ0 _GPIO(200) +#define TEGRA_PIN_SDMMC1_CMD_PZ1 _GPIO(201) +#define TEGRA_PIN_SYS_CLK_REQ_PZ5 _GPIO(205) +#define TEGRA_PIN_PWR_I2C_SCL_PZ6 _GPIO(206) +#define TEGRA_PIN_PWR_I2C_SDA_PZ7 _GPIO(207) +#define TEGRA_PIN_SDMMC4_DAT0_PAA0 _GPIO(208) +#define TEGRA_PIN_SDMMC4_DAT1_PAA1 _GPIO(209) +#define TEGRA_PIN_SDMMC4_DAT2_PAA2 _GPIO(210) +#define TEGRA_PIN_SDMMC4_DAT3_PAA3 _GPIO(211) +#define TEGRA_PIN_SDMMC4_DAT4_PAA4 _GPIO(212) +#define TEGRA_PIN_SDMMC4_DAT5_PAA5 _GPIO(213) +#define TEGRA_PIN_SDMMC4_DAT6_PAA6 _GPIO(214) +#define TEGRA_PIN_SDMMC4_DAT7_PAA7 _GPIO(215) +#define TEGRA_PIN_PBB0 _GPIO(216) +#define TEGRA_PIN_CAM_I2C_SCL_PBB1 _GPIO(217) +#define TEGRA_PIN_CAM_I2C_SDA_PBB2 _GPIO(218) +#define TEGRA_PIN_PBB3 _GPIO(219) +#define TEGRA_PIN_PBB4 _GPIO(220) +#define TEGRA_PIN_PBB5 _GPIO(221) +#define TEGRA_PIN_PBB6 _GPIO(222) +#define TEGRA_PIN_PBB7 _GPIO(223) +#define TEGRA_PIN_CAM_MCLK_PCC0 _GPIO(224) +#define TEGRA_PIN_PCC1 _GPIO(225) +#define TEGRA_PIN_PCC2 _GPIO(226) +#define TEGRA_PIN_SDMMC4_CLK_PCC4 _GPIO(228) +#define TEGRA_PIN_CLK2_REQ_PCC5 _GPIO(229) +#define TEGRA_PIN_CLK3_OUT_PEE0 _GPIO(240) +#define TEGRA_PIN_CLK3_REQ_PEE1 _GPIO(241) +#define TEGRA_PIN_CLK1_REQ_PEE2 _GPIO(242) +#define TEGRA_PIN_HDMI_CEC_PEE3 _GPIO(243) +#define TEGRA_PIN_SDMMC3_CLK_LB_OUT_PEE4 _GPIO(244) +#define TEGRA_PIN_SDMMC3_CLK_LB_IN_PEE5 _GPIO(245) + +/* All non-GPIO pins follow */ +#define NUM_GPIOS (TEGRA_PIN_SDMMC3_CLK_LB_IN_PEE5 + 1) +#define _PIN(offset) (NUM_GPIOS + (offset)) + +/* Non-GPIO pins */ +#define TEGRA_PIN_CORE_PWR_REQ _PIN(0) +#define TEGRA_PIN_CPU_PWR_REQ _PIN(1) +#define TEGRA_PIN_PWR_INT_N _PIN(2) +#define TEGRA_PIN_RESET_OUT_N _PIN(3) +#define TEGRA_PIN_OWR _PIN(4) + +static const struct pinctrl_pin_desc tegra114_pins[] = { + PINCTRL_PIN(TEGRA_PIN_CLK_32K_OUT_PA0, "CLK_32K_OUT PA0"), + PINCTRL_PIN(TEGRA_PIN_UART3_CTS_N_PA1, "UART3_CTS_N PA1"), + PINCTRL_PIN(TEGRA_PIN_DAP2_FS_PA2, "DAP2_FS PA2"), + PINCTRL_PIN(TEGRA_PIN_DAP2_SCLK_PA3, "DAP2_SCLK PA3"), + PINCTRL_PIN(TEGRA_PIN_DAP2_DIN_PA4, "DAP2_DIN PA4"), + PINCTRL_PIN(TEGRA_PIN_DAP2_DOUT_PA5, "DAP2_DOUT PA5"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_CLK_PA6, "SDMMC3_CLK PA6"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_CMD_PA7, "SDMMC3_CMD PA7"), + PINCTRL_PIN(TEGRA_PIN_GMI_A17_PB0, "GMI_A17 PB0"), + PINCTRL_PIN(TEGRA_PIN_GMI_A18_PB1, "GMI_A18 PB1"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT3_PB4, "SDMMC3_DAT3 PB4"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT2_PB5, "SDMMC3_DAT2 PB5"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT1_PB6, "SDMMC3_DAT1 PB6"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT0_PB7, "SDMMC3_DAT0 PB7"), + PINCTRL_PIN(TEGRA_PIN_UART3_RTS_N_PC0, "UART3_RTS_N PC0"), + PINCTRL_PIN(TEGRA_PIN_UART2_TXD_PC2, "UART2_TXD PC2"), + PINCTRL_PIN(TEGRA_PIN_UART2_RXD_PC3, "UART2_RXD PC3"), + PINCTRL_PIN(TEGRA_PIN_GEN1_I2C_SCL_PC4, "GEN1_I2C_SCL PC4"), + PINCTRL_PIN(TEGRA_PIN_GEN1_I2C_SDA_PC5, "GEN1_I2C_SDA PC5"), + PINCTRL_PIN(TEGRA_PIN_GMI_WP_N_PC7, "GMI_WP_N PC7"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD0_PG0, "GMI_AD0 PG0"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD1_PG1, "GMI_AD1 PG1"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD2_PG2, "GMI_AD2 PG2"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD3_PG3, "GMI_AD3 PG3"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD4_PG4, "GMI_AD4 PG4"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD5_PG5, "GMI_AD5 PG5"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD6_PG6, "GMI_AD6 PG6"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD7_PG7, "GMI_AD7 PG7"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD8_PH0, "GMI_AD8 PH0"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD9_PH1, "GMI_AD9 PH1"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD10_PH2, "GMI_AD10 PH2"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD11_PH3, "GMI_AD11 PH3"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD12_PH4, "GMI_AD12 PH4"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD13_PH5, "GMI_AD13 PH5"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD14_PH6, "GMI_AD14 PH6"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD15_PH7, "GMI_AD15 PH7"), + PINCTRL_PIN(TEGRA_PIN_GMI_WR_N_PI0, "GMI_WR_N PI0"), + PINCTRL_PIN(TEGRA_PIN_GMI_OE_N_PI1, "GMI_OE_N PI1"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS6_N_PI3, "GMI_CS6_N PI3"), + PINCTRL_PIN(TEGRA_PIN_GMI_RST_N_PI4, "GMI_RST_N PI4"), + PINCTRL_PIN(TEGRA_PIN_GMI_IORDY_PI5, "GMI_IORDY PI5"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS7_N_PI6, "GMI_CS7_N PI6"), + PINCTRL_PIN(TEGRA_PIN_GMI_WAIT_PI7, "GMI_WAIT PI7"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS0_N_PJ0, "GMI_CS0_N PJ0"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS1_N_PJ2, "GMI_CS1_N PJ2"), + PINCTRL_PIN(TEGRA_PIN_GMI_DQS_P_PJ3, "GMI_DQS_P PJ3"), + PINCTRL_PIN(TEGRA_PIN_UART2_CTS_N_PJ5, "UART2_CTS_N PJ5"), + PINCTRL_PIN(TEGRA_PIN_UART2_RTS_N_PJ6, "UART2_RTS_N PJ6"), + PINCTRL_PIN(TEGRA_PIN_GMI_A16_PJ7, "GMI_A16 PJ7"), + PINCTRL_PIN(TEGRA_PIN_GMI_ADV_N_PK0, "GMI_ADV_N PK0"), + PINCTRL_PIN(TEGRA_PIN_GMI_CLK_PK1, "GMI_CLK PK1"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS4_N_PK2, "GMI_CS4_N PK2"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS2_N_PK3, "GMI_CS2_N PK3"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS3_N_PK4, "GMI_CS3_N PK4"), + PINCTRL_PIN(TEGRA_PIN_SPDIF_OUT_PK5, "SPDIF_OUT PK5"), + PINCTRL_PIN(TEGRA_PIN_SPDIF_IN_PK6, "SPDIF_IN PK6"), + PINCTRL_PIN(TEGRA_PIN_GMI_A19_PK7, "GMI_A19 PK7"), + PINCTRL_PIN(TEGRA_PIN_DAP1_FS_PN0, "DAP1_FS PN0"), + PINCTRL_PIN(TEGRA_PIN_DAP1_DIN_PN1, "DAP1_DIN PN1"), + PINCTRL_PIN(TEGRA_PIN_DAP1_DOUT_PN2, "DAP1_DOUT PN2"), + PINCTRL_PIN(TEGRA_PIN_DAP1_SCLK_PN3, "DAP1_SCLK PN3"), + PINCTRL_PIN(TEGRA_PIN_USB_VBUS_EN0_PN4, "USB_VBUS_EN0 PN4"), + PINCTRL_PIN(TEGRA_PIN_USB_VBUS_EN1_PN5, "USB_VBUS_EN1 PN5"), + PINCTRL_PIN(TEGRA_PIN_HDMI_INT_PN7, "HDMI_INT PN7"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA7_PO0, "ULPI_DATA7 PO0"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA0_PO1, "ULPI_DATA0 PO1"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA1_PO2, "ULPI_DATA1 PO2"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA2_PO3, "ULPI_DATA2 PO3"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA3_PO4, "ULPI_DATA3 PO4"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA4_PO5, "ULPI_DATA4 PO5"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA5_PO6, "ULPI_DATA5 PO6"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA6_PO7, "ULPI_DATA6 PO7"), + PINCTRL_PIN(TEGRA_PIN_DAP3_FS_PP0, "DAP3_FS PP0"), + PINCTRL_PIN(TEGRA_PIN_DAP3_DIN_PP1, "DAP3_DIN PP1"), + PINCTRL_PIN(TEGRA_PIN_DAP3_DOUT_PP2, "DAP3_DOUT PP2"), + PINCTRL_PIN(TEGRA_PIN_DAP3_SCLK_PP3, "DAP3_SCLK PP3"), + PINCTRL_PIN(TEGRA_PIN_DAP4_FS_PP4, "DAP4_FS PP4"), + PINCTRL_PIN(TEGRA_PIN_DAP4_DIN_PP5, "DAP4_DIN PP5"), + PINCTRL_PIN(TEGRA_PIN_DAP4_DOUT_PP6, "DAP4_DOUT PP6"), + PINCTRL_PIN(TEGRA_PIN_DAP4_SCLK_PP7, "DAP4_SCLK PP7"), + PINCTRL_PIN(TEGRA_PIN_KB_COL0_PQ0, "KB_COL0 PQ0"), + PINCTRL_PIN(TEGRA_PIN_KB_COL1_PQ1, "KB_COL1 PQ1"), + PINCTRL_PIN(TEGRA_PIN_KB_COL2_PQ2, "KB_COL2 PQ2"), + PINCTRL_PIN(TEGRA_PIN_KB_COL3_PQ3, "KB_COL3 PQ3"), + PINCTRL_PIN(TEGRA_PIN_KB_COL4_PQ4, "KB_COL4 PQ4"), + PINCTRL_PIN(TEGRA_PIN_KB_COL5_PQ5, "KB_COL5 PQ5"), + PINCTRL_PIN(TEGRA_PIN_KB_COL6_PQ6, "KB_COL6 PQ6"), + PINCTRL_PIN(TEGRA_PIN_KB_COL7_PQ7, "KB_COL7 PQ7"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW0_PR0, "KB_ROW0 PR0"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW1_PR1, "KB_ROW1 PR1"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW2_PR2, "KB_ROW2 PR2"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW3_PR3, "KB_ROW3 PR3"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW4_PR4, "KB_ROW4 PR4"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW5_PR5, "KB_ROW5 PR5"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW6_PR6, "KB_ROW6 PR6"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW7_PR7, "KB_ROW7 PR7"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW8_PS0, "KB_ROW8 PS0"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW9_PS1, "KB_ROW9 PS1"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW10_PS2, "KB_ROW10 PS2"), + PINCTRL_PIN(TEGRA_PIN_GEN2_I2C_SCL_PT5, "GEN2_I2C_SCL PT5"), + PINCTRL_PIN(TEGRA_PIN_GEN2_I2C_SDA_PT6, "GEN2_I2C_SDA PT6"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_CMD_PT7, "SDMMC4_CMD PT7"), + PINCTRL_PIN(TEGRA_PIN_PU0, "PU0"), + PINCTRL_PIN(TEGRA_PIN_PU1, "PU1"), + PINCTRL_PIN(TEGRA_PIN_PU2, "PU2"), + PINCTRL_PIN(TEGRA_PIN_PU3, "PU3"), + PINCTRL_PIN(TEGRA_PIN_PU4, "PU4"), + PINCTRL_PIN(TEGRA_PIN_PU5, "PU5"), + PINCTRL_PIN(TEGRA_PIN_PU6, "PU6"), + PINCTRL_PIN(TEGRA_PIN_PV0, "PV0"), + PINCTRL_PIN(TEGRA_PIN_PV1, "PV1"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_CD_N_PV2, "SDMMC3_CD_N PV2"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_WP_N_PV3, "SDMMC1_WP_N PV3"), + PINCTRL_PIN(TEGRA_PIN_DDC_SCL_PV4, "DDC_SCL PV4"), + PINCTRL_PIN(TEGRA_PIN_DDC_SDA_PV5, "DDC_SDA PV5"), + PINCTRL_PIN(TEGRA_PIN_GPIO_W2_AUD_PW2, "GPIO_W2_AUD PW2"), + PINCTRL_PIN(TEGRA_PIN_GPIO_W3_AUD_PW3, "GPIO_W3_AUD PW3"), + PINCTRL_PIN(TEGRA_PIN_CLK1_OUT_PW4, "CLK1_OUT PW4"), + PINCTRL_PIN(TEGRA_PIN_CLK2_OUT_PW5, "CLK2_OUT PW5"), + PINCTRL_PIN(TEGRA_PIN_UART3_TXD_PW6, "UART3_TXD PW6"), + PINCTRL_PIN(TEGRA_PIN_UART3_RXD_PW7, "UART3_RXD PW7"), + PINCTRL_PIN(TEGRA_PIN_DVFS_PWM_PX0, "DVFS_PWM PX0"), + PINCTRL_PIN(TEGRA_PIN_GPIO_X1_AUD_PX1, "GPIO_X1_AUD PX1"), + PINCTRL_PIN(TEGRA_PIN_DVFS_CLK_PX2, "DVFS_CLK PX2"), + PINCTRL_PIN(TEGRA_PIN_GPIO_X3_AUD_PX3, "GPIO_X3_AUD PX3"), + PINCTRL_PIN(TEGRA_PIN_GPIO_X4_AUD_PX4, "GPIO_X4_AUD PX4"), + PINCTRL_PIN(TEGRA_PIN_GPIO_X5_AUD_PX5, "GPIO_X5_AUD PX5"), + PINCTRL_PIN(TEGRA_PIN_GPIO_X6_AUD_PX6, "GPIO_X6_AUD PX6"), + PINCTRL_PIN(TEGRA_PIN_GPIO_X7_AUD_PX7, "GPIO_X7_AUD PX7"), + PINCTRL_PIN(TEGRA_PIN_ULPI_CLK_PY0, "ULPI_CLK PY0"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DIR_PY1, "ULPI_DIR PY1"), + PINCTRL_PIN(TEGRA_PIN_ULPI_NXT_PY2, "ULPI_NXT PY2"), + PINCTRL_PIN(TEGRA_PIN_ULPI_STP_PY3, "ULPI_STP PY3"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT3_PY4, "SDMMC1_DAT3 PY4"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT2_PY5, "SDMMC1_DAT2 PY5"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT1_PY6, "SDMMC1_DAT1 PY6"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT0_PY7, "SDMMC1_DAT0 PY7"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_CLK_PZ0, "SDMMC1_CLK PZ0"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_CMD_PZ1, "SDMMC1_CMD PZ1"), + PINCTRL_PIN(TEGRA_PIN_SYS_CLK_REQ_PZ5, "SYS_CLK_REQ PZ5"), + PINCTRL_PIN(TEGRA_PIN_PWR_I2C_SCL_PZ6, "PWR_I2C_SCL PZ6"), + PINCTRL_PIN(TEGRA_PIN_PWR_I2C_SDA_PZ7, "PWR_I2C_SDA PZ7"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT0_PAA0, "SDMMC4_DAT0 PAA0"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT1_PAA1, "SDMMC4_DAT1 PAA1"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT2_PAA2, "SDMMC4_DAT2 PAA2"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT3_PAA3, "SDMMC4_DAT3 PAA3"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT4_PAA4, "SDMMC4_DAT4 PAA4"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT5_PAA5, "SDMMC4_DAT5 PAA5"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT6_PAA6, "SDMMC4_DAT6 PAA6"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT7_PAA7, "SDMMC4_DAT7 PAA7"), + PINCTRL_PIN(TEGRA_PIN_PBB0, "PBB0"), + PINCTRL_PIN(TEGRA_PIN_CAM_I2C_SCL_PBB1, "CAM_I2C_SCL PBB1"), + PINCTRL_PIN(TEGRA_PIN_CAM_I2C_SDA_PBB2, "CAM_I2C_SDA PBB2"), + PINCTRL_PIN(TEGRA_PIN_PBB3, "PBB3"), + PINCTRL_PIN(TEGRA_PIN_PBB4, "PBB4"), + PINCTRL_PIN(TEGRA_PIN_PBB5, "PBB5"), + PINCTRL_PIN(TEGRA_PIN_PBB6, "PBB6"), + PINCTRL_PIN(TEGRA_PIN_PBB7, "PBB7"), + PINCTRL_PIN(TEGRA_PIN_CAM_MCLK_PCC0, "CAM_MCLK PCC0"), + PINCTRL_PIN(TEGRA_PIN_PCC1, "PCC1"), + PINCTRL_PIN(TEGRA_PIN_PCC2, "PCC2"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_CLK_PCC4, "SDMMC4_CLK PCC4"), + PINCTRL_PIN(TEGRA_PIN_CLK2_REQ_PCC5, "CLK2_REQ PCC5"), + PINCTRL_PIN(TEGRA_PIN_CLK3_OUT_PEE0, "CLK3_OUT PEE0"), + PINCTRL_PIN(TEGRA_PIN_CLK3_REQ_PEE1, "CLK3_REQ PEE1"), + PINCTRL_PIN(TEGRA_PIN_CLK1_REQ_PEE2, "CLK1_REQ PEE2"), + PINCTRL_PIN(TEGRA_PIN_HDMI_CEC_PEE3, "HDMI_CEC PEE3"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_CLK_LB_OUT_PEE4, "SDMMC3_CLK_LB_OUT PEE4"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_CLK_LB_IN_PEE5, "SDMMC3_CLK_LB_IN PEE5"), + PINCTRL_PIN(TEGRA_PIN_CORE_PWR_REQ, "CORE_PWR_REQ"), + PINCTRL_PIN(TEGRA_PIN_CPU_PWR_REQ, "CPU_PWR_REQ"), + PINCTRL_PIN(TEGRA_PIN_OWR, "OWR"), + PINCTRL_PIN(TEGRA_PIN_PWR_INT_N, "PWR_INT_N"), + PINCTRL_PIN(TEGRA_PIN_RESET_OUT_N, "RESET_OUT_N"), +}; + +static const unsigned clk_32k_out_pa0_pins[] = { + TEGRA_PIN_CLK_32K_OUT_PA0, +}; + +static const unsigned uart3_cts_n_pa1_pins[] = { + TEGRA_PIN_UART3_CTS_N_PA1, +}; + +static const unsigned dap2_fs_pa2_pins[] = { + TEGRA_PIN_DAP2_FS_PA2, +}; + +static const unsigned dap2_sclk_pa3_pins[] = { + TEGRA_PIN_DAP2_SCLK_PA3, +}; + +static const unsigned dap2_din_pa4_pins[] = { + TEGRA_PIN_DAP2_DIN_PA4, +}; + +static const unsigned dap2_dout_pa5_pins[] = { + TEGRA_PIN_DAP2_DOUT_PA5, +}; + +static const unsigned sdmmc3_clk_pa6_pins[] = { + TEGRA_PIN_SDMMC3_CLK_PA6, +}; + +static const unsigned sdmmc3_cmd_pa7_pins[] = { + TEGRA_PIN_SDMMC3_CMD_PA7, +}; + +static const unsigned gmi_a17_pb0_pins[] = { + TEGRA_PIN_GMI_A17_PB0, +}; + +static const unsigned gmi_a18_pb1_pins[] = { + TEGRA_PIN_GMI_A18_PB1, +}; + +static const unsigned sdmmc3_dat3_pb4_pins[] = { + TEGRA_PIN_SDMMC3_DAT3_PB4, +}; + +static const unsigned sdmmc3_dat2_pb5_pins[] = { + TEGRA_PIN_SDMMC3_DAT2_PB5, +}; + +static const unsigned sdmmc3_dat1_pb6_pins[] = { + TEGRA_PIN_SDMMC3_DAT1_PB6, +}; + +static const unsigned sdmmc3_dat0_pb7_pins[] = { + TEGRA_PIN_SDMMC3_DAT0_PB7, +}; + +static const unsigned uart3_rts_n_pc0_pins[] = { + TEGRA_PIN_UART3_RTS_N_PC0, +}; + +static const unsigned uart2_txd_pc2_pins[] = { + TEGRA_PIN_UART2_TXD_PC2, +}; + +static const unsigned uart2_rxd_pc3_pins[] = { + TEGRA_PIN_UART2_RXD_PC3, +}; + +static const unsigned gen1_i2c_scl_pc4_pins[] = { + TEGRA_PIN_GEN1_I2C_SCL_PC4, +}; + +static const unsigned gen1_i2c_sda_pc5_pins[] = { + TEGRA_PIN_GEN1_I2C_SDA_PC5, +}; + +static const unsigned gmi_wp_n_pc7_pins[] = { + TEGRA_PIN_GMI_WP_N_PC7, +}; + +static const unsigned gmi_ad0_pg0_pins[] = { + TEGRA_PIN_GMI_AD0_PG0, +}; + +static const unsigned gmi_ad1_pg1_pins[] = { + TEGRA_PIN_GMI_AD1_PG1, +}; + +static const unsigned gmi_ad2_pg2_pins[] = { + TEGRA_PIN_GMI_AD2_PG2, +}; + +static const unsigned gmi_ad3_pg3_pins[] = { + TEGRA_PIN_GMI_AD3_PG3, +}; + +static const unsigned gmi_ad4_pg4_pins[] = { + TEGRA_PIN_GMI_AD4_PG4, +}; + +static const unsigned gmi_ad5_pg5_pins[] = { + TEGRA_PIN_GMI_AD5_PG5, +}; + +static const unsigned gmi_ad6_pg6_pins[] = { + TEGRA_PIN_GMI_AD6_PG6, +}; + +static const unsigned gmi_ad7_pg7_pins[] = { + TEGRA_PIN_GMI_AD7_PG7, +}; + +static const unsigned gmi_ad8_ph0_pins[] = { + TEGRA_PIN_GMI_AD8_PH0, +}; + +static const unsigned gmi_ad9_ph1_pins[] = { + TEGRA_PIN_GMI_AD9_PH1, +}; + +static const unsigned gmi_ad10_ph2_pins[] = { + TEGRA_PIN_GMI_AD10_PH2, +}; + +static const unsigned gmi_ad11_ph3_pins[] = { + TEGRA_PIN_GMI_AD11_PH3, +}; + +static const unsigned gmi_ad12_ph4_pins[] = { + TEGRA_PIN_GMI_AD12_PH4, +}; + +static const unsigned gmi_ad13_ph5_pins[] = { + TEGRA_PIN_GMI_AD13_PH5, +}; + +static const unsigned gmi_ad14_ph6_pins[] = { + TEGRA_PIN_GMI_AD14_PH6, +}; + +static const unsigned gmi_ad15_ph7_pins[] = { + TEGRA_PIN_GMI_AD15_PH7, +}; + +static const unsigned gmi_wr_n_pi0_pins[] = { + TEGRA_PIN_GMI_WR_N_PI0, +}; + +static const unsigned gmi_oe_n_pi1_pins[] = { + TEGRA_PIN_GMI_OE_N_PI1, +}; + +static const unsigned gmi_cs6_n_pi3_pins[] = { + TEGRA_PIN_GMI_CS6_N_PI3, +}; + +static const unsigned gmi_rst_n_pi4_pins[] = { + TEGRA_PIN_GMI_RST_N_PI4, +}; + +static const unsigned gmi_iordy_pi5_pins[] = { + TEGRA_PIN_GMI_IORDY_PI5, +}; + +static const unsigned gmi_cs7_n_pi6_pins[] = { + TEGRA_PIN_GMI_CS7_N_PI6, +}; + +static const unsigned gmi_wait_pi7_pins[] = { + TEGRA_PIN_GMI_WAIT_PI7, +}; + +static const unsigned gmi_cs0_n_pj0_pins[] = { + TEGRA_PIN_GMI_CS0_N_PJ0, +}; + +static const unsigned gmi_cs1_n_pj2_pins[] = { + TEGRA_PIN_GMI_CS1_N_PJ2, +}; + +static const unsigned gmi_dqs_p_pj3_pins[] = { + TEGRA_PIN_GMI_DQS_P_PJ3, +}; + +static const unsigned uart2_cts_n_pj5_pins[] = { + TEGRA_PIN_UART2_CTS_N_PJ5, +}; + +static const unsigned uart2_rts_n_pj6_pins[] = { + TEGRA_PIN_UART2_RTS_N_PJ6, +}; + +static const unsigned gmi_a16_pj7_pins[] = { + TEGRA_PIN_GMI_A16_PJ7, +}; + +static const unsigned gmi_adv_n_pk0_pins[] = { + TEGRA_PIN_GMI_ADV_N_PK0, +}; + +static const unsigned gmi_clk_pk1_pins[] = { + TEGRA_PIN_GMI_CLK_PK1, +}; + +static const unsigned gmi_cs4_n_pk2_pins[] = { + TEGRA_PIN_GMI_CS4_N_PK2, +}; + +static const unsigned gmi_cs2_n_pk3_pins[] = { + TEGRA_PIN_GMI_CS2_N_PK3, +}; + +static const unsigned gmi_cs3_n_pk4_pins[] = { + TEGRA_PIN_GMI_CS3_N_PK4, +}; + +static const unsigned spdif_out_pk5_pins[] = { + TEGRA_PIN_SPDIF_OUT_PK5, +}; + +static const unsigned spdif_in_pk6_pins[] = { + TEGRA_PIN_SPDIF_IN_PK6, +}; + +static const unsigned gmi_a19_pk7_pins[] = { + TEGRA_PIN_GMI_A19_PK7, +}; + +static const unsigned dap1_fs_pn0_pins[] = { + TEGRA_PIN_DAP1_FS_PN0, +}; + +static const unsigned dap1_din_pn1_pins[] = { + TEGRA_PIN_DAP1_DIN_PN1, +}; + +static const unsigned dap1_dout_pn2_pins[] = { + TEGRA_PIN_DAP1_DOUT_PN2, +}; + +static const unsigned dap1_sclk_pn3_pins[] = { + TEGRA_PIN_DAP1_SCLK_PN3, +}; + +static const unsigned usb_vbus_en0_pn4_pins[] = { + TEGRA_PIN_USB_VBUS_EN0_PN4, +}; + +static const unsigned usb_vbus_en1_pn5_pins[] = { + TEGRA_PIN_USB_VBUS_EN1_PN5, +}; + +static const unsigned hdmi_int_pn7_pins[] = { + TEGRA_PIN_HDMI_INT_PN7, +}; + +static const unsigned ulpi_data7_po0_pins[] = { + TEGRA_PIN_ULPI_DATA7_PO0, +}; + +static const unsigned ulpi_data0_po1_pins[] = { + TEGRA_PIN_ULPI_DATA0_PO1, +}; + +static const unsigned ulpi_data1_po2_pins[] = { + TEGRA_PIN_ULPI_DATA1_PO2, +}; + +static const unsigned ulpi_data2_po3_pins[] = { + TEGRA_PIN_ULPI_DATA2_PO3, +}; + +static const unsigned ulpi_data3_po4_pins[] = { + TEGRA_PIN_ULPI_DATA3_PO4, +}; + +static const unsigned ulpi_data4_po5_pins[] = { + TEGRA_PIN_ULPI_DATA4_PO5, +}; + +static const unsigned ulpi_data5_po6_pins[] = { + TEGRA_PIN_ULPI_DATA5_PO6, +}; + +static const unsigned ulpi_data6_po7_pins[] = { + TEGRA_PIN_ULPI_DATA6_PO7, +}; + +static const unsigned dap3_fs_pp0_pins[] = { + TEGRA_PIN_DAP3_FS_PP0, +}; + +static const unsigned dap3_din_pp1_pins[] = { + TEGRA_PIN_DAP3_DIN_PP1, +}; + +static const unsigned dap3_dout_pp2_pins[] = { + TEGRA_PIN_DAP3_DOUT_PP2, +}; + +static const unsigned dap3_sclk_pp3_pins[] = { + TEGRA_PIN_DAP3_SCLK_PP3, +}; + +static const unsigned dap4_fs_pp4_pins[] = { + TEGRA_PIN_DAP4_FS_PP4, +}; + +static const unsigned dap4_din_pp5_pins[] = { + TEGRA_PIN_DAP4_DIN_PP5, +}; + +static const unsigned dap4_dout_pp6_pins[] = { + TEGRA_PIN_DAP4_DOUT_PP6, +}; + +static const unsigned dap4_sclk_pp7_pins[] = { + TEGRA_PIN_DAP4_SCLK_PP7, +}; + +static const unsigned kb_col0_pq0_pins[] = { + TEGRA_PIN_KB_COL0_PQ0, +}; + +static const unsigned kb_col1_pq1_pins[] = { + TEGRA_PIN_KB_COL1_PQ1, +}; + +static const unsigned kb_col2_pq2_pins[] = { + TEGRA_PIN_KB_COL2_PQ2, +}; + +static const unsigned kb_col3_pq3_pins[] = { + TEGRA_PIN_KB_COL3_PQ3, +}; + +static const unsigned kb_col4_pq4_pins[] = { + TEGRA_PIN_KB_COL4_PQ4, +}; + +static const unsigned kb_col5_pq5_pins[] = { + TEGRA_PIN_KB_COL5_PQ5, +}; + +static const unsigned kb_col6_pq6_pins[] = { + TEGRA_PIN_KB_COL6_PQ6, +}; + +static const unsigned kb_col7_pq7_pins[] = { + TEGRA_PIN_KB_COL7_PQ7, +}; + +static const unsigned kb_row0_pr0_pins[] = { + TEGRA_PIN_KB_ROW0_PR0, +}; + +static const unsigned kb_row1_pr1_pins[] = { + TEGRA_PIN_KB_ROW1_PR1, +}; + +static const unsigned kb_row2_pr2_pins[] = { + TEGRA_PIN_KB_ROW2_PR2, +}; + +static const unsigned kb_row3_pr3_pins[] = { + TEGRA_PIN_KB_ROW3_PR3, +}; + +static const unsigned kb_row4_pr4_pins[] = { + TEGRA_PIN_KB_ROW4_PR4, +}; + +static const unsigned kb_row5_pr5_pins[] = { + TEGRA_PIN_KB_ROW5_PR5, +}; + +static const unsigned kb_row6_pr6_pins[] = { + TEGRA_PIN_KB_ROW6_PR6, +}; + +static const unsigned kb_row7_pr7_pins[] = { + TEGRA_PIN_KB_ROW7_PR7, +}; + +static const unsigned kb_row8_ps0_pins[] = { + TEGRA_PIN_KB_ROW8_PS0, +}; + +static const unsigned kb_row9_ps1_pins[] = { + TEGRA_PIN_KB_ROW9_PS1, +}; + +static const unsigned kb_row10_ps2_pins[] = { + TEGRA_PIN_KB_ROW10_PS2, +}; + +static const unsigned gen2_i2c_scl_pt5_pins[] = { + TEGRA_PIN_GEN2_I2C_SCL_PT5, +}; + +static const unsigned gen2_i2c_sda_pt6_pins[] = { + TEGRA_PIN_GEN2_I2C_SDA_PT6, +}; + +static const unsigned sdmmc4_cmd_pt7_pins[] = { + TEGRA_PIN_SDMMC4_CMD_PT7, +}; + +static const unsigned pu0_pins[] = { + TEGRA_PIN_PU0, +}; + +static const unsigned pu1_pins[] = { + TEGRA_PIN_PU1, +}; + +static const unsigned pu2_pins[] = { + TEGRA_PIN_PU2, +}; + +static const unsigned pu3_pins[] = { + TEGRA_PIN_PU3, +}; + +static const unsigned pu4_pins[] = { + TEGRA_PIN_PU4, +}; + +static const unsigned pu5_pins[] = { + TEGRA_PIN_PU5, +}; + +static const unsigned pu6_pins[] = { + TEGRA_PIN_PU6, +}; + +static const unsigned pv0_pins[] = { + TEGRA_PIN_PV0, +}; + +static const unsigned pv1_pins[] = { + TEGRA_PIN_PV1, +}; + +static const unsigned sdmmc3_cd_n_pv2_pins[] = { + TEGRA_PIN_SDMMC3_CD_N_PV2, +}; + +static const unsigned sdmmc1_wp_n_pv3_pins[] = { + TEGRA_PIN_SDMMC1_WP_N_PV3, +}; + +static const unsigned ddc_scl_pv4_pins[] = { + TEGRA_PIN_DDC_SCL_PV4, +}; + +static const unsigned ddc_sda_pv5_pins[] = { + TEGRA_PIN_DDC_SDA_PV5, +}; + +static const unsigned gpio_w2_aud_pw2_pins[] = { + TEGRA_PIN_GPIO_W2_AUD_PW2, +}; + +static const unsigned gpio_w3_aud_pw3_pins[] = { + TEGRA_PIN_GPIO_W3_AUD_PW3, +}; + +static const unsigned clk1_out_pw4_pins[] = { + TEGRA_PIN_CLK1_OUT_PW4, +}; + +static const unsigned clk2_out_pw5_pins[] = { + TEGRA_PIN_CLK2_OUT_PW5, +}; + +static const unsigned uart3_txd_pw6_pins[] = { + TEGRA_PIN_UART3_TXD_PW6, +}; + +static const unsigned uart3_rxd_pw7_pins[] = { + TEGRA_PIN_UART3_RXD_PW7, +}; + +static const unsigned dvfs_pwm_px0_pins[] = { + TEGRA_PIN_DVFS_PWM_PX0, +}; + +static const unsigned gpio_x1_aud_px1_pins[] = { + TEGRA_PIN_GPIO_X1_AUD_PX1, +}; + +static const unsigned dvfs_clk_px2_pins[] = { + TEGRA_PIN_DVFS_CLK_PX2, +}; + +static const unsigned gpio_x3_aud_px3_pins[] = { + TEGRA_PIN_GPIO_X3_AUD_PX3, +}; + +static const unsigned gpio_x4_aud_px4_pins[] = { + TEGRA_PIN_GPIO_X4_AUD_PX4, +}; + +static const unsigned gpio_x5_aud_px5_pins[] = { + TEGRA_PIN_GPIO_X5_AUD_PX5, +}; + +static const unsigned gpio_x6_aud_px6_pins[] = { + TEGRA_PIN_GPIO_X6_AUD_PX6, +}; + +static const unsigned gpio_x7_aud_px7_pins[] = { + TEGRA_PIN_GPIO_X7_AUD_PX7, +}; + +static const unsigned ulpi_clk_py0_pins[] = { + TEGRA_PIN_ULPI_CLK_PY0, +}; + +static const unsigned ulpi_dir_py1_pins[] = { + TEGRA_PIN_ULPI_DIR_PY1, +}; + +static const unsigned ulpi_nxt_py2_pins[] = { + TEGRA_PIN_ULPI_NXT_PY2, +}; + +static const unsigned ulpi_stp_py3_pins[] = { + TEGRA_PIN_ULPI_STP_PY3, +}; + +static const unsigned sdmmc1_dat3_py4_pins[] = { + TEGRA_PIN_SDMMC1_DAT3_PY4, +}; + +static const unsigned sdmmc1_dat2_py5_pins[] = { + TEGRA_PIN_SDMMC1_DAT2_PY5, +}; + +static const unsigned sdmmc1_dat1_py6_pins[] = { + TEGRA_PIN_SDMMC1_DAT1_PY6, +}; + +static const unsigned sdmmc1_dat0_py7_pins[] = { + TEGRA_PIN_SDMMC1_DAT0_PY7, +}; + +static const unsigned sdmmc1_clk_pz0_pins[] = { + TEGRA_PIN_SDMMC1_CLK_PZ0, +}; + +static const unsigned sdmmc1_cmd_pz1_pins[] = { + TEGRA_PIN_SDMMC1_CMD_PZ1, +}; + +static const unsigned sys_clk_req_pz5_pins[] = { + TEGRA_PIN_SYS_CLK_REQ_PZ5, +}; + +static const unsigned pwr_i2c_scl_pz6_pins[] = { + TEGRA_PIN_PWR_I2C_SCL_PZ6, +}; + +static const unsigned pwr_i2c_sda_pz7_pins[] = { + TEGRA_PIN_PWR_I2C_SDA_PZ7, +}; + +static const unsigned sdmmc4_dat0_paa0_pins[] = { + TEGRA_PIN_SDMMC4_DAT0_PAA0, +}; + +static const unsigned sdmmc4_dat1_paa1_pins[] = { + TEGRA_PIN_SDMMC4_DAT1_PAA1, +}; + +static const unsigned sdmmc4_dat2_paa2_pins[] = { + TEGRA_PIN_SDMMC4_DAT2_PAA2, +}; + +static const unsigned sdmmc4_dat3_paa3_pins[] = { + TEGRA_PIN_SDMMC4_DAT3_PAA3, +}; + +static const unsigned sdmmc4_dat4_paa4_pins[] = { + TEGRA_PIN_SDMMC4_DAT4_PAA4, +}; + +static const unsigned sdmmc4_dat5_paa5_pins[] = { + TEGRA_PIN_SDMMC4_DAT5_PAA5, +}; + +static const unsigned sdmmc4_dat6_paa6_pins[] = { + TEGRA_PIN_SDMMC4_DAT6_PAA6, +}; + +static const unsigned sdmmc4_dat7_paa7_pins[] = { + TEGRA_PIN_SDMMC4_DAT7_PAA7, +}; + +static const unsigned pbb0_pins[] = { + TEGRA_PIN_PBB0, +}; + +static const unsigned cam_i2c_scl_pbb1_pins[] = { + TEGRA_PIN_CAM_I2C_SCL_PBB1, +}; + +static const unsigned cam_i2c_sda_pbb2_pins[] = { + TEGRA_PIN_CAM_I2C_SDA_PBB2, +}; + +static const unsigned pbb3_pins[] = { + TEGRA_PIN_PBB3, +}; + +static const unsigned pbb4_pins[] = { + TEGRA_PIN_PBB4, +}; + +static const unsigned pbb5_pins[] = { + TEGRA_PIN_PBB5, +}; + +static const unsigned pbb6_pins[] = { + TEGRA_PIN_PBB6, +}; + +static const unsigned pbb7_pins[] = { + TEGRA_PIN_PBB7, +}; + +static const unsigned cam_mclk_pcc0_pins[] = { + TEGRA_PIN_CAM_MCLK_PCC0, +}; + +static const unsigned pcc1_pins[] = { + TEGRA_PIN_PCC1, +}; + +static const unsigned pcc2_pins[] = { + TEGRA_PIN_PCC2, +}; + +static const unsigned sdmmc4_clk_pcc4_pins[] = { + TEGRA_PIN_SDMMC4_CLK_PCC4, +}; + +static const unsigned clk2_req_pcc5_pins[] = { + TEGRA_PIN_CLK2_REQ_PCC5, +}; + +static const unsigned clk3_out_pee0_pins[] = { + TEGRA_PIN_CLK3_OUT_PEE0, +}; + +static const unsigned clk3_req_pee1_pins[] = { + TEGRA_PIN_CLK3_REQ_PEE1, +}; + +static const unsigned clk1_req_pee2_pins[] = { + TEGRA_PIN_CLK1_REQ_PEE2, +}; + +static const unsigned hdmi_cec_pee3_pins[] = { + TEGRA_PIN_HDMI_CEC_PEE3, +}; + +static const unsigned sdmmc3_clk_lb_out_pee4_pins[] = { + TEGRA_PIN_SDMMC3_CLK_LB_OUT_PEE4, +}; + +static const unsigned sdmmc3_clk_lb_in_pee5_pins[] = { + TEGRA_PIN_SDMMC3_CLK_LB_IN_PEE5, +}; + +static const unsigned core_pwr_req_pins[] = { + TEGRA_PIN_CORE_PWR_REQ, +}; + +static const unsigned cpu_pwr_req_pins[] = { + TEGRA_PIN_CPU_PWR_REQ, +}; + +static const unsigned owr_pins[] = { + TEGRA_PIN_OWR, +}; + +static const unsigned pwr_int_n_pins[] = { + TEGRA_PIN_PWR_INT_N, +}; + +static const unsigned reset_out_n_pins[] = { + TEGRA_PIN_RESET_OUT_N, +}; + +static const unsigned drive_ao1_pins[] = { + TEGRA_PIN_KB_ROW0_PR0, + TEGRA_PIN_KB_ROW1_PR1, + TEGRA_PIN_KB_ROW2_PR2, + TEGRA_PIN_KB_ROW3_PR3, + TEGRA_PIN_KB_ROW4_PR4, + TEGRA_PIN_KB_ROW5_PR5, + TEGRA_PIN_KB_ROW6_PR6, + TEGRA_PIN_KB_ROW7_PR7, + TEGRA_PIN_PWR_I2C_SCL_PZ6, + TEGRA_PIN_PWR_I2C_SDA_PZ7, +}; + +static const unsigned drive_ao2_pins[] = { + TEGRA_PIN_CLK_32K_OUT_PA0, + TEGRA_PIN_KB_COL0_PQ0, + TEGRA_PIN_KB_COL1_PQ1, + TEGRA_PIN_KB_COL2_PQ2, + TEGRA_PIN_KB_COL3_PQ3, + TEGRA_PIN_KB_COL4_PQ4, + TEGRA_PIN_KB_COL5_PQ5, + TEGRA_PIN_KB_COL6_PQ6, + TEGRA_PIN_KB_COL7_PQ7, + TEGRA_PIN_KB_ROW8_PS0, + TEGRA_PIN_KB_ROW9_PS1, + TEGRA_PIN_KB_ROW10_PS2, + TEGRA_PIN_SYS_CLK_REQ_PZ5, + TEGRA_PIN_CORE_PWR_REQ, + TEGRA_PIN_CPU_PWR_REQ, + TEGRA_PIN_RESET_OUT_N, +}; + +static const unsigned drive_at1_pins[] = { + TEGRA_PIN_GMI_AD8_PH0, + TEGRA_PIN_GMI_AD9_PH1, + TEGRA_PIN_GMI_AD10_PH2, + TEGRA_PIN_GMI_AD11_PH3, + TEGRA_PIN_GMI_AD12_PH4, + TEGRA_PIN_GMI_AD13_PH5, + TEGRA_PIN_GMI_AD14_PH6, + TEGRA_PIN_GMI_AD15_PH7, + + TEGRA_PIN_GMI_IORDY_PI5, + TEGRA_PIN_GMI_CS7_N_PI6, +}; + +static const unsigned drive_at2_pins[] = { + TEGRA_PIN_GMI_AD0_PG0, + TEGRA_PIN_GMI_AD1_PG1, + TEGRA_PIN_GMI_AD2_PG2, + TEGRA_PIN_GMI_AD3_PG3, + TEGRA_PIN_GMI_AD4_PG4, + TEGRA_PIN_GMI_AD5_PG5, + TEGRA_PIN_GMI_AD6_PG6, + TEGRA_PIN_GMI_AD7_PG7, + + TEGRA_PIN_GMI_WR_N_PI0, + TEGRA_PIN_GMI_OE_N_PI1, + TEGRA_PIN_GMI_CS6_N_PI3, + TEGRA_PIN_GMI_RST_N_PI4, + TEGRA_PIN_GMI_WAIT_PI7, + + TEGRA_PIN_GMI_DQS_P_PJ3, + + TEGRA_PIN_GMI_ADV_N_PK0, + TEGRA_PIN_GMI_CLK_PK1, + TEGRA_PIN_GMI_CS4_N_PK2, + TEGRA_PIN_GMI_CS2_N_PK3, + TEGRA_PIN_GMI_CS3_N_PK4, +}; + +static const unsigned drive_at3_pins[] = { + TEGRA_PIN_GMI_WP_N_PC7, + TEGRA_PIN_GMI_CS0_N_PJ0, +}; + +static const unsigned drive_at4_pins[] = { + TEGRA_PIN_GMI_A17_PB0, + TEGRA_PIN_GMI_A18_PB1, + TEGRA_PIN_GMI_CS1_N_PJ2, + TEGRA_PIN_GMI_A16_PJ7, + TEGRA_PIN_GMI_A19_PK7, +}; + +static const unsigned drive_at5_pins[] = { + TEGRA_PIN_GEN2_I2C_SCL_PT5, + TEGRA_PIN_GEN2_I2C_SDA_PT6, +}; + +static const unsigned drive_cdev1_pins[] = { + TEGRA_PIN_CLK1_OUT_PW4, + TEGRA_PIN_CLK1_REQ_PEE2, +}; + +static const unsigned drive_cdev2_pins[] = { + TEGRA_PIN_CLK2_OUT_PW5, + TEGRA_PIN_CLK2_REQ_PCC5, + TEGRA_PIN_SDMMC1_WP_N_PV3, +}; + +static const unsigned drive_dap1_pins[] = { + TEGRA_PIN_DAP1_FS_PN0, + TEGRA_PIN_DAP1_DIN_PN1, + TEGRA_PIN_DAP1_DOUT_PN2, + TEGRA_PIN_DAP1_SCLK_PN3, +}; + +static const unsigned drive_dap2_pins[] = { + TEGRA_PIN_DAP2_FS_PA2, + TEGRA_PIN_DAP2_SCLK_PA3, + TEGRA_PIN_DAP2_DIN_PA4, + TEGRA_PIN_DAP2_DOUT_PA5, +}; + +static const unsigned drive_dap3_pins[] = { + TEGRA_PIN_DAP3_FS_PP0, + TEGRA_PIN_DAP3_DIN_PP1, + TEGRA_PIN_DAP3_DOUT_PP2, + TEGRA_PIN_DAP3_SCLK_PP3, +}; + +static const unsigned drive_dap4_pins[] = { + TEGRA_PIN_DAP4_FS_PP4, + TEGRA_PIN_DAP4_DIN_PP5, + TEGRA_PIN_DAP4_DOUT_PP6, + TEGRA_PIN_DAP4_SCLK_PP7, +}; + +static const unsigned drive_dbg_pins[] = { + TEGRA_PIN_GEN1_I2C_SCL_PC4, + TEGRA_PIN_GEN1_I2C_SDA_PC5, + TEGRA_PIN_PU0, + TEGRA_PIN_PU1, + TEGRA_PIN_PU2, + TEGRA_PIN_PU3, + TEGRA_PIN_PU4, + TEGRA_PIN_PU5, + TEGRA_PIN_PU6, +}; + +static const unsigned drive_sdio3_pins[] = { + TEGRA_PIN_SDMMC3_CLK_PA6, + TEGRA_PIN_SDMMC3_CMD_PA7, + TEGRA_PIN_SDMMC3_DAT3_PB4, + TEGRA_PIN_SDMMC3_DAT2_PB5, + TEGRA_PIN_SDMMC3_DAT1_PB6, + TEGRA_PIN_SDMMC3_DAT0_PB7, + TEGRA_PIN_SDMMC3_CLK_LB_OUT_PEE4, + TEGRA_PIN_SDMMC3_CLK_LB_IN_PEE5, +}; + +static const unsigned drive_spi_pins[] = { + TEGRA_PIN_DVFS_PWM_PX0, + TEGRA_PIN_GPIO_X1_AUD_PX1, + TEGRA_PIN_DVFS_CLK_PX2, + TEGRA_PIN_GPIO_X3_AUD_PX3, + TEGRA_PIN_GPIO_X4_AUD_PX4, + TEGRA_PIN_GPIO_X5_AUD_PX5, + TEGRA_PIN_GPIO_X6_AUD_PX6, + TEGRA_PIN_GPIO_X7_AUD_PX7, + TEGRA_PIN_GPIO_W2_AUD_PW2, + TEGRA_PIN_GPIO_W3_AUD_PW3, +}; + +static const unsigned drive_uaa_pins[] = { + TEGRA_PIN_ULPI_DATA0_PO1, + TEGRA_PIN_ULPI_DATA1_PO2, + TEGRA_PIN_ULPI_DATA2_PO3, + TEGRA_PIN_ULPI_DATA3_PO4, +}; + +static const unsigned drive_uab_pins[] = { + TEGRA_PIN_ULPI_DATA7_PO0, + TEGRA_PIN_ULPI_DATA4_PO5, + TEGRA_PIN_ULPI_DATA5_PO6, + TEGRA_PIN_ULPI_DATA6_PO7, + TEGRA_PIN_PV0, + TEGRA_PIN_PV1, +}; + +static const unsigned drive_uart2_pins[] = { + TEGRA_PIN_UART2_TXD_PC2, + TEGRA_PIN_UART2_RXD_PC3, + TEGRA_PIN_UART2_CTS_N_PJ5, + TEGRA_PIN_UART2_RTS_N_PJ6, +}; + +static const unsigned drive_uart3_pins[] = { + TEGRA_PIN_UART3_CTS_N_PA1, + TEGRA_PIN_UART3_RTS_N_PC0, + TEGRA_PIN_UART3_TXD_PW6, + TEGRA_PIN_UART3_RXD_PW7, +}; + +static const unsigned drive_sdio1_pins[] = { + TEGRA_PIN_SDMMC1_DAT3_PY4, + TEGRA_PIN_SDMMC1_DAT2_PY5, + TEGRA_PIN_SDMMC1_DAT1_PY6, + TEGRA_PIN_SDMMC1_DAT0_PY7, + TEGRA_PIN_SDMMC1_CLK_PZ0, + TEGRA_PIN_SDMMC1_CMD_PZ1, +}; + +static const unsigned drive_ddc_pins[] = { + TEGRA_PIN_DDC_SCL_PV4, + TEGRA_PIN_DDC_SDA_PV5, +}; + +static const unsigned drive_gma_pins[] = { + TEGRA_PIN_SDMMC4_CLK_PCC4, + TEGRA_PIN_SDMMC4_CMD_PT7, + TEGRA_PIN_SDMMC4_DAT0_PAA0, + TEGRA_PIN_SDMMC4_DAT1_PAA1, + TEGRA_PIN_SDMMC4_DAT2_PAA2, + TEGRA_PIN_SDMMC4_DAT3_PAA3, + TEGRA_PIN_SDMMC4_DAT4_PAA4, + TEGRA_PIN_SDMMC4_DAT5_PAA5, + TEGRA_PIN_SDMMC4_DAT6_PAA6, + TEGRA_PIN_SDMMC4_DAT7_PAA7, +}; + +static const unsigned drive_gme_pins[] = { + TEGRA_PIN_PBB0, + TEGRA_PIN_CAM_I2C_SCL_PBB1, + TEGRA_PIN_CAM_I2C_SDA_PBB2, + TEGRA_PIN_PBB3, + TEGRA_PIN_PCC2, +}; + +static const unsigned drive_gmf_pins[] = { + TEGRA_PIN_PBB4, + TEGRA_PIN_PBB5, + TEGRA_PIN_PBB6, + TEGRA_PIN_PBB7, +}; + +static const unsigned drive_gmg_pins[] = { + TEGRA_PIN_CAM_MCLK_PCC0, +}; + +static const unsigned drive_gmh_pins[] = { + TEGRA_PIN_PCC1, +}; + +static const unsigned drive_owr_pins[] = { + TEGRA_PIN_SDMMC3_CD_N_PV2, +}; + +static const unsigned drive_uda_pins[] = { + TEGRA_PIN_ULPI_CLK_PY0, + TEGRA_PIN_ULPI_DIR_PY1, + TEGRA_PIN_ULPI_NXT_PY2, + TEGRA_PIN_ULPI_STP_PY3, +}; + +static const unsigned drive_dev3_pins[] = { + TEGRA_PIN_CLK3_OUT_PEE0, + TEGRA_PIN_CLK3_REQ_PEE1, +}; + +enum tegra_mux { + TEGRA_MUX_BLINK, + TEGRA_MUX_CEC, + TEGRA_MUX_CLK12, + TEGRA_MUX_CPU, + TEGRA_MUX_DAP, + TEGRA_MUX_DAP1, + TEGRA_MUX_DAP2, + TEGRA_MUX_DEV3, + TEGRA_MUX_DISPLAYA, + TEGRA_MUX_DISPLAYA_ALT, + TEGRA_MUX_DISPLAYB, + TEGRA_MUX_DTV, + TEGRA_MUX_EMC_DLL, + TEGRA_MUX_EXTPERIPH1, + TEGRA_MUX_EXTPERIPH2, + TEGRA_MUX_EXTPERIPH3, + TEGRA_MUX_GMI, + TEGRA_MUX_GMI_ALT, + TEGRA_MUX_HDA, + TEGRA_MUX_HSI, + TEGRA_MUX_I2C1, + TEGRA_MUX_I2C2, + TEGRA_MUX_I2C3, + TEGRA_MUX_I2C4, + TEGRA_MUX_I2CPWR, + TEGRA_MUX_I2S0, + TEGRA_MUX_I2S1, + TEGRA_MUX_I2S2, + TEGRA_MUX_I2S3, + TEGRA_MUX_I2S4, + TEGRA_MUX_IRDA, + TEGRA_MUX_KBC, + TEGRA_MUX_NAND, + TEGRA_MUX_NAND_ALT, + TEGRA_MUX_OWR, + TEGRA_MUX_PMI, + TEGRA_MUX_PWM0, + TEGRA_MUX_PWM1, + TEGRA_MUX_PWM2, + TEGRA_MUX_PWM3, + TEGRA_MUX_PWRON, + TEGRA_MUX_RESET_OUT_N, + TEGRA_MUX_RSVD1, + TEGRA_MUX_RSVD2, + TEGRA_MUX_RSVD3, + TEGRA_MUX_RSVD4, + TEGRA_MUX_SDMMC1, + TEGRA_MUX_SDMMC2, + TEGRA_MUX_SDMMC3, + TEGRA_MUX_SDMMC4, + TEGRA_MUX_SOC, + TEGRA_MUX_SPDIF, + TEGRA_MUX_SPI1, + TEGRA_MUX_SPI2, + TEGRA_MUX_SPI3, + TEGRA_MUX_SPI4, + TEGRA_MUX_SPI5, + TEGRA_MUX_SPI6, + TEGRA_MUX_SYSCLK, + TEGRA_MUX_TRACE, + TEGRA_MUX_UARTA, + TEGRA_MUX_UARTB, + TEGRA_MUX_UARTC, + TEGRA_MUX_UARTD, + TEGRA_MUX_ULPI, + TEGRA_MUX_USB, + TEGRA_MUX_VGP1, + TEGRA_MUX_VGP2, + TEGRA_MUX_VGP3, + TEGRA_MUX_VGP4, + TEGRA_MUX_VGP5, + TEGRA_MUX_VGP6, + TEGRA_MUX_VI, + TEGRA_MUX_VI_ALT1, + TEGRA_MUX_VI_ALT3, +}; + +static const char * const blink_groups[] = { + "clk_32k_out_pa0", +}; + +static const char * const cec_groups[] = { + "hdmi_cec_pee3", +}; + +static const char * const clk12_groups[] = { + "sdmmc1_wp_n_pv3", + "sdmmc1_clk_pz0", +}; + +static const char * const cpu_groups[] = { + "cpu_pwr_req", +}; + +static const char * const dap_groups[] = { + "clk1_req_pee2", + "clk2_req_pcc5", +}; + +static const char * const dap1_groups[] = { + "clk1_req_pee2", +}; + +static const char * const dap2_groups[] = { + "clk1_out_pw4", + "gpio_x4_aud_px4", +}; + +static const char * const dev3_groups[] = { + "clk3_req_pee1", +}; + +static const char * const displaya_groups[] = { + "dap3_fs_pp0", + "dap3_din_pp1", + "dap3_dout_pp2", + "dap3_sclk_pp3", + "uart3_rts_n_pc0", + "pu3", + "pu4", + "pu5", + "pbb3", + "pbb4", + "pbb5", + "pbb6", + "kb_row3_pr3", + "kb_row4_pr4", + "kb_row5_pr5", + "kb_row6_pr6", + "kb_col3_pq3", + "sdmmc3_dat2_pb5", +}; + +static const char * const displaya_alt_groups[] = { + "kb_row6_pr6", +}; + +static const char * const displayb_groups[] = { + "dap3_fs_pp0", + "dap3_din_pp1", + "dap3_dout_pp2", + "dap3_sclk_pp3", + "pu3", + "pu4", + "pu5", + "pu6", + "pbb3", + "pbb4", + "pbb5", + "pbb6", + "kb_row3_pr3", + "kb_row4_pr4", + "kb_row5_pr5", + "kb_row6_pr6", + "sdmmc3_dat3_pb4", +}; + +static const char * const dtv_groups[] = { + "uart3_cts_n_pa1", + "uart3_rts_n_pc0", + "dap4_fs_pp4", + "dap4_dout_pp6", + "gmi_wait_pi7", + "gmi_ad8_ph0", + "gmi_ad14_ph6", + "gmi_ad15_ph7", +}; + +static const char * const emc_dll_groups[] = { + "kb_col0_pq0", + "kb_col1_pq1", +}; + +static const char * const extperiph1_groups[] = { + "clk1_out_pw4", +}; + +static const char * const extperiph2_groups[] = { + "clk2_out_pw5", +}; + +static const char * const extperiph3_groups[] = { + "clk3_out_pee0", +}; + +static const char * const gmi_groups[] = { + "gmi_wp_n_pc7", + + "gmi_ad0_pg0", + "gmi_ad1_pg1", + "gmi_ad2_pg2", + "gmi_ad3_pg3", + "gmi_ad4_pg4", + "gmi_ad5_pg5", + "gmi_ad6_pg6", + "gmi_ad7_pg7", + "gmi_ad8_ph0", + "gmi_ad9_ph1", + "gmi_ad10_ph2", + "gmi_ad11_ph3", + "gmi_ad12_ph4", + "gmi_ad13_ph5", + "gmi_ad14_ph6", + "gmi_ad15_ph7", + "gmi_wr_n_pi0", + "gmi_oe_n_pi1", + "gmi_cs6_n_pi3", + "gmi_rst_n_pi4", + "gmi_iordy_pi5", + "gmi_cs7_n_pi6", + "gmi_wait_pi7", + "gmi_cs0_n_pj0", + "gmi_cs1_n_pj2", + "gmi_dqs_p_pj3", + "gmi_adv_n_pk0", + "gmi_clk_pk1", + "gmi_cs4_n_pk2", + "gmi_cs2_n_pk3", + "gmi_cs3_n_pk4", + "gmi_a16_pj7", + "gmi_a17_pb0", + "gmi_a18_pb1", + "gmi_a19_pk7", + "gen2_i2c_scl_pt5", + "gen2_i2c_sda_pt6", + "sdmmc4_dat0_paa0", + "sdmmc4_dat1_paa1", + "sdmmc4_dat2_paa2", + "sdmmc4_dat3_paa3", + "sdmmc4_dat4_paa4", + "sdmmc4_dat5_paa5", + "sdmmc4_dat6_paa6", + "sdmmc4_dat7_paa7", + "sdmmc4_clk_pcc4", + "sdmmc4_cmd_pt7", + "dap1_fs_pn0", + "dap1_din_pn1", + "dap1_dout_pn2", + "dap1_sclk_pn3", +}; + +static const char * const gmi_alt_groups[] = { + "gmi_wp_n_pc7", + "gmi_cs3_n_pk4", + "gmi_a16_pj7", +}; + +static const char * const hda_groups[] = { + "dap1_fs_pn0", + "dap1_din_pn1", + "dap1_dout_pn2", + "dap1_sclk_pn3", + "dap2_fs_pa2", + "dap2_sclk_pa3", + "dap2_din_pa4", + "dap2_dout_pa5", +}; + +static const char * const hsi_groups[] = { + "ulpi_data0_po1", + "ulpi_data1_po2", + "ulpi_data2_po3", + "ulpi_data3_po4", + "ulpi_data4_po5", + "ulpi_data5_po6", + "ulpi_data6_po7", + "ulpi_data7_po0", +}; + +static const char * const i2c1_groups[] = { + "gen1_i2c_scl_pc4", + "gen1_i2c_sda_pc5", + "gpio_w2_aud_pw2", + "gpio_w3_aud_pw3", +}; + +static const char * const i2c2_groups[] = { + "gen2_i2c_scl_pt5", + "gen2_i2c_sda_pt6", +}; + +static const char * const i2c3_groups[] = { + "cam_i2c_scl_pbb1", + "cam_i2c_sda_pbb2", +}; + +static const char * const i2c4_groups[] = { + "ddc_scl_pv4", + "ddc_sda_pv5", +}; + +static const char * const i2cpwr_groups[] = { + "pwr_i2c_scl_pz6", + "pwr_i2c_sda_pz7", +}; + +static const char * const i2s0_groups[] = { + "dap1_fs_pn0", + "dap1_din_pn1", + "dap1_dout_pn2", + "dap1_sclk_pn3", +}; + +static const char * const i2s1_groups[] = { + "dap2_fs_pa2", + "dap2_sclk_pa3", + "dap2_din_pa4", + "dap2_dout_pa5", +}; + +static const char * const i2s2_groups[] = { + "dap3_fs_pp0", + "dap3_din_pp1", + "dap3_dout_pp2", + "dap3_sclk_pp3", +}; + +static const char * const i2s3_groups[] = { + "dap4_fs_pp4", + "dap4_din_pp5", + "dap4_dout_pp6", + "dap4_sclk_pp7", +}; + +static const char * const i2s4_groups[] = { + "pcc1", + "pbb0", + "pbb7", + "pcc2", +}; + +static const char * const irda_groups[] = { + "uart2_rxd_pc3", + "uart2_txd_pc2", +}; + +static const char * const kbc_groups[] = { + "kb_row0_pr0", + "kb_row1_pr1", + "kb_row2_pr2", + "kb_row3_pr3", + "kb_row4_pr4", + "kb_row5_pr5", + "kb_row6_pr6", + "kb_row7_pr7", + "kb_row8_ps0", + "kb_row9_ps1", + "kb_row10_ps2", + "kb_col0_pq0", + "kb_col1_pq1", + "kb_col2_pq2", + "kb_col3_pq3", + "kb_col4_pq4", + "kb_col5_pq5", + "kb_col6_pq6", + "kb_col7_pq7", +}; + +static const char * const nand_groups[] = { + "gmi_wp_n_pc7", + "gmi_wait_pi7", + "gmi_adv_n_pk0", + "gmi_clk_pk1", + "gmi_cs0_n_pj0", + "gmi_cs1_n_pj2", + "gmi_cs2_n_pk3", + "gmi_cs3_n_pk4", + "gmi_cs4_n_pk2", + "gmi_cs6_n_pi3", + "gmi_cs7_n_pi6", + "gmi_ad0_pg0", + "gmi_ad1_pg1", + "gmi_ad2_pg2", + "gmi_ad3_pg3", + "gmi_ad4_pg4", + "gmi_ad5_pg5", + "gmi_ad6_pg6", + "gmi_ad7_pg7", + "gmi_ad8_ph0", + "gmi_ad9_ph1", + "gmi_ad10_ph2", + "gmi_ad11_ph3", + "gmi_ad12_ph4", + "gmi_ad13_ph5", + "gmi_ad14_ph6", + "gmi_ad15_ph7", + "gmi_wr_n_pi0", + "gmi_oe_n_pi1", + "gmi_dqs_p_pj3", + "gmi_rst_n_pi4", +}; + +static const char * const nand_alt_groups[] = { + "gmi_cs6_n_pi3", + "gmi_cs7_n_pi6", + "gmi_rst_n_pi4", +}; + +static const char * const owr_groups[] = { + "pu0", + "kb_col4_pq4", + "owr", + "sdmmc3_cd_n_pv2", +}; + +static const char * const pmi_groups[] = { + "pwr_int_n", +}; + +static const char * const pwm0_groups[] = { + "sdmmc1_dat2_py5", + "uart3_rts_n_pc0", + "pu3", + "gmi_ad8_ph0", + "sdmmc3_dat3_pb4", +}; + +static const char * const pwm1_groups[] = { + "sdmmc1_dat1_py6", + "pu4", + "gmi_ad9_ph1", + "sdmmc3_dat2_pb5", +}; + +static const char * const pwm2_groups[] = { + "pu5", + "gmi_ad10_ph2", + "kb_col3_pq3", + "sdmmc3_dat1_pb6", +}; + +static const char * const pwm3_groups[] = { + "pu6", + "gmi_ad11_ph3", + "sdmmc3_cmd_pa7", +}; + +static const char * const pwron_groups[] = { + "core_pwr_req", +}; + +static const char * const reset_out_n_groups[] = { + "reset_out_n", +}; + +static const char * const rsvd1_groups[] = { + "pv1", + "hdmi_int_pn7", + "pu1", + "pu2", + "gmi_wp_n_pc7", + "gmi_adv_n_pk0", + "gmi_cs0_n_pj0", + "gmi_cs1_n_pj2", + "gmi_ad0_pg0", + "gmi_ad1_pg1", + "gmi_ad2_pg2", + "gmi_ad3_pg3", + "gmi_ad4_pg4", + "gmi_ad5_pg5", + "gmi_ad6_pg6", + "gmi_ad7_pg7", + "gmi_wr_n_pi0", + "gmi_oe_n_pi1", + "gpio_x4_aud_px4", + "gpio_x5_aud_px5", + "gpio_x7_aud_px7", + + "reset_out_n", +}; + +static const char * const rsvd2_groups[] = { + "pv0", + "pv1", + "sdmmc1_dat0_py7", + "clk2_out_pw5", + "clk2_req_pcc5", + "hdmi_int_pn7", + "ddc_scl_pv4", + "ddc_sda_pv5", + "uart3_txd_pw6", + "uart3_rxd_pw7", + "gen1_i2c_scl_pc4", + "gen1_i2c_sda_pc5", + "dap4_fs_pp4", + "dap4_din_pp5", + "dap4_dout_pp6", + "dap4_sclk_pp7", + "clk3_out_pee0", + "clk3_req_pee1", + "gmi_iordy_pi5", + "gmi_a17_pb0", + "gmi_a18_pb1", + "gen2_i2c_scl_pt5", + "gen2_i2c_sda_pt6", + "sdmmc4_clk_pcc4", + "sdmmc4_cmd_pt7", + "sdmmc4_dat7_paa7", + "pcc1", + "pbb7", + "pcc2", + "pwr_i2c_scl_pz6", + "pwr_i2c_sda_pz7", + "kb_row0_pr0", + "kb_row1_pr1", + "kb_row2_pr2", + "kb_row7_pr7", + "kb_row8_ps0", + "kb_row9_ps1", + "kb_row10_ps2", + "kb_col1_pq1", + "kb_col2_pq2", + "kb_col5_pq5", + "kb_col6_pq6", + "kb_col7_pq7", + "sys_clk_req_pz5", + "core_pwr_req", + "cpu_pwr_req", + "pwr_int_n", + "owr", + "spdif_out_pk5", + "gpio_x1_aud_px1", + "sdmmc3_clk_pa6", + "sdmmc3_dat0_pb7", + "gpio_w2_aud_pw2", + "usb_vbus_en0_pn4", + "usb_vbus_en1_pn5", + "sdmmc3_clk_lb_out_pee4", + "sdmmc3_clk_lb_in_pee5", + "reset_out_n", +}; + +static const char * const rsvd3_groups[] = { + "pv0", + "pv1", + "sdmmc1_clk_pz0", + "clk2_out_pw5", + "clk2_req_pcc5", + "hdmi_int_pn7", + "ddc_scl_pv4", + "ddc_sda_pv5", + "uart2_rts_n_pj6", + "uart2_cts_n_pj5", + "uart3_txd_pw6", + "uart3_rxd_pw7", + "pu0", + "pu1", + "pu2", + "gen1_i2c_scl_pc4", + "gen1_i2c_sda_pc5", + "dap4_din_pp5", + "dap4_sclk_pp7", + "clk3_out_pee0", + "clk3_req_pee1", + "pcc1", + "cam_i2c_scl_pbb1", + "cam_i2c_sda_pbb2", + "pbb7", + "pcc2", + "pwr_i2c_scl_pz6", + "pwr_i2c_sda_pz7", + "kb_row0_pr0", + "kb_row1_pr1", + "kb_row2_pr2", + "kb_row3_pr3", + "kb_row9_ps1", + "kb_row10_ps2", + "clk_32k_out_pa0", + "sys_clk_req_pz5", + "core_pwr_req", + "cpu_pwr_req", + "pwr_int_n", + "owr", + "clk1_req_pee2", + "clk1_out_pw4", + "spdif_out_pk5", + "spdif_in_pk6", + "dap2_fs_pa2", + "dap2_sclk_pa3", + "dap2_din_pa4", + "dap2_dout_pa5", + "dvfs_pwm_px0", + "gpio_x1_aud_px1", + "gpio_x3_aud_px3", + "dvfs_clk_px2", + "sdmmc3_clk_pa6", + "sdmmc3_dat0_pb7", + "hdmi_cec_pee3", + "sdmmc3_cd_n_pv2", + "usb_vbus_en0_pn4", + "usb_vbus_en1_pn5", + "sdmmc3_clk_lb_out_pee4", + "sdmmc3_clk_lb_in_pee5", + "reset_out_n", +}; + +static const char * const rsvd4_groups[] = { + "pv0", + "pv1", + "sdmmc1_clk_pz0", + "clk2_out_pw5", + "clk2_req_pcc5", + "hdmi_int_pn7", + "ddc_scl_pv4", + "ddc_sda_pv5", + "pu0", + "pu1", + "pu2", + "gen1_i2c_scl_pc4", + "gen1_i2c_sda_pc5", + "dap4_fs_pp4", + "dap4_din_pp5", + "dap4_dout_pp6", + "dap4_sclk_pp7", + "clk3_out_pee0", + "clk3_req_pee1", + "gmi_ad0_pg0", + "gmi_ad1_pg1", + "gmi_ad2_pg2", + "gmi_ad3_pg3", + "gmi_ad4_pg4", + "gmi_ad12_ph4", + "gmi_ad13_ph5", + "gmi_rst_n_pi4", + "gen2_i2c_scl_pt5", + "gen2_i2c_sda_pt6", + "sdmmc4_clk_pcc4", + "sdmmc4_cmd_pt7", + "sdmmc4_dat0_paa0", + "sdmmc4_dat1_paa1", + "sdmmc4_dat2_paa2", + "sdmmc4_dat3_paa3", + "sdmmc4_dat4_paa4", + "sdmmc4_dat5_paa5", + "sdmmc4_dat6_paa6", + "sdmmc4_dat7_paa7", + "cam_mclk_pcc0", + "pcc1", + "cam_i2c_scl_pbb1", + "cam_i2c_sda_pbb2", + "pbb3", + "pbb4", + "pbb5", + "pbb6", + "pbb7", + "pcc2", + "pwr_i2c_scl_pz6", + "pwr_i2c_sda_pz7", + "kb_row0_pr0", + "kb_row1_pr1", + "kb_row2_pr2", + "kb_col2_pq2", + "kb_col5_pq5", + "kb_col6_pq6", + "kb_col7_pq7", + "clk_32k_out_pa0", + "sys_clk_req_pz5", + "core_pwr_req", + "cpu_pwr_req", + "pwr_int_n", + "owr", + "dap1_fs_pn0", + "dap1_din_pn1", + "dap1_dout_pn2", + "dap1_sclk_pn3", + "clk1_req_pee2", + "clk1_out_pw4", + "spdif_in_pk6", + "spdif_out_pk5", + "dap2_fs_pa2", + "dap2_sclk_pa3", + "dap2_din_pa4", + "dap2_dout_pa5", + "dvfs_pwm_px0", + "gpio_x1_aud_px1", + "gpio_x3_aud_px3", + "dvfs_clk_px2", + "gpio_x5_aud_px5", + "gpio_x6_aud_px6", + "gpio_x7_aud_px7", + "sdmmc3_cd_n_pv2", + "usb_vbus_en0_pn4", + "usb_vbus_en1_pn5", + "sdmmc3_clk_lb_in_pee5", + "sdmmc3_clk_lb_out_pee4", +}; + +static const char * const sdmmc1_groups[] = { + + "sdmmc1_clk_pz0", + "sdmmc1_cmd_pz1", + "sdmmc1_dat3_py4", + "sdmmc1_dat2_py5", + "sdmmc1_dat1_py6", + "sdmmc1_dat0_py7", + "uart3_cts_n_pa1", + "kb_col5_pq5", + "sdmmc1_wp_n_pv3", +}; + +static const char * const sdmmc2_groups[] = { + "gmi_iordy_pi5", + "gmi_clk_pk1", + "gmi_cs2_n_pk3", + "gmi_cs3_n_pk4", + "gmi_cs7_n_pi6", + "gmi_ad12_ph4", + "gmi_ad13_ph5", + "gmi_ad14_ph6", + "gmi_ad15_ph7", + "gmi_dqs_p_pj3", +}; + +static const char * const sdmmc3_groups[] = { + "kb_col4_pq4", + "sdmmc3_clk_pa6", + "sdmmc3_cmd_pa7", + "sdmmc3_dat0_pb7", + "sdmmc3_dat1_pb6", + "sdmmc3_dat2_pb5", + "sdmmc3_dat3_pb4", + "hdmi_cec_pee3", + "sdmmc3_cd_n_pv2", + "sdmmc3_clk_lb_in_pee5", + "sdmmc3_clk_lb_out_pee4", +}; + +static const char * const sdmmc4_groups[] = { + "sdmmc4_clk_pcc4", + "sdmmc4_cmd_pt7", + "sdmmc4_dat0_paa0", + "sdmmc4_dat1_paa1", + "sdmmc4_dat2_paa2", + "sdmmc4_dat3_paa3", + "sdmmc4_dat4_paa4", + "sdmmc4_dat5_paa5", + "sdmmc4_dat6_paa6", + "sdmmc4_dat7_paa7", +}; + +static const char * const soc_groups[] = { + "gmi_cs1_n_pj2", + "gmi_oe_n_pi1", + "clk_32k_out_pa0", + "hdmi_cec_pee3", +}; + +static const char * const spdif_groups[] = { + "sdmmc1_cmd_pz1", + "sdmmc1_dat3_py4", + "uart2_rxd_pc3", + "uart2_txd_pc2", + "spdif_in_pk6", + "spdif_out_pk5", +}; + +static const char * const spi1_groups[] = { + "ulpi_clk_py0", + "ulpi_dir_py1", + "ulpi_nxt_py2", + "ulpi_stp_py3", + "gpio_x3_aud_px3", + "gpio_x4_aud_px4", + "gpio_x5_aud_px5", + "gpio_x6_aud_px6", + "gpio_x7_aud_px7", + "gpio_w3_aud_pw3", +}; + +static const char * const spi2_groups[] = { + "ulpi_data4_po5", + "ulpi_data5_po6", + "ulpi_data6_po7", + "ulpi_data7_po0", + "kb_row4_pr4", + "kb_row5_pr5", + "kb_col0_pq0", + "kb_col1_pq1", + "kb_col2_pq2", + "kb_col6_pq6", + "kb_col7_pq7", + "gpio_x4_aud_px4", + "gpio_x5_aud_px5", + "gpio_x6_aud_px6", + "gpio_x7_aud_px7", + "gpio_w2_aud_pw2", + "gpio_w3_aud_pw3", +}; + +static const char * const spi3_groups[] = { + "ulpi_data0_po1", + "ulpi_data1_po2", + "ulpi_data2_po3", + "ulpi_data3_po4", + "sdmmc4_dat0_paa0", + "sdmmc4_dat1_paa1", + "sdmmc4_dat2_paa2", + "sdmmc4_dat3_paa3", + "sdmmc4_dat4_paa4", + "sdmmc4_dat5_paa5", + "sdmmc4_dat6_paa6", + "sdmmc3_clk_pa6", + "sdmmc3_cmd_pa7", + "sdmmc3_dat0_pb7", + "sdmmc3_dat1_pb6", + "sdmmc3_dat2_pb5", + "sdmmc3_dat3_pb4", +}; + +static const char * const spi4_groups[] = { + "sdmmc1_cmd_pz1", + "sdmmc1_dat3_py4", + "sdmmc1_dat2_py5", + "sdmmc1_dat1_py6", + "sdmmc1_dat0_py7", + "uart2_rxd_pc3", + "uart2_txd_pc2", + "uart2_rts_n_pj6", + "uart2_cts_n_pj5", + "uart3_txd_pw6", + "uart3_rxd_pw7", + "uart3_cts_n_pa1", + "gmi_wait_pi7", + "gmi_cs6_n_pi3", + "gmi_ad5_pg5", + "gmi_ad6_pg6", + "gmi_ad7_pg7", + "gmi_a19_pk7", + "gmi_wr_n_pi0", + "sdmmc1_wp_n_pv3", +}; + +static const char * const spi5_groups[] = { + "ulpi_clk_py0", + "ulpi_dir_py1", + "ulpi_nxt_py2", + "ulpi_stp_py3", + "dap3_fs_pp0", + "dap3_din_pp1", + "dap3_dout_pp2", + "dap3_sclk_pp3", +}; + +static const char * const spi6_groups[] = { + "dvfs_pwm_px0", + "gpio_x1_aud_px1", + "gpio_x3_aud_px3", + "dvfs_clk_px2", + "gpio_x6_aud_px6", + "gpio_w2_aud_pw2", + "gpio_w3_aud_pw3", +}; + +static const char * const sysclk_groups[] = { + "sys_clk_req_pz5", +}; + +static const char * const trace_groups[] = { + "gmi_iordy_pi5", + "gmi_adv_n_pk0", + "gmi_clk_pk1", + "gmi_cs2_n_pk3", + "gmi_cs4_n_pk2", + "gmi_a16_pj7", + "gmi_a17_pb0", + "gmi_a18_pb1", + "gmi_a19_pk7", + "gmi_dqs_p_pj3", +}; + +static const char * const uarta_groups[] = { + "ulpi_data0_po1", + "ulpi_data1_po2", + "ulpi_data2_po3", + "ulpi_data3_po4", + "ulpi_data4_po5", + "ulpi_data5_po6", + "ulpi_data6_po7", + "ulpi_data7_po0", + "sdmmc1_cmd_pz1", + "sdmmc1_dat3_py4", + "sdmmc1_dat2_py5", + "sdmmc1_dat1_py6", + "sdmmc1_dat0_py7", + "uart2_rxd_pc3", + "uart2_txd_pc2", + "uart2_rts_n_pj6", + "uart2_cts_n_pj5", + "pu0", + "pu1", + "pu2", + "pu3", + "pu4", + "pu5", + "pu6", + "kb_row7_pr7", + "kb_row8_ps0", + "kb_row9_ps1", + "kb_row10_ps2", + "kb_col3_pq3", + "kb_col4_pq4", + "sdmmc3_cmd_pa7", + "sdmmc3_dat1_pb6", + "sdmmc1_wp_n_pv3", +}; + +static const char * const uartb_groups[] = { + "uart2_rts_n_pj6", + "uart2_cts_n_pj5", +}; + +static const char * const uartc_groups[] = { + "uart3_txd_pw6", + "uart3_rxd_pw7", + "uart3_cts_n_pa1", + "uart3_rts_n_pc0", +}; + +static const char * const uartd_groups[] = { + "ulpi_clk_py0", + "ulpi_dir_py1", + "ulpi_nxt_py2", + "ulpi_stp_py3", + "gmi_a16_pj7", + "gmi_a17_pb0", + "gmi_a18_pb1", + "gmi_a19_pk7", +}; + +static const char * const ulpi_groups[] = { + "ulpi_data0_po1", + "ulpi_data1_po2", + "ulpi_data2_po3", + "ulpi_data3_po4", + "ulpi_data4_po5", + "ulpi_data5_po6", + "ulpi_data6_po7", + "ulpi_data7_po0", + "ulpi_clk_py0", + "ulpi_dir_py1", + "ulpi_nxt_py2", + "ulpi_stp_py3", +}; + +static const char * const usb_groups[] = { + "pv0", + "pu6", + "gmi_cs0_n_pj0", + "gmi_cs4_n_pk2", + "gmi_ad11_ph3", + "kb_col0_pq0", + "spdif_in_pk6", + "usb_vbus_en0_pn4", + "usb_vbus_en1_pn5", +}; + +static const char * const vgp1_groups[] = { + "cam_i2c_scl_pbb1", +}; + +static const char * const vgp2_groups[] = { + "cam_i2c_sda_pbb2", +}; + +static const char * const vgp3_groups[] = { + "pbb3", +}; + +static const char * const vgp4_groups[] = { + "pbb4", +}; + +static const char * const vgp5_groups[] = { + "pbb5", +}; + +static const char * const vgp6_groups[] = { + "pbb6", +}; + +static const char * const vi_groups[] = { + "cam_mclk_pcc0", + "pbb0", +}; + +static const char * const vi_alt1_groups[] = { + "cam_mclk_pcc0", + "pbb0", +}; + +static const char * const vi_alt3_groups[] = { + "cam_mclk_pcc0", + "pbb0", +}; + +#define FUNCTION(fname) \ + { \ + .name = #fname, \ + .groups = fname##_groups, \ + .ngroups = ARRAY_SIZE(fname##_groups), \ + } + +static const struct tegra_function tegra114_functions[] = { + FUNCTION(blink), + FUNCTION(cec), + FUNCTION(clk12), + FUNCTION(cpu), + FUNCTION(dap), + FUNCTION(dap1), + FUNCTION(dap2), + FUNCTION(dev3), + FUNCTION(displaya), + FUNCTION(displaya_alt), + FUNCTION(displayb), + FUNCTION(dtv), + FUNCTION(emc_dll), + FUNCTION(extperiph1), + FUNCTION(extperiph2), + FUNCTION(extperiph3), + FUNCTION(gmi), + FUNCTION(gmi_alt), + FUNCTION(hda), + FUNCTION(hsi), + FUNCTION(i2c1), + FUNCTION(i2c2), + FUNCTION(i2c3), + FUNCTION(i2c4), + FUNCTION(i2cpwr), + FUNCTION(i2s0), + FUNCTION(i2s1), + FUNCTION(i2s2), + FUNCTION(i2s3), + FUNCTION(i2s4), + FUNCTION(irda), + FUNCTION(kbc), + FUNCTION(nand), + FUNCTION(nand_alt), + FUNCTION(owr), + FUNCTION(pmi), + FUNCTION(pwm0), + FUNCTION(pwm1), + FUNCTION(pwm2), + FUNCTION(pwm3), + FUNCTION(pwron), + FUNCTION(reset_out_n), + FUNCTION(rsvd1), + FUNCTION(rsvd2), + FUNCTION(rsvd3), + FUNCTION(rsvd4), + FUNCTION(sdmmc1), + FUNCTION(sdmmc2), + FUNCTION(sdmmc3), + FUNCTION(sdmmc4), + FUNCTION(soc), + FUNCTION(spdif), + FUNCTION(spi1), + FUNCTION(spi2), + FUNCTION(spi3), + FUNCTION(spi4), + FUNCTION(spi5), + FUNCTION(spi6), + FUNCTION(sysclk), + FUNCTION(trace), + FUNCTION(uarta), + FUNCTION(uartb), + FUNCTION(uartc), + FUNCTION(uartd), + FUNCTION(ulpi), + FUNCTION(usb), + FUNCTION(vgp1), + FUNCTION(vgp2), + FUNCTION(vgp3), + FUNCTION(vgp4), + FUNCTION(vgp5), + FUNCTION(vgp6), + FUNCTION(vi), + FUNCTION(vi_alt1), + FUNCTION(vi_alt3), +}; + +#define DRV_PINGROUP_REG_START 0x868 /* bank 0 */ +#define PINGROUP_REG_START 0x3000 /* bank 1 */ + +#define PINGROUP_REG_Y(r) ((r) - PINGROUP_REG_START) +#define PINGROUP_REG_N(r) -1 + +#define PINGROUP(pg_name, f0, f1, f2, f3, f_safe, r, od, ior, rcv_sel) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = ARRAY_SIZE(pg_name##_pins), \ + .funcs = { \ + TEGRA_MUX_##f0, \ + TEGRA_MUX_##f1, \ + TEGRA_MUX_##f2, \ + TEGRA_MUX_##f3, \ + }, \ + .func_safe = TEGRA_MUX_##f_safe, \ + .mux_reg = PINGROUP_REG_Y(r), \ + .mux_bank = 1, \ + .mux_bit = 0, \ + .pupd_reg = PINGROUP_REG_Y(r), \ + .pupd_bank = 1, \ + .pupd_bit = 2, \ + .tri_reg = PINGROUP_REG_Y(r), \ + .tri_bank = 1, \ + .tri_bit = 4, \ + .einput_reg = PINGROUP_REG_Y(r), \ + .einput_bank = 1, \ + .einput_bit = 5, \ + .odrain_reg = PINGROUP_REG_##od(r), \ + .odrain_bank = 1, \ + .odrain_bit = 6, \ + .lock_reg = PINGROUP_REG_Y(r), \ + .lock_bank = 1, \ + .lock_bit = 7, \ + .ioreset_reg = PINGROUP_REG_##ior(r), \ + .ioreset_bank = 1, \ + .ioreset_bit = 8, \ + .rcv_sel_reg = PINGROUP_REG_##rcv_sel(r), \ + .rcv_sel_bank = 1, \ + .rcv_sel_bit = 9, \ + .drv_reg = -1, \ + .drvtype_reg = -1, \ + } + +#define DRV_PINGROUP_DVRTYPE_Y(r) ((r) - DRV_PINGROUP_REG_START) +#define DRV_PINGROUP_DVRTYPE_N(r) -1 + +#define DRV_PINGROUP(pg_name, r, hsm_b, schmitt_b, lpmd_b, \ + drvdn_b, drvdn_w, drvup_b, drvup_w, \ + slwr_b, slwr_w, slwf_b, slwf_w, \ + drvtype) \ + { \ + .name = "drive_" #pg_name, \ + .pins = drive_##pg_name##_pins, \ + .npins = ARRAY_SIZE(drive_##pg_name##_pins), \ + .mux_reg = -1, \ + .pupd_reg = -1, \ + .tri_reg = -1, \ + .einput_reg = -1, \ + .odrain_reg = -1, \ + .lock_reg = -1, \ + .ioreset_reg = -1, \ + .rcv_sel_reg = -1, \ + .drv_reg = DRV_PINGROUP_DVRTYPE_Y(r), \ + .drv_bank = 0, \ + .hsm_bit = hsm_b, \ + .schmitt_bit = schmitt_b, \ + .lpmd_bit = lpmd_b, \ + .drvdn_bit = drvdn_b, \ + .drvdn_width = drvdn_w, \ + .drvup_bit = drvup_b, \ + .drvup_width = drvup_w, \ + .slwr_bit = slwr_b, \ + .slwr_width = slwr_w, \ + .slwf_bit = slwf_b, \ + .slwf_width = slwf_w, \ + .drvtype_reg = DRV_PINGROUP_DVRTYPE_##drvtype(r), \ + .drvtype_bank = 0, \ + .drvtype_bit = 6, \ + } + +static const struct tegra_pingroup tegra114_groups[] = { + /* pg_name, f0, f1, f2, f3, safe, r, od, ior, rcv_sel */ + /* FIXME: Fill in correct data in safe column */ + PINGROUP(ulpi_data0_po1, SPI3, HSI, UARTA, ULPI, ULPI, 0x3000, N, N, N), + PINGROUP(ulpi_data1_po2, SPI3, HSI, UARTA, ULPI, ULPI, 0x3004, N, N, N), + PINGROUP(ulpi_data2_po3, SPI3, HSI, UARTA, ULPI, ULPI, 0x3008, N, N, N), + PINGROUP(ulpi_data3_po4, SPI3, HSI, UARTA, ULPI, ULPI, 0x300c, N, N, N), + PINGROUP(ulpi_data4_po5, SPI2, HSI, UARTA, ULPI, ULPI, 0x3010, N, N, N), + PINGROUP(ulpi_data5_po6, SPI2, HSI, UARTA, ULPI, ULPI, 0x3014, N, N, N), + PINGROUP(ulpi_data6_po7, SPI2, HSI, UARTA, ULPI, ULPI, 0x3018, N, N, N), + PINGROUP(ulpi_data7_po0, SPI2, HSI, UARTA, ULPI, ULPI, 0x301c, N, N, N), + PINGROUP(ulpi_clk_py0, SPI1, SPI5, UARTD, ULPI, ULPI, 0x3020, N, N, N), + PINGROUP(ulpi_dir_py1, SPI1, SPI5, UARTD, ULPI, ULPI, 0x3024, N, N, N), + PINGROUP(ulpi_nxt_py2, SPI1, SPI5, UARTD, ULPI, ULPI, 0x3028, N, N, N), + PINGROUP(ulpi_stp_py3, SPI1, SPI5, UARTD, ULPI, ULPI, 0x302c, N, N, N), + PINGROUP(dap3_fs_pp0, I2S2, SPI5, DISPLAYA, DISPLAYB, I2S2, 0x3030, N, N, N), + PINGROUP(dap3_din_pp1, I2S2, SPI5, DISPLAYA, DISPLAYB, I2S2, 0x3034, N, N, N), + PINGROUP(dap3_dout_pp2, I2S2, SPI5, DISPLAYA, DISPLAYB, I2S2, 0x3038, N, N, N), + PINGROUP(dap3_sclk_pp3, I2S2, SPI5, DISPLAYA, DISPLAYB, I2S2, 0x303c, N, N, N), + PINGROUP(pv0, USB, RSVD2, RSVD3, RSVD4, RSVD4, 0x3040, N, N, N), + PINGROUP(pv1, RSVD1, RSVD2, RSVD3, RSVD4, RSVD4, 0x3044, N, N, N), + PINGROUP(sdmmc1_clk_pz0, SDMMC1, CLK12, RSVD3, RSVD4, RSVD4, 0x3048, N, N, N), + PINGROUP(sdmmc1_cmd_pz1, SDMMC1, SPDIF, SPI4, UARTA, SDMMC1, 0x304c, N, N, N), + PINGROUP(sdmmc1_dat3_py4, SDMMC1, SPDIF, SPI4, UARTA, SDMMC1, 0x3050, N, N, N), + PINGROUP(sdmmc1_dat2_py5, SDMMC1, PWM0, SPI4, UARTA, SDMMC1, 0x3054, N, N, N), + PINGROUP(sdmmc1_dat1_py6, SDMMC1, PWM1, SPI4, UARTA, SDMMC1, 0x3058, N, N, N), + PINGROUP(sdmmc1_dat0_py7, SDMMC1, RSVD2, SPI4, UARTA, RSVD2, 0x305c, N, N, N), + PINGROUP(clk2_out_pw5, EXTPERIPH2, RSVD2, RSVD3, RSVD4, RSVD4, 0x3068, N, N, N), + PINGROUP(clk2_req_pcc5, DAP, RSVD2, RSVD3, RSVD4, RSVD4, 0x306c, N, N, N), + PINGROUP(hdmi_int_pn7, RSVD1, RSVD2, RSVD3, RSVD4, RSVD4, 0x3110, N, N, Y), + PINGROUP(ddc_scl_pv4, I2C4, RSVD2, RSVD3, RSVD4, RSVD4, 0x3114, N, N, Y), + PINGROUP(ddc_sda_pv5, I2C4, RSVD2, RSVD3, RSVD4, RSVD4, 0x3118, N, N, Y), + PINGROUP(uart2_rxd_pc3, IRDA, SPDIF, UARTA, SPI4, IRDA, 0x3164, N, N, N), + PINGROUP(uart2_txd_pc2, IRDA, SPDIF, UARTA, SPI4, IRDA, 0x3168, N, N, N), + PINGROUP(uart2_rts_n_pj6, UARTA, UARTB, RSVD3, SPI4, RSVD3, 0x316c, N, N, N), + PINGROUP(uart2_cts_n_pj5, UARTA, UARTB, RSVD3, SPI4, RSVD3, 0x3170, N, N, N), + PINGROUP(uart3_txd_pw6, UARTC, RSVD2, RSVD3, SPI4, RSVD3, 0x3174, N, N, N), + PINGROUP(uart3_rxd_pw7, UARTC, RSVD2, RSVD3, SPI4, RSVD3, 0x3178, N, N, N), + PINGROUP(uart3_cts_n_pa1, UARTC, SDMMC1, DTV, SPI4, UARTC, 0x317c, N, N, N), + PINGROUP(uart3_rts_n_pc0, UARTC, PWM0, DTV, DISPLAYA, UARTC, 0x3180, N, N, N), + PINGROUP(pu0, OWR, UARTA, RSVD3, RSVD4, RSVD4, 0x3184, N, N, N), + PINGROUP(pu1, RSVD1, UARTA, RSVD3, RSVD4, RSVD4, 0x3188, N, N, N), + PINGROUP(pu2, RSVD1, UARTA, RSVD3, RSVD4, RSVD4, 0x318c, N, N, N), + PINGROUP(pu3, PWM0, UARTA, DISPLAYA, DISPLAYB, PWM0, 0x3190, N, N, N), + PINGROUP(pu4, PWM1, UARTA, DISPLAYA, DISPLAYB, PWM1, 0x3194, N, N, N), + PINGROUP(pu5, PWM2, UARTA, DISPLAYA, DISPLAYB, PWM2, 0x3198, N, N, N), + PINGROUP(pu6, PWM3, UARTA, USB, DISPLAYB, PWM3, 0x319c, N, N, N), + PINGROUP(gen1_i2c_sda_pc5, I2C1, RSVD2, RSVD3, RSVD4, RSVD4, 0x31a0, Y, N, N), + PINGROUP(gen1_i2c_scl_pc4, I2C1, RSVD2, RSVD3, RSVD4, RSVD4, 0x31a4, Y, N, N), + PINGROUP(dap4_fs_pp4, I2S3, RSVD2, DTV, RSVD4, RSVD4, 0x31a8, N, N, N), + PINGROUP(dap4_din_pp5, I2S3, RSVD2, RSVD3, RSVD4, RSVD4, 0x31ac, N, N, N), + PINGROUP(dap4_dout_pp6, I2S3, RSVD2, DTV, RSVD4, RSVD4, 0x31b0, N, N, N), + PINGROUP(dap4_sclk_pp7, I2S3, RSVD2, RSVD3, RSVD4, RSVD4, 0x31b4, N, N, N), + PINGROUP(clk3_out_pee0, EXTPERIPH3, RSVD2, RSVD3, RSVD4, RSVD4, 0x31b8, N, N, N), + PINGROUP(clk3_req_pee1, DEV3, RSVD2, RSVD3, RSVD4, RSVD4, 0x31bc, N, N, N), + PINGROUP(gmi_wp_n_pc7, RSVD1, NAND, GMI, GMI_ALT, RSVD1, 0x31c0, N, N, N), + PINGROUP(gmi_iordy_pi5, SDMMC2, RSVD2, GMI, TRACE, RSVD2, 0x31c4, N, N, N), + PINGROUP(gmi_wait_pi7, SPI4, NAND, GMI, DTV, NAND, 0x31c8, N, N, N), + PINGROUP(gmi_adv_n_pk0, RSVD1, NAND, GMI, TRACE, RSVD1, 0x31cc, N, N, N), + PINGROUP(gmi_clk_pk1, SDMMC2, NAND, GMI, TRACE, GMI, 0x31d0, N, N, N), + PINGROUP(gmi_cs0_n_pj0, RSVD1, NAND, GMI, USB, RSVD1, 0x31d4, N, N, N), + PINGROUP(gmi_cs1_n_pj2, RSVD1, NAND, GMI, SOC, RSVD1, 0x31d8, N, N, N), + PINGROUP(gmi_cs2_n_pk3, SDMMC2, NAND, GMI, TRACE, GMI, 0x31dc, N, N, N), + PINGROUP(gmi_cs3_n_pk4, SDMMC2, NAND, GMI, GMI_ALT, GMI, 0x31e0, N, N, N), + PINGROUP(gmi_cs4_n_pk2, USB, NAND, GMI, TRACE, GMI, 0x31e4, N, N, N), + PINGROUP(gmi_cs6_n_pi3, NAND, NAND_ALT, GMI, SPI4, NAND, 0x31e8, N, N, N), + PINGROUP(gmi_cs7_n_pi6, NAND, NAND_ALT, GMI, SDMMC2, NAND, 0x31ec, N, N, N), + PINGROUP(gmi_ad0_pg0, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31f0, N, N, N), + PINGROUP(gmi_ad1_pg1, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31f4, N, N, N), + PINGROUP(gmi_ad2_pg2, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31f8, N, N, N), + PINGROUP(gmi_ad3_pg3, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31fc, N, N, N), + PINGROUP(gmi_ad4_pg4, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x3200, N, N, N), + PINGROUP(gmi_ad5_pg5, RSVD1, NAND, GMI, SPI4, RSVD1, 0x3204, N, N, N), + PINGROUP(gmi_ad6_pg6, RSVD1, NAND, GMI, SPI4, RSVD1, 0x3208, N, N, N), + PINGROUP(gmi_ad7_pg7, RSVD1, NAND, GMI, SPI4, RSVD1, 0x320c, N, N, N), + PINGROUP(gmi_ad8_ph0, PWM0, NAND, GMI, DTV, GMI, 0x3210, N, N, N), + PINGROUP(gmi_ad9_ph1, PWM1, NAND, GMI, RSVD4, GMI, 0x3214, N, N, N), + PINGROUP(gmi_ad10_ph2, PWM2, NAND, GMI, RSVD4, GMI, 0x3218, N, N, N), + PINGROUP(gmi_ad11_ph3, PWM3, NAND, GMI, USB, GMI, 0x321c, N, N, N), + PINGROUP(gmi_ad12_ph4, SDMMC2, NAND, GMI, RSVD4, RSVD4, 0x3220, N, N, N), + PINGROUP(gmi_ad13_ph5, SDMMC2, NAND, GMI, RSVD4, RSVD4, 0x3224, N, N, N), + PINGROUP(gmi_ad14_ph6, SDMMC2, NAND, GMI, DTV, GMI, 0x3228, N, N, N), + PINGROUP(gmi_ad15_ph7, SDMMC2, NAND, GMI, DTV, GMI, 0x322c, N, N, N), + PINGROUP(gmi_a16_pj7, UARTD, TRACE, GMI, GMI_ALT, GMI, 0x3230, N, N, N), + PINGROUP(gmi_a17_pb0, UARTD, RSVD2, GMI, TRACE, RSVD2, 0x3234, N, N, N), + PINGROUP(gmi_a18_pb1, UARTD, RSVD2, GMI, TRACE, RSVD2, 0x3238, N, N, N), + PINGROUP(gmi_a19_pk7, UARTD, SPI4, GMI, TRACE, GMI, 0x323c, N, N, N), + PINGROUP(gmi_wr_n_pi0, RSVD1, NAND, GMI, SPI4, RSVD1, 0x3240, N, N, N), + PINGROUP(gmi_oe_n_pi1, RSVD1, NAND, GMI, SOC, RSVD1, 0x3244, N, N, N), + PINGROUP(gmi_dqs_p_pj3, SDMMC2, NAND, GMI, TRACE, NAND, 0x3248, N, N, N), + PINGROUP(gmi_rst_n_pi4, NAND, NAND_ALT, GMI, RSVD4, RSVD4, 0x324c, N, N, N), + PINGROUP(gen2_i2c_scl_pt5, I2C2, RSVD2, GMI, RSVD4, RSVD4, 0x3250, Y, N, N), + PINGROUP(gen2_i2c_sda_pt6, I2C2, RSVD2, GMI, RSVD4, RSVD4, 0x3254, Y, N, N), + PINGROUP(sdmmc4_clk_pcc4, SDMMC4, RSVD2, GMI, RSVD4, RSVD4, 0x3258, N, Y, N), + PINGROUP(sdmmc4_cmd_pt7, SDMMC4, RSVD2, GMI, RSVD4, RSVD4, 0x325c, N, Y, N), + PINGROUP(sdmmc4_dat0_paa0, SDMMC4, SPI3, GMI, RSVD4, RSVD4, 0x3260, N, Y, N), + PINGROUP(sdmmc4_dat1_paa1, SDMMC4, SPI3, GMI, RSVD4, RSVD4, 0x3264, N, Y, N), + PINGROUP(sdmmc4_dat2_paa2, SDMMC4, SPI3, GMI, RSVD4, RSVD4, 0x3268, N, Y, N), + PINGROUP(sdmmc4_dat3_paa3, SDMMC4, SPI3, GMI, RSVD4, RSVD4, 0x326c, N, Y, N), + PINGROUP(sdmmc4_dat4_paa4, SDMMC4, SPI3, GMI, RSVD4, RSVD4, 0x3270, N, Y, N), + PINGROUP(sdmmc4_dat5_paa5, SDMMC4, SPI3, GMI, RSVD4, RSVD4, 0x3274, N, Y, N), + PINGROUP(sdmmc4_dat6_paa6, SDMMC4, SPI3, GMI, RSVD4, RSVD4, 0x3278, N, Y, N), + PINGROUP(sdmmc4_dat7_paa7, SDMMC4, RSVD2, GMI, RSVD4, RSVD4, 0x327c, N, Y, N), + PINGROUP(cam_mclk_pcc0, VI, VI_ALT1, VI_ALT3, RSVD4, RSVD4, 0x3284, N, N, N), + PINGROUP(pcc1, I2S4, RSVD2, RSVD3, RSVD4, RSVD4, 0x3288, N, N, N), + PINGROUP(pbb0, I2S4, VI, VI_ALT1, VI_ALT3, I2S4, 0x328c, N, N, N), + PINGROUP(cam_i2c_scl_pbb1, VGP1, I2C3, RSVD3, RSVD4, RSVD4, 0x3290, Y, N, N), + PINGROUP(cam_i2c_sda_pbb2, VGP2, I2C3, RSVD3, RSVD4, RSVD4, 0x3294, Y, N, N), + PINGROUP(pbb3, VGP3, DISPLAYA, DISPLAYB, RSVD4, RSVD4, 0x3298, N, N, N), + PINGROUP(pbb4, VGP4, DISPLAYA, DISPLAYB, RSVD4, RSVD4, 0x329c, N, N, N), + PINGROUP(pbb5, VGP5, DISPLAYA, DISPLAYB, RSVD4, RSVD4, 0x32a0, N, N, N), + PINGROUP(pbb6, VGP6, DISPLAYA, DISPLAYB, RSVD4, RSVD4, 0x32a4, N, N, N), + PINGROUP(pbb7, I2S4, RSVD2, RSVD3, RSVD4, RSVD4, 0x32a8, N, N, N), + PINGROUP(pcc2, I2S4, RSVD2, RSVD3, RSVD4, RSVD4, 0x32ac, N, N, N), + PINGROUP(pwr_i2c_scl_pz6, I2CPWR, RSVD2, RSVD3, RSVD4, RSVD4, 0x32b4, Y, N, N), + PINGROUP(pwr_i2c_sda_pz7, I2CPWR, RSVD2, RSVD3, RSVD4, RSVD4, 0x32b8, Y, N, N), + PINGROUP(kb_row0_pr0, KBC, RSVD2, RSVD3, RSVD4, RSVD4, 0x32bc, N, N, N), + PINGROUP(kb_row1_pr1, KBC, RSVD2, RSVD3, RSVD4, RSVD4, 0x32c0, N, N, N), + PINGROUP(kb_row2_pr2, KBC, RSVD2, RSVD3, RSVD4, RSVD4, 0x32c4, N, N, N), + PINGROUP(kb_row3_pr3, KBC, DISPLAYA, RSVD3, DISPLAYB, RSVD3, 0x32c8, N, N, N), + PINGROUP(kb_row4_pr4, KBC, DISPLAYA, SPI2, DISPLAYB, KBC, 0x32cc, N, N, N), + PINGROUP(kb_row5_pr5, KBC, DISPLAYA, SPI2, DISPLAYB, KBC, 0x32d0, N, N, N), + PINGROUP(kb_row6_pr6, KBC, DISPLAYA, DISPLAYA_ALT, DISPLAYB, KBC, 0x32d4, N, N, N), + PINGROUP(kb_row7_pr7, KBC, RSVD2, RSVD3, UARTA, RSVD2, 0x32d8, N, N, N), + PINGROUP(kb_row8_ps0, KBC, RSVD2, RSVD3, UARTA, RSVD2, 0x32dc, N, N, N), + PINGROUP(kb_row9_ps1, KBC, RSVD2, RSVD3, UARTA, RSVD3, 0x32e0, N, N, N), + PINGROUP(kb_row10_ps2, KBC, RSVD2, RSVD3, UARTA, RSVD3, 0x32e4, N, N, N), + PINGROUP(kb_col0_pq0, KBC, USB, SPI2, EMC_DLL, KBC, 0x32fc, N, N, N), + PINGROUP(kb_col1_pq1, KBC, RSVD2, SPI2, EMC_DLL, RSVD2, 0x3300, N, N, N), + PINGROUP(kb_col2_pq2, KBC, RSVD2, SPI2, RSVD4, RSVD2, 0x3304, N, N, N), + PINGROUP(kb_col3_pq3, KBC, DISPLAYA, PWM2, UARTA, KBC, 0x3308, N, N, N), + PINGROUP(kb_col4_pq4, KBC, OWR, SDMMC3, UARTA, KBC, 0x330c, N, N, N), + PINGROUP(kb_col5_pq5, KBC, RSVD2, SDMMC1, RSVD4, RSVD4, 0x3310, N, N, N), + PINGROUP(kb_col6_pq6, KBC, RSVD2, SPI2, RSVD4, RSVD4, 0x3314, N, N, N), + PINGROUP(kb_col7_pq7, KBC, RSVD2, SPI2, RSVD4, RSVD4, 0x3318, N, N, N), + PINGROUP(clk_32k_out_pa0, BLINK, SOC, RSVD3, RSVD4, RSVD4, 0x331c, N, N, N), + PINGROUP(sys_clk_req_pz5, SYSCLK, RSVD2, RSVD3, RSVD4, RSVD4, 0x3320, N, N, N), + PINGROUP(core_pwr_req, PWRON, RSVD2, RSVD3, RSVD4, RSVD4, 0x3324, N, N, N), + PINGROUP(cpu_pwr_req, CPU, RSVD2, RSVD3, RSVD4, RSVD4, 0x3328, N, N, N), + PINGROUP(pwr_int_n, PMI, RSVD2, RSVD3, RSVD4, RSVD4, 0x332c, N, N, N), + PINGROUP(owr, OWR, RSVD2, RSVD3, RSVD4, RSVD4, 0x3334, N, N, Y), + PINGROUP(dap1_fs_pn0, I2S0, HDA, GMI, RSVD4, RSVD4, 0x3338, N, N, N), + PINGROUP(dap1_din_pn1, I2S0, HDA, GMI, RSVD4, RSVD4, 0x333c, N, N, N), + PINGROUP(dap1_dout_pn2, I2S0, HDA, GMI, RSVD4, RSVD4, 0x3340, N, N, N), + PINGROUP(dap1_sclk_pn3, I2S0, HDA, GMI, RSVD4, RSVD4, 0x3344, N, N, N), + PINGROUP(clk1_req_pee2, DAP, DAP1, RSVD3, RSVD4, RSVD4, 0x3348, N, N, N), + PINGROUP(clk1_out_pw4, EXTPERIPH1, DAP2, RSVD3, RSVD4, RSVD4, 0x334c, N, N, N), + PINGROUP(spdif_in_pk6, SPDIF, USB, RSVD3, RSVD4, RSVD4, 0x3350, N, N, N), + PINGROUP(spdif_out_pk5, SPDIF, RSVD2, RSVD3, RSVD4, RSVD4, 0x3354, N, N, N), + PINGROUP(dap2_fs_pa2, I2S1, HDA, RSVD3, RSVD4, RSVD4, 0x3358, N, N, N), + PINGROUP(dap2_din_pa4, I2S1, HDA, RSVD3, RSVD4, RSVD4, 0x335c, N, N, N), + PINGROUP(dap2_dout_pa5, I2S1, HDA, RSVD3, RSVD4, RSVD4, 0x3360, N, N, N), + PINGROUP(dap2_sclk_pa3, I2S1, HDA, RSVD3, RSVD4, RSVD4, 0x3364, N, N, N), + PINGROUP(dvfs_pwm_px0, SPI6, RSVD2, RSVD3, RSVD4, RSVD4, 0x3368, N, N, N), + PINGROUP(gpio_x1_aud_px1, SPI6, RSVD2, RSVD3, RSVD4, RSVD4, 0x336c, N, N, N), + PINGROUP(gpio_x3_aud_px3, SPI6, SPI1, RSVD3, RSVD4, RSVD4, 0x3370, N, N, N), + PINGROUP(gpio_x4_aud_px4, RSVD1, SPI1, SPI2, DAP2, RSVD1, 0x3378, N, N, N), + PINGROUP(gpio_x5_aud_px5, RSVD1, SPI1, SPI2, RSVD4, RSVD1, 0x337c, N, N, N), + PINGROUP(gpio_x6_aud_px6, SPI6, SPI1, SPI2, RSVD4, RSVD4, 0x3380, N, N, N), + PINGROUP(gpio_x7_aud_px7, RSVD1, SPI1, SPI2, RSVD4, RSVD4, 0x3384, N, N, N), + PINGROUP(sdmmc3_clk_pa6, SDMMC3, RSVD2, RSVD3, SPI3, RSVD3, 0x3390, N, N, N), + PINGROUP(sdmmc3_cmd_pa7, SDMMC3, PWM3, UARTA, SPI3, SDMMC3, 0x3394, N, N, N), + PINGROUP(sdmmc3_dat0_pb7, SDMMC3, RSVD2, RSVD3, SPI3, RSVD3, 0x3398, N, N, N), + PINGROUP(sdmmc3_dat1_pb6, SDMMC3, PWM2, UARTA, SPI3, SDMMC3, 0x339c, N, N, N), + PINGROUP(sdmmc3_dat2_pb5, SDMMC3, PWM1, DISPLAYA, SPI3, SDMMC3, 0x33a0, N, N, N), + PINGROUP(sdmmc3_dat3_pb4, SDMMC3, PWM0, DISPLAYB, SPI3, SDMMC3, 0x33a4, N, N, N), + PINGROUP(hdmi_cec_pee3, CEC, SDMMC3, RSVD3, SOC, RSVD3, 0x33e0, Y, N, N), + PINGROUP(sdmmc1_wp_n_pv3, SDMMC1, CLK12, SPI4, UARTA, SDMMC1, 0x33e4, N, N, N), + PINGROUP(sdmmc3_cd_n_pv2, SDMMC3, OWR, RSVD3, RSVD4, RSVD4, 0x33e8, N, N, N), + PINGROUP(gpio_w2_aud_pw2, SPI6, RSVD2, SPI2, I2C1, RSVD2, 0x33ec, N, N, N), + PINGROUP(gpio_w3_aud_pw3, SPI6, SPI1, SPI2, I2C1, SPI6, 0x33f0, N, N, N), + PINGROUP(usb_vbus_en0_pn4, USB, RSVD2, RSVD3, RSVD4, RSVD4, 0x33f4, Y, N, N), + PINGROUP(usb_vbus_en1_pn5, USB, RSVD2, RSVD3, RSVD4, RSVD4, 0x33f8, Y, N, N), + PINGROUP(sdmmc3_clk_lb_in_pee5, SDMMC3, RSVD2, RSVD3, RSVD4, RSVD4, 0x33fc, N, N, N), + PINGROUP(sdmmc3_clk_lb_out_pee4, SDMMC3, RSVD2, RSVD3, RSVD4, RSVD4, 0x3400, N, N, N), + PINGROUP(reset_out_n, RSVD1, RSVD2, RSVD3, RESET_OUT_N, RSVD3, 0x3408, N, N, N), + + /* pg_name, r, hsm_b, schmitt_b, lpmd_b, drvdn_b, drvdn_w, drvup_b, drvup_w, slwr_b, slwr_w, slwf_b, slwf_w, drvtype */ + DRV_PINGROUP(ao1, 0x868, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(ao2, 0x86c, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(at1, 0x870, 2, 3, 4, 12, 7, 20, 7, 28, 2, 30, 2, Y), + DRV_PINGROUP(at2, 0x874, 2, 3, 4, 12, 7, 20, 7, 28, 2, 30, 2, Y), + DRV_PINGROUP(at3, 0x878, 2, 3, 4, 12, 7, 20, 7, 28, 2, 30, 2, Y), + DRV_PINGROUP(at4, 0x87c, 2, 3, 4, 12, 7, 20, 7, 28, 2, 30, 2, Y), + DRV_PINGROUP(at5, 0x880, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(cdev1, 0x884, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(cdev2, 0x888, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(dap1, 0x890, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(dap2, 0x894, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(dap3, 0x898, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(dap4, 0x89c, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(dbg, 0x8a0, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(sdio3, 0x8b0, 2, 3, -1, 12, 7, 20, 7, 28, 2, 30, 2, N), + DRV_PINGROUP(spi, 0x8b4, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(uaa, 0x8b8, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(uab, 0x8bc, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(uart2, 0x8c0, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(uart3, 0x8c4, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(sdio1, 0x8ec, 2, 3, -1, 12, 7, 20, 7, 28, 2, 30, 2, N), + DRV_PINGROUP(ddc, 0x8fc, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(gma, 0x900, 2, 3, 4, 14, 5, 20, 5, 28, 2, 30, 2, Y), + DRV_PINGROUP(gme, 0x910, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(gmf, 0x914, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(gmg, 0x918, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(gmh, 0x91c, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(owr, 0x920, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(uda, 0x924, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), +}; + +static const struct tegra_pinctrl_soc_data tegra114_pinctrl = { + .ngpios = NUM_GPIOS, + .pins = tegra114_pins, + .npins = ARRAY_SIZE(tegra114_pins), + .functions = tegra114_functions, + .nfunctions = ARRAY_SIZE(tegra114_functions), + .groups = tegra114_groups, + .ngroups = ARRAY_SIZE(tegra114_groups), +}; + +static int tegra114_pinctrl_probe(struct platform_device *pdev) +{ + return tegra_pinctrl_probe(pdev, &tegra114_pinctrl); +} + +static struct of_device_id tegra114_pinctrl_of_match[] = { + { .compatible = "nvidia,tegra114-pinmux", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra114_pinctrl_of_match); + +static struct platform_driver tegra114_pinctrl_driver = { + .driver = { + .name = "tegra114-pinctrl", + .owner = THIS_MODULE, + .of_match_table = tegra114_pinctrl_of_match, + }, + .probe = tegra114_pinctrl_probe, + .remove = tegra_pinctrl_remove, +}; +module_platform_driver(tegra114_pinctrl_driver); + +MODULE_ALIAS("platform:tegra114-pinctrl"); +MODULE_AUTHOR("Pritesh Raithatha "); +MODULE_DESCRIPTION("NVIDIA Tegra114 pincontrol driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From eb8b8f2e7164b819ca35a3d6240198fdb98d0ef3 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Thu, 20 Dec 2012 16:51:31 +0800 Subject: clk: prima2: enable dt-binding clkdev mapping this patche deletes hard code that registers clkdev by things like: clk_register_clkdev(clk, NULL, "b0030000.nand"); clk_register_clkdev(clk, NULL, "b0040000.audio"); clk_register_clkdev(clk, NULL, "b0080000.usp"); prima2 clock controller becomes a clock provider and every dt node just declares its clock sources by dt prop. it also makes us easier to extend this driver to support both prima2 and marco as marco has different address mapping with prima2. Signed-off-by: Barry Song Signed-off-by: Mike Turquette --- .../devicetree/bindings/clock/prima2-clock.txt | 73 ++++++++ arch/arm/boot/dts/prima2.dtsi | 31 +++- drivers/clk/clk-prima2.c | 205 ++++++++------------- 3 files changed, 183 insertions(+), 126 deletions(-) create mode 100644 Documentation/devicetree/bindings/clock/prima2-clock.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/clock/prima2-clock.txt b/Documentation/devicetree/bindings/clock/prima2-clock.txt new file mode 100644 index 000000000000..5016979c0f78 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/prima2-clock.txt @@ -0,0 +1,73 @@ +* Clock bindings for CSR SiRFprimaII + +Required properties: +- compatible: Should be "sirf,prima2-clkc" +- reg: Address and length of the register set +- interrupts: Should contain clock controller interrupt +- #clock-cells: Should be <1> + +The clock consumer should specify the desired clock by having the clock +ID in its "clocks" phandle cell. The following is a full list of prima2 +clocks and IDs. + + Clock ID + --------------------------- + rtc 0 + osc 1 + pll1 2 + pll2 3 + pll3 4 + mem 5 + sys 6 + security 7 + dsp 8 + gps 9 + mf 10 + io 11 + cpu 12 + uart0 13 + uart1 14 + uart2 15 + tsc 16 + i2c0 17 + i2c1 18 + spi0 19 + spi1 20 + pwmc 21 + efuse 22 + pulse 23 + dmac0 24 + dmac1 25 + nand 26 + audio 27 + usp0 28 + usp1 29 + usp2 30 + vip 31 + gfx 32 + mm 33 + lcd 34 + vpp 35 + mmc01 36 + mmc23 37 + mmc45 38 + usbpll 39 + usb0 40 + usb1 41 + +Examples: + +clks: clock-controller@88000000 { + compatible = "sirf,prima2-clkc"; + reg = <0x88000000 0x1000>; + interrupts = <3>; + #clock-cells = <1>; +}; + +i2c0: i2c@b00e0000 { + cell-index = <0>; + compatible = "sirf,prima2-i2c"; + reg = <0xb00e0000 0x10000>; + interrupts = <24>; + clocks = <&clks 17>; +}; diff --git a/arch/arm/boot/dts/prima2.dtsi b/arch/arm/boot/dts/prima2.dtsi index 055fca542120..3329719a9412 100644 --- a/arch/arm/boot/dts/prima2.dtsi +++ b/arch/arm/boot/dts/prima2.dtsi @@ -58,10 +58,11 @@ #size-cells = <1>; ranges = <0x88000000 0x88000000 0x40000>; - clock-controller@88000000 { + clks: clock-controller@88000000 { compatible = "sirf,prima2-clkc"; reg = <0x88000000 0x1000>; interrupts = <3>; + #clock-cells = <1>; }; reset-controller@88010000 { @@ -85,6 +86,7 @@ compatible = "sirf,prima2-memc"; reg = <0x90000000 0x10000>; interrupts = <27>; + clocks = <&clks 5>; }; }; @@ -104,6 +106,7 @@ compatible = "sirf,prima2-vpp"; reg = <0x90020000 0x10000>; interrupts = <31>; + clocks = <&clks 35>; }; }; @@ -117,6 +120,7 @@ compatible = "powervr,sgx531"; reg = <0x98000000 0x8000000>; interrupts = <6>; + clocks = <&clks 32>; }; }; @@ -130,6 +134,7 @@ compatible = "sirf,prima2-video-codec"; reg = <0xa0000000 0x8000000>; interrupts = <5>; + clocks = <&clks 33>; }; }; @@ -149,12 +154,14 @@ compatible = "sirf,prima2-gps"; reg = <0xa8010000 0x10000>; interrupts = <7>; + clocks = <&clks 9>; }; dsp@a9000000 { compatible = "sirf,prima2-dsp"; reg = <0xa9000000 0x1000000>; interrupts = <8>; + clocks = <&clks 8>; }; }; @@ -174,12 +181,14 @@ compatible = "sirf,prima2-nand"; reg = <0xb0030000 0x10000>; interrupts = <41>; + clocks = <&clks 26>; }; audio@b0040000 { compatible = "sirf,prima2-audio"; reg = <0xb0040000 0x10000>; interrupts = <35>; + clocks = <&clks 27>; }; uart0: uart@b0050000 { @@ -187,6 +196,7 @@ compatible = "sirf,prima2-uart"; reg = <0xb0050000 0x10000>; interrupts = <17>; + clocks = <&clks 13>; }; uart1: uart@b0060000 { @@ -194,6 +204,7 @@ compatible = "sirf,prima2-uart"; reg = <0xb0060000 0x10000>; interrupts = <18>; + clocks = <&clks 14>; }; uart2: uart@b0070000 { @@ -201,6 +212,7 @@ compatible = "sirf,prima2-uart"; reg = <0xb0070000 0x10000>; interrupts = <19>; + clocks = <&clks 15>; }; usp0: usp@b0080000 { @@ -208,6 +220,7 @@ compatible = "sirf,prima2-usp"; reg = <0xb0080000 0x10000>; interrupts = <20>; + clocks = <&clks 28>; }; usp1: usp@b0090000 { @@ -215,6 +228,7 @@ compatible = "sirf,prima2-usp"; reg = <0xb0090000 0x10000>; interrupts = <21>; + clocks = <&clks 29>; }; usp2: usp@b00a0000 { @@ -222,6 +236,7 @@ compatible = "sirf,prima2-usp"; reg = <0xb00a0000 0x10000>; interrupts = <22>; + clocks = <&clks 30>; }; dmac0: dma-controller@b00b0000 { @@ -229,6 +244,7 @@ compatible = "sirf,prima2-dmac"; reg = <0xb00b0000 0x10000>; interrupts = <12>; + clocks = <&clks 24>; }; dmac1: dma-controller@b0160000 { @@ -236,11 +252,13 @@ compatible = "sirf,prima2-dmac"; reg = <0xb0160000 0x10000>; interrupts = <13>; + clocks = <&clks 25>; }; vip@b00C0000 { compatible = "sirf,prima2-vip"; reg = <0xb00C0000 0x10000>; + clocks = <&clks 31>; }; spi0: spi@b00d0000 { @@ -248,6 +266,7 @@ compatible = "sirf,prima2-spi"; reg = <0xb00d0000 0x10000>; interrupts = <15>; + clocks = <&clks 19>; }; spi1: spi@b0170000 { @@ -255,6 +274,7 @@ compatible = "sirf,prima2-spi"; reg = <0xb0170000 0x10000>; interrupts = <16>; + clocks = <&clks 20>; }; i2c0: i2c@b00e0000 { @@ -262,6 +282,7 @@ compatible = "sirf,prima2-i2c"; reg = <0xb00e0000 0x10000>; interrupts = <24>; + clocks = <&clks 17>; }; i2c1: i2c@b00f0000 { @@ -269,12 +290,14 @@ compatible = "sirf,prima2-i2c"; reg = <0xb00f0000 0x10000>; interrupts = <25>; + clocks = <&clks 18>; }; tsc@b0110000 { compatible = "sirf,prima2-tsc"; reg = <0xb0110000 0x10000>; interrupts = <33>; + clocks = <&clks 16>; }; gpio: pinctrl@b0120000 { @@ -507,17 +530,20 @@ pwm@b0130000 { compatible = "sirf,prima2-pwm"; reg = <0xb0130000 0x10000>; + clocks = <&clks 21>; }; efusesys@b0140000 { compatible = "sirf,prima2-efuse"; reg = <0xb0140000 0x10000>; + clocks = <&clks 22>; }; pulsec@b0150000 { compatible = "sirf,prima2-pulsec"; reg = <0xb0150000 0x10000>; interrupts = <48>; + clocks = <&clks 23>; }; pci-iobg { @@ -616,12 +642,14 @@ compatible = "chipidea,ci13611a-prima2"; reg = <0xb8000000 0x10000>; interrupts = <10>; + clocks = <&clks 40>; }; usb1: usb@b00f0000 { compatible = "chipidea,ci13611a-prima2"; reg = <0xb8010000 0x10000>; interrupts = <11>; + clocks = <&clks 41>; }; sata@b00f0000 { @@ -634,6 +662,7 @@ compatible = "sirf,prima2-security"; reg = <0xb8030000 0x10000>; interrupts = <42>; + clocks = <&clks 7>; }; }; }; diff --git a/drivers/clk/clk-prima2.c b/drivers/clk/clk-prima2.c index a203ecccdc4f..f8e9d0c27be2 100644 --- a/drivers/clk/clk-prima2.c +++ b/drivers/clk/clk-prima2.c @@ -1025,20 +1025,67 @@ static struct of_device_id rsc_ids[] = { {}, }; +enum prima2_clk_index { + /* 0 1 2 3 4 5 6 7 8 9 */ + rtc, osc, pll1, pll2, pll3, mem, sys, security, dsp, gps, + mf, io, cpu, uart0, uart1, uart2, tsc, i2c0, i2c1, spi0, + spi1, pwmc, efuse, pulse, dmac0, dmac1, nand, audio, usp0, usp1, + usp2, vip, gfx, mm, lcd, vpp, mmc01, mmc23, mmc45, usbpll, + usb0, usb1, maxclk, +}; + +static __initdata struct clk_hw* prima2_clk_hw_array[maxclk] = { + NULL, /* dummy */ + NULL, + &clk_pll1.hw, + &clk_pll2.hw, + &clk_pll3.hw, + &clk_mem.hw, + &clk_sys.hw, + &clk_security.hw, + &clk_dsp.hw, + &clk_gps.hw, + &clk_mf.hw, + &clk_io.hw, + &clk_cpu.hw, + &clk_uart0.hw, + &clk_uart1.hw, + &clk_uart2.hw, + &clk_tsc.hw, + &clk_i2c0.hw, + &clk_i2c1.hw, + &clk_spi0.hw, + &clk_spi1.hw, + &clk_pwmc.hw, + &clk_efuse.hw, + &clk_pulse.hw, + &clk_dmac0.hw, + &clk_dmac1.hw, + &clk_nand.hw, + &clk_audio.hw, + &clk_usp0.hw, + &clk_usp1.hw, + &clk_usp2.hw, + &clk_vip.hw, + &clk_gfx.hw, + &clk_mm.hw, + &clk_lcd.hw, + &clk_vpp.hw, + &clk_mmc01.hw, + &clk_mmc23.hw, + &clk_mmc45.hw, + &usb_pll_clk_hw, + &clk_usb0.hw, + &clk_usb1.hw, +}; + +static struct clk *prima2_clks[maxclk]; +static struct clk_onecell_data clk_data; + void __init sirfsoc_of_clk_init(void) { - struct clk *clk; struct device_node *np; - - np = of_find_matching_node(NULL, clkc_ids); - if (!np) - panic("unable to find compatible clkc node in dtb\n"); - - sirfsoc_clk_vbase = of_iomap(np, 0); - if (!sirfsoc_clk_vbase) - panic("unable to map clkc registers\n"); - - of_node_put(np); + int i; np = of_find_matching_node(NULL, rsc_ids); if (!np) @@ -1050,122 +1097,30 @@ void __init sirfsoc_of_clk_init(void) of_node_put(np); + np = of_find_matching_node(NULL, clkc_ids); + if (!np) + return; + + sirfsoc_clk_vbase = of_iomap(np, 0); + if (!sirfsoc_clk_vbase) + panic("unable to map clkc registers\n"); /* These are always available (RTC and 26MHz OSC)*/ - clk = clk_register_fixed_rate(NULL, "rtc", NULL, + prima2_clks[rtc] = clk_register_fixed_rate(NULL, "rtc", NULL, CLK_IS_ROOT, 32768); - BUG_ON(IS_ERR(clk)); - clk = clk_register_fixed_rate(NULL, "osc", NULL, + prima2_clks[osc]= clk_register_fixed_rate(NULL, "osc", NULL, CLK_IS_ROOT, 26000000); - BUG_ON(IS_ERR(clk)); - - clk = clk_register(NULL, &clk_pll1.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_pll2.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_pll3.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_mem.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_sys.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_security.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b8030000.security"); - clk = clk_register(NULL, &clk_dsp.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_gps.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "a8010000.gps"); - clk = clk_register(NULL, &clk_mf.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_io.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "io"); - clk = clk_register(NULL, &clk_cpu.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "cpu"); - clk = clk_register(NULL, &clk_uart0.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0050000.uart"); - clk = clk_register(NULL, &clk_uart1.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0060000.uart"); - clk = clk_register(NULL, &clk_uart2.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0070000.uart"); - clk = clk_register(NULL, &clk_tsc.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0110000.tsc"); - clk = clk_register(NULL, &clk_i2c0.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00e0000.i2c"); - clk = clk_register(NULL, &clk_i2c1.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00f0000.i2c"); - clk = clk_register(NULL, &clk_spi0.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00d0000.spi"); - clk = clk_register(NULL, &clk_spi1.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0170000.spi"); - clk = clk_register(NULL, &clk_pwmc.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0130000.pwm"); - clk = clk_register(NULL, &clk_efuse.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0140000.efusesys"); - clk = clk_register(NULL, &clk_pulse.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0150000.pulsec"); - clk = clk_register(NULL, &clk_dmac0.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00b0000.dma-controller"); - clk = clk_register(NULL, &clk_dmac1.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0160000.dma-controller"); - clk = clk_register(NULL, &clk_nand.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0030000.nand"); - clk = clk_register(NULL, &clk_audio.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0040000.audio"); - clk = clk_register(NULL, &clk_usp0.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0080000.usp"); - clk = clk_register(NULL, &clk_usp1.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0090000.usp"); - clk = clk_register(NULL, &clk_usp2.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00a0000.usp"); - clk = clk_register(NULL, &clk_vip.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00c0000.vip"); - clk = clk_register(NULL, &clk_gfx.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "98000000.graphics"); - clk = clk_register(NULL, &clk_mm.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "a0000000.multimedia"); - clk = clk_register(NULL, &clk_lcd.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "90010000.display"); - clk = clk_register(NULL, &clk_vpp.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "90020000.vpp"); - clk = clk_register(NULL, &clk_mmc01.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_mmc23.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_mmc45.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &usb_pll_clk_hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_usb0.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00e0000.usb"); - clk = clk_register(NULL, &clk_usb1.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00f0000.usb"); + + for (i = pll1; i < maxclk; i++) { + prima2_clks[i] = clk_register(NULL, prima2_clk_hw_array[i]); + BUG_ON(!prima2_clks[i]); + } + clk_register_clkdev(prima2_clks[cpu], NULL, "cpu"); + clk_register_clkdev(prima2_clks[io], NULL, "io"); + clk_register_clkdev(prima2_clks[mem], NULL, "mem"); + + clk_data.clks = prima2_clks; + clk_data.clk_num = maxclk; + + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); } -- cgit v1.2.3-59-g8ed1b From c9f9e0e1597f99d605212356f5a220c757d190ac Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 17 Jan 2013 23:05:08 +0000 Subject: netfilter: doc: add nf_conntrack sysctl api documentation I grepped through the code and picked bits about nf_conntrack sysctl api and put that into one documentation file. [ I have mangled this patch including comments from several grammar improvements proposed by Neal Murphy , any new grammar error is my mistake --pablo ] Signed-off-by: Jiri Pirko Signed-off-by: Pablo Neira Ayuso --- Documentation/networking/nf_conntrack-sysctl.txt | 176 +++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 Documentation/networking/nf_conntrack-sysctl.txt (limited to 'Documentation') diff --git a/Documentation/networking/nf_conntrack-sysctl.txt b/Documentation/networking/nf_conntrack-sysctl.txt new file mode 100644 index 000000000000..70da5086153d --- /dev/null +++ b/Documentation/networking/nf_conntrack-sysctl.txt @@ -0,0 +1,176 @@ +/proc/sys/net/netfilter/nf_conntrack_* Variables: + +nf_conntrack_acct - BOOLEAN + 0 - disabled (default) + not 0 - enabled + + Enable connection tracking flow accounting. 64-bit byte and packet + counters per flow are added. + +nf_conntrack_buckets - INTEGER (read-only) + Size of hash table. If not specified as parameter during module + loading, the default size is calculated by dividing total memory + by 16384 to determine the number of buckets but the hash table will + never have fewer than 32 or more than 16384 buckets. + +nf_conntrack_checksum - BOOLEAN + 0 - disabled + not 0 - enabled (default) + + Verify checksum of incoming packets. Packets with bad checksums are + in INVALID state. If this is enabled, such packets will not be + considered for connection tracking. + +nf_conntrack_count - INTEGER (read-only) + Number of currently allocated flow entries. + +nf_conntrack_events - BOOLEAN + 0 - disabled + not 0 - enabled (default) + + If this option is enabled, the connection tracking code will + provide userspace with connection tracking events via ctnetlink. + +nf_conntrack_events_retry_timeout - INTEGER (seconds) + default 15 + + This option is only relevant when "reliable connection tracking + events" are used. Normally, ctnetlink is "lossy", that is, + events are normally dropped when userspace listeners can't keep up. + + Userspace can request "reliable event mode". When this mode is + active, the conntrack will only be destroyed after the event was + delivered. If event delivery fails, the kernel periodically + re-tries to send the event to userspace. + + This is the maximum interval the kernel should use when re-trying + to deliver the destroy event. + + A higher number means there will be fewer delivery retries and it + will take longer for a backlog to be processed. + +nf_conntrack_expect_max - INTEGER + Maximum size of expectation table. Default value is + nf_conntrack_buckets / 256. Minimum is 1. + +nf_conntrack_frag6_high_thresh - INTEGER + default 262144 + + Maximum memory used to reassemble IPv6 fragments. When + nf_conntrack_frag6_high_thresh bytes of memory is allocated for this + purpose, the fragment handler will toss packets until + nf_conntrack_frag6_low_thresh is reached. + +nf_conntrack_frag6_low_thresh - INTEGER + default 196608 + + See nf_conntrack_frag6_low_thresh + +nf_conntrack_frag6_timeout - INTEGER (seconds) + default 60 + + Time to keep an IPv6 fragment in memory. + +nf_conntrack_generic_timeout - INTEGER (seconds) + default 600 + + Default for generic timeout. This refers to layer 4 unknown/unsupported + protocols. + +nf_conntrack_helper - BOOLEAN + 0 - disabled + not 0 - enabled (default) + + Enable automatic conntrack helper assignment. + +nf_conntrack_icmp_timeout - INTEGER (seconds) + default 30 + + Default for ICMP timeout. + +nf_conntrack_icmpv6_timeout - INTEGER (seconds) + default 30 + + Default for ICMP6 timeout. + +nf_conntrack_log_invalid - INTEGER + 0 - disable (default) + 1 - log ICMP packets + 6 - log TCP packets + 17 - log UDP packets + 33 - log DCCP packets + 41 - log ICMPv6 packets + 136 - log UDPLITE packets + 255 - log packets of any protocol + + Log invalid packets of a type specified by value. + +nf_conntrack_max - INTEGER + Size of connection tracking table. Default value is + nf_conntrack_buckets value * 4. + +nf_conntrack_tcp_be_liberal - BOOLEAN + 0 - disabled (default) + not 0 - enabled + + Be conservative in what you do, be liberal in what you accept from others. + If it's non-zero, we mark only out of window RST segments as INVALID. + +nf_conntrack_tcp_loose - BOOLEAN + 0 - disabled + not 0 - enabled (default) + + If it is set to zero, we disable picking up already established + connections. + +nf_conntrack_tcp_max_retrans - INTEGER + default 3 + + Maximum number of packets that can be retransmitted without + received an (acceptable) ACK from the destination. If this number + is reached, a shorter timer will be started. + +nf_conntrack_tcp_timeout_close - INTEGER (seconds) + default 10 + +nf_conntrack_tcp_timeout_close_wait - INTEGER (seconds) + default 60 + +nf_conntrack_tcp_timeout_established - INTEGER (seconds) + default 432000 (5 days) + +nf_conntrack_tcp_timeout_fin_wait - INTEGER (seconds) + default 120 + +nf_conntrack_tcp_timeout_last_ack - INTEGER (seconds) + default 30 + +nf_conntrack_tcp_timeout_max_retrans - INTEGER (seconds) + default 300 + +nf_conntrack_tcp_timeout_syn_recv - INTEGER (seconds) + default 60 + +nf_conntrack_tcp_timeout_syn_sent - INTEGER (seconds) + default 120 + +nf_conntrack_tcp_timeout_time_wait - INTEGER (seconds) + default 120 + +nf_conntrack_tcp_timeout_unacknowledged - INTEGER (seconds) + default 300 + +nf_conntrack_timestamp - BOOLEAN + 0 - disabled (default) + not 0 - enabled + + Enable connection tracking flow timestamping. + +nf_conntrack_udp_timeout - INTEGER (seconds) + default 30 + +nf_conntrack_udp_timeout_stream2 - INTEGER (seconds) + default 180 + + This extended timeout will be used in case there is an UDP stream + detected. -- cgit v1.2.3-59-g8ed1b From 202dc3fc599c1dded235d3b448d9ca924252e354 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 21 Jan 2013 06:17:41 +0000 Subject: Documentation: remove obsolete networking/multicast.txt file The original intent of this file was to list limitations in drivers/hardware relating to multicast use, back when some modest hardware from the early 1990s did not support things we might take for granted today. I was intending to delete some now-gone MCA/token ring entries in this file, but once I opened it, I found it only contained information on the earliest (pre-2000) linux networking drivers. Checking the git history shows that the file hasn't been touched since 2005. Clearly nobody is actively consulting this file as a meaningful reference. Rather than add a "YES YES YES" line for all of the drivers we currently have, lets just take advantage of the fact that nobody is using the file to delete it. This has the side benefit of not having to do a line-by-line deletion of the file content as each older driver is expired. Signed-off-by: Paul Gortmaker Signed-off-by: David S. Miller --- Documentation/networking/00-INDEX | 2 -- Documentation/networking/multicast.txt | 63 ---------------------------------- 2 files changed, 65 deletions(-) delete mode 100644 Documentation/networking/multicast.txt (limited to 'Documentation') diff --git a/Documentation/networking/00-INDEX b/Documentation/networking/00-INDEX index 2cc3c7733a2f..1abfff26f8a0 100644 --- a/Documentation/networking/00-INDEX +++ b/Documentation/networking/00-INDEX @@ -126,8 +126,6 @@ ltpc.txt - the Apple or Farallon LocalTalk PC card driver mac80211-injection.txt - HOWTO use packet injection with mac80211 -multicast.txt - - Behaviour of cards under Multicast multiqueue.txt - HOWTO for multiqueue network device support. netconsole.txt diff --git a/Documentation/networking/multicast.txt b/Documentation/networking/multicast.txt deleted file mode 100644 index b06c8c69266f..000000000000 --- a/Documentation/networking/multicast.txt +++ /dev/null @@ -1,63 +0,0 @@ -Behaviour of Cards Under Multicast -================================== - -This is how they currently behave, not what the hardware can do--for example, -the Lance driver doesn't use its filter, even though the code for loading -it is in the DEC Lance-based driver. - -The following are requirements for multicasting ------------------------------------------------ -AppleTalk Multicast hardware filtering not important but - avoid cards only doing promisc -IP-Multicast Multicast hardware filters really help -IP-MRoute AllMulti hardware filters are of no help - - -Board Multicast AllMulti Promisc Filter ------------------------------------------------------------------------- -3c501 YES YES YES Software -3c503 YES YES YES Hardware -3c505 YES NO YES Hardware -3c507 NO NO NO N/A -3c509 YES YES YES Software -3c59x YES YES YES Software -ac3200 YES YES YES Hardware -apricot YES PROMISC YES Hardware -arcnet NO NO NO N/A -at1700 PROMISC PROMISC YES Software -atp PROMISC PROMISC YES Software -cs89x0 YES YES YES Software -de4x5 YES YES YES Hardware -de600 NO NO NO N/A -de620 PROMISC PROMISC YES Software -depca YES PROMISC YES Hardware -dmfe YES YES YES Software(*) -e2100 YES YES YES Hardware -eepro YES PROMISC YES Hardware -eexpress NO NO NO N/A -ewrk3 YES PROMISC YES Hardware -hp-plus YES YES YES Hardware -hp YES YES YES Hardware -hp100 YES YES YES Hardware -ibmtr NO NO NO N/A -ioc3-eth YES YES YES Hardware -lance YES YES YES Software(#) -ne YES YES YES Hardware -ni52 <------------------ Buggy ------------------> -ni65 YES YES YES Software(#) -seeq NO NO NO N/A -sgiseek <------------------ Buggy ------------------> -smc-ultra YES YES YES Hardware -sunlance YES YES YES Hardware -tulip YES YES YES Hardware -wavelan YES PROMISC YES Hardware -wd YES YES YES Hardware -xirc2ps_cs YES YES YES Hardware -znet YES YES YES Software - - -PROMISC = This multicast mode is in fact promiscuous mode. Avoid using -cards who go PROMISC on any multicast in a multicast kernel. - -(#) = Hardware multicast support is not used yet. -(*) = Hardware support for Davicom 9132 chipset only. -- cgit v1.2.3-59-g8ed1b From 0e37f88d9ad800f5dd94c9fc9dc304b4e9cb7d2c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 18 Jan 2013 22:30:34 +0100 Subject: ARM: sunxi: Add pinctrl driver for Allwinner SoCs The Allwinner SoCs have an IP module that handle both the muxing and the GPIOs. This IP has 8 banks of 32 bits, with a number of pins actually useful for each of these banks varying from one to another, and depending on the SoC used on the board. This driver only implements the pinctrl part, the gpio part will come eventually. Acked-by: Arnd Bergmann Acked-by: Olof Johansson Signed-off-by: Maxime Ripard Signed-off-by: Linus Walleij --- .../bindings/pinctrl/allwinner,sunxi-pinctrl.txt | 60 +++ arch/arm/mach-sunxi/Kconfig | 1 + drivers/pinctrl/Kconfig | 5 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-sunxi.c | 548 +++++++++++++++++++++ drivers/pinctrl/pinctrl-sunxi.h | 387 +++++++++++++++ 6 files changed, 1002 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt create mode 100644 drivers/pinctrl/pinctrl-sunxi.c create mode 100644 drivers/pinctrl/pinctrl-sunxi.h (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt new file mode 100644 index 000000000000..dff0e5f995e2 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt @@ -0,0 +1,60 @@ +* Allwinner A1X Pin Controller + +The pins controlled by sunXi pin controller are organized in banks, +each bank has 32 pins. Each pin has 7 multiplexing functions, with +the first two functions being GPIO in and out. The configuration on +the pins includes drive strength and pull-up. + +Required properties: +- compatible: "allwinner,-pinctrl". Supported SoCs for now are: + sun5i-a13. +- reg: Should contain the register physical address and length for the + pin controller. + +Please refer to pinctrl-bindings.txt in this directory for details of the +common pinctrl bindings used by client devices. + +A pinctrl node should contain at least one subnodes representing the +pinctrl groups available on the machine. Each subnode will list the +pins it needs, and how they should be configured, with regard to muxer +configuration, drive strength and pullups. If one of these options is +not set, its actual value will be unspecified. + +Required subnode-properties: + +- allwinner,pins: List of strings containing the pin name. +- allwinner,function: Function to mux the pins listed above to. + +Optional subnode-properties: +- allwinner,drive: Integer. Represents the current sent to the pin + 0: 10 mA + 1: 20 mA + 2: 30 mA + 3: 40 mA +- allwinner,pull: Integer. + 0: No resistor + 1: Pull-up resistor + 2: Pull-down resistor + +Examples: + +pinctrl@01c20800 { + compatible = "allwinner,sun5i-a13-pinctrl"; + reg = <0x01c20800 0x400>; + #address-cells = <1>; + #size-cells = <0>; + + uart1_pins_a: uart1@0 { + allwinner,pins = "PE10", "PE11"; + allwinner,function = "uart1"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + uart1_pins_b: uart1@1 { + allwinner,pins = "PG3", "PG4"; + allwinner,function = "uart1"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; +}; diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index 3fdd0085e306..8709a39bd34c 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -7,3 +7,4 @@ config ARCH_SUNXI select PINCTRL select SPARSE_IRQ select SUNXI_TIMER + select PINCTRL_SUNXI \ No newline at end of file diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index c31aeb01bb00..88840a421c6e 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -151,6 +151,11 @@ config PINCTRL_SIRF depends on ARCH_SIRF select PINMUX +config PINCTRL_SUNXI + bool + select PINMUX + select GENERIC_PINCONF + config PINCTRL_TEGRA bool select PINMUX diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index fc4606f27dc7..a2427da4d58e 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_PINCTRL_PXA168) += pinctrl-pxa168.o obj-$(CONFIG_PINCTRL_PXA910) += pinctrl-pxa910.o obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o obj-$(CONFIG_PINCTRL_SIRF) += pinctrl-sirf.o +obj-$(CONFIG_PINCTRL_SUNXI) += pinctrl-sunxi.o obj-$(CONFIG_PINCTRL_TEGRA) += pinctrl-tegra.o obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o diff --git a/drivers/pinctrl/pinctrl-sunxi.c b/drivers/pinctrl/pinctrl-sunxi.c new file mode 100644 index 000000000000..1a81613e8f77 --- /dev/null +++ b/drivers/pinctrl/pinctrl-sunxi.c @@ -0,0 +1,548 @@ +/* + * Allwinner A1X SoCs pinctrl driver. + * + * Copyright (C) 2012 Maxime Ripard + * + * Maxime Ripard + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "pinctrl-sunxi.h" + +static struct sunxi_pinctrl_group * +sunxi_pinctrl_find_group_by_name(struct sunxi_pinctrl *pctl, const char *group) +{ + int i; + + for (i = 0; i < pctl->ngroups; i++) { + struct sunxi_pinctrl_group *grp = pctl->groups + i; + + if (!strcmp(grp->name, group)) + return grp; + } + + return NULL; +} + +static struct sunxi_pinctrl_function * +sunxi_pinctrl_find_function_by_name(struct sunxi_pinctrl *pctl, + const char *name) +{ + struct sunxi_pinctrl_function *func = pctl->functions; + int i; + + for (i = 0; i < pctl->nfunctions; i++) { + if (!func[i].name) + break; + + if (!strcmp(func[i].name, name)) + return func + i; + } + + return NULL; +} + +static struct sunxi_desc_function * +sunxi_pinctrl_desc_find_function_by_name(struct sunxi_pinctrl *pctl, + const char *pin_name, + const char *func_name) +{ + int i; + + for (i = 0; i < pctl->desc->npins; i++) { + const struct sunxi_desc_pin *pin = pctl->desc->pins + i; + + if (!strcmp(pin->pin.name, pin_name)) { + struct sunxi_desc_function *func = pin->functions; + + while (func->name) { + if (!strcmp(func->name, func_name)) + return func; + + func++; + } + } + } + + return NULL; +} + +static int sunxi_pctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + return pctl->ngroups; +} + +static const char *sunxi_pctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned group) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + return pctl->groups[group].name; +} + +static int sunxi_pctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned group, + const unsigned **pins, + unsigned *num_pins) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + *pins = (unsigned *)&pctl->groups[group].pin; + *num_pins = 1; + + return 0; +} + +static int sunxi_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *node, + struct pinctrl_map **map, + unsigned *num_maps) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + unsigned long *pinconfig; + struct property *prop; + const char *function; + const char *group; + int ret, nmaps, i = 0; + u32 val; + + *map = NULL; + *num_maps = 0; + + ret = of_property_read_string(node, "allwinner,function", &function); + if (ret) { + dev_err(pctl->dev, + "missing allwinner,function property in node %s\n", + node->name); + return -EINVAL; + } + + nmaps = of_property_count_strings(node, "allwinner,pins") * 2; + if (nmaps < 0) { + dev_err(pctl->dev, + "missing allwinner,pins property in node %s\n", + node->name); + return -EINVAL; + } + + *map = kmalloc(nmaps * sizeof(struct pinctrl_map), GFP_KERNEL); + if (!map) + return -ENOMEM; + + of_property_for_each_string(node, "allwinner,pins", prop, group) { + struct sunxi_pinctrl_group *grp = + sunxi_pinctrl_find_group_by_name(pctl, group); + int j = 0, configlen = 0; + + if (!grp) { + dev_err(pctl->dev, "unknown pin %s", group); + continue; + } + + if (!sunxi_pinctrl_desc_find_function_by_name(pctl, + grp->name, + function)) { + dev_err(pctl->dev, "unsupported function %s on pin %s", + function, group); + continue; + } + + (*map)[i].type = PIN_MAP_TYPE_MUX_GROUP; + (*map)[i].data.mux.group = group; + (*map)[i].data.mux.function = function; + + i++; + + (*map)[i].type = PIN_MAP_TYPE_CONFIGS_GROUP; + (*map)[i].data.configs.group_or_pin = group; + + if (of_find_property(node, "allwinner,drive", NULL)) + configlen++; + if (of_find_property(node, "allwinner,pull", NULL)) + configlen++; + + pinconfig = kzalloc(configlen * sizeof(*pinconfig), GFP_KERNEL); + + if (!of_property_read_u32(node, "allwinner,drive", &val)) { + u16 strength = (val + 1) * 10; + pinconfig[j++] = + pinconf_to_config_packed(PIN_CONFIG_DRIVE_STRENGTH, + strength); + } + + if (!of_property_read_u32(node, "allwinner,pull", &val)) { + enum pin_config_param pull = PIN_CONFIG_END; + if (val == 1) + pull = PIN_CONFIG_BIAS_PULL_UP; + else if (val == 2) + pull = PIN_CONFIG_BIAS_PULL_DOWN; + pinconfig[j++] = pinconf_to_config_packed(pull, 0); + } + + (*map)[i].data.configs.configs = pinconfig; + (*map)[i].data.configs.num_configs = configlen; + + i++; + } + + *num_maps = nmaps; + + return 0; +} + +static void sunxi_pctrl_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, + unsigned num_maps) +{ + int i; + + for (i = 0; i < num_maps; i++) { + if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP) + kfree(map[i].data.configs.configs); + } + + kfree(map); +} + +static struct pinctrl_ops sunxi_pctrl_ops = { + .dt_node_to_map = sunxi_pctrl_dt_node_to_map, + .dt_free_map = sunxi_pctrl_dt_free_map, + .get_groups_count = sunxi_pctrl_get_groups_count, + .get_group_name = sunxi_pctrl_get_group_name, + .get_group_pins = sunxi_pctrl_get_group_pins, +}; + +static int sunxi_pconf_group_get(struct pinctrl_dev *pctldev, + unsigned group, + unsigned long *config) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + *config = pctl->groups[group].config; + + return 0; +} + +static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, + unsigned group, + unsigned long config) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + struct sunxi_pinctrl_group *g = &pctl->groups[group]; + u32 val, mask; + u16 strength; + u8 dlevel; + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_DRIVE_STRENGTH: + strength = pinconf_to_config_argument(config); + if (strength > 40) + return -EINVAL; + /* + * We convert from mA to what the register expects: + * 0: 10mA + * 1: 20mA + * 2: 30mA + * 3: 40mA + */ + dlevel = strength / 10 - 1; + val = readl(pctl->membase + sunxi_dlevel_reg(g->pin)); + mask = DLEVEL_PINS_MASK << sunxi_dlevel_offset(g->pin); + writel((val & ~mask) | dlevel << sunxi_dlevel_offset(g->pin), + pctl->membase + sunxi_dlevel_reg(g->pin)); + break; + case PIN_CONFIG_BIAS_PULL_UP: + val = readl(pctl->membase + sunxi_pull_reg(g->pin)); + mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin); + writel((val & ~mask) | 1 << sunxi_pull_offset(g->pin), + pctl->membase + sunxi_pull_reg(g->pin)); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + val = readl(pctl->membase + sunxi_pull_reg(g->pin)); + mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin); + writel((val & ~mask) | 2 << sunxi_pull_offset(g->pin), + pctl->membase + sunxi_pull_reg(g->pin)); + break; + default: + break; + } + + /* cache the config value */ + g->config = config; + + return 0; +} + +static struct pinconf_ops sunxi_pconf_ops = { + .pin_config_group_get = sunxi_pconf_group_get, + .pin_config_group_set = sunxi_pconf_group_set, +}; + +static int sunxi_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + return pctl->nfunctions; +} + +static const char *sunxi_pmx_get_func_name(struct pinctrl_dev *pctldev, + unsigned function) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + return pctl->functions[function].name; +} + +static int sunxi_pmx_get_func_groups(struct pinctrl_dev *pctldev, + unsigned function, + const char * const **groups, + unsigned * const num_groups) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + *groups = pctl->functions[function].groups; + *num_groups = pctl->functions[function].ngroups; + + return 0; +} + +static void sunxi_pmx_set(struct pinctrl_dev *pctldev, + unsigned pin, + u8 config) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + u32 val = readl(pctl->membase + sunxi_mux_reg(pin)); + u32 mask = MUX_PINS_MASK << sunxi_mux_offset(pin); + writel((val & ~mask) | config << sunxi_mux_offset(pin), + pctl->membase + sunxi_mux_reg(pin)); +} + +static int sunxi_pmx_enable(struct pinctrl_dev *pctldev, + unsigned function, + unsigned group) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + struct sunxi_pinctrl_group *g = pctl->groups + group; + struct sunxi_pinctrl_function *func = pctl->functions + function; + struct sunxi_desc_function *desc = + sunxi_pinctrl_desc_find_function_by_name(pctl, + g->name, + func->name); + + if (!desc) + return -EINVAL; + + sunxi_pmx_set(pctldev, g->pin, desc->muxval); + + return 0; +} + +static struct pinmux_ops sunxi_pmx_ops = { + .get_functions_count = sunxi_pmx_get_funcs_cnt, + .get_function_name = sunxi_pmx_get_func_name, + .get_function_groups = sunxi_pmx_get_func_groups, + .enable = sunxi_pmx_enable, +}; + +static struct pinctrl_desc sunxi_pctrl_desc = { + .confops = &sunxi_pconf_ops, + .pctlops = &sunxi_pctrl_ops, + .pmxops = &sunxi_pmx_ops, +}; + +static struct of_device_id sunxi_pinctrl_match[] = { + {} +}; +MODULE_DEVICE_TABLE(of, sunxi_pinctrl_match); + +static int sunxi_pinctrl_add_function(struct sunxi_pinctrl *pctl, + const char *name) +{ + struct sunxi_pinctrl_function *func = pctl->functions; + + while (func->name) { + /* function already there */ + if (strcmp(func->name, name) == 0) { + func->ngroups++; + return -EEXIST; + } + func++; + } + + func->name = name; + func->ngroups = 1; + + pctl->nfunctions++; + + return 0; +} + +static int sunxi_pinctrl_build_state(struct platform_device *pdev) +{ + struct sunxi_pinctrl *pctl = platform_get_drvdata(pdev); + int i; + + pctl->ngroups = pctl->desc->npins; + + /* Allocate groups */ + pctl->groups = devm_kzalloc(&pdev->dev, + pctl->ngroups * sizeof(*pctl->groups), + GFP_KERNEL); + if (!pctl->groups) + return -ENOMEM; + + for (i = 0; i < pctl->desc->npins; i++) { + const struct sunxi_desc_pin *pin = pctl->desc->pins + i; + struct sunxi_pinctrl_group *group = pctl->groups + i; + + group->name = pin->pin.name; + group->pin = pin->pin.number; + } + + /* + * We suppose that we won't have any more functions than pins, + * we'll reallocate that later anyway + */ + pctl->functions = devm_kzalloc(&pdev->dev, + pctl->desc->npins * sizeof(*pctl->functions), + GFP_KERNEL); + if (!pctl->functions) + return -ENOMEM; + + /* Count functions and their associated groups */ + for (i = 0; i < pctl->desc->npins; i++) { + const struct sunxi_desc_pin *pin = pctl->desc->pins + i; + struct sunxi_desc_function *func = pin->functions; + + while (func->name) { + sunxi_pinctrl_add_function(pctl, func->name); + func++; + } + } + + pctl->functions = krealloc(pctl->functions, + pctl->nfunctions * sizeof(*pctl->functions), + GFP_KERNEL); + + for (i = 0; i < pctl->desc->npins; i++) { + const struct sunxi_desc_pin *pin = pctl->desc->pins + i; + struct sunxi_desc_function *func = pin->functions; + + while (func->name) { + struct sunxi_pinctrl_function *func_item; + const char **func_grp; + + func_item = sunxi_pinctrl_find_function_by_name(pctl, + func->name); + if (!func_item) + return -EINVAL; + + if (!func_item->groups) { + func_item->groups = + devm_kzalloc(&pdev->dev, + func_item->ngroups * sizeof(*func_item->groups), + GFP_KERNEL); + if (!func_item->groups) + return -ENOMEM; + } + + func_grp = func_item->groups; + while (*func_grp) + func_grp++; + + *func_grp = pin->pin.name; + func++; + } + } + + return 0; +} + +static int sunxi_pinctrl_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + const struct of_device_id *device; + struct pinctrl_pin_desc *pins; + struct sunxi_pinctrl *pctl; + int i, ret; + + pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL); + if (!pctl) + return -ENOMEM; + platform_set_drvdata(pdev, pctl); + + pctl->membase = of_iomap(node, 0); + if (!pctl->membase) + return -ENOMEM; + + device = of_match_device(sunxi_pinctrl_match, &pdev->dev); + if (!device) + return -ENODEV; + + pctl->desc = (struct sunxi_pinctrl_desc *)device->data; + + ret = sunxi_pinctrl_build_state(pdev); + if (ret) { + dev_err(&pdev->dev, "dt probe failed: %d\n", ret); + return ret; + } + + pins = devm_kzalloc(&pdev->dev, + pctl->desc->npins * sizeof(*pins), + GFP_KERNEL); + if (!pins) + return -ENOMEM; + + for (i = 0; i < pctl->desc->npins; i++) + pins[i] = pctl->desc->pins[i].pin; + + sunxi_pctrl_desc.name = dev_name(&pdev->dev); + sunxi_pctrl_desc.owner = THIS_MODULE; + sunxi_pctrl_desc.pins = pins; + sunxi_pctrl_desc.npins = pctl->desc->npins; + pctl->dev = &pdev->dev; + pctl->pctl_dev = pinctrl_register(&sunxi_pctrl_desc, + &pdev->dev, pctl); + if (!pctl->pctl_dev) { + dev_err(&pdev->dev, "couldn't register pinctrl driver\n"); + return -EINVAL; + } + + dev_info(&pdev->dev, "initialized sunXi pin control driver\n"); + + return 0; +} + +static struct platform_driver sunxi_pinctrl_driver = { + .probe = sunxi_pinctrl_probe, + .driver = { + .name = "sunxi-pinctrl", + .owner = THIS_MODULE, + .of_match_table = sunxi_pinctrl_match, + }, +}; +module_platform_driver(sunxi_pinctrl_driver); + +MODULE_AUTHOR("Maxime Ripard + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __PINCTRL_SUNXI_H +#define __PINCTRL_SUNXI_H + +#include + +#define PA_BASE 0 +#define PB_BASE 32 +#define PC_BASE 64 +#define PD_BASE 96 +#define PE_BASE 128 +#define PF_BASE 160 +#define PG_BASE 192 + +#define SUNXI_PINCTRL_PIN_PA0 PINCTRL_PIN(PA_BASE + 0, "PA0") +#define SUNXI_PINCTRL_PIN_PA1 PINCTRL_PIN(PA_BASE + 1, "PA1") +#define SUNXI_PINCTRL_PIN_PA2 PINCTRL_PIN(PA_BASE + 2, "PA2") +#define SUNXI_PINCTRL_PIN_PA3 PINCTRL_PIN(PA_BASE + 3, "PA3") +#define SUNXI_PINCTRL_PIN_PA4 PINCTRL_PIN(PA_BASE + 4, "PA4") +#define SUNXI_PINCTRL_PIN_PA5 PINCTRL_PIN(PA_BASE + 5, "PA5") +#define SUNXI_PINCTRL_PIN_PA6 PINCTRL_PIN(PA_BASE + 6, "PA6") +#define SUNXI_PINCTRL_PIN_PA7 PINCTRL_PIN(PA_BASE + 7, "PA7") +#define SUNXI_PINCTRL_PIN_PA8 PINCTRL_PIN(PA_BASE + 8, "PA8") +#define SUNXI_PINCTRL_PIN_PA9 PINCTRL_PIN(PA_BASE + 9, "PA9") +#define SUNXI_PINCTRL_PIN_PA10 PINCTRL_PIN(PA_BASE + 10, "PA10") +#define SUNXI_PINCTRL_PIN_PA11 PINCTRL_PIN(PA_BASE + 11, "PA11") +#define SUNXI_PINCTRL_PIN_PA12 PINCTRL_PIN(PA_BASE + 12, "PA12") +#define SUNXI_PINCTRL_PIN_PA13 PINCTRL_PIN(PA_BASE + 13, "PA13") +#define SUNXI_PINCTRL_PIN_PA14 PINCTRL_PIN(PA_BASE + 14, "PA14") +#define SUNXI_PINCTRL_PIN_PA15 PINCTRL_PIN(PA_BASE + 15, "PA15") +#define SUNXI_PINCTRL_PIN_PA16 PINCTRL_PIN(PA_BASE + 16, "PA16") +#define SUNXI_PINCTRL_PIN_PA17 PINCTRL_PIN(PA_BASE + 17, "PA17") +#define SUNXI_PINCTRL_PIN_PA18 PINCTRL_PIN(PA_BASE + 18, "PA18") +#define SUNXI_PINCTRL_PIN_PA19 PINCTRL_PIN(PA_BASE + 19, "PA19") +#define SUNXI_PINCTRL_PIN_PA20 PINCTRL_PIN(PA_BASE + 20, "PA20") +#define SUNXI_PINCTRL_PIN_PA21 PINCTRL_PIN(PA_BASE + 21, "PA21") +#define SUNXI_PINCTRL_PIN_PA22 PINCTRL_PIN(PA_BASE + 22, "PA22") +#define SUNXI_PINCTRL_PIN_PA23 PINCTRL_PIN(PA_BASE + 23, "PA23") +#define SUNXI_PINCTRL_PIN_PA24 PINCTRL_PIN(PA_BASE + 24, "PA24") +#define SUNXI_PINCTRL_PIN_PA25 PINCTRL_PIN(PA_BASE + 25, "PA25") +#define SUNXI_PINCTRL_PIN_PA26 PINCTRL_PIN(PA_BASE + 26, "PA26") +#define SUNXI_PINCTRL_PIN_PA27 PINCTRL_PIN(PA_BASE + 27, "PA27") +#define SUNXI_PINCTRL_PIN_PA28 PINCTRL_PIN(PA_BASE + 28, "PA28") +#define SUNXI_PINCTRL_PIN_PA29 PINCTRL_PIN(PA_BASE + 29, "PA29") +#define SUNXI_PINCTRL_PIN_PA30 PINCTRL_PIN(PA_BASE + 30, "PA30") +#define SUNXI_PINCTRL_PIN_PA31 PINCTRL_PIN(PA_BASE + 31, "PA31") + +#define SUNXI_PINCTRL_PIN_PB0 PINCTRL_PIN(PB_BASE + 0, "PB0") +#define SUNXI_PINCTRL_PIN_PB1 PINCTRL_PIN(PB_BASE + 1, "PB1") +#define SUNXI_PINCTRL_PIN_PB2 PINCTRL_PIN(PB_BASE + 2, "PB2") +#define SUNXI_PINCTRL_PIN_PB3 PINCTRL_PIN(PB_BASE + 3, "PB3") +#define SUNXI_PINCTRL_PIN_PB4 PINCTRL_PIN(PB_BASE + 4, "PB4") +#define SUNXI_PINCTRL_PIN_PB5 PINCTRL_PIN(PB_BASE + 5, "PB5") +#define SUNXI_PINCTRL_PIN_PB6 PINCTRL_PIN(PB_BASE + 6, "PB6") +#define SUNXI_PINCTRL_PIN_PB7 PINCTRL_PIN(PB_BASE + 7, "PB7") +#define SUNXI_PINCTRL_PIN_PB8 PINCTRL_PIN(PB_BASE + 8, "PB8") +#define SUNXI_PINCTRL_PIN_PB9 PINCTRL_PIN(PB_BASE + 9, "PB9") +#define SUNXI_PINCTRL_PIN_PB10 PINCTRL_PIN(PB_BASE + 10, "PB10") +#define SUNXI_PINCTRL_PIN_PB11 PINCTRL_PIN(PB_BASE + 11, "PB11") +#define SUNXI_PINCTRL_PIN_PB12 PINCTRL_PIN(PB_BASE + 12, "PB12") +#define SUNXI_PINCTRL_PIN_PB13 PINCTRL_PIN(PB_BASE + 13, "PB13") +#define SUNXI_PINCTRL_PIN_PB14 PINCTRL_PIN(PB_BASE + 14, "PB14") +#define SUNXI_PINCTRL_PIN_PB15 PINCTRL_PIN(PB_BASE + 15, "PB15") +#define SUNXI_PINCTRL_PIN_PB16 PINCTRL_PIN(PB_BASE + 16, "PB16") +#define SUNXI_PINCTRL_PIN_PB17 PINCTRL_PIN(PB_BASE + 17, "PB17") +#define SUNXI_PINCTRL_PIN_PB18 PINCTRL_PIN(PB_BASE + 18, "PB18") +#define SUNXI_PINCTRL_PIN_PB19 PINCTRL_PIN(PB_BASE + 19, "PB19") +#define SUNXI_PINCTRL_PIN_PB20 PINCTRL_PIN(PB_BASE + 20, "PB20") +#define SUNXI_PINCTRL_PIN_PB21 PINCTRL_PIN(PB_BASE + 21, "PB21") +#define SUNXI_PINCTRL_PIN_PB22 PINCTRL_PIN(PB_BASE + 22, "PB22") +#define SUNXI_PINCTRL_PIN_PB23 PINCTRL_PIN(PB_BASE + 23, "PB23") +#define SUNXI_PINCTRL_PIN_PB24 PINCTRL_PIN(PB_BASE + 24, "PB24") +#define SUNXI_PINCTRL_PIN_PB25 PINCTRL_PIN(PB_BASE + 25, "PB25") +#define SUNXI_PINCTRL_PIN_PB26 PINCTRL_PIN(PB_BASE + 26, "PB26") +#define SUNXI_PINCTRL_PIN_PB27 PINCTRL_PIN(PB_BASE + 27, "PB27") +#define SUNXI_PINCTRL_PIN_PB28 PINCTRL_PIN(PB_BASE + 28, "PB28") +#define SUNXI_PINCTRL_PIN_PB29 PINCTRL_PIN(PB_BASE + 29, "PB29") +#define SUNXI_PINCTRL_PIN_PB30 PINCTRL_PIN(PB_BASE + 30, "PB30") +#define SUNXI_PINCTRL_PIN_PB31 PINCTRL_PIN(PB_BASE + 31, "PB31") + +#define SUNXI_PINCTRL_PIN_PC0 PINCTRL_PIN(PC_BASE + 0, "PC0") +#define SUNXI_PINCTRL_PIN_PC1 PINCTRL_PIN(PC_BASE + 1, "PC1") +#define SUNXI_PINCTRL_PIN_PC2 PINCTRL_PIN(PC_BASE + 2, "PC2") +#define SUNXI_PINCTRL_PIN_PC3 PINCTRL_PIN(PC_BASE + 3, "PC3") +#define SUNXI_PINCTRL_PIN_PC4 PINCTRL_PIN(PC_BASE + 4, "PC4") +#define SUNXI_PINCTRL_PIN_PC5 PINCTRL_PIN(PC_BASE + 5, "PC5") +#define SUNXI_PINCTRL_PIN_PC6 PINCTRL_PIN(PC_BASE + 6, "PC6") +#define SUNXI_PINCTRL_PIN_PC7 PINCTRL_PIN(PC_BASE + 7, "PC7") +#define SUNXI_PINCTRL_PIN_PC8 PINCTRL_PIN(PC_BASE + 8, "PC8") +#define SUNXI_PINCTRL_PIN_PC9 PINCTRL_PIN(PC_BASE + 9, "PC9") +#define SUNXI_PINCTRL_PIN_PC10 PINCTRL_PIN(PC_BASE + 10, "PC10") +#define SUNXI_PINCTRL_PIN_PC11 PINCTRL_PIN(PC_BASE + 11, "PC11") +#define SUNXI_PINCTRL_PIN_PC12 PINCTRL_PIN(PC_BASE + 12, "PC12") +#define SUNXI_PINCTRL_PIN_PC13 PINCTRL_PIN(PC_BASE + 13, "PC13") +#define SUNXI_PINCTRL_PIN_PC14 PINCTRL_PIN(PC_BASE + 14, "PC14") +#define SUNXI_PINCTRL_PIN_PC15 PINCTRL_PIN(PC_BASE + 15, "PC15") +#define SUNXI_PINCTRL_PIN_PC16 PINCTRL_PIN(PC_BASE + 16, "PC16") +#define SUNXI_PINCTRL_PIN_PC17 PINCTRL_PIN(PC_BASE + 17, "PC17") +#define SUNXI_PINCTRL_PIN_PC18 PINCTRL_PIN(PC_BASE + 18, "PC18") +#define SUNXI_PINCTRL_PIN_PC19 PINCTRL_PIN(PC_BASE + 19, "PC19") +#define SUNXI_PINCTRL_PIN_PC20 PINCTRL_PIN(PC_BASE + 20, "PC20") +#define SUNXI_PINCTRL_PIN_PC21 PINCTRL_PIN(PC_BASE + 21, "PC21") +#define SUNXI_PINCTRL_PIN_PC22 PINCTRL_PIN(PC_BASE + 22, "PC22") +#define SUNXI_PINCTRL_PIN_PC23 PINCTRL_PIN(PC_BASE + 23, "PC23") +#define SUNXI_PINCTRL_PIN_PC24 PINCTRL_PIN(PC_BASE + 24, "PC24") +#define SUNXI_PINCTRL_PIN_PC25 PINCTRL_PIN(PC_BASE + 25, "PC25") +#define SUNXI_PINCTRL_PIN_PC26 PINCTRL_PIN(PC_BASE + 26, "PC26") +#define SUNXI_PINCTRL_PIN_PC27 PINCTRL_PIN(PC_BASE + 27, "PC27") +#define SUNXI_PINCTRL_PIN_PC28 PINCTRL_PIN(PC_BASE + 28, "PC28") +#define SUNXI_PINCTRL_PIN_PC29 PINCTRL_PIN(PC_BASE + 29, "PC29") +#define SUNXI_PINCTRL_PIN_PC30 PINCTRL_PIN(PC_BASE + 30, "PC30") +#define SUNXI_PINCTRL_PIN_PC31 PINCTRL_PIN(PC_BASE + 31, "PC31") + +#define SUNXI_PINCTRL_PIN_PD0 PINCTRL_PIN(PD_BASE + 0, "PD0") +#define SUNXI_PINCTRL_PIN_PD1 PINCTRL_PIN(PD_BASE + 1, "PD1") +#define SUNXI_PINCTRL_PIN_PD2 PINCTRL_PIN(PD_BASE + 2, "PD2") +#define SUNXI_PINCTRL_PIN_PD3 PINCTRL_PIN(PD_BASE + 3, "PD3") +#define SUNXI_PINCTRL_PIN_PD4 PINCTRL_PIN(PD_BASE + 4, "PD4") +#define SUNXI_PINCTRL_PIN_PD5 PINCTRL_PIN(PD_BASE + 5, "PD5") +#define SUNXI_PINCTRL_PIN_PD6 PINCTRL_PIN(PD_BASE + 6, "PD6") +#define SUNXI_PINCTRL_PIN_PD7 PINCTRL_PIN(PD_BASE + 7, "PD7") +#define SUNXI_PINCTRL_PIN_PD8 PINCTRL_PIN(PD_BASE + 8, "PD8") +#define SUNXI_PINCTRL_PIN_PD9 PINCTRL_PIN(PD_BASE + 9, "PD9") +#define SUNXI_PINCTRL_PIN_PD10 PINCTRL_PIN(PD_BASE + 10, "PD10") +#define SUNXI_PINCTRL_PIN_PD11 PINCTRL_PIN(PD_BASE + 11, "PD11") +#define SUNXI_PINCTRL_PIN_PD12 PINCTRL_PIN(PD_BASE + 12, "PD12") +#define SUNXI_PINCTRL_PIN_PD13 PINCTRL_PIN(PD_BASE + 13, "PD13") +#define SUNXI_PINCTRL_PIN_PD14 PINCTRL_PIN(PD_BASE + 14, "PD14") +#define SUNXI_PINCTRL_PIN_PD15 PINCTRL_PIN(PD_BASE + 15, "PD15") +#define SUNXI_PINCTRL_PIN_PD16 PINCTRL_PIN(PD_BASE + 16, "PD16") +#define SUNXI_PINCTRL_PIN_PD17 PINCTRL_PIN(PD_BASE + 17, "PD17") +#define SUNXI_PINCTRL_PIN_PD18 PINCTRL_PIN(PD_BASE + 18, "PD18") +#define SUNXI_PINCTRL_PIN_PD19 PINCTRL_PIN(PD_BASE + 19, "PD19") +#define SUNXI_PINCTRL_PIN_PD20 PINCTRL_PIN(PD_BASE + 20, "PD20") +#define SUNXI_PINCTRL_PIN_PD21 PINCTRL_PIN(PD_BASE + 21, "PD21") +#define SUNXI_PINCTRL_PIN_PD22 PINCTRL_PIN(PD_BASE + 22, "PD22") +#define SUNXI_PINCTRL_PIN_PD23 PINCTRL_PIN(PD_BASE + 23, "PD23") +#define SUNXI_PINCTRL_PIN_PD24 PINCTRL_PIN(PD_BASE + 24, "PD24") +#define SUNXI_PINCTRL_PIN_PD25 PINCTRL_PIN(PD_BASE + 25, "PD25") +#define SUNXI_PINCTRL_PIN_PD26 PINCTRL_PIN(PD_BASE + 26, "PD26") +#define SUNXI_PINCTRL_PIN_PD27 PINCTRL_PIN(PD_BASE + 27, "PD27") +#define SUNXI_PINCTRL_PIN_PD28 PINCTRL_PIN(PD_BASE + 28, "PD28") +#define SUNXI_PINCTRL_PIN_PD29 PINCTRL_PIN(PD_BASE + 29, "PD29") +#define SUNXI_PINCTRL_PIN_PD30 PINCTRL_PIN(PD_BASE + 30, "PD30") +#define SUNXI_PINCTRL_PIN_PD31 PINCTRL_PIN(PD_BASE + 31, "PD31") + +#define SUNXI_PINCTRL_PIN_PE0 PINCTRL_PIN(PE_BASE + 0, "PE0") +#define SUNXI_PINCTRL_PIN_PE1 PINCTRL_PIN(PE_BASE + 1, "PE1") +#define SUNXI_PINCTRL_PIN_PE2 PINCTRL_PIN(PE_BASE + 2, "PE2") +#define SUNXI_PINCTRL_PIN_PE3 PINCTRL_PIN(PE_BASE + 3, "PE3") +#define SUNXI_PINCTRL_PIN_PE4 PINCTRL_PIN(PE_BASE + 4, "PE4") +#define SUNXI_PINCTRL_PIN_PE5 PINCTRL_PIN(PE_BASE + 5, "PE5") +#define SUNXI_PINCTRL_PIN_PE6 PINCTRL_PIN(PE_BASE + 6, "PE6") +#define SUNXI_PINCTRL_PIN_PE7 PINCTRL_PIN(PE_BASE + 7, "PE7") +#define SUNXI_PINCTRL_PIN_PE8 PINCTRL_PIN(PE_BASE + 8, "PE8") +#define SUNXI_PINCTRL_PIN_PE9 PINCTRL_PIN(PE_BASE + 9, "PE9") +#define SUNXI_PINCTRL_PIN_PE10 PINCTRL_PIN(PE_BASE + 10, "PE10") +#define SUNXI_PINCTRL_PIN_PE11 PINCTRL_PIN(PE_BASE + 11, "PE11") +#define SUNXI_PINCTRL_PIN_PE12 PINCTRL_PIN(PE_BASE + 12, "PE12") +#define SUNXI_PINCTRL_PIN_PE13 PINCTRL_PIN(PE_BASE + 13, "PE13") +#define SUNXI_PINCTRL_PIN_PE14 PINCTRL_PIN(PE_BASE + 14, "PE14") +#define SUNXI_PINCTRL_PIN_PE15 PINCTRL_PIN(PE_BASE + 15, "PE15") +#define SUNXI_PINCTRL_PIN_PE16 PINCTRL_PIN(PE_BASE + 16, "PE16") +#define SUNXI_PINCTRL_PIN_PE17 PINCTRL_PIN(PE_BASE + 17, "PE17") +#define SUNXI_PINCTRL_PIN_PE18 PINCTRL_PIN(PE_BASE + 18, "PE18") +#define SUNXI_PINCTRL_PIN_PE19 PINCTRL_PIN(PE_BASE + 19, "PE19") +#define SUNXI_PINCTRL_PIN_PE20 PINCTRL_PIN(PE_BASE + 20, "PE20") +#define SUNXI_PINCTRL_PIN_PE21 PINCTRL_PIN(PE_BASE + 21, "PE21") +#define SUNXI_PINCTRL_PIN_PE22 PINCTRL_PIN(PE_BASE + 22, "PE22") +#define SUNXI_PINCTRL_PIN_PE23 PINCTRL_PIN(PE_BASE + 23, "PE23") +#define SUNXI_PINCTRL_PIN_PE24 PINCTRL_PIN(PE_BASE + 24, "PE24") +#define SUNXI_PINCTRL_PIN_PE25 PINCTRL_PIN(PE_BASE + 25, "PE25") +#define SUNXI_PINCTRL_PIN_PE26 PINCTRL_PIN(PE_BASE + 26, "PE26") +#define SUNXI_PINCTRL_PIN_PE27 PINCTRL_PIN(PE_BASE + 27, "PE27") +#define SUNXI_PINCTRL_PIN_PE28 PINCTRL_PIN(PE_BASE + 28, "PE28") +#define SUNXI_PINCTRL_PIN_PE29 PINCTRL_PIN(PE_BASE + 29, "PE29") +#define SUNXI_PINCTRL_PIN_PE30 PINCTRL_PIN(PE_BASE + 30, "PE30") +#define SUNXI_PINCTRL_PIN_PE31 PINCTRL_PIN(PE_BASE + 31, "PE31") + +#define SUNXI_PINCTRL_PIN_PF0 PINCTRL_PIN(PF_BASE + 0, "PF0") +#define SUNXI_PINCTRL_PIN_PF1 PINCTRL_PIN(PF_BASE + 1, "PF1") +#define SUNXI_PINCTRL_PIN_PF2 PINCTRL_PIN(PF_BASE + 2, "PF2") +#define SUNXI_PINCTRL_PIN_PF3 PINCTRL_PIN(PF_BASE + 3, "PF3") +#define SUNXI_PINCTRL_PIN_PF4 PINCTRL_PIN(PF_BASE + 4, "PF4") +#define SUNXI_PINCTRL_PIN_PF5 PINCTRL_PIN(PF_BASE + 5, "PF5") +#define SUNXI_PINCTRL_PIN_PF6 PINCTRL_PIN(PF_BASE + 6, "PF6") +#define SUNXI_PINCTRL_PIN_PF7 PINCTRL_PIN(PF_BASE + 7, "PF7") +#define SUNXI_PINCTRL_PIN_PF8 PINCTRL_PIN(PF_BASE + 8, "PF8") +#define SUNXI_PINCTRL_PIN_PF9 PINCTRL_PIN(PF_BASE + 9, "PF9") +#define SUNXI_PINCTRL_PIN_PF10 PINCTRL_PIN(PF_BASE + 10, "PF10") +#define SUNXI_PINCTRL_PIN_PF11 PINCTRL_PIN(PF_BASE + 11, "PF11") +#define SUNXI_PINCTRL_PIN_PF12 PINCTRL_PIN(PF_BASE + 12, "PF12") +#define SUNXI_PINCTRL_PIN_PF13 PINCTRL_PIN(PF_BASE + 13, "PF13") +#define SUNXI_PINCTRL_PIN_PF14 PINCTRL_PIN(PF_BASE + 14, "PF14") +#define SUNXI_PINCTRL_PIN_PF15 PINCTRL_PIN(PF_BASE + 15, "PF15") +#define SUNXI_PINCTRL_PIN_PF16 PINCTRL_PIN(PF_BASE + 16, "PF16") +#define SUNXI_PINCTRL_PIN_PF17 PINCTRL_PIN(PF_BASE + 17, "PF17") +#define SUNXI_PINCTRL_PIN_PF18 PINCTRL_PIN(PF_BASE + 18, "PF18") +#define SUNXI_PINCTRL_PIN_PF19 PINCTRL_PIN(PF_BASE + 19, "PF19") +#define SUNXI_PINCTRL_PIN_PF20 PINCTRL_PIN(PF_BASE + 20, "PF20") +#define SUNXI_PINCTRL_PIN_PF21 PINCTRL_PIN(PF_BASE + 21, "PF21") +#define SUNXI_PINCTRL_PIN_PF22 PINCTRL_PIN(PF_BASE + 22, "PF22") +#define SUNXI_PINCTRL_PIN_PF23 PINCTRL_PIN(PF_BASE + 23, "PF23") +#define SUNXI_PINCTRL_PIN_PF24 PINCTRL_PIN(PF_BASE + 24, "PF24") +#define SUNXI_PINCTRL_PIN_PF25 PINCTRL_PIN(PF_BASE + 25, "PF25") +#define SUNXI_PINCTRL_PIN_PF26 PINCTRL_PIN(PF_BASE + 26, "PF26") +#define SUNXI_PINCTRL_PIN_PF27 PINCTRL_PIN(PF_BASE + 27, "PF27") +#define SUNXI_PINCTRL_PIN_PF28 PINCTRL_PIN(PF_BASE + 28, "PF28") +#define SUNXI_PINCTRL_PIN_PF29 PINCTRL_PIN(PF_BASE + 29, "PF29") +#define SUNXI_PINCTRL_PIN_PF30 PINCTRL_PIN(PF_BASE + 30, "PF30") +#define SUNXI_PINCTRL_PIN_PF31 PINCTRL_PIN(PF_BASE + 31, "PF31") + +#define SUNXI_PINCTRL_PIN_PG0 PINCTRL_PIN(PG_BASE + 0, "PG0") +#define SUNXI_PINCTRL_PIN_PG1 PINCTRL_PIN(PG_BASE + 1, "PG1") +#define SUNXI_PINCTRL_PIN_PG2 PINCTRL_PIN(PG_BASE + 2, "PG2") +#define SUNXI_PINCTRL_PIN_PG3 PINCTRL_PIN(PG_BASE + 3, "PG3") +#define SUNXI_PINCTRL_PIN_PG4 PINCTRL_PIN(PG_BASE + 4, "PG4") +#define SUNXI_PINCTRL_PIN_PG5 PINCTRL_PIN(PG_BASE + 5, "PG5") +#define SUNXI_PINCTRL_PIN_PG6 PINCTRL_PIN(PG_BASE + 6, "PG6") +#define SUNXI_PINCTRL_PIN_PG7 PINCTRL_PIN(PG_BASE + 7, "PG7") +#define SUNXI_PINCTRL_PIN_PG8 PINCTRL_PIN(PG_BASE + 8, "PG8") +#define SUNXI_PINCTRL_PIN_PG9 PINCTRL_PIN(PG_BASE + 9, "PG9") +#define SUNXI_PINCTRL_PIN_PG10 PINCTRL_PIN(PG_BASE + 10, "PG10") +#define SUNXI_PINCTRL_PIN_PG11 PINCTRL_PIN(PG_BASE + 11, "PG11") +#define SUNXI_PINCTRL_PIN_PG12 PINCTRL_PIN(PG_BASE + 12, "PG12") +#define SUNXI_PINCTRL_PIN_PG13 PINCTRL_PIN(PG_BASE + 13, "PG13") +#define SUNXI_PINCTRL_PIN_PG14 PINCTRL_PIN(PG_BASE + 14, "PG14") +#define SUNXI_PINCTRL_PIN_PG15 PINCTRL_PIN(PG_BASE + 15, "PG15") +#define SUNXI_PINCTRL_PIN_PG16 PINCTRL_PIN(PG_BASE + 16, "PG16") +#define SUNXI_PINCTRL_PIN_PG17 PINCTRL_PIN(PG_BASE + 17, "PG17") +#define SUNXI_PINCTRL_PIN_PG18 PINCTRL_PIN(PG_BASE + 18, "PG18") +#define SUNXI_PINCTRL_PIN_PG19 PINCTRL_PIN(PG_BASE + 19, "PG19") +#define SUNXI_PINCTRL_PIN_PG20 PINCTRL_PIN(PG_BASE + 20, "PG20") +#define SUNXI_PINCTRL_PIN_PG21 PINCTRL_PIN(PG_BASE + 21, "PG21") +#define SUNXI_PINCTRL_PIN_PG22 PINCTRL_PIN(PG_BASE + 22, "PG22") +#define SUNXI_PINCTRL_PIN_PG23 PINCTRL_PIN(PG_BASE + 23, "PG23") +#define SUNXI_PINCTRL_PIN_PG24 PINCTRL_PIN(PG_BASE + 24, "PG24") +#define SUNXI_PINCTRL_PIN_PG25 PINCTRL_PIN(PG_BASE + 25, "PG25") +#define SUNXI_PINCTRL_PIN_PG26 PINCTRL_PIN(PG_BASE + 26, "PG26") +#define SUNXI_PINCTRL_PIN_PG27 PINCTRL_PIN(PG_BASE + 27, "PG27") +#define SUNXI_PINCTRL_PIN_PG28 PINCTRL_PIN(PG_BASE + 28, "PG28") +#define SUNXI_PINCTRL_PIN_PG29 PINCTRL_PIN(PG_BASE + 29, "PG29") +#define SUNXI_PINCTRL_PIN_PG30 PINCTRL_PIN(PG_BASE + 30, "PG30") +#define SUNXI_PINCTRL_PIN_PG31 PINCTRL_PIN(PG_BASE + 31, "PG31") + +#define BANK_MEM_SIZE 0x24 +#define MUX_REGS_OFFSET 0x0 +#define DLEVEL_REGS_OFFSET 0x14 +#define PULL_REGS_OFFSET 0x1c + +#define PINS_PER_BANK 32 +#define MUX_PINS_PER_REG 8 +#define MUX_PINS_BITS 4 +#define MUX_PINS_MASK 0x0f +#define DLEVEL_PINS_PER_REG 16 +#define DLEVEL_PINS_BITS 2 +#define DLEVEL_PINS_MASK 0x03 +#define PULL_PINS_PER_REG 16 +#define PULL_PINS_BITS 2 +#define PULL_PINS_MASK 0x03 + +struct sunxi_desc_function { + const char *name; + u8 muxval; +}; + +struct sunxi_desc_pin { + struct pinctrl_pin_desc pin; + struct sunxi_desc_function *functions; +}; + +struct sunxi_pinctrl_desc { + const struct sunxi_desc_pin *pins; + int npins; +}; + +struct sunxi_pinctrl_function { + const char *name; + const char **groups; + unsigned ngroups; +}; + +struct sunxi_pinctrl_group { + const char *name; + unsigned long config; + unsigned pin; +}; + +struct sunxi_pinctrl { + void __iomem *membase; + struct sunxi_pinctrl_desc *desc; + struct device *dev; + struct sunxi_pinctrl_function *functions; + unsigned nfunctions; + struct sunxi_pinctrl_group *groups; + unsigned ngroups; + struct pinctrl_dev *pctl_dev; +}; + +#define SUNXI_PIN(_pin, ...) \ + { \ + .pin = _pin, \ + .functions = (struct sunxi_desc_function[]){ \ + __VA_ARGS__, { } }, \ + } + +#define SUNXI_FUNCTION(_val, _name) \ + { \ + .name = _name, \ + .muxval = _val, \ + } + + +/* + * The sunXi PIO registers are organized as is: + * 0x00 - 0x0c Muxing values. + * 8 pins per register, each pin having a 4bits value + * 0x10 Pin values + * 32 bits per register, each pin corresponding to one bit + * 0x14 - 0x18 Drive level + * 16 pins per register, each pin having a 2bits value + * 0x1c - 0x20 Pull-Up values + * 16 pins per register, each pin having a 2bits value + * + * This is for the first bank. Each bank will have the same layout, + * with an offset being a multiple of 0x24. + * + * The following functions calculate from the pin number the register + * and the bit offset that we should access. + */ +static inline u32 sunxi_mux_reg(u16 pin) +{ + u8 bank = pin / PINS_PER_BANK; + u32 offset = bank * BANK_MEM_SIZE; + offset += MUX_REGS_OFFSET; + offset += pin % PINS_PER_BANK / MUX_PINS_PER_REG * 0x04; + return round_down(offset, 4); +} + +static inline u32 sunxi_mux_offset(u16 pin) +{ + u32 pin_num = pin % MUX_PINS_PER_REG; + return pin_num * MUX_PINS_BITS; +} + +static inline u32 sunxi_dlevel_reg(u16 pin) +{ + u8 bank = pin / PINS_PER_BANK; + u32 offset = bank * BANK_MEM_SIZE; + offset += DLEVEL_REGS_OFFSET; + offset += pin % PINS_PER_BANK / DLEVEL_PINS_PER_REG * 0x04; + return round_down(offset, 4); +} + +static inline u32 sunxi_dlevel_offset(u16 pin) +{ + u32 pin_num = pin % DLEVEL_PINS_PER_REG; + return pin_num * DLEVEL_PINS_BITS; +} + +static inline u32 sunxi_pull_reg(u16 pin) +{ + u8 bank = pin / PINS_PER_BANK; + u32 offset = bank * BANK_MEM_SIZE; + offset += PULL_REGS_OFFSET; + offset += pin % PINS_PER_BANK / PULL_PINS_PER_REG * 0x04; + return round_down(offset, 4); +} + +static inline u32 sunxi_pull_offset(u16 pin) +{ + u32 pin_num = pin % PULL_PINS_PER_REG; + return pin_num * PULL_PINS_BITS; +} + +#endif /* __PINCTRL_SUNXI_H */ -- cgit v1.2.3-59-g8ed1b From 168e06ae26dd327df347e70b7244218ff1766a1f Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 9 Jan 2013 20:30:26 -0500 Subject: drivers/net: delete old parallel port de600/de620 drivers The parallel port is largely replaced by USB, and even in the day where these drivers were current, the documented speed was less than 100kB/s. Let us not pretend that anyone cares about these drivers anymore, or worse - pretend that anyone is using them on a modern kernel. As a side bonus, this is the end of legacy parallel port ethernet, so we get to drop the whole chunk relating to that in the legacy Space.c file containing the non-PCI unified probe dispatch. Signed-off-by: Paul Gortmaker --- Documentation/networking/DLINK.txt | 203 -------- drivers/net/Space.c | 13 +- drivers/net/ethernet/dlink/Kconfig | 32 +- drivers/net/ethernet/dlink/Makefile | 2 - drivers/net/ethernet/dlink/de600.c | 529 ------------------- drivers/net/ethernet/dlink/de600.h | 168 ------ drivers/net/ethernet/dlink/de620.c | 987 ------------------------------------ drivers/net/ethernet/dlink/de620.h | 117 ----- 8 files changed, 2 insertions(+), 2049 deletions(-) delete mode 100644 Documentation/networking/DLINK.txt delete mode 100644 drivers/net/ethernet/dlink/de600.c delete mode 100644 drivers/net/ethernet/dlink/de600.h delete mode 100644 drivers/net/ethernet/dlink/de620.c delete mode 100644 drivers/net/ethernet/dlink/de620.h (limited to 'Documentation') diff --git a/Documentation/networking/DLINK.txt b/Documentation/networking/DLINK.txt deleted file mode 100644 index 55d24433d151..000000000000 --- a/Documentation/networking/DLINK.txt +++ /dev/null @@ -1,203 +0,0 @@ -Released 1994-06-13 - - - CONTENTS: - - 1. Introduction. - 2. License. - 3. Files in this release. - 4. Installation. - 5. Problems and tuning. - 6. Using the drivers with earlier releases. - 7. Acknowledgments. - - - 1. INTRODUCTION. - - This is a set of Ethernet drivers for the D-Link DE-600/DE-620 - pocket adapters, for the parallel port on a Linux based machine. - Some adapter "clones" will also work. Xircom is _not_ a clone... - These drivers _can_ be used as loadable modules, - and were developed for use on Linux 1.1.13 and above. - For use on Linux 1.0.X, or earlier releases, see below. - - I have used these drivers for NFS, ftp, telnet and X-clients on - remote machines. Transmissions with ftp seems to work as - good as can be expected (i.e. > 80k bytes/sec) from a - parallel port...:-) Receive speeds will be about 60-80% of this. - Depending on your machine, somewhat higher speeds can be achieved. - - All comments/fixes to Bjorn Ekwall (bj0rn@blox.se). - - - 2. LICENSE. - - 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, 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. - - - 3. FILES IN THIS RELEASE. - - README.DLINK This file. - de600.c The Source (may it be with You :-) for the DE-600 - de620.c ditto for the DE-620 - de620.h Macros for de620.c - - If you are upgrading from the d-link tar release, there will - also be a "dlink-patches" file that will patch Linux 1.1.18: - linux/drivers/net/Makefile - linux/drivers/net/CONFIG - linux/drivers/net/MODULES - linux/drivers/net/Space.c - linux/config.in - Apply the patch by: - "cd /usr/src; patch -p0 < linux/drivers/net/dlink-patches" - The old source, "linux/drivers/net/d_link.c", can be removed. - - - 4. INSTALLATION. - - o Get the latest net binaries, according to current net.wisdom. - - o Read the NET-2 and Ethernet HOWTOs and modify your setup. - - o If your parallel port has a strange address or irq, - modify "linux/drivers/net/CONFIG" accordingly, or adjust - the parameters in the "tuning" section in the sources. - - If you are going to use the drivers as loadable modules, do _not_ - enable them while doing "make config", but instead make sure that - the drivers are included in "linux/drivers/net/MODULES". - - If you are _not_ going to use the driver(s) as loadable modules, - but instead have them included in the kernel, remember to enable - the drivers while doing "make config". - - o To include networking and DE600/DE620 support in your kernel: - # cd /linux - (as modules:) - # make config (answer yes on CONFIG_NET and CONFIG_INET) - (else included in the kernel:) - # make config (answer yes on CONFIG _NET, _INET and _DE600 or _DE620) - # make clean - # make zImage (or whatever magic you usually do) - - o I use lilo to boot multiple kernels, so that I at least - can have one working kernel :-). If you do too, append - these lines to /etc/lilo/config: - - image = /linux/zImage - label = newlinux - root = /dev/hda2 (or whatever YOU have...) - - # /etc/lilo/install - - o Do "sync" and reboot the new kernel with a D-Link - DE-600/DE-620 pocket adapter connected. - - o The adapter can be configured with ifconfig eth? - where the actual number is decided by the kernel - when the drivers are initialized. - - - 5. "PROBLEMS" AND TUNING, - - o If you see error messages from the driver, and if the traffic - stops on the adapter, try to do "ifconfig" and "route" once - more, just as in "rc.inet1". This should take care of most - problems, including effects from power loss, or adapters that - aren't connected to the printer port in some way or another. - You can somewhat change the behaviour by enabling/disabling - the macro SHUTDOWN_WHEN_LOST in the "tuning" section. - For the DE-600 there is another macro, CHECK_LOST_DE600, - that you might want to read about in the "tuning" section. - - o Some machines have trouble handling the parallel port and - the adapter at high speed. If you experience problems: - - DE-600: - - The adapter is not recognized at boot, i.e. an Ethernet - address of 00:80:c8:... is not shown, try to add another - "; SLOW_DOWN_IO" - at DE600_SLOW_DOWN in the "tuning" section. As a last resort, - uncomment: "#define REALLY_SLOW_IO" (see for hints). - - - You experience "timeout" messages: first try to add another - "; SLOW_DOWN_IO" - at DE600_SLOW_DOWN in the "tuning" section, _then_ try to - increase the value (original value: 5) at - "if (tickssofar < 5)" near line 422. - - DE-620: - - Your parallel port might be "sluggish". To cater for - this, there are the macros LOWSPEED and READ_DELAY/WRITE_DELAY - in the "tuning" section. Your first step should be to enable - LOWSPEED, and after that you can "tune" the XXX_DELAY values. - - o If the adapter _is_ recognized at boot but you get messages - about "Network Unreachable", then the problem is probably - _not_ with the driver. Check your net configuration instead - (ifconfig and route) in "rc.inet1". - - o There is some rudimentary support for debugging, look at - the source. Use "-DDE600_DEBUG=3" or "-DDE620_DEBUG=3" - when compiling, or include it in "linux/drivers/net/CONFIG". - IF YOU HAVE PROBLEMS YOU CAN'T SOLVE: PLEASE COMPILE THE DRIVER - WITH DEBUGGING ENABLED, AND SEND ME THE RESULTING OUTPUT! - - - 6. USING THE DRIVERS WITH EARLIER RELEASES. - - The later 1.1.X releases of the Linux kernel include some - changes in the networking layer (a.k.a. NET3). This affects - these drivers in a few places. The hints that follow are - _not_ tested by me, since I don't have the disk space to keep - all releases on-line. - Known needed changes to date: - - release patchfile: some patches will fail, but they should - be easy to apply "by hand", since they are trivial. - (Space.c: d_link_init() is now called de600_probe()) - - de600.c: change "mark_bh(NET_BH)" to "mark_bh(INET_BH)". - - de620.c: (maybe) change the code around "netif_rx(skb);" to be - similar to the code around "dev_rint(...)" in de600.c - - - 7. ACKNOWLEDGMENTS. - - These drivers wouldn't have been done without the base - (and support) from Ross Biro, and D-Link Systems Inc. - The driver relies upon GPL-ed source from D-Link Systems Inc. - and from Russel Nelson at Crynwr Software . - - Additional input also from: - Donald Becker , Alan Cox - and Fred N. van Kempen - - DE-600 alpha release primary victim^H^H^H^H^H^Htester: - - Erik Proper . - Good input also from several users, most notably - - Mark Burton . - - DE-620 alpha release victims^H^H^H^H^H^H^Htesters: - - J. Joshua Kopper - - Olav Kvittem - - Germano Caronni - - Jeremy Fitzhardinge - - - Happy hacking! - - Bjorn Ekwall == bj0rn@blox.se diff --git a/drivers/net/Space.c b/drivers/net/Space.c index 64cd86efab96..d48712751357 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -73,9 +73,6 @@ extern struct net_device *mac89x0_probe(int unit); extern struct net_device *cops_probe(int unit); extern struct net_device *ltpc_probe(void); -/* Detachable devices ("pocket adaptors") */ -extern struct net_device *de620_probe(int unit); - /* Fibre Channel adapters */ extern int iph5526_probe(struct net_device *dev); @@ -185,13 +182,6 @@ static struct devprobe2 isa_probes[] __initdata = { {NULL, 0}, }; -static struct devprobe2 parport_probes[] __initdata = { -#ifdef CONFIG_DE620 /* D-Link DE-620 adapter */ - {de620_probe, 0}, -#endif - {NULL, 0}, -}; - static struct devprobe2 m68k_probes[] __initdata = { #ifdef CONFIG_ATARILANCE /* Lance-based Atari ethernet boards */ {atarilance_probe, 0}, @@ -230,8 +220,7 @@ static void __init ethif_probe2(int unit) return; (void)( probe_list2(unit, m68k_probes, base_addr == 0) && - probe_list2(unit, isa_probes, base_addr == 0) && - probe_list2(unit, parport_probes, base_addr == 0)); + probe_list2(unit, isa_probes, base_addr == 0)); } /* Statically configured drivers -- order matters here. */ diff --git a/drivers/net/ethernet/dlink/Kconfig b/drivers/net/ethernet/dlink/Kconfig index b5afe218c31b..ee26ce78e270 100644 --- a/drivers/net/ethernet/dlink/Kconfig +++ b/drivers/net/ethernet/dlink/Kconfig @@ -5,7 +5,7 @@ config NET_VENDOR_DLINK bool "D-Link devices" default y - depends on PCI || PARPORT + depends on PCI ---help--- If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available from @@ -18,36 +18,6 @@ config NET_VENDOR_DLINK if NET_VENDOR_DLINK -config DE600 - tristate "D-Link DE600 pocket adapter support" - depends on PARPORT - ---help--- - This is a network (Ethernet) device which attaches to your parallel - port. Read as well as the - Ethernet-HOWTO, available from - , if you want to use - this. It is possible to have several devices share a single parallel - port and it is safe to compile the corresponding drivers into the - kernel. - - To compile this driver as a module, choose M here: the module - will be called de600. - -config DE620 - tristate "D-Link DE620 pocket adapter support" - depends on PARPORT - ---help--- - This is a network (Ethernet) device which attaches to your parallel - port. Read as well as the - Ethernet-HOWTO, available from - , if you want to use - this. It is possible to have several devices share a single parallel - port and it is safe to compile the corresponding drivers into the - kernel. - - To compile this driver as a module, choose M here: the module - will be called de620. - config DL2K tristate "DL2000/TC902x-based Gigabit Ethernet support" depends on PCI diff --git a/drivers/net/ethernet/dlink/Makefile b/drivers/net/ethernet/dlink/Makefile index c705eaa4f5b2..40085f67157b 100644 --- a/drivers/net/ethernet/dlink/Makefile +++ b/drivers/net/ethernet/dlink/Makefile @@ -2,7 +2,5 @@ # Makefile for the D-Link network device drivers. # -obj-$(CONFIG_DE600) += de600.o -obj-$(CONFIG_DE620) += de620.o obj-$(CONFIG_DL2K) += dl2k.o obj-$(CONFIG_SUNDANCE) += sundance.o diff --git a/drivers/net/ethernet/dlink/de600.c b/drivers/net/ethernet/dlink/de600.c deleted file mode 100644 index 414f0eea1049..000000000000 --- a/drivers/net/ethernet/dlink/de600.c +++ /dev/null @@ -1,529 +0,0 @@ -static const char version[] = "de600.c: $Revision: 1.41-2.5 $, Bjorn Ekwall (bj0rn@blox.se)\n"; -/* - * de600.c - * - * Linux driver for the D-Link DE-600 Ethernet pocket adapter. - * - * Portions (C) Copyright 1993, 1994 by Bjorn Ekwall - * The Author may be reached as bj0rn@blox.se - * - * Based on adapter information gathered from DE600.ASM by D-Link Inc., - * as included on disk C in the v.2.11 of PC/TCP from FTP Software. - * For DE600.asm: - * Portions (C) Copyright 1990 D-Link, Inc. - * Copyright, 1988-1992, Russell Nelson, Crynwr Software - * - * Adapted to the sample network driver core for linux, - * written by: Donald Becker - * (Now at ) - * - **************************************************************/ -/* - * 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, 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. - * - **************************************************************/ - -/* Add more time here if your adapter won't work OK: */ -#define DE600_SLOW_DOWN udelay(delay_time) - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "de600.h" - -static bool check_lost = true; -module_param(check_lost, bool, 0); -MODULE_PARM_DESC(check_lost, "If set then check for unplugged de600"); - -static unsigned int delay_time = 10; -module_param(delay_time, int, 0); -MODULE_PARM_DESC(delay_time, "DE-600 deley on I/O in microseconds"); - - -/* - * D-Link driver variables: - */ - -static volatile int rx_page; - -#define TX_PAGES 2 -static volatile int tx_fifo[TX_PAGES]; -static volatile int tx_fifo_in; -static volatile int tx_fifo_out; -static volatile int free_tx_pages = TX_PAGES; -static int was_down; -static DEFINE_SPINLOCK(de600_lock); - -static inline u8 de600_read_status(struct net_device *dev) -{ - u8 status; - - outb_p(STATUS, DATA_PORT); - status = inb(STATUS_PORT); - outb_p(NULL_COMMAND | HI_NIBBLE, DATA_PORT); - - return status; -} - -static inline u8 de600_read_byte(unsigned char type, struct net_device *dev) -{ - /* dev used by macros */ - u8 lo; - outb_p((type), DATA_PORT); - lo = ((unsigned char)inb(STATUS_PORT)) >> 4; - outb_p((type) | HI_NIBBLE, DATA_PORT); - return ((unsigned char)inb(STATUS_PORT) & (unsigned char)0xf0) | lo; -} - -/* - * Open/initialize the board. This is called (in the current kernel) - * after booting when 'ifconfig name> $IP_ADDR' is run (in rc.inet1). - * - * This routine should set everything up anew at each open, even - * registers that "should" only need to be set once at boot, so that - * there is a non-reboot way to recover if something goes wrong. - */ - -static int de600_open(struct net_device *dev) -{ - unsigned long flags; - int ret = request_irq(DE600_IRQ, de600_interrupt, 0, dev->name, dev); - if (ret) { - printk(KERN_ERR "%s: unable to get IRQ %d\n", dev->name, DE600_IRQ); - return ret; - } - spin_lock_irqsave(&de600_lock, flags); - ret = adapter_init(dev); - spin_unlock_irqrestore(&de600_lock, flags); - return ret; -} - -/* - * The inverse routine to de600_open(). - */ - -static int de600_close(struct net_device *dev) -{ - select_nic(); - rx_page = 0; - de600_put_command(RESET); - de600_put_command(STOP_RESET); - de600_put_command(0); - select_prn(); - free_irq(DE600_IRQ, dev); - return 0; -} - -static inline void trigger_interrupt(struct net_device *dev) -{ - de600_put_command(FLIP_IRQ); - select_prn(); - DE600_SLOW_DOWN; - select_nic(); - de600_put_command(0); -} - -/* - * Copy a buffer to the adapter transmit page memory. - * Start sending. - */ - -static int de600_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - unsigned long flags; - int transmit_from; - int len; - int tickssofar; - u8 *buffer = skb->data; - int i; - - if (free_tx_pages <= 0) { /* Do timeouts, to avoid hangs. */ - tickssofar = jiffies - dev_trans_start(dev); - if (tickssofar < HZ/20) - return NETDEV_TX_BUSY; - /* else */ - printk(KERN_WARNING "%s: transmit timed out (%d), %s?\n", dev->name, tickssofar, "network cable problem"); - /* Restart the adapter. */ - spin_lock_irqsave(&de600_lock, flags); - if (adapter_init(dev)) { - spin_unlock_irqrestore(&de600_lock, flags); - return NETDEV_TX_BUSY; - } - spin_unlock_irqrestore(&de600_lock, flags); - } - - /* Start real output */ - pr_debug("de600_start_xmit:len=%d, page %d/%d\n", skb->len, tx_fifo_in, free_tx_pages); - - if ((len = skb->len) < RUNT) - len = RUNT; - - spin_lock_irqsave(&de600_lock, flags); - select_nic(); - tx_fifo[tx_fifo_in] = transmit_from = tx_page_adr(tx_fifo_in) - len; - tx_fifo_in = (tx_fifo_in + 1) % TX_PAGES; /* Next free tx page */ - - if(check_lost) - { - /* This costs about 40 instructions per packet... */ - de600_setup_address(NODE_ADDRESS, RW_ADDR); - de600_read_byte(READ_DATA, dev); - if (was_down || (de600_read_byte(READ_DATA, dev) != 0xde)) { - if (adapter_init(dev)) { - spin_unlock_irqrestore(&de600_lock, flags); - return NETDEV_TX_BUSY; - } - } - } - - de600_setup_address(transmit_from, RW_ADDR); - for (i = 0; i < skb->len ; ++i, ++buffer) - de600_put_byte(*buffer); - for (; i < len; ++i) - de600_put_byte(0); - - if (free_tx_pages-- == TX_PAGES) { /* No transmission going on */ - dev->trans_start = jiffies; - netif_start_queue(dev); /* allow more packets into adapter */ - /* Send page and generate a faked interrupt */ - de600_setup_address(transmit_from, TX_ADDR); - de600_put_command(TX_ENABLE); - } - else { - if (free_tx_pages) - netif_start_queue(dev); - else - netif_stop_queue(dev); - select_prn(); - } - spin_unlock_irqrestore(&de600_lock, flags); - dev_kfree_skb(skb); - return NETDEV_TX_OK; -} - -/* - * The typical workload of the driver: - * Handle the network interface interrupts. - */ - -static irqreturn_t de600_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - u8 irq_status; - int retrig = 0; - int boguscount = 0; - - spin_lock(&de600_lock); - - select_nic(); - irq_status = de600_read_status(dev); - - do { - pr_debug("de600_interrupt (%02X)\n", irq_status); - - if (irq_status & RX_GOOD) - de600_rx_intr(dev); - else if (!(irq_status & RX_BUSY)) - de600_put_command(RX_ENABLE); - - /* Any transmission in progress? */ - if (free_tx_pages < TX_PAGES) - retrig = de600_tx_intr(dev, irq_status); - else - retrig = 0; - - irq_status = de600_read_status(dev); - } while ( (irq_status & RX_GOOD) || ((++boguscount < 100) && retrig) ); - /* - * Yeah, it _looks_ like busy waiting, smells like busy waiting - * and I know it's not PC, but please, it will only occur once - * in a while and then only for a loop or so (< 1ms for sure!) - */ - - /* Enable adapter interrupts */ - select_prn(); - if (retrig) - trigger_interrupt(dev); - spin_unlock(&de600_lock); - return IRQ_HANDLED; -} - -static int de600_tx_intr(struct net_device *dev, int irq_status) -{ - /* - * Returns 1 if tx still not done - */ - - /* Check if current transmission is done yet */ - if (irq_status & TX_BUSY) - return 1; /* tx not done, try again */ - - /* else */ - /* If last transmission OK then bump fifo index */ - if (!(irq_status & TX_FAILED16)) { - tx_fifo_out = (tx_fifo_out + 1) % TX_PAGES; - ++free_tx_pages; - dev->stats.tx_packets++; - netif_wake_queue(dev); - } - - /* More to send, or resend last packet? */ - if ((free_tx_pages < TX_PAGES) || (irq_status & TX_FAILED16)) { - dev->trans_start = jiffies; - de600_setup_address(tx_fifo[tx_fifo_out], TX_ADDR); - de600_put_command(TX_ENABLE); - return 1; - } - /* else */ - - return 0; -} - -/* - * We have a good packet, get it out of the adapter. - */ -static void de600_rx_intr(struct net_device *dev) -{ - struct sk_buff *skb; - int i; - int read_from; - int size; - unsigned char *buffer; - - /* Get size of received packet */ - size = de600_read_byte(RX_LEN, dev); /* low byte */ - size += (de600_read_byte(RX_LEN, dev) << 8); /* high byte */ - size -= 4; /* Ignore trailing 4 CRC-bytes */ - - /* Tell adapter where to store next incoming packet, enable receiver */ - read_from = rx_page_adr(); - next_rx_page(); - de600_put_command(RX_ENABLE); - - if ((size < 32) || (size > 1535)) { - printk(KERN_WARNING "%s: Bogus packet size %d.\n", dev->name, size); - if (size > 10000) - adapter_init(dev); - return; - } - - skb = netdev_alloc_skb(dev, size + 2); - if (skb == NULL) { - printk("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, size); - return; - } - /* else */ - - skb_reserve(skb,2); /* Align */ - - /* 'skb->data' points to the start of sk_buff data area. */ - buffer = skb_put(skb,size); - - /* copy the packet into the buffer */ - de600_setup_address(read_from, RW_ADDR); - for (i = size; i > 0; --i, ++buffer) - *buffer = de600_read_byte(READ_DATA, dev); - - skb->protocol=eth_type_trans(skb,dev); - - netif_rx(skb); - - /* update stats */ - dev->stats.rx_packets++; /* count all receives */ - dev->stats.rx_bytes += size; /* count all received bytes */ - - /* - * If any worth-while packets have been received, netif_rx() - * will work on them when we get to the tasklets. - */ -} - -static const struct net_device_ops de600_netdev_ops = { - .ndo_open = de600_open, - .ndo_stop = de600_close, - .ndo_start_xmit = de600_start_xmit, - .ndo_change_mtu = eth_change_mtu, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - - -static struct net_device * __init de600_probe(void) -{ - int i; - struct net_device *dev; - int err; - - dev = alloc_etherdev(0); - if (!dev) - return ERR_PTR(-ENOMEM); - - - if (!request_region(DE600_IO, 3, "de600")) { - printk(KERN_WARNING "DE600: port 0x%x busy\n", DE600_IO); - err = -EBUSY; - goto out; - } - - printk(KERN_INFO "%s: D-Link DE-600 pocket adapter", dev->name); - /* Alpha testers must have the version number to report bugs. */ - pr_debug("%s", version); - - /* probe for adapter */ - err = -ENODEV; - rx_page = 0; - select_nic(); - (void)de600_read_status(dev); - de600_put_command(RESET); - de600_put_command(STOP_RESET); - if (de600_read_status(dev) & 0xf0) { - printk(": not at I/O %#3x.\n", DATA_PORT); - goto out1; - } - - /* - * Maybe we found one, - * have to check if it is a D-Link DE-600 adapter... - */ - - /* Get the adapter ethernet address from the ROM */ - de600_setup_address(NODE_ADDRESS, RW_ADDR); - for (i = 0; i < ETH_ALEN; i++) { - dev->dev_addr[i] = de600_read_byte(READ_DATA, dev); - dev->broadcast[i] = 0xff; - } - - /* Check magic code */ - if ((dev->dev_addr[1] == 0xde) && (dev->dev_addr[2] == 0x15)) { - /* OK, install real address */ - dev->dev_addr[0] = 0x00; - dev->dev_addr[1] = 0x80; - dev->dev_addr[2] = 0xc8; - dev->dev_addr[3] &= 0x0f; - dev->dev_addr[3] |= 0x70; - } else { - printk(" not identified in the printer port\n"); - goto out1; - } - - printk(", Ethernet Address: %pM\n", dev->dev_addr); - - dev->netdev_ops = &de600_netdev_ops; - - dev->flags&=~IFF_MULTICAST; - - select_prn(); - - err = register_netdev(dev); - if (err) - goto out1; - - return dev; - -out1: - release_region(DE600_IO, 3); -out: - free_netdev(dev); - return ERR_PTR(err); -} - -static int adapter_init(struct net_device *dev) -{ - int i; - - select_nic(); - rx_page = 0; /* used by RESET */ - de600_put_command(RESET); - de600_put_command(STOP_RESET); - - /* Check if it is still there... */ - /* Get the some bytes of the adapter ethernet address from the ROM */ - de600_setup_address(NODE_ADDRESS, RW_ADDR); - de600_read_byte(READ_DATA, dev); - if ((de600_read_byte(READ_DATA, dev) != 0xde) || - (de600_read_byte(READ_DATA, dev) != 0x15)) { - /* was: if (de600_read_status(dev) & 0xf0) { */ - printk("Something has happened to the DE-600! Please check it and do a new ifconfig!\n"); - /* Goodbye, cruel world... */ - dev->flags &= ~IFF_UP; - de600_close(dev); - was_down = 1; - netif_stop_queue(dev); /* Transmit busy... */ - return 1; /* failed */ - } - - if (was_down) { - printk(KERN_INFO "%s: Thanks, I feel much better now!\n", dev->name); - was_down = 0; - } - - tx_fifo_in = 0; - tx_fifo_out = 0; - free_tx_pages = TX_PAGES; - - - /* set the ether address. */ - de600_setup_address(NODE_ADDRESS, RW_ADDR); - for (i = 0; i < ETH_ALEN; i++) - de600_put_byte(dev->dev_addr[i]); - - /* where to start saving incoming packets */ - rx_page = RX_BP | RX_BASE_PAGE; - de600_setup_address(MEM_4K, RW_ADDR); - /* Enable receiver */ - de600_put_command(RX_ENABLE); - select_prn(); - - netif_start_queue(dev); - - return 0; /* OK */ -} - -static struct net_device *de600_dev; - -static int __init de600_init(void) -{ - de600_dev = de600_probe(); - if (IS_ERR(de600_dev)) - return PTR_ERR(de600_dev); - return 0; -} - -static void __exit de600_exit(void) -{ - unregister_netdev(de600_dev); - release_region(DE600_IO, 3); - free_netdev(de600_dev); -} - -module_init(de600_init); -module_exit(de600_exit); - -MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/dlink/de600.h b/drivers/net/ethernet/dlink/de600.h deleted file mode 100644 index e80ecbabcf4e..000000000000 --- a/drivers/net/ethernet/dlink/de600.h +++ /dev/null @@ -1,168 +0,0 @@ -/************************************************** - * * - * Definition of D-Link Ethernet Pocket adapter * - * * - **************************************************/ -/* - * D-Link Ethernet pocket adapter ports - */ -/* - * OK, so I'm cheating, but there are an awful lot of - * reads and writes in order to get anything in and out - * of the DE-600 with 4 bits at a time in the parallel port, - * so every saved instruction really helps :-) - */ - -#ifndef DE600_IO -#define DE600_IO 0x378 -#endif - -#define DATA_PORT (DE600_IO) -#define STATUS_PORT (DE600_IO + 1) -#define COMMAND_PORT (DE600_IO + 2) - -#ifndef DE600_IRQ -#define DE600_IRQ 7 -#endif -/* - * It really should look like this, and autoprobing as well... - * -#define DATA_PORT (dev->base_addr + 0) -#define STATUS_PORT (dev->base_addr + 1) -#define COMMAND_PORT (dev->base_addr + 2) -#define DE600_IRQ dev->irq - */ - -/* - * D-Link COMMAND_PORT commands - */ -#define SELECT_NIC 0x04 /* select Network Interface Card */ -#define SELECT_PRN 0x1c /* select Printer */ -#define NML_PRN 0xec /* normal Printer situation */ -#define IRQEN 0x10 /* enable IRQ line */ - -/* - * D-Link STATUS_PORT - */ -#define RX_BUSY 0x80 -#define RX_GOOD 0x40 -#define TX_FAILED16 0x10 -#define TX_BUSY 0x08 - -/* - * D-Link DATA_PORT commands - * command in low 4 bits - * data in high 4 bits - * select current data nibble with HI_NIBBLE bit - */ -#define WRITE_DATA 0x00 /* write memory */ -#define READ_DATA 0x01 /* read memory */ -#define STATUS 0x02 /* read status register */ -#define COMMAND 0x03 /* write command register (see COMMAND below) */ -#define NULL_COMMAND 0x04 /* null command */ -#define RX_LEN 0x05 /* read received packet length */ -#define TX_ADDR 0x06 /* set adapter transmit memory address */ -#define RW_ADDR 0x07 /* set adapter read/write memory address */ -#define HI_NIBBLE 0x08 /* read/write the high nibble of data, - or-ed with rest of command */ - -/* - * command register, accessed through DATA_PORT with low bits = COMMAND - */ -#define RX_ALL 0x01 /* PROMISCUOUS */ -#define RX_BP 0x02 /* default: BROADCAST & PHYSICAL ADDRESS */ -#define RX_MBP 0x03 /* MULTICAST, BROADCAST & PHYSICAL ADDRESS */ - -#define TX_ENABLE 0x04 /* bit 2 */ -#define RX_ENABLE 0x08 /* bit 3 */ - -#define RESET 0x80 /* set bit 7 high */ -#define STOP_RESET 0x00 /* set bit 7 low */ - -/* - * data to command register - * (high 4 bits in write to DATA_PORT) - */ -#define RX_PAGE2_SELECT 0x10 /* bit 4, only 2 pages to select */ -#define RX_BASE_PAGE 0x20 /* bit 5, always set when specifying RX_ADDR */ -#define FLIP_IRQ 0x40 /* bit 6 */ - -/* - * D-Link adapter internal memory: - * - * 0-2K 1:st transmit page (send from pointer up to 2K) - * 2-4K 2:nd transmit page (send from pointer up to 4K) - * - * 4-6K 1:st receive page (data from 4K upwards) - * 6-8K 2:nd receive page (data from 6K upwards) - * - * 8K+ Adapter ROM (contains magic code and last 3 bytes of Ethernet address) - */ -#define MEM_2K 0x0800 /* 2048 */ -#define MEM_4K 0x1000 /* 4096 */ -#define MEM_6K 0x1800 /* 6144 */ -#define NODE_ADDRESS 0x2000 /* 8192 */ - -#define RUNT 60 /* Too small Ethernet packet */ - -/************************************************** - * * - * End of definition * - * * - **************************************************/ - -/* - * Index to functions, as function prototypes. - */ -/* Routines used internally. (See "convenience macros") */ -static u8 de600_read_status(struct net_device *dev); -static u8 de600_read_byte(unsigned char type, struct net_device *dev); - -/* Put in the device structure. */ -static int de600_open(struct net_device *dev); -static int de600_close(struct net_device *dev); -static int de600_start_xmit(struct sk_buff *skb, struct net_device *dev); - -/* Dispatch from interrupts. */ -static irqreturn_t de600_interrupt(int irq, void *dev_id); -static int de600_tx_intr(struct net_device *dev, int irq_status); -static void de600_rx_intr(struct net_device *dev); - -/* Initialization */ -static void trigger_interrupt(struct net_device *dev); -static int adapter_init(struct net_device *dev); - -/* - * Convenience macros/functions for D-Link adapter - */ - -#define select_prn() outb_p(SELECT_PRN, COMMAND_PORT); DE600_SLOW_DOWN -#define select_nic() outb_p(SELECT_NIC, COMMAND_PORT); DE600_SLOW_DOWN - -/* Thanks for hints from Mark Burton */ -#define de600_put_byte(data) ( \ - outb_p(((data) << 4) | WRITE_DATA , DATA_PORT), \ - outb_p(((data) & 0xf0) | WRITE_DATA | HI_NIBBLE, DATA_PORT)) - -/* - * The first two outb_p()'s below could perhaps be deleted if there - * would be more delay in the last two. Not certain about it yet... - */ -#define de600_put_command(cmd) ( \ - outb_p(( rx_page << 4) | COMMAND , DATA_PORT), \ - outb_p(( rx_page & 0xf0) | COMMAND | HI_NIBBLE, DATA_PORT), \ - outb_p(((rx_page | cmd) << 4) | COMMAND , DATA_PORT), \ - outb_p(((rx_page | cmd) & 0xf0) | COMMAND | HI_NIBBLE, DATA_PORT)) - -#define de600_setup_address(addr,type) ( \ - outb_p((((addr) << 4) & 0xf0) | type , DATA_PORT), \ - outb_p(( (addr) & 0xf0) | type | HI_NIBBLE, DATA_PORT), \ - outb_p((((addr) >> 4) & 0xf0) | type , DATA_PORT), \ - outb_p((((addr) >> 8) & 0xf0) | type | HI_NIBBLE, DATA_PORT)) - -#define rx_page_adr() ((rx_page & RX_PAGE2_SELECT)?(MEM_6K):(MEM_4K)) - -/* Flip bit, only 2 pages */ -#define next_rx_page() (rx_page ^= RX_PAGE2_SELECT) - -#define tx_page_adr(a) (((a) + 1) * MEM_2K) diff --git a/drivers/net/ethernet/dlink/de620.c b/drivers/net/ethernet/dlink/de620.c deleted file mode 100644 index 2e2bc60ee811..000000000000 --- a/drivers/net/ethernet/dlink/de620.c +++ /dev/null @@ -1,987 +0,0 @@ -/* - * de620.c $Revision: 1.40 $ BETA - * - * - * Linux driver for the D-Link DE-620 Ethernet pocket adapter. - * - * Portions (C) Copyright 1993, 1994 by Bjorn Ekwall - * - * Based on adapter information gathered from DOS packetdriver - * sources from D-Link Inc: (Special thanks to Henry Ngai of D-Link.) - * Portions (C) Copyright D-Link SYSTEM Inc. 1991, 1992 - * Copyright, 1988, Russell Nelson, Crynwr Software - * - * Adapted to the sample network driver core for linux, - * written by: Donald Becker - * (Now at ) - * - * Valuable assistance from: - * J. Joshua Kopper - * Olav Kvittem - * Germano Caronni - * Jeremy Fitzhardinge - * - *****************************************************************************/ -/* - * 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, 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. - * - *****************************************************************************/ -static const char version[] = - "de620.c: $Revision: 1.40 $, Bjorn Ekwall \n"; - -/*********************************************************************** - * - * "Tuning" section. - * - * Compile-time options: (see below for descriptions) - * -DDE620_IO=0x378 (lpt1) - * -DDE620_IRQ=7 (lpt1) - * -DSHUTDOWN_WHEN_LOST - * -DCOUNT_LOOPS - * -DLOWSPEED - * -DREAD_DELAY - * -DWRITE_DELAY - */ - -/* - * This driver assumes that the printer port is a "normal", - * dumb, uni-directional port! - * If your port is "fancy" in any way, please try to set it to "normal" - * with your BIOS setup. I have no access to machines with bi-directional - * ports, so I can't test such a driver :-( - * (Yes, I _know_ it is possible to use DE620 with bidirectional ports...) - * - * There are some clones of DE620 out there, with different names. - * If the current driver does not recognize a clone, try to change - * the following #define to: - * - * #define DE620_CLONE 1 - */ -#define DE620_CLONE 0 - -/* - * If the adapter has problems with high speeds, enable this #define - * otherwise full printerport speed will be attempted. - * - * You can tune the READ_DELAY/WRITE_DELAY below if you enable LOWSPEED - * -#define LOWSPEED - */ - -#ifndef READ_DELAY -#define READ_DELAY 100 /* adapter internal read delay in 100ns units */ -#endif - -#ifndef WRITE_DELAY -#define WRITE_DELAY 100 /* adapter internal write delay in 100ns units */ -#endif - -/* - * Enable this #define if you want the adapter to do a "ifconfig down" on - * itself when we have detected that something is possibly wrong with it. - * The default behaviour is to retry with "adapter_init()" until success. - * This should be used for debugging purposes only. - * -#define SHUTDOWN_WHEN_LOST - */ - -#ifdef LOWSPEED -/* - * Enable this #define if you want to see debugging output that show how long - * we have to wait before the DE-620 is ready for the next read/write/command. - * -#define COUNT_LOOPS - */ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* Constant definitions for the DE-620 registers, commands and bits */ -#include "de620.h" - -typedef unsigned char byte; - -/******************************************************* - * * - * Definition of D-Link DE-620 Ethernet Pocket adapter * - * See also "de620.h" * - * * - *******************************************************/ -#ifndef DE620_IO /* Compile-time configurable */ -#define DE620_IO 0x378 -#endif - -#ifndef DE620_IRQ /* Compile-time configurable */ -#define DE620_IRQ 7 -#endif - -#define DATA_PORT (dev->base_addr) -#define STATUS_PORT (dev->base_addr + 1) -#define COMMAND_PORT (dev->base_addr + 2) - -#define RUNT 60 /* Too small Ethernet packet */ -#define GIANT 1514 /* largest legal size packet, no fcs */ - -/* - * Force media with insmod: - * insmod de620.o bnc=1 - * or - * insmod de620.o utp=1 - * - * Force io and/or irq with insmod: - * insmod de620.o io=0x378 irq=7 - * - * Make a clone skip the Ethernet-address range check: - * insmod de620.o clone=1 - */ -static int bnc; -static int utp; -static int io = DE620_IO; -static int irq = DE620_IRQ; -static int clone = DE620_CLONE; - -static spinlock_t de620_lock; - -module_param(bnc, int, 0); -module_param(utp, int, 0); -module_param(io, int, 0); -module_param(irq, int, 0); -module_param(clone, int, 0); -MODULE_PARM_DESC(bnc, "DE-620 set BNC medium (0-1)"); -MODULE_PARM_DESC(utp, "DE-620 set UTP medium (0-1)"); -MODULE_PARM_DESC(io, "DE-620 I/O base address,required"); -MODULE_PARM_DESC(irq, "DE-620 IRQ number,required"); -MODULE_PARM_DESC(clone, "Check also for non-D-Link DE-620 clones (0-1)"); - -/*********************************************** - * * - * Index to functions, as function prototypes. * - * * - ***********************************************/ - -/* - * Routines used internally. (See also "convenience macros.. below") - */ - -/* Put in the device structure. */ -static int de620_open(struct net_device *); -static int de620_close(struct net_device *); -static void de620_set_multicast_list(struct net_device *); -static int de620_start_xmit(struct sk_buff *, struct net_device *); - -/* Dispatch from interrupts. */ -static irqreturn_t de620_interrupt(int, void *); -static int de620_rx_intr(struct net_device *); - -/* Initialization */ -static int adapter_init(struct net_device *); -static int read_eeprom(struct net_device *); - - -/* - * D-Link driver variables: - */ -#define SCR_DEF NIBBLEMODE |INTON | SLEEP | AUTOTX -#define TCR_DEF RXPB /* not used: | TXSUCINT | T16INT */ -#define DE620_RX_START_PAGE 12 /* 12 pages (=3k) reserved for tx */ -#define DEF_NIC_CMD IRQEN | ICEN | DS1 - -static volatile byte NIC_Cmd; -static volatile byte next_rx_page; -static byte first_rx_page; -static byte last_rx_page; -static byte EIPRegister; - -static struct nic { - byte NodeID[6]; - byte RAM_Size; - byte Model; - byte Media; - byte SCR; -} nic_data; - -/********************************************************** - * * - * Convenience macros/functions for D-Link DE-620 adapter * - * * - **********************************************************/ -#define de620_tx_buffs(dd) (inb(STATUS_PORT) & (TXBF0 | TXBF1)) -#define de620_flip_ds(dd) NIC_Cmd ^= DS0 | DS1; outb(NIC_Cmd, COMMAND_PORT); - -/* Check for ready-status, and return a nibble (high 4 bits) for data input */ -#ifdef COUNT_LOOPS -static int tot_cnt; -#endif -static inline byte -de620_ready(struct net_device *dev) -{ - byte value; - register short int cnt = 0; - - while ((((value = inb(STATUS_PORT)) & READY) == 0) && (cnt <= 1000)) - ++cnt; - -#ifdef COUNT_LOOPS - tot_cnt += cnt; -#endif - return value & 0xf0; /* nibble */ -} - -static inline void -de620_send_command(struct net_device *dev, byte cmd) -{ - de620_ready(dev); - if (cmd == W_DUMMY) - outb(NIC_Cmd, COMMAND_PORT); - - outb(cmd, DATA_PORT); - - outb(NIC_Cmd ^ CS0, COMMAND_PORT); - de620_ready(dev); - outb(NIC_Cmd, COMMAND_PORT); -} - -static inline void -de620_put_byte(struct net_device *dev, byte value) -{ - /* The de620_ready() makes 7 loops, on the average, on a DX2/66 */ - de620_ready(dev); - outb(value, DATA_PORT); - de620_flip_ds(dev); -} - -static inline byte -de620_read_byte(struct net_device *dev) -{ - byte value; - - /* The de620_ready() makes 7 loops, on the average, on a DX2/66 */ - value = de620_ready(dev); /* High nibble */ - de620_flip_ds(dev); - value |= de620_ready(dev) >> 4; /* Low nibble */ - return value; -} - -static inline void -de620_write_block(struct net_device *dev, byte *buffer, int count, int pad) -{ -#ifndef LOWSPEED - byte uflip = NIC_Cmd ^ (DS0 | DS1); - byte dflip = NIC_Cmd; -#else /* LOWSPEED */ -#ifdef COUNT_LOOPS - int bytes = count; -#endif /* COUNT_LOOPS */ -#endif /* LOWSPEED */ - -#ifdef LOWSPEED -#ifdef COUNT_LOOPS - tot_cnt = 0; -#endif /* COUNT_LOOPS */ - /* No further optimization useful, the limit is in the adapter. */ - for ( ; count > 0; --count, ++buffer) { - de620_put_byte(dev,*buffer); - } - for ( count = pad ; count > 0; --count, ++buffer) { - de620_put_byte(dev, 0); - } - de620_send_command(dev,W_DUMMY); -#ifdef COUNT_LOOPS - /* trial debug output: loops per byte in de620_ready() */ - printk("WRITE(%d)\n", tot_cnt/((bytes?bytes:1))); -#endif /* COUNT_LOOPS */ -#else /* not LOWSPEED */ - for ( ; count > 0; count -=2) { - outb(*buffer++, DATA_PORT); - outb(uflip, COMMAND_PORT); - outb(*buffer++, DATA_PORT); - outb(dflip, COMMAND_PORT); - } - de620_send_command(dev,W_DUMMY); -#endif /* LOWSPEED */ -} - -static inline void -de620_read_block(struct net_device *dev, byte *data, int count) -{ -#ifndef LOWSPEED - byte value; - byte uflip = NIC_Cmd ^ (DS0 | DS1); - byte dflip = NIC_Cmd; -#else /* LOWSPEED */ -#ifdef COUNT_LOOPS - int bytes = count; - - tot_cnt = 0; -#endif /* COUNT_LOOPS */ -#endif /* LOWSPEED */ - -#ifdef LOWSPEED - /* No further optimization useful, the limit is in the adapter. */ - while (count-- > 0) { - *data++ = de620_read_byte(dev); - de620_flip_ds(dev); - } -#ifdef COUNT_LOOPS - /* trial debug output: loops per byte in de620_ready() */ - printk("READ(%d)\n", tot_cnt/(2*(bytes?bytes:1))); -#endif /* COUNT_LOOPS */ -#else /* not LOWSPEED */ - while (count-- > 0) { - value = inb(STATUS_PORT) & 0xf0; /* High nibble */ - outb(uflip, COMMAND_PORT); - *data++ = value | inb(STATUS_PORT) >> 4; /* Low nibble */ - outb(dflip , COMMAND_PORT); - } -#endif /* LOWSPEED */ -} - -static inline void -de620_set_delay(struct net_device *dev) -{ - de620_ready(dev); - outb(W_DFR, DATA_PORT); - outb(NIC_Cmd ^ CS0, COMMAND_PORT); - - de620_ready(dev); -#ifdef LOWSPEED - outb(WRITE_DELAY, DATA_PORT); -#else - outb(0, DATA_PORT); -#endif - de620_flip_ds(dev); - - de620_ready(dev); -#ifdef LOWSPEED - outb(READ_DELAY, DATA_PORT); -#else - outb(0, DATA_PORT); -#endif - de620_flip_ds(dev); -} - -static inline void -de620_set_register(struct net_device *dev, byte reg, byte value) -{ - de620_ready(dev); - outb(reg, DATA_PORT); - outb(NIC_Cmd ^ CS0, COMMAND_PORT); - - de620_put_byte(dev, value); -} - -static inline byte -de620_get_register(struct net_device *dev, byte reg) -{ - byte value; - - de620_send_command(dev,reg); - value = de620_read_byte(dev); - de620_send_command(dev,W_DUMMY); - - return value; -} - -/********************************************************************* - * - * Open/initialize the board. - * - * This routine should set everything up anew at each open, even - * registers that "should" only need to be set once at boot, so that - * there is a non-reboot way to recover if something goes wrong. - * - */ -static int de620_open(struct net_device *dev) -{ - int ret = request_irq(dev->irq, de620_interrupt, 0, dev->name, dev); - if (ret) { - printk (KERN_ERR "%s: unable to get IRQ %d\n", dev->name, dev->irq); - return ret; - } - - if (adapter_init(dev)) { - ret = -EIO; - goto out_free_irq; - } - - netif_start_queue(dev); - return 0; - -out_free_irq: - free_irq(dev->irq, dev); - return ret; -} - -/************************************************ - * - * The inverse routine to de620_open(). - * - */ - -static int de620_close(struct net_device *dev) -{ - netif_stop_queue(dev); - /* disable recv */ - de620_set_register(dev, W_TCR, RXOFF); - free_irq(dev->irq, dev); - return 0; -} - -/********************************************* - * - * Set or clear the multicast filter for this adaptor. - * (no real multicast implemented for the DE-620, but she can be promiscuous...) - * - */ - -static void de620_set_multicast_list(struct net_device *dev) -{ - if (!netdev_mc_empty(dev) || dev->flags&(IFF_ALLMULTI|IFF_PROMISC)) - { /* Enable promiscuous mode */ - de620_set_register(dev, W_TCR, (TCR_DEF & ~RXPBM) | RXALL); - } - else - { /* Disable promiscuous mode, use normal mode */ - de620_set_register(dev, W_TCR, TCR_DEF); - } -} - -/******************************************************* - * - * Handle timeouts on transmit - */ - -static void de620_timeout(struct net_device *dev) -{ - printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name, "network cable problem"); - /* Restart the adapter. */ - if (!adapter_init(dev)) /* maybe close it */ - netif_wake_queue(dev); -} - -/******************************************************* - * - * Copy a buffer to the adapter transmit page memory. - * Start sending. - */ -static int de620_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - unsigned long flags; - int len; - byte *buffer = skb->data; - byte using_txbuf; - - using_txbuf = de620_tx_buffs(dev); /* Peek at the adapter */ - - netif_stop_queue(dev); - - - if ((len = skb->len) < RUNT) - len = RUNT; - if (len & 1) /* send an even number of bytes */ - ++len; - - /* Start real output */ - - spin_lock_irqsave(&de620_lock, flags); - pr_debug("de620_start_xmit: len=%d, bufs 0x%02x\n", - (int)skb->len, using_txbuf); - - /* select a free tx buffer. if there is one... */ - switch (using_txbuf) { - default: /* both are free: use TXBF0 */ - case TXBF1: /* use TXBF0 */ - de620_send_command(dev,W_CR | RW0); - using_txbuf |= TXBF0; - break; - - case TXBF0: /* use TXBF1 */ - de620_send_command(dev,W_CR | RW1); - using_txbuf |= TXBF1; - break; - - case (TXBF0 | TXBF1): /* NONE!!! */ - printk(KERN_WARNING "%s: No tx-buffer available!\n", dev->name); - spin_unlock_irqrestore(&de620_lock, flags); - return NETDEV_TX_BUSY; - } - de620_write_block(dev, buffer, skb->len, len-skb->len); - - if(!(using_txbuf == (TXBF0 | TXBF1))) - netif_wake_queue(dev); - - dev->stats.tx_packets++; - spin_unlock_irqrestore(&de620_lock, flags); - dev_kfree_skb (skb); - return NETDEV_TX_OK; -} - -/***************************************************** - * - * Handle the network interface interrupts. - * - */ -static irqreturn_t -de620_interrupt(int irq_in, void *dev_id) -{ - struct net_device *dev = dev_id; - byte irq_status; - int bogus_count = 0; - int again = 0; - - spin_lock(&de620_lock); - - /* Read the status register (_not_ the status port) */ - irq_status = de620_get_register(dev, R_STS); - - pr_debug("de620_interrupt (%2.2X)\n", irq_status); - - if (irq_status & RXGOOD) { - do { - again = de620_rx_intr(dev); - pr_debug("again=%d\n", again); - } - while (again && (++bogus_count < 100)); - } - - if(de620_tx_buffs(dev) != (TXBF0 | TXBF1)) - netif_wake_queue(dev); - - spin_unlock(&de620_lock); - return IRQ_HANDLED; -} - -/************************************** - * - * Get a packet from the adapter - * - * Send it "upstairs" - * - */ -static int de620_rx_intr(struct net_device *dev) -{ - struct header_buf { - byte status; - byte Rx_NextPage; - unsigned short Rx_ByteCount; - } header_buf; - struct sk_buff *skb; - int size; - byte *buffer; - byte pagelink; - byte curr_page; - - pr_debug("de620_rx_intr: next_rx_page = %d\n", next_rx_page); - - /* Tell the adapter that we are going to read data, and from where */ - de620_send_command(dev, W_CR | RRN); - de620_set_register(dev, W_RSA1, next_rx_page); - de620_set_register(dev, W_RSA0, 0); - - /* Deep breath, and away we goooooo */ - de620_read_block(dev, (byte *)&header_buf, sizeof(struct header_buf)); - pr_debug("page status=0x%02x, nextpage=%d, packetsize=%d\n", - header_buf.status, header_buf.Rx_NextPage, - header_buf.Rx_ByteCount); - - /* Plausible page header? */ - pagelink = header_buf.Rx_NextPage; - if ((pagelink < first_rx_page) || (last_rx_page < pagelink)) { - /* Ouch... Forget it! Skip all and start afresh... */ - printk(KERN_WARNING "%s: Ring overrun? Restoring...\n", dev->name); - /* You win some, you lose some. And sometimes plenty... */ - adapter_init(dev); - netif_wake_queue(dev); - dev->stats.rx_over_errors++; - return 0; - } - - /* OK, this look good, so far. Let's see if it's consistent... */ - /* Let's compute the start of the next packet, based on where we are */ - pagelink = next_rx_page + - ((header_buf.Rx_ByteCount + (4 - 1 + 0x100)) >> 8); - - /* Are we going to wrap around the page counter? */ - if (pagelink > last_rx_page) - pagelink -= (last_rx_page - first_rx_page + 1); - - /* Is the _computed_ next page number equal to what the adapter says? */ - if (pagelink != header_buf.Rx_NextPage) { - /* Naah, we'll skip this packet. Probably bogus data as well */ - printk(KERN_WARNING "%s: Page link out of sync! Restoring...\n", dev->name); - next_rx_page = header_buf.Rx_NextPage; /* at least a try... */ - de620_send_command(dev, W_DUMMY); - de620_set_register(dev, W_NPRF, next_rx_page); - dev->stats.rx_over_errors++; - return 0; - } - next_rx_page = pagelink; - - size = header_buf.Rx_ByteCount - 4; - if ((size < RUNT) || (GIANT < size)) { - printk(KERN_WARNING "%s: Illegal packet size: %d!\n", dev->name, size); - } - else { /* Good packet? */ - skb = netdev_alloc_skb(dev, size + 2); - if (skb == NULL) { /* Yeah, but no place to put it... */ - printk(KERN_WARNING "%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, size); - dev->stats.rx_dropped++; - } - else { /* Yep! Go get it! */ - skb_reserve(skb,2); /* Align */ - /* skb->data points to the start of sk_buff data area */ - buffer = skb_put(skb,size); - /* copy the packet into the buffer */ - de620_read_block(dev, buffer, size); - pr_debug("Read %d bytes\n", size); - skb->protocol=eth_type_trans(skb,dev); - netif_rx(skb); /* deliver it "upstairs" */ - /* count all receives */ - dev->stats.rx_packets++; - dev->stats.rx_bytes += size; - } - } - - /* Let's peek ahead to see if we have read the last current packet */ - /* NOTE! We're _not_ checking the 'EMPTY'-flag! This seems better... */ - curr_page = de620_get_register(dev, R_CPR); - de620_set_register(dev, W_NPRF, next_rx_page); - pr_debug("next_rx_page=%d CPR=%d\n", next_rx_page, curr_page); - - return next_rx_page != curr_page; /* That was slightly tricky... */ -} - -/********************************************* - * - * Reset the adapter to a known state - * - */ -static int adapter_init(struct net_device *dev) -{ - int i; - static int was_down; - - if ((nic_data.Model == 3) || (nic_data.Model == 0)) { /* CT */ - EIPRegister = NCTL0; - if (nic_data.Media != 1) - EIPRegister |= NIS0; /* not BNC */ - } - else if (nic_data.Model == 2) { /* UTP */ - EIPRegister = NCTL0 | NIS0; - } - - if (utp) - EIPRegister = NCTL0 | NIS0; - if (bnc) - EIPRegister = NCTL0; - - de620_send_command(dev, W_CR | RNOP | CLEAR); - de620_send_command(dev, W_CR | RNOP); - - de620_set_register(dev, W_SCR, SCR_DEF); - /* disable recv to wait init */ - de620_set_register(dev, W_TCR, RXOFF); - - /* Set the node ID in the adapter */ - for (i = 0; i < 6; ++i) { /* W_PARn = 0xaa + n */ - de620_set_register(dev, W_PAR0 + i, dev->dev_addr[i]); - } - - de620_set_register(dev, W_EIP, EIPRegister); - - next_rx_page = first_rx_page = DE620_RX_START_PAGE; - if (nic_data.RAM_Size) - last_rx_page = nic_data.RAM_Size - 1; - else /* 64k RAM */ - last_rx_page = 255; - - de620_set_register(dev, W_SPR, first_rx_page); /* Start Page Register*/ - de620_set_register(dev, W_EPR, last_rx_page); /* End Page Register */ - de620_set_register(dev, W_CPR, first_rx_page);/*Current Page Register*/ - de620_send_command(dev, W_NPR | first_rx_page); /* Next Page Register*/ - de620_send_command(dev, W_DUMMY); - de620_set_delay(dev); - - /* Final sanity check: Anybody out there? */ - /* Let's hope some bits from the statusregister make a good check */ -#define CHECK_MASK ( 0 | TXSUC | T16 | 0 | RXCRC | RXSHORT | 0 | 0 ) -#define CHECK_OK ( 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 ) - /* success: X 0 0 X 0 0 X X */ - /* ignore: EEDI RXGOOD COLS LNKS*/ - - if (((i = de620_get_register(dev, R_STS)) & CHECK_MASK) != CHECK_OK) { - printk(KERN_ERR "%s: Something has happened to the DE-620! Please check it" -#ifdef SHUTDOWN_WHEN_LOST - " and do a new ifconfig" -#endif - "! (%02x)\n", dev->name, i); -#ifdef SHUTDOWN_WHEN_LOST - /* Goodbye, cruel world... */ - dev->flags &= ~IFF_UP; - de620_close(dev); -#endif - was_down = 1; - return 1; /* failed */ - } - if (was_down) { - printk(KERN_WARNING "%s: Thanks, I feel much better now!\n", dev->name); - was_down = 0; - } - - /* All OK, go ahead... */ - de620_set_register(dev, W_TCR, TCR_DEF); - - return 0; /* all ok */ -} - -static const struct net_device_ops de620_netdev_ops = { - .ndo_open = de620_open, - .ndo_stop = de620_close, - .ndo_start_xmit = de620_start_xmit, - .ndo_tx_timeout = de620_timeout, - .ndo_set_rx_mode = de620_set_multicast_list, - .ndo_change_mtu = eth_change_mtu, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -/****************************************************************************** - * - * Only start-up code below - * - */ -/**************************************** - * - * Check if there is a DE-620 connected - */ -struct net_device * __init de620_probe(int unit) -{ - byte checkbyte = 0xa5; - struct net_device *dev; - int err = -ENOMEM; - int i; - - dev = alloc_etherdev(0); - if (!dev) - goto out; - - spin_lock_init(&de620_lock); - - /* - * This is where the base_addr and irq gets set. - * Tunable at compile-time and insmod-time - */ - dev->base_addr = io; - dev->irq = irq; - - /* allow overriding parameters on command line */ - if (unit >= 0) { - sprintf(dev->name, "eth%d", unit); - netdev_boot_setup_check(dev); - } - - pr_debug("%s", version); - - printk(KERN_INFO "D-Link DE-620 pocket adapter"); - - if (!request_region(dev->base_addr, 3, "de620")) { - printk(" io 0x%3lX, which is busy.\n", dev->base_addr); - err = -EBUSY; - goto out1; - } - - /* Initially, configure basic nibble mode, so we can read the EEPROM */ - NIC_Cmd = DEF_NIC_CMD; - de620_set_register(dev, W_EIP, EIPRegister); - - /* Anybody out there? */ - de620_set_register(dev, W_CPR, checkbyte); - checkbyte = de620_get_register(dev, R_CPR); - - if ((checkbyte != 0xa5) || (read_eeprom(dev) != 0)) { - printk(" not identified in the printer port\n"); - err = -ENODEV; - goto out2; - } - - /* else, got it! */ - dev->dev_addr[0] = nic_data.NodeID[0]; - for (i = 1; i < ETH_ALEN; i++) { - dev->dev_addr[i] = nic_data.NodeID[i]; - dev->broadcast[i] = 0xff; - } - - printk(", Ethernet Address: %pM", dev->dev_addr); - - printk(" (%dk RAM,", - (nic_data.RAM_Size) ? (nic_data.RAM_Size >> 2) : 64); - - if (nic_data.Media == 1) - printk(" BNC)\n"); - else - printk(" UTP)\n"); - - dev->netdev_ops = &de620_netdev_ops; - dev->watchdog_timeo = HZ*2; - - /* base_addr and irq are already set, see above! */ - - /* dump eeprom */ - pr_debug("\nEEPROM contents:\n" - "RAM_Size = 0x%02X\n" - "NodeID = %pM\n" - "Model = %d\n" - "Media = %d\n" - "SCR = 0x%02x\n", nic_data.RAM_Size, nic_data.NodeID, - nic_data.Model, nic_data.Media, nic_data.SCR); - - err = register_netdev(dev); - if (err) - goto out2; - return dev; - -out2: - release_region(dev->base_addr, 3); -out1: - free_netdev(dev); -out: - return ERR_PTR(err); -} - -/********************************** - * - * Read info from on-board EEPROM - * - * Note: Bitwise serial I/O to/from the EEPROM vi the status _register_! - */ -#define sendit(dev,data) de620_set_register(dev, W_EIP, data | EIPRegister); - -static unsigned short __init ReadAWord(struct net_device *dev, int from) -{ - unsigned short data; - int nbits; - - /* cs [__~~] SET SEND STATE */ - /* di [____] */ - /* sck [_~~_] */ - sendit(dev, 0); sendit(dev, 1); sendit(dev, 5); sendit(dev, 4); - - /* Send the 9-bit address from where we want to read the 16-bit word */ - for (nbits = 9; nbits > 0; --nbits, from <<= 1) { - if (from & 0x0100) { /* bit set? */ - /* cs [~~~~] SEND 1 */ - /* di [~~~~] */ - /* sck [_~~_] */ - sendit(dev, 6); sendit(dev, 7); sendit(dev, 7); sendit(dev, 6); - } - else { - /* cs [~~~~] SEND 0 */ - /* di [____] */ - /* sck [_~~_] */ - sendit(dev, 4); sendit(dev, 5); sendit(dev, 5); sendit(dev, 4); - } - } - - /* Shift in the 16-bit word. The bits appear serially in EEDI (=0x80) */ - for (data = 0, nbits = 16; nbits > 0; --nbits) { - /* cs [~~~~] SEND 0 */ - /* di [____] */ - /* sck [_~~_] */ - sendit(dev, 4); sendit(dev, 5); sendit(dev, 5); sendit(dev, 4); - data = (data << 1) | ((de620_get_register(dev, R_STS) & EEDI) >> 7); - } - /* cs [____] RESET SEND STATE */ - /* di [____] */ - /* sck [_~~_] */ - sendit(dev, 0); sendit(dev, 1); sendit(dev, 1); sendit(dev, 0); - - return data; -} - -static int __init read_eeprom(struct net_device *dev) -{ - unsigned short wrd; - - /* D-Link Ethernet addresses are in the series 00:80:c8:7X:XX:XX:XX */ - wrd = ReadAWord(dev, 0x1aa); /* bytes 0 + 1 of NodeID */ - if (!clone && (wrd != htons(0x0080))) /* Valid D-Link ether sequence? */ - return -1; /* Nope, not a DE-620 */ - nic_data.NodeID[0] = wrd & 0xff; - nic_data.NodeID[1] = wrd >> 8; - - wrd = ReadAWord(dev, 0x1ab); /* bytes 2 + 3 of NodeID */ - if (!clone && ((wrd & 0xff) != 0xc8)) /* Valid D-Link ether sequence? */ - return -1; /* Nope, not a DE-620 */ - nic_data.NodeID[2] = wrd & 0xff; - nic_data.NodeID[3] = wrd >> 8; - - wrd = ReadAWord(dev, 0x1ac); /* bytes 4 + 5 of NodeID */ - nic_data.NodeID[4] = wrd & 0xff; - nic_data.NodeID[5] = wrd >> 8; - - wrd = ReadAWord(dev, 0x1ad); /* RAM size in pages (256 bytes). 0 = 64k */ - nic_data.RAM_Size = (wrd >> 8); - - wrd = ReadAWord(dev, 0x1ae); /* hardware model (CT = 3) */ - nic_data.Model = (wrd & 0xff); - - wrd = ReadAWord(dev, 0x1af); /* media (indicates BNC/UTP) */ - nic_data.Media = (wrd & 0xff); - - wrd = ReadAWord(dev, 0x1a8); /* System Configuration Register */ - nic_data.SCR = (wrd >> 8); - - return 0; /* no errors */ -} - -/****************************************************************************** - * - * Loadable module skeleton - * - */ -#ifdef MODULE -static struct net_device *de620_dev; - -int __init init_module(void) -{ - de620_dev = de620_probe(-1); - if (IS_ERR(de620_dev)) - return PTR_ERR(de620_dev); - return 0; -} - -void cleanup_module(void) -{ - unregister_netdev(de620_dev); - release_region(de620_dev->base_addr, 3); - free_netdev(de620_dev); -} -#endif /* MODULE */ -MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/dlink/de620.h b/drivers/net/ethernet/dlink/de620.h deleted file mode 100644 index e8d9a88f4cb5..000000000000 --- a/drivers/net/ethernet/dlink/de620.h +++ /dev/null @@ -1,117 +0,0 @@ -/********************************************************* - * * - * Definition of D-Link DE-620 Ethernet Pocket adapter * - * * - *********************************************************/ - -/* DE-620's CMD port Command */ -#define CS0 0x08 /* 1->0 command strobe */ -#define ICEN 0x04 /* 0=enable DL3520 host interface */ -#define DS0 0x02 /* 1->0 data strobe 0 */ -#define DS1 0x01 /* 1->0 data strobe 1 */ - -#define WDIR 0x20 /* general 0=read 1=write */ -#define RDIR 0x00 /* (not 100% confirm ) */ -#define PS2WDIR 0x00 /* ps/2 mode 1=read, 0=write */ -#define PS2RDIR 0x20 - -#define IRQEN 0x10 /* 1 = enable printer IRQ line */ -#define SELECTIN 0x08 /* 1 = select printer */ -#define INITP 0x04 /* 0 = initial printer */ -#define AUTOFEED 0x02 /* 1 = printer auto form feed */ -#define STROBE 0x01 /* 0->1 data strobe */ - -#define RESET 0x08 -#define NIS0 0x20 /* 0 = BNC, 1 = UTP */ -#define NCTL0 0x10 - -/* DE-620 DIC Command */ -#define W_DUMMY 0x00 /* DIC reserved command */ -#define W_CR 0x20 /* DIC write command register */ -#define W_NPR 0x40 /* DIC write Next Page Register */ -#define W_TBR 0x60 /* DIC write Tx Byte Count 1 reg */ -#define W_RSA 0x80 /* DIC write Remote Start Addr 1 */ - -/* DE-620's STAT port bits 7-4 */ -#define EMPTY 0x80 /* 1 = receive buffer empty */ -#define INTLEVEL 0x40 /* 1 = interrupt level is high */ -#define TXBF1 0x20 /* 1 = transmit buffer 1 is in use */ -#define TXBF0 0x10 /* 1 = transmit buffer 0 is in use */ -#define READY 0x08 /* 1 = h/w ready to accept cmd/data */ - -/* IDC 1 Command */ -#define W_RSA1 0xa0 /* write remote start address 1 */ -#define W_RSA0 0xa1 /* write remote start address 0 */ -#define W_NPRF 0xa2 /* write next page register NPR15-NPR8 */ -#define W_DFR 0xa3 /* write delay factor register */ -#define W_CPR 0xa4 /* write current page register */ -#define W_SPR 0xa5 /* write start page register */ -#define W_EPR 0xa6 /* write end page register */ -#define W_SCR 0xa7 /* write system configuration register */ -#define W_TCR 0xa8 /* write Transceiver Configuration reg */ -#define W_EIP 0xa9 /* write EEPM Interface port */ -#define W_PAR0 0xaa /* write physical address register 0 */ -#define W_PAR1 0xab /* write physical address register 1 */ -#define W_PAR2 0xac /* write physical address register 2 */ -#define W_PAR3 0xad /* write physical address register 3 */ -#define W_PAR4 0xae /* write physical address register 4 */ -#define W_PAR5 0xaf /* write physical address register 5 */ - -/* IDC 2 Command */ -#define R_STS 0xc0 /* read status register */ -#define R_CPR 0xc1 /* read current page register */ -#define R_BPR 0xc2 /* read boundary page register */ -#define R_TDR 0xc3 /* read time domain reflectometry reg */ - -/* STATUS Register */ -#define EEDI 0x80 /* EEPM DO pin */ -#define TXSUC 0x40 /* tx success */ -#define T16 0x20 /* tx fail 16 times */ -#define TS1 0x40 /* 0=Tx success, 1=T16 */ -#define TS0 0x20 /* 0=Tx success, 1=T16 */ -#define RXGOOD 0x10 /* rx a good packet */ -#define RXCRC 0x08 /* rx a CRC error packet */ -#define RXSHORT 0x04 /* rx a short packet */ -#define COLS 0x02 /* coaxial collision status */ -#define LNKS 0x01 /* UTP link status */ - -/* Command Register */ -#define CLEAR 0x10 /* reset part of hardware */ -#define NOPER 0x08 /* No Operation */ -#define RNOP 0x08 -#define RRA 0x06 /* After RR then auto-advance NPR & BPR(=NPR-1) */ -#define RRN 0x04 /* Normal Remote Read mode */ -#define RW1 0x02 /* Remote Write tx buffer 1 ( page 6 - 11 ) */ -#define RW0 0x00 /* Remote Write tx buffer 0 ( page 0 - 5 ) */ -#define TXEN 0x01 /* 0->1 tx enable */ - -/* System Configuration Register */ -#define TESTON 0x80 /* test host data transfer reliability */ -#define SLEEP 0x40 /* sleep mode */ -#if 0 -#define FASTMODE 0x04 /* fast mode for intel 82360SL fast mode */ -#define BYTEMODE 0x02 /* byte mode */ -#else -#define FASTMODE 0x20 /* fast mode for intel 82360SL fast mode */ -#define BYTEMODE 0x10 /* byte mode */ -#endif -#define NIBBLEMODE 0x00 /* nibble mode */ -#define IRQINV 0x08 /* turn off IRQ line inverter */ -#define IRQNML 0x00 /* turn on IRQ line inverter */ -#define INTON 0x04 -#define AUTOFFSET 0x02 /* auto shift address to TPR+12 */ -#define AUTOTX 0x01 /* auto tx when leave RW mode */ - -/* Transceiver Configuration Register */ -#define JABBER 0x80 /* generate jabber condition */ -#define TXSUCINT 0x40 /* enable tx success interrupt */ -#define T16INT 0x20 /* enable T16 interrupt */ -#define RXERRPKT 0x10 /* accept CRC error or short packet */ -#define EXTERNALB2 0x0C /* external loopback 2 */ -#define EXTERNALB1 0x08 /* external loopback 1 */ -#define INTERNALB 0x04 /* internal loopback */ -#define NMLOPERATE 0x00 /* normal operation */ -#define RXPBM 0x03 /* rx physical, broadcast, multicast */ -#define RXPB 0x02 /* rx physical, broadcast */ -#define RXALL 0x01 /* rx all packet */ -#define RXOFF 0x00 /* rx disable */ -- cgit v1.2.3-59-g8ed1b From 1f1c7a5c1dca01dd8f3f740420f92c7d1d2ae080 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 16 Jan 2013 19:32:34 -0500 Subject: drivers/net: delete old DEC depca ISA drivers support. These are old ISA 10Mbit cards from the 1st 1/2 of the 1990s and required manual jumper settings in order to configure them. Here we remove them on the premise that they are no longer used in any modern 3.9+ kernels. Signed-off-by: Paul Gortmaker --- Documentation/networking/00-INDEX | 2 - Documentation/networking/depca.txt | 92 -- drivers/net/ethernet/amd/Kconfig | 13 - drivers/net/ethernet/amd/Makefile | 1 - drivers/net/ethernet/amd/depca.c | 1910 ------------------------------------ drivers/net/ethernet/amd/depca.h | 183 ---- 6 files changed, 2201 deletions(-) delete mode 100644 Documentation/networking/depca.txt delete mode 100644 drivers/net/ethernet/amd/depca.c delete mode 100644 drivers/net/ethernet/amd/depca.h (limited to 'Documentation') diff --git a/Documentation/networking/00-INDEX b/Documentation/networking/00-INDEX index 1abfff26f8a0..6ec1d6f368af 100644 --- a/Documentation/networking/00-INDEX +++ b/Documentation/networking/00-INDEX @@ -52,8 +52,6 @@ de4x5.txt - the Digital EtherWORKS DE4?? and DE5?? PCI Ethernet driver decnet.txt - info on using the DECnet networking layer in Linux. -depca.txt - - the Digital DEPCA/EtherWORKS DE1?? and DE2?? LANCE Ethernet driver dl2k.txt - README for D-Link DL2000-based Gigabit Ethernet Adapters (dl2k.ko). dm9000.txt diff --git a/Documentation/networking/depca.txt b/Documentation/networking/depca.txt deleted file mode 100644 index 24c6b26e5658..000000000000 --- a/Documentation/networking/depca.txt +++ /dev/null @@ -1,92 +0,0 @@ - -DE10x -===== - -Memory Addresses: - - SW1 SW2 SW3 SW4 -64K on on on on d0000 dbfff - off on on on c0000 cbfff - off off on on e0000 ebfff - -32K on on off on d8000 dbfff - off on off on c8000 cbfff - off off off on e8000 ebfff - -DBR ROM on on dc000 dffff - off on cc000 cffff - off off ec000 effff - -Note that the 2K mode is set by SW3/SW4 on/off or off/off. Address -assignment is through the RBSA register. - -I/O Address: - SW5 -0x300 on -0x200 off - -Remote Boot: - SW6 -Disable on -Enable off - -Remote Boot Timeout: - SW7 -2.5min on -30s off - -IRQ: - SW8 SW9 SW10 SW11 SW12 -2 on off off off off -3 off on off off off -4 off off on off off -5 off off off on off -7 off off off off on - -DE20x -===== - -Memory Size: - - SW3 SW4 -64K on on -32K off on -2K on off -2K off off - -Start Addresses: - - SW1 SW2 SW3 SW4 -64K on on on on c0000 cffff - on off on on d0000 dffff - off on on on e0000 effff - -32K on on off off c8000 cffff - on off off off d8000 dffff - off on off off e8000 effff - -Illegal off off - - - - - -I/O Address: - SW5 -0x300 on -0x200 off - -Remote Boot: - SW6 -Disable on -Enable off - -Remote Boot Timeout: - SW7 -2.5min on -30s off - -IRQ: - SW8 SW9 SW10 SW11 SW12 -5 on off off off off -9 off on off off off -10 off off on off off -11 off off off on off -15 off off off off on - diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig index 304613302f01..13d74aa4033d 100644 --- a/drivers/net/ethernet/amd/Kconfig +++ b/drivers/net/ethernet/amd/Kconfig @@ -105,19 +105,6 @@ config DECLANCE DEC (now Compaq) based on the AMD LANCE chipset, including the DEPCA series. (This chipset is better known via the NE2100 cards.) -config DEPCA - tristate "DEPCA, DE10x, DE200, DE201, DE202, DE422 support" - depends on (ISA || EISA) - select CRC32 - ---help--- - If you have a network (Ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available from - as well as - . - - To compile this driver as a module, choose M here. The module - will be called depca. - config HPLANCE bool "HP on-board LANCE support" depends on DIO diff --git a/drivers/net/ethernet/amd/Makefile b/drivers/net/ethernet/amd/Makefile index 175caa5328c9..cdd4301a973d 100644 --- a/drivers/net/ethernet/amd/Makefile +++ b/drivers/net/ethernet/amd/Makefile @@ -8,7 +8,6 @@ obj-$(CONFIG_ARM_AM79C961A) += am79c961a.o obj-$(CONFIG_ARIADNE) += ariadne.o obj-$(CONFIG_ATARILANCE) += atarilance.o obj-$(CONFIG_DECLANCE) += declance.o -obj-$(CONFIG_DEPCA) += depca.o obj-$(CONFIG_HPLANCE) += hplance.o 7990.o obj-$(CONFIG_LANCE) += lance.o obj-$(CONFIG_MIPS_AU1X00_ENET) += au1000_eth.o diff --git a/drivers/net/ethernet/amd/depca.c b/drivers/net/ethernet/amd/depca.c deleted file mode 100644 index 34a485363d5b..000000000000 --- a/drivers/net/ethernet/amd/depca.c +++ /dev/null @@ -1,1910 +0,0 @@ -/* depca.c: A DIGITAL DEPCA & EtherWORKS ethernet driver for linux. - - Written 1994, 1995 by David C. Davies. - - - Copyright 1994 David C. Davies - and - United States Government - (as represented by the Director, National Security Agency). - - Copyright 1995 Digital Equipment Corporation. - - - This software may be used and distributed according to the terms of - the GNU General Public License, incorporated herein by reference. - - This driver is written for the Digital Equipment Corporation series - of DEPCA and EtherWORKS ethernet cards: - - DEPCA (the original) - DE100 - DE101 - DE200 Turbo - DE201 Turbo - DE202 Turbo (TP BNC) - DE210 - DE422 (EISA) - - The driver has been tested on DE100, DE200 and DE202 cards in a - relatively busy network. The DE422 has been tested a little. - - This driver will NOT work for the DE203, DE204 and DE205 series of - cards, since they have a new custom ASIC in place of the AMD LANCE - chip. See the 'ewrk3.c' driver in the Linux source tree for running - those cards. - - I have benchmarked the driver with a DE100 at 595kB/s to (542kB/s from) - a DECstation 5000/200. - - The author may be reached at davies@maniac.ultranet.com - - ========================================================================= - - The driver was originally based on the 'lance.c' driver from Donald - Becker which is included with the standard driver distribution for - linux. V0.4 is a complete re-write with only the kernel interface - remaining from the original code. - - 1) Lance.c code in /linux/drivers/net/ - 2) "Ethernet/IEEE 802.3 Family. 1992 World Network Data Book/Handbook", - AMD, 1992 [(800) 222-9323]. - 3) "Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE)", - AMD, Pub. #17881, May 1993. - 4) "Am79C960 PCnet-ISA(tm), Single-Chip Ethernet Controller for ISA", - AMD, Pub. #16907, May 1992 - 5) "DEC EtherWORKS LC Ethernet Controller Owners Manual", - Digital Equipment corporation, 1990, Pub. #EK-DE100-OM.003 - 6) "DEC EtherWORKS Turbo Ethernet Controller Owners Manual", - Digital Equipment corporation, 1990, Pub. #EK-DE200-OM.003 - 7) "DEPCA Hardware Reference Manual", Pub. #EK-DEPCA-PR - Digital Equipment Corporation, 1989 - 8) "DEC EtherWORKS Turbo_(TP BNC) Ethernet Controller Owners Manual", - Digital Equipment corporation, 1991, Pub. #EK-DE202-OM.001 - - - Peter Bauer's depca.c (V0.5) was referred to when debugging V0.1 of this - driver. - - The original DEPCA card requires that the ethernet ROM address counter - be enabled to count and has an 8 bit NICSR. The ROM counter enabling is - only done when a 0x08 is read as the first address octet (to minimise - the chances of writing over some other hardware's I/O register). The - NICSR accesses have been changed to byte accesses for all the cards - supported by this driver, since there is only one useful bit in the MSB - (remote boot timeout) and it is not used. Also, there is a maximum of - only 48kB network RAM for this card. My thanks to Torbjorn Lindh for - help debugging all this (and holding my feet to the fire until I got it - right). - - The DE200 series boards have on-board 64kB RAM for use as a shared - memory network buffer. Only the DE100 cards make use of a 2kB buffer - mode which has not been implemented in this driver (only the 32kB and - 64kB modes are supported [16kB/48kB for the original DEPCA]). - - At the most only 2 DEPCA cards can be supported on the ISA bus because - there is only provision for two I/O base addresses on each card (0x300 - and 0x200). The I/O address is detected by searching for a byte sequence - in the Ethernet station address PROM at the expected I/O address for the - Ethernet PROM. The shared memory base address is 'autoprobed' by - looking for the self test PROM and detecting the card name. When a - second DEPCA is detected, information is placed in the base_addr - variable of the next device structure (which is created if necessary), - thus enabling ethif_probe initialization for the device. More than 2 - EISA cards can be supported, but care will be needed assigning the - shared memory to ensure that each slot has the correct IRQ, I/O address - and shared memory address assigned. - - ************************************************************************ - - NOTE: If you are using two ISA DEPCAs, it is important that you assign - the base memory addresses correctly. The driver autoprobes I/O 0x300 - then 0x200. The base memory address for the first device must be less - than that of the second so that the auto probe will correctly assign the - I/O and memory addresses on the same card. I can't think of a way to do - this unambiguously at the moment, since there is nothing on the cards to - tie I/O and memory information together. - - I am unable to test 2 cards together for now, so this code is - unchecked. All reports, good or bad, are welcome. - - ************************************************************************ - - The board IRQ setting must be at an unused IRQ which is auto-probed - using Donald Becker's autoprobe routines. DEPCA and DE100 board IRQs are - {2,3,4,5,7}, whereas the DE200 is at {5,9,10,11,15}. Note that IRQ2 is - really IRQ9 in machines with 16 IRQ lines. - - No 16MB memory limitation should exist with this driver as DMA is not - used and the common memory area is in low memory on the network card (my - current system has 20MB and I've not had problems yet). - - The ability to load this driver as a loadable module has been added. To - utilise this ability, you have to do <8 things: - - 0) have a copy of the loadable modules code installed on your system. - 1) copy depca.c from the /linux/drivers/net directory to your favourite - temporary directory. - 2) if you wish, edit the source code near line 1530 to reflect the I/O - address and IRQ you're using (see also 5). - 3) compile depca.c, but include -DMODULE in the command line to ensure - that the correct bits are compiled (see end of source code). - 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a - kernel with the depca configuration turned off and reboot. - 5) insmod depca.o [irq=7] [io=0x200] [mem=0xd0000] [adapter_name=DE100] - [Alan Cox: Changed the code to allow command line irq/io assignments] - [Dave Davies: Changed the code to allow command line mem/name - assignments] - 6) run the net startup bits for your eth?? interface manually - (usually /etc/rc.inet[12] at boot time). - 7) enjoy! - - Note that autoprobing is not allowed in loadable modules - the system is - already up and running and you're messing with interrupts. - - To unload a module, turn off the associated interface - 'ifconfig eth?? down' then 'rmmod depca'. - - To assign a base memory address for the shared memory when running as a - loadable module, see 5 above. To include the adapter name (if you have - no PROM but know the card name) also see 5 above. Note that this last - option will not work with kernel built-in depca's. - - The shared memory assignment for a loadable module makes sense to avoid - the 'memory autoprobe' picking the wrong shared memory (for the case of - 2 depca's in a PC). - - ************************************************************************ - Support for MCA EtherWORKS cards added 11-3-98. (MCA since deleted) - Verified to work with up to 2 DE212 cards in a system (although not - fully stress-tested). - - Revision History - ---------------- - - Version Date Description - - 0.1 25-jan-94 Initial writing. - 0.2 27-jan-94 Added LANCE TX hardware buffer chaining. - 0.3 1-feb-94 Added multiple DEPCA support. - 0.31 4-feb-94 Added DE202 recognition. - 0.32 19-feb-94 Tidy up. Improve multi-DEPCA support. - 0.33 25-feb-94 Fix DEPCA ethernet ROM counter enable. - Add jabber packet fix from murf@perftech.com - and becker@super.org - 0.34 7-mar-94 Fix DEPCA max network memory RAM & NICSR access. - 0.35 8-mar-94 Added DE201 recognition. Tidied up. - 0.351 30-apr-94 Added EISA support. Added DE422 recognition. - 0.36 16-may-94 DE422 fix released. - 0.37 22-jul-94 Added MODULE support - 0.38 15-aug-94 Added DBR ROM switch in depca_close(). - Multi DEPCA bug fix. - 0.38axp 15-sep-94 Special version for Alpha AXP Linux V1.0. - 0.381 12-dec-94 Added DE101 recognition, fix multicast bug. - 0.382 9-feb-95 Fix recognition bug reported by . - 0.383 22-feb-95 Fix for conflict with VESA SCSI reported by - - 0.384 17-mar-95 Fix a ring full bug reported by - 0.385 3-apr-95 Fix a recognition bug reported by - - 0.386 21-apr-95 Fix the last fix...sorry, must be galloping senility - 0.40 25-May-95 Rewrite for portability & updated. - ALPHA support from - 0.41 26-Jun-95 Added verify_area() calls in depca_ioctl() from - suggestion by - 0.42 27-Dec-95 Add 'mem' shared memory assignment for loadable - modules. - Add 'adapter_name' for loadable modules when no PROM. - Both above from a suggestion by - . - Add new multicasting code. - 0.421 22-Apr-96 Fix alloc_device() bug - 0.422 29-Apr-96 Fix depca_hw_init() bug - 0.423 7-Jun-96 Fix module load bug - 0.43 16-Aug-96 Update alloc_device() to conform to de4x5.c - 0.44 1-Sep-97 Fix *_probe() to test check_region() first - bug - reported by - 0.45 3-Nov-98 Added support for MCA EtherWORKS (DE210/DE212) cards - by - 0.451 5-Nov-98 Fixed mca stuff cuz I'm a dummy. - 0.5 14-Nov-98 Re-spin for 2.1.x kernels. - 0.51 27-Jun-99 Correct received packet length for CRC from - report by - 0.52 16-Oct-00 Fixes for 2.3 io memory accesses - Fix show-stopper (ints left masked) in depca_interrupt - by - 0.53 12-Jan-01 Release resources on failure, bss tidbits - by acme@conectiva.com.br - 0.54 08-Nov-01 use library crc32 functions - by Matt_Domsch@dell.com - 0.55 01-Mar-03 Use EISA/sysfs framework - - ========================================================================= -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#ifdef CONFIG_EISA -#include -#endif - -#include "depca.h" - -static char version[] __initdata = "depca.c:v0.53 2001/1/12 davies@maniac.ultranet.com\n"; - -#ifdef DEPCA_DEBUG -static int depca_debug = DEPCA_DEBUG; -#else -static int depca_debug = 1; -#endif - -#define DEPCA_NDA 0xffe0 /* No Device Address */ - -#define TX_TIMEOUT (1*HZ) - -/* -** Ethernet PROM defines -*/ -#define PROBE_LENGTH 32 -#define ETH_PROM_SIG 0xAA5500FFUL - -/* -** Set the number of Tx and Rx buffers. Ensure that the memory requested -** here is <= to the amount of shared memory set up by the board switches. -** The number of descriptors MUST BE A POWER OF 2. -** -** total_memory = NUM_RX_DESC*(8+RX_BUFF_SZ) + NUM_TX_DESC*(8+TX_BUFF_SZ) -*/ -#define NUM_RX_DESC 8 /* Number of RX descriptors */ -#define NUM_TX_DESC 8 /* Number of TX descriptors */ -#define RX_BUFF_SZ 1536 /* Buffer size for each Rx buffer */ -#define TX_BUFF_SZ 1536 /* Buffer size for each Tx buffer */ - -/* -** EISA bus defines -*/ -#define DEPCA_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */ - -/* -** ISA Bus defines -*/ -#define DEPCA_RAM_BASE_ADDRESSES {0xc0000,0xd0000,0xe0000,0x00000} -#define DEPCA_TOTAL_SIZE 0x10 - -static struct { - u_long iobase; - struct platform_device *device; -} depca_io_ports[] = { - { 0x300, NULL }, - { 0x200, NULL }, - { 0 , NULL }, -}; - -/* -** Name <-> Adapter mapping -*/ -#define DEPCA_SIGNATURE {"DEPCA",\ - "DE100","DE101",\ - "DE200","DE201","DE202",\ - "DE210","DE212",\ - "DE422",\ - ""} - -static char* __initdata depca_signature[] = DEPCA_SIGNATURE; - -enum depca_type { - DEPCA, de100, de101, de200, de201, de202, de210, de212, de422, unknown -}; - -static char depca_string[] = "depca"; - -static int depca_device_remove (struct device *device); - -#ifdef CONFIG_EISA -static struct eisa_device_id depca_eisa_ids[] = { - { "DEC4220", de422 }, - { "" } -}; -MODULE_DEVICE_TABLE(eisa, depca_eisa_ids); - -static int depca_eisa_probe (struct device *device); - -static struct eisa_driver depca_eisa_driver = { - .id_table = depca_eisa_ids, - .driver = { - .name = depca_string, - .probe = depca_eisa_probe, - .remove = depca_device_remove - } -}; -#endif - -static int depca_isa_probe (struct platform_device *); - -static int depca_isa_remove(struct platform_device *pdev) -{ - return depca_device_remove(&pdev->dev); -} - -static struct platform_driver depca_isa_driver = { - .probe = depca_isa_probe, - .remove = depca_isa_remove, - .driver = { - .name = depca_string, - }, -}; - -/* -** Miscellaneous info... -*/ -#define DEPCA_STRLEN 16 - -/* -** Memory Alignment. Each descriptor is 4 longwords long. To force a -** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and -** DESC_ALIGN. DEPCA_ALIGN aligns the start address of the private memory area -** and hence the RX descriptor ring's first entry. -*/ -#define DEPCA_ALIGN4 ((u_long)4 - 1) /* 1 longword align */ -#define DEPCA_ALIGN8 ((u_long)8 - 1) /* 2 longword (quadword) align */ -#define DEPCA_ALIGN DEPCA_ALIGN8 /* Keep the LANCE happy... */ - -/* -** The DEPCA Rx and Tx ring descriptors. -*/ -struct depca_rx_desc { - volatile s32 base; - s16 buf_length; /* This length is negative 2's complement! */ - s16 msg_length; /* This length is "normal". */ -}; - -struct depca_tx_desc { - volatile s32 base; - s16 length; /* This length is negative 2's complement! */ - s16 misc; /* Errors and TDR info */ -}; - -#define LA_MASK 0x0000ffff /* LANCE address mask for mapping network RAM - to LANCE memory address space */ - -/* -** The Lance initialization block, described in databook, in common memory. -*/ -struct depca_init { - u16 mode; /* Mode register */ - u8 phys_addr[ETH_ALEN]; /* Physical ethernet address */ - u8 mcast_table[8]; /* Multicast Hash Table. */ - u32 rx_ring; /* Rx ring base pointer & ring length */ - u32 tx_ring; /* Tx ring base pointer & ring length */ -}; - -#define DEPCA_PKT_STAT_SZ 16 -#define DEPCA_PKT_BIN_SZ 128 /* Should be >=100 unless you - increase DEPCA_PKT_STAT_SZ */ -struct depca_private { - char adapter_name[DEPCA_STRLEN]; /* /proc/ioports string */ - enum depca_type adapter; /* Adapter type */ - enum { - DEPCA_BUS_ISA = 1, - DEPCA_BUS_EISA, - } depca_bus; /* type of bus */ - struct depca_init init_block; /* Shadow Initialization block */ -/* CPU address space fields */ - struct depca_rx_desc __iomem *rx_ring; /* Pointer to start of RX descriptor ring */ - struct depca_tx_desc __iomem *tx_ring; /* Pointer to start of TX descriptor ring */ - void __iomem *rx_buff[NUM_RX_DESC]; /* CPU virt address of sh'd memory buffs */ - void __iomem *tx_buff[NUM_TX_DESC]; /* CPU virt address of sh'd memory buffs */ - void __iomem *sh_mem; /* CPU mapped virt address of device RAM */ - u_long mem_start; /* Bus address of device RAM (before remap) */ - u_long mem_len; /* device memory size */ -/* Device address space fields */ - u_long device_ram_start; /* Start of RAM in device addr space */ -/* Offsets used in both address spaces */ - u_long rx_ring_offset; /* Offset from start of RAM to rx_ring */ - u_long tx_ring_offset; /* Offset from start of RAM to tx_ring */ - u_long buffs_offset; /* LANCE Rx and Tx buffers start address. */ -/* Kernel-only (not device) fields */ - int rx_new, tx_new; /* The next free ring entry */ - int rx_old, tx_old; /* The ring entries to be free()ed. */ - spinlock_t lock; - struct { /* Private stats counters */ - u32 bins[DEPCA_PKT_STAT_SZ]; - u32 unicast; - u32 multicast; - u32 broadcast; - u32 excessive_collisions; - u32 tx_underruns; - u32 excessive_underruns; - } pktStats; - int txRingMask; /* TX ring mask */ - int rxRingMask; /* RX ring mask */ - s32 rx_rlen; /* log2(rxRingMask+1) for the descriptors */ - s32 tx_rlen; /* log2(txRingMask+1) for the descriptors */ -}; - -/* -** The transmit ring full condition is described by the tx_old and tx_new -** pointers by: -** tx_old = tx_new Empty ring -** tx_old = tx_new+1 Full ring -** tx_old+txRingMask = tx_new Full ring (wrapped condition) -*/ -#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\ - lp->tx_old+lp->txRingMask-lp->tx_new:\ - lp->tx_old -lp->tx_new-1) - -/* -** Public Functions -*/ -static int depca_open(struct net_device *dev); -static netdev_tx_t depca_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static irqreturn_t depca_interrupt(int irq, void *dev_id); -static int depca_close(struct net_device *dev); -static int depca_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static void depca_tx_timeout(struct net_device *dev); -static void set_multicast_list(struct net_device *dev); - -/* -** Private functions -*/ -static void depca_init_ring(struct net_device *dev); -static int depca_rx(struct net_device *dev); -static int depca_tx(struct net_device *dev); - -static void LoadCSRs(struct net_device *dev); -static int InitRestartDepca(struct net_device *dev); -static int DepcaSignature(char *name, u_long paddr); -static int DevicePresent(u_long ioaddr); -static int get_hw_addr(struct net_device *dev); -static void SetMulticastFilter(struct net_device *dev); -static int load_packet(struct net_device *dev, struct sk_buff *skb); -static void depca_dbg_open(struct net_device *dev); - -static u_char de1xx_irq[] __initdata = { 2, 3, 4, 5, 7, 9, 0 }; -static u_char de2xx_irq[] __initdata = { 5, 9, 10, 11, 15, 0 }; -static u_char de422_irq[] __initdata = { 5, 9, 10, 11, 0 }; -static u_char *depca_irq; - -static int irq; -static int io; -static char *adapter_name; -static int mem; /* For loadable module assignment - use insmod mem=0x????? .... */ -module_param (irq, int, 0); -module_param (io, int, 0); -module_param (adapter_name, charp, 0); -module_param (mem, int, 0); -MODULE_PARM_DESC(irq, "DEPCA IRQ number"); -MODULE_PARM_DESC(io, "DEPCA I/O base address"); -MODULE_PARM_DESC(adapter_name, "DEPCA adapter name"); -MODULE_PARM_DESC(mem, "DEPCA shared memory address"); -MODULE_LICENSE("GPL"); - -/* -** Miscellaneous defines... -*/ -#define STOP_DEPCA \ - outw(CSR0, DEPCA_ADDR);\ - outw(STOP, DEPCA_DATA) - -static const struct net_device_ops depca_netdev_ops = { - .ndo_open = depca_open, - .ndo_start_xmit = depca_start_xmit, - .ndo_stop = depca_close, - .ndo_set_rx_mode = set_multicast_list, - .ndo_do_ioctl = depca_ioctl, - .ndo_tx_timeout = depca_tx_timeout, - .ndo_change_mtu = eth_change_mtu, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int __init depca_hw_init (struct net_device *dev, struct device *device) -{ - struct depca_private *lp; - int i, j, offset, netRAM, mem_len, status = 0; - s16 nicsr; - u_long ioaddr; - u_long mem_start; - - /* - * We are now supposed to enter this function with the - * following fields filled with proper values : - * - * dev->base_addr - * lp->mem_start - * lp->depca_bus - * lp->adapter - * - * dev->irq can be set if known from device configuration (on - * MCA or EISA) or module option. Otherwise, it will be auto - * detected. - */ - - ioaddr = dev->base_addr; - - STOP_DEPCA; - - nicsr = inb(DEPCA_NICSR); - nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | IM); - outb(nicsr, DEPCA_NICSR); - - if (inw(DEPCA_DATA) != STOP) { - return -ENXIO; - } - - lp = netdev_priv(dev); - mem_start = lp->mem_start; - - if (!mem_start || lp->adapter < DEPCA || lp->adapter >=unknown) - return -ENXIO; - - printk("%s: %s at 0x%04lx", - dev_name(device), depca_signature[lp->adapter], ioaddr); - - switch (lp->depca_bus) { -#ifdef CONFIG_EISA - case DEPCA_BUS_EISA: - printk(" (EISA slot %d)", to_eisa_device(device)->slot); - break; -#endif - - case DEPCA_BUS_ISA: - break; - - default: - printk("Unknown DEPCA bus %d\n", lp->depca_bus); - return -ENXIO; - } - - printk(", h/w address "); - status = get_hw_addr(dev); - printk("%pM", dev->dev_addr); - if (status != 0) { - printk(" which has an Ethernet PROM CRC error.\n"); - return -ENXIO; - } - - /* Set up the maximum amount of network RAM(kB) */ - netRAM = ((lp->adapter != DEPCA) ? 64 : 48); - if ((nicsr & _128KB) && (lp->adapter == de422)) - netRAM = 128; - - /* Shared Memory Base Address */ - if (nicsr & BUF) { - nicsr &= ~BS; /* DEPCA RAM in top 32k */ - netRAM -= 32; - mem_start += 0x8000; - } - - if ((mem_len = (NUM_RX_DESC * (sizeof(struct depca_rx_desc) + RX_BUFF_SZ) + NUM_TX_DESC * (sizeof(struct depca_tx_desc) + TX_BUFF_SZ) + sizeof(struct depca_init))) - > (netRAM << 10)) { - printk(",\n requests %dkB RAM: only %dkB is available!\n", (mem_len >> 10), netRAM); - return -ENXIO; - } - - printk(",\n has %dkB RAM at 0x%.5lx", netRAM, mem_start); - - /* Enable the shadow RAM. */ - if (lp->adapter != DEPCA) { - nicsr |= SHE; - outb(nicsr, DEPCA_NICSR); - } - - spin_lock_init(&lp->lock); - sprintf(lp->adapter_name, "%s (%s)", - depca_signature[lp->adapter], dev_name(device)); - status = -EBUSY; - - /* Initialisation Block */ - if (!request_mem_region (mem_start, mem_len, lp->adapter_name)) { - printk(KERN_ERR "depca: cannot request ISA memory, aborting\n"); - goto out_priv; - } - - status = -EIO; - lp->sh_mem = ioremap(mem_start, mem_len); - if (lp->sh_mem == NULL) { - printk(KERN_ERR "depca: cannot remap ISA memory, aborting\n"); - goto out1; - } - - lp->mem_start = mem_start; - lp->mem_len = mem_len; - lp->device_ram_start = mem_start & LA_MASK; - - offset = 0; - offset += sizeof(struct depca_init); - - /* Tx & Rx descriptors (aligned to a quadword boundary) */ - offset = (offset + DEPCA_ALIGN) & ~DEPCA_ALIGN; - lp->rx_ring = lp->sh_mem + offset; - lp->rx_ring_offset = offset; - - offset += (sizeof(struct depca_rx_desc) * NUM_RX_DESC); - lp->tx_ring = lp->sh_mem + offset; - lp->tx_ring_offset = offset; - - offset += (sizeof(struct depca_tx_desc) * NUM_TX_DESC); - - lp->buffs_offset = offset; - - /* Finish initialising the ring information. */ - lp->rxRingMask = NUM_RX_DESC - 1; - lp->txRingMask = NUM_TX_DESC - 1; - - /* Calculate Tx/Rx RLEN size for the descriptors. */ - for (i = 0, j = lp->rxRingMask; j > 0; i++) { - j >>= 1; - } - lp->rx_rlen = (s32) (i << 29); - for (i = 0, j = lp->txRingMask; j > 0; i++) { - j >>= 1; - } - lp->tx_rlen = (s32) (i << 29); - - /* Load the initialisation block */ - depca_init_ring(dev); - - /* Initialise the control and status registers */ - LoadCSRs(dev); - - /* Enable DEPCA board interrupts for autoprobing */ - nicsr = ((nicsr & ~IM) | IEN); - outb(nicsr, DEPCA_NICSR); - - /* To auto-IRQ we enable the initialization-done and DMA err, - interrupts. For now we will always get a DMA error. */ - if (dev->irq < 2) { - unsigned char irqnum; - unsigned long irq_mask, delay; - - irq_mask = probe_irq_on(); - - /* Assign the correct irq list */ - switch (lp->adapter) { - case DEPCA: - case de100: - case de101: - depca_irq = de1xx_irq; - break; - case de200: - case de201: - case de202: - case de210: - case de212: - depca_irq = de2xx_irq; - break; - case de422: - depca_irq = de422_irq; - break; - - default: - break; /* Not reached */ - } - - /* Trigger an initialization just for the interrupt. */ - outw(INEA | INIT, DEPCA_DATA); - - delay = jiffies + HZ/50; - while (time_before(jiffies, delay)) - yield(); - - irqnum = probe_irq_off(irq_mask); - - status = -ENXIO; - if (!irqnum) { - printk(" and failed to detect IRQ line.\n"); - goto out2; - } else { - for (dev->irq = 0, i = 0; (depca_irq[i]) && (!dev->irq); i++) - if (irqnum == depca_irq[i]) { - dev->irq = irqnum; - printk(" and uses IRQ%d.\n", dev->irq); - } - - if (!dev->irq) { - printk(" but incorrect IRQ line detected.\n"); - goto out2; - } - } - } else { - printk(" and assigned IRQ%d.\n", dev->irq); - } - - if (depca_debug > 1) { - printk(version); - } - - /* The DEPCA-specific entries in the device structure. */ - dev->netdev_ops = &depca_netdev_ops; - dev->watchdog_timeo = TX_TIMEOUT; - - dev->mem_start = 0; - - dev_set_drvdata(device, dev); - SET_NETDEV_DEV (dev, device); - - status = register_netdev(dev); - if (status == 0) - return 0; -out2: - iounmap(lp->sh_mem); -out1: - release_mem_region (mem_start, mem_len); -out_priv: - return status; -} - - -static int depca_open(struct net_device *dev) -{ - struct depca_private *lp = netdev_priv(dev); - u_long ioaddr = dev->base_addr; - s16 nicsr; - int status = 0; - - STOP_DEPCA; - nicsr = inb(DEPCA_NICSR); - - /* Make sure the shadow RAM is enabled */ - if (lp->adapter != DEPCA) { - nicsr |= SHE; - outb(nicsr, DEPCA_NICSR); - } - - /* Re-initialize the DEPCA... */ - depca_init_ring(dev); - LoadCSRs(dev); - - depca_dbg_open(dev); - - if (request_irq(dev->irq, depca_interrupt, 0, lp->adapter_name, dev)) { - printk("depca_open(): Requested IRQ%d is busy\n", dev->irq); - status = -EAGAIN; - } else { - - /* Enable DEPCA board interrupts and turn off LED */ - nicsr = ((nicsr & ~IM & ~LED) | IEN); - outb(nicsr, DEPCA_NICSR); - outw(CSR0, DEPCA_ADDR); - - netif_start_queue(dev); - - status = InitRestartDepca(dev); - - if (depca_debug > 1) { - printk("CSR0: 0x%4.4x\n", inw(DEPCA_DATA)); - printk("nicsr: 0x%02x\n", inb(DEPCA_NICSR)); - } - } - return status; -} - -/* Initialize the lance Rx and Tx descriptor rings. */ -static void depca_init_ring(struct net_device *dev) -{ - struct depca_private *lp = netdev_priv(dev); - u_int i; - u_long offset; - - /* Lock out other processes whilst setting up the hardware */ - netif_stop_queue(dev); - - lp->rx_new = lp->tx_new = 0; - lp->rx_old = lp->tx_old = 0; - - /* Initialize the base address and length of each buffer in the ring */ - for (i = 0; i <= lp->rxRingMask; i++) { - offset = lp->buffs_offset + i * RX_BUFF_SZ; - writel((lp->device_ram_start + offset) | R_OWN, &lp->rx_ring[i].base); - writew(-RX_BUFF_SZ, &lp->rx_ring[i].buf_length); - lp->rx_buff[i] = lp->sh_mem + offset; - } - - for (i = 0; i <= lp->txRingMask; i++) { - offset = lp->buffs_offset + (i + lp->rxRingMask + 1) * TX_BUFF_SZ; - writel((lp->device_ram_start + offset) & 0x00ffffff, &lp->tx_ring[i].base); - lp->tx_buff[i] = lp->sh_mem + offset; - } - - /* Set up the initialization block */ - lp->init_block.rx_ring = (lp->device_ram_start + lp->rx_ring_offset) | lp->rx_rlen; - lp->init_block.tx_ring = (lp->device_ram_start + lp->tx_ring_offset) | lp->tx_rlen; - - SetMulticastFilter(dev); - - for (i = 0; i < ETH_ALEN; i++) { - lp->init_block.phys_addr[i] = dev->dev_addr[i]; - } - - lp->init_block.mode = 0x0000; /* Enable the Tx and Rx */ -} - - -static void depca_tx_timeout(struct net_device *dev) -{ - u_long ioaddr = dev->base_addr; - - printk("%s: transmit timed out, status %04x, resetting.\n", dev->name, inw(DEPCA_DATA)); - - STOP_DEPCA; - depca_init_ring(dev); - LoadCSRs(dev); - dev->trans_start = jiffies; /* prevent tx timeout */ - netif_wake_queue(dev); - InitRestartDepca(dev); -} - - -/* -** Writes a socket buffer to TX descriptor ring and starts transmission -*/ -static netdev_tx_t depca_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct depca_private *lp = netdev_priv(dev); - u_long ioaddr = dev->base_addr; - int status = 0; - - /* Transmitter timeout, serious problems. */ - if (skb->len < 1) - goto out; - - if (skb_padto(skb, ETH_ZLEN)) - goto out; - - netif_stop_queue(dev); - - if (TX_BUFFS_AVAIL) { /* Fill in a Tx ring entry */ - status = load_packet(dev, skb); - - if (!status) { - /* Trigger an immediate send demand. */ - outw(CSR0, DEPCA_ADDR); - outw(INEA | TDMD, DEPCA_DATA); - - dev_kfree_skb(skb); - } - if (TX_BUFFS_AVAIL) - netif_start_queue(dev); - } else - status = NETDEV_TX_LOCKED; - - out: - return status; -} - -/* -** The DEPCA interrupt handler. -*/ -static irqreturn_t depca_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct depca_private *lp; - s16 csr0, nicsr; - u_long ioaddr; - - if (dev == NULL) { - printk("depca_interrupt(): irq %d for unknown device.\n", irq); - return IRQ_NONE; - } - - lp = netdev_priv(dev); - ioaddr = dev->base_addr; - - spin_lock(&lp->lock); - - /* mask the DEPCA board interrupts and turn on the LED */ - nicsr = inb(DEPCA_NICSR); - nicsr |= (IM | LED); - outb(nicsr, DEPCA_NICSR); - - outw(CSR0, DEPCA_ADDR); - csr0 = inw(DEPCA_DATA); - - /* Acknowledge all of the current interrupt sources ASAP. */ - outw(csr0 & INTE, DEPCA_DATA); - - if (csr0 & RINT) /* Rx interrupt (packet arrived) */ - depca_rx(dev); - - if (csr0 & TINT) /* Tx interrupt (packet sent) */ - depca_tx(dev); - - /* Any resources available? */ - if ((TX_BUFFS_AVAIL >= 0) && netif_queue_stopped(dev)) { - netif_wake_queue(dev); - } - - /* Unmask the DEPCA board interrupts and turn off the LED */ - nicsr = (nicsr & ~IM & ~LED); - outb(nicsr, DEPCA_NICSR); - - spin_unlock(&lp->lock); - return IRQ_HANDLED; -} - -/* Called with lp->lock held */ -static int depca_rx(struct net_device *dev) -{ - struct depca_private *lp = netdev_priv(dev); - int i, entry; - s32 status; - - for (entry = lp->rx_new; !(readl(&lp->rx_ring[entry].base) & R_OWN); entry = lp->rx_new) { - status = readl(&lp->rx_ring[entry].base) >> 16; - if (status & R_STP) { /* Remember start of frame */ - lp->rx_old = entry; - } - if (status & R_ENP) { /* Valid frame status */ - if (status & R_ERR) { /* There was an error. */ - dev->stats.rx_errors++; /* Update the error stats. */ - if (status & R_FRAM) - dev->stats.rx_frame_errors++; - if (status & R_OFLO) - dev->stats.rx_over_errors++; - if (status & R_CRC) - dev->stats.rx_crc_errors++; - if (status & R_BUFF) - dev->stats.rx_fifo_errors++; - } else { - short len, pkt_len = readw(&lp->rx_ring[entry].msg_length) - 4; - struct sk_buff *skb; - - skb = netdev_alloc_skb(dev, pkt_len + 2); - if (skb != NULL) { - unsigned char *buf; - skb_reserve(skb, 2); /* 16 byte align the IP header */ - buf = skb_put(skb, pkt_len); - if (entry < lp->rx_old) { /* Wrapped buffer */ - len = (lp->rxRingMask - lp->rx_old + 1) * RX_BUFF_SZ; - memcpy_fromio(buf, lp->rx_buff[lp->rx_old], len); - memcpy_fromio(buf + len, lp->rx_buff[0], pkt_len - len); - } else { /* Linear buffer */ - memcpy_fromio(buf, lp->rx_buff[lp->rx_old], pkt_len); - } - - /* - ** Notify the upper protocol layers that there is another - ** packet to handle - */ - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - - /* - ** Update stats - */ - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - for (i = 1; i < DEPCA_PKT_STAT_SZ - 1; i++) { - if (pkt_len < (i * DEPCA_PKT_BIN_SZ)) { - lp->pktStats.bins[i]++; - i = DEPCA_PKT_STAT_SZ; - } - } - if (is_multicast_ether_addr(buf)) { - if (is_broadcast_ether_addr(buf)) { - lp->pktStats.broadcast++; - } else { - lp->pktStats.multicast++; - } - } else if (ether_addr_equal(buf, - dev->dev_addr)) { - lp->pktStats.unicast++; - } - - lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */ - if (lp->pktStats.bins[0] == 0) { /* Reset counters */ - memset((char *) &lp->pktStats, 0, sizeof(lp->pktStats)); - } - } else { - printk("%s: Memory squeeze, deferring packet.\n", dev->name); - dev->stats.rx_dropped++; /* Really, deferred. */ - break; - } - } - /* Change buffer ownership for this last frame, back to the adapter */ - for (; lp->rx_old != entry; lp->rx_old = (lp->rx_old + 1) & lp->rxRingMask) { - writel(readl(&lp->rx_ring[lp->rx_old].base) | R_OWN, &lp->rx_ring[lp->rx_old].base); - } - writel(readl(&lp->rx_ring[entry].base) | R_OWN, &lp->rx_ring[entry].base); - } - - /* - ** Update entry information - */ - lp->rx_new = (lp->rx_new + 1) & lp->rxRingMask; - } - - return 0; -} - -/* -** Buffer sent - check for buffer errors. -** Called with lp->lock held -*/ -static int depca_tx(struct net_device *dev) -{ - struct depca_private *lp = netdev_priv(dev); - int entry; - s32 status; - u_long ioaddr = dev->base_addr; - - for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) { - status = readl(&lp->tx_ring[entry].base) >> 16; - - if (status < 0) { /* Packet not yet sent! */ - break; - } else if (status & T_ERR) { /* An error occurred. */ - status = readl(&lp->tx_ring[entry].misc); - dev->stats.tx_errors++; - if (status & TMD3_RTRY) - dev->stats.tx_aborted_errors++; - if (status & TMD3_LCAR) - dev->stats.tx_carrier_errors++; - if (status & TMD3_LCOL) - dev->stats.tx_window_errors++; - if (status & TMD3_UFLO) - dev->stats.tx_fifo_errors++; - if (status & (TMD3_BUFF | TMD3_UFLO)) { - /* Trigger an immediate send demand. */ - outw(CSR0, DEPCA_ADDR); - outw(INEA | TDMD, DEPCA_DATA); - } - } else if (status & (T_MORE | T_ONE)) { - dev->stats.collisions++; - } else { - dev->stats.tx_packets++; - } - - /* Update all the pointers */ - lp->tx_old = (lp->tx_old + 1) & lp->txRingMask; - } - - return 0; -} - -static int depca_close(struct net_device *dev) -{ - struct depca_private *lp = netdev_priv(dev); - s16 nicsr; - u_long ioaddr = dev->base_addr; - - netif_stop_queue(dev); - - outw(CSR0, DEPCA_ADDR); - - if (depca_debug > 1) { - printk("%s: Shutting down ethercard, status was %2.2x.\n", dev->name, inw(DEPCA_DATA)); - } - - /* - ** We stop the DEPCA here -- it occasionally polls - ** memory if we don't. - */ - outw(STOP, DEPCA_DATA); - - /* - ** Give back the ROM in case the user wants to go to DOS - */ - if (lp->adapter != DEPCA) { - nicsr = inb(DEPCA_NICSR); - nicsr &= ~SHE; - outb(nicsr, DEPCA_NICSR); - } - - /* - ** Free the associated irq - */ - free_irq(dev->irq, dev); - return 0; -} - -static void LoadCSRs(struct net_device *dev) -{ - struct depca_private *lp = netdev_priv(dev); - u_long ioaddr = dev->base_addr; - - outw(CSR1, DEPCA_ADDR); /* initialisation block address LSW */ - outw((u16) lp->device_ram_start, DEPCA_DATA); - outw(CSR2, DEPCA_ADDR); /* initialisation block address MSW */ - outw((u16) (lp->device_ram_start >> 16), DEPCA_DATA); - outw(CSR3, DEPCA_ADDR); /* ALE control */ - outw(ACON, DEPCA_DATA); - - outw(CSR0, DEPCA_ADDR); /* Point back to CSR0 */ -} - -static int InitRestartDepca(struct net_device *dev) -{ - struct depca_private *lp = netdev_priv(dev); - u_long ioaddr = dev->base_addr; - int i, status = 0; - - /* Copy the shadow init_block to shared memory */ - memcpy_toio(lp->sh_mem, &lp->init_block, sizeof(struct depca_init)); - - outw(CSR0, DEPCA_ADDR); /* point back to CSR0 */ - outw(INIT, DEPCA_DATA); /* initialize DEPCA */ - - /* wait for lance to complete initialisation */ - for (i = 0; (i < 100) && !(inw(DEPCA_DATA) & IDON); i++); - - if (i != 100) { - /* clear IDON by writing a "1", enable interrupts and start lance */ - outw(IDON | INEA | STRT, DEPCA_DATA); - if (depca_debug > 2) { - printk("%s: DEPCA open after %d ticks, init block 0x%08lx csr0 %4.4x.\n", dev->name, i, lp->mem_start, inw(DEPCA_DATA)); - } - } else { - printk("%s: DEPCA unopen after %d ticks, init block 0x%08lx csr0 %4.4x.\n", dev->name, i, lp->mem_start, inw(DEPCA_DATA)); - status = -1; - } - - return status; -} - -/* -** Set or clear the multicast filter for this adaptor. -*/ -static void set_multicast_list(struct net_device *dev) -{ - struct depca_private *lp = netdev_priv(dev); - u_long ioaddr = dev->base_addr; - - netif_stop_queue(dev); - while (lp->tx_old != lp->tx_new); /* Wait for the ring to empty */ - - STOP_DEPCA; /* Temporarily stop the depca. */ - depca_init_ring(dev); /* Initialize the descriptor rings */ - - if (dev->flags & IFF_PROMISC) { /* Set promiscuous mode */ - lp->init_block.mode |= PROM; - } else { - SetMulticastFilter(dev); - lp->init_block.mode &= ~PROM; /* Unset promiscuous mode */ - } - - LoadCSRs(dev); /* Reload CSR3 */ - InitRestartDepca(dev); /* Resume normal operation. */ - netif_start_queue(dev); /* Unlock the TX ring */ -} - -/* -** Calculate the hash code and update the logical address filter -** from a list of ethernet multicast addresses. -** Big endian crc one liner is mine, all mine, ha ha ha ha! -** LANCE calculates its hash codes big endian. -*/ -static void SetMulticastFilter(struct net_device *dev) -{ - struct depca_private *lp = netdev_priv(dev); - struct netdev_hw_addr *ha; - int i, j, bit, byte; - u16 hashcode; - u32 crc; - - if (dev->flags & IFF_ALLMULTI) { /* Set all multicast bits */ - for (i = 0; i < (HASH_TABLE_LEN >> 3); i++) { - lp->init_block.mcast_table[i] = (char) 0xff; - } - } else { - for (i = 0; i < (HASH_TABLE_LEN >> 3); i++) { /* Clear the multicast table */ - lp->init_block.mcast_table[i] = 0; - } - /* Add multicast addresses */ - netdev_for_each_mc_addr(ha, dev) { - crc = ether_crc(ETH_ALEN, ha->addr); - hashcode = (crc & 1); /* hashcode is 6 LSb of CRC ... */ - for (j = 0; j < 5; j++) { /* ... in reverse order. */ - hashcode = (hashcode << 1) | ((crc >>= 1) & 1); - } - - byte = hashcode >> 3; /* bit[3-5] -> byte in filter */ - bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */ - lp->init_block.mcast_table[byte] |= bit; - } - } -} - -static int __init depca_common_init (u_long ioaddr, struct net_device **devp) -{ - int status = 0; - - if (!request_region (ioaddr, DEPCA_TOTAL_SIZE, depca_string)) { - status = -EBUSY; - goto out; - } - - if (DevicePresent(ioaddr)) { - status = -ENODEV; - goto out_release; - } - - if (!(*devp = alloc_etherdev (sizeof (struct depca_private)))) { - status = -ENOMEM; - goto out_release; - } - - return 0; - - out_release: - release_region (ioaddr, DEPCA_TOTAL_SIZE); - out: - return status; -} - -/* -** ISA bus I/O device probe -*/ - -static void __init depca_platform_probe (void) -{ - int i; - struct platform_device *pldev; - - for (i = 0; depca_io_ports[i].iobase; i++) { - depca_io_ports[i].device = NULL; - - /* if an address has been specified on the command - * line, use it (if valid) */ - if (io && io != depca_io_ports[i].iobase) - continue; - - pldev = platform_device_alloc(depca_string, i); - if (!pldev) - continue; - - pldev->dev.platform_data = (void *) depca_io_ports[i].iobase; - depca_io_ports[i].device = pldev; - - if (platform_device_add(pldev)) { - depca_io_ports[i].device = NULL; - pldev->dev.platform_data = NULL; - platform_device_put(pldev); - continue; - } - - if (!pldev->dev.driver) { - /* The driver was not bound to this device, there was - * no hardware at this address. Unregister it, as the - * release function will take care of freeing the - * allocated structure */ - - depca_io_ports[i].device = NULL; - pldev->dev.platform_data = NULL; - platform_device_unregister (pldev); - } - } -} - -static enum depca_type __init depca_shmem_probe (ulong *mem_start) -{ - u_long mem_base[] = DEPCA_RAM_BASE_ADDRESSES; - enum depca_type adapter = unknown; - int i; - - for (i = 0; mem_base[i]; i++) { - *mem_start = mem ? mem : mem_base[i]; - adapter = DepcaSignature (adapter_name, *mem_start); - if (adapter != unknown) - break; - } - - return adapter; -} - -static int depca_isa_probe(struct platform_device *device) -{ - struct net_device *dev; - struct depca_private *lp; - u_long ioaddr, mem_start = 0; - enum depca_type adapter = unknown; - int status = 0; - - ioaddr = (u_long) device->dev.platform_data; - - if ((status = depca_common_init (ioaddr, &dev))) - goto out; - - adapter = depca_shmem_probe (&mem_start); - - if (adapter == unknown) { - status = -ENODEV; - goto out_free; - } - - dev->base_addr = ioaddr; - dev->irq = irq; /* Use whatever value the user gave - * us, and 0 if he didn't. */ - lp = netdev_priv(dev); - lp->depca_bus = DEPCA_BUS_ISA; - lp->adapter = adapter; - lp->mem_start = mem_start; - - if ((status = depca_hw_init(dev, &device->dev))) - goto out_free; - - return 0; - - out_free: - free_netdev (dev); - release_region (ioaddr, DEPCA_TOTAL_SIZE); - out: - return status; -} - -/* -** EISA callbacks from sysfs. -*/ - -#ifdef CONFIG_EISA -static int __init depca_eisa_probe (struct device *device) -{ - enum depca_type adapter = unknown; - struct eisa_device *edev; - struct net_device *dev; - struct depca_private *lp; - u_long ioaddr, mem_start; - int status = 0; - - edev = to_eisa_device (device); - ioaddr = edev->base_addr + DEPCA_EISA_IO_PORTS; - - if ((status = depca_common_init (ioaddr, &dev))) - goto out; - - /* It would have been nice to get card configuration from the - * card. Unfortunately, this register is write-only (shares - * it's address with the ethernet prom)... As we don't parse - * the EISA configuration structures (yet... :-), just rely on - * the ISA probing to sort it out... */ - - adapter = depca_shmem_probe (&mem_start); - if (adapter == unknown) { - status = -ENODEV; - goto out_free; - } - - dev->base_addr = ioaddr; - dev->irq = irq; - lp = netdev_priv(dev); - lp->depca_bus = DEPCA_BUS_EISA; - lp->adapter = edev->id.driver_data; - lp->mem_start = mem_start; - - if ((status = depca_hw_init(dev, device))) - goto out_free; - - return 0; - - out_free: - free_netdev (dev); - release_region (ioaddr, DEPCA_TOTAL_SIZE); - out: - return status; -} -#endif - -static int depca_device_remove(struct device *device) -{ - struct net_device *dev; - struct depca_private *lp; - int bus; - - dev = dev_get_drvdata(device); - lp = netdev_priv(dev); - - unregister_netdev (dev); - iounmap (lp->sh_mem); - release_mem_region (lp->mem_start, lp->mem_len); - release_region (dev->base_addr, DEPCA_TOTAL_SIZE); - bus = lp->depca_bus; - free_netdev (dev); - - return 0; -} - -/* -** Look for a particular board name in the on-board Remote Diagnostics -** and Boot (readb) ROM. This will also give us a clue to the network RAM -** base address. -*/ -static int __init DepcaSignature(char *name, u_long base_addr) -{ - u_int i, j, k; - void __iomem *ptr; - char tmpstr[16]; - u_long prom_addr = base_addr + 0xc000; - u_long mem_addr = base_addr + 0x8000; /* 32KB */ - - /* Can't reserve the prom region, it is already marked as - * used, at least on x86. Instead, reserve a memory region a - * board would certainly use. If it works, go ahead. If not, - * run like hell... */ - - if (!request_mem_region (mem_addr, 16, depca_string)) - return unknown; - - /* Copy the first 16 bytes of ROM */ - - ptr = ioremap(prom_addr, 16); - if (ptr == NULL) { - printk(KERN_ERR "depca: I/O remap failed at %lx\n", prom_addr); - return unknown; - } - for (i = 0; i < 16; i++) { - tmpstr[i] = readb(ptr + i); - } - iounmap(ptr); - - release_mem_region (mem_addr, 16); - - /* Check if PROM contains a valid string */ - for (i = 0; *depca_signature[i] != '\0'; i++) { - for (j = 0, k = 0; j < 16 && k < strlen(depca_signature[i]); j++) { - if (depca_signature[i][k] == tmpstr[j]) { /* track signature */ - k++; - } else { /* lost signature; begin search again */ - k = 0; - } - } - if (k == strlen(depca_signature[i])) - break; - } - - /* Check if name string is valid, provided there's no PROM */ - if (name && *name && (i == unknown)) { - for (i = 0; *depca_signature[i] != '\0'; i++) { - if (strcmp(name, depca_signature[i]) == 0) - break; - } - } - - return i; -} - -/* -** Look for a special sequence in the Ethernet station address PROM that -** is common across all DEPCA products. Note that the original DEPCA needs -** its ROM address counter to be initialized and enabled. Only enable -** if the first address octet is a 0x08 - this minimises the chances of -** messing around with some other hardware, but it assumes that this DEPCA -** card initialized itself correctly. -** -** Search the Ethernet address ROM for the signature. Since the ROM address -** counter can start at an arbitrary point, the search must include the entire -** probe sequence length plus the (length_of_the_signature - 1). -** Stop the search IMMEDIATELY after the signature is found so that the -** PROM address counter is correctly positioned at the start of the -** ethernet address for later read out. -*/ -static int __init DevicePresent(u_long ioaddr) -{ - union { - struct { - u32 a; - u32 b; - } llsig; - char Sig[sizeof(u32) << 1]; - } - dev; - short sigLength = 0; - s8 data; - s16 nicsr; - int i, j, status = 0; - - data = inb(DEPCA_PROM); /* clear counter on DEPCA */ - data = inb(DEPCA_PROM); /* read data */ - - if (data == 0x08) { /* Enable counter on DEPCA */ - nicsr = inb(DEPCA_NICSR); - nicsr |= AAC; - outb(nicsr, DEPCA_NICSR); - } - - dev.llsig.a = ETH_PROM_SIG; - dev.llsig.b = ETH_PROM_SIG; - sigLength = sizeof(u32) << 1; - - for (i = 0, j = 0; j < sigLength && i < PROBE_LENGTH + sigLength - 1; i++) { - data = inb(DEPCA_PROM); - if (dev.Sig[j] == data) { /* track signature */ - j++; - } else { /* lost signature; begin search again */ - if (data == dev.Sig[0]) { /* rare case.... */ - j = 1; - } else { - j = 0; - } - } - } - - if (j != sigLength) { - status = -ENODEV; /* search failed */ - } - - return status; -} - -/* -** The DE100 and DE101 PROM accesses were made non-standard for some bizarre -** reason: access the upper half of the PROM with x=0; access the lower half -** with x=1. -*/ -static int __init get_hw_addr(struct net_device *dev) -{ - u_long ioaddr = dev->base_addr; - struct depca_private *lp = netdev_priv(dev); - int i, k, tmp, status = 0; - u_short j, x, chksum; - - x = (((lp->adapter == de100) || (lp->adapter == de101)) ? 1 : 0); - - for (i = 0, k = 0, j = 0; j < 3; j++) { - k <<= 1; - if (k > 0xffff) - k -= 0xffff; - - k += (u_char) (tmp = inb(DEPCA_PROM + x)); - dev->dev_addr[i++] = (u_char) tmp; - k += (u_short) ((tmp = inb(DEPCA_PROM + x)) << 8); - dev->dev_addr[i++] = (u_char) tmp; - - if (k > 0xffff) - k -= 0xffff; - } - if (k == 0xffff) - k = 0; - - chksum = (u_char) inb(DEPCA_PROM + x); - chksum |= (u_short) (inb(DEPCA_PROM + x) << 8); - if (k != chksum) - status = -1; - - return status; -} - -/* -** Load a packet into the shared memory -*/ -static int load_packet(struct net_device *dev, struct sk_buff *skb) -{ - struct depca_private *lp = netdev_priv(dev); - int i, entry, end, len, status = NETDEV_TX_OK; - - entry = lp->tx_new; /* Ring around buffer number. */ - end = (entry + (skb->len - 1) / TX_BUFF_SZ) & lp->txRingMask; - if (!(readl(&lp->tx_ring[end].base) & T_OWN)) { /* Enough room? */ - /* - ** Caution: the write order is important here... don't set up the - ** ownership rights until all the other information is in place. - */ - if (end < entry) { /* wrapped buffer */ - len = (lp->txRingMask - entry + 1) * TX_BUFF_SZ; - memcpy_toio(lp->tx_buff[entry], skb->data, len); - memcpy_toio(lp->tx_buff[0], skb->data + len, skb->len - len); - } else { /* linear buffer */ - memcpy_toio(lp->tx_buff[entry], skb->data, skb->len); - } - - /* set up the buffer descriptors */ - len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len; - for (i = entry; i != end; i = (i+1) & lp->txRingMask) { - /* clean out flags */ - writel(readl(&lp->tx_ring[i].base) & ~T_FLAGS, &lp->tx_ring[i].base); - writew(0x0000, &lp->tx_ring[i].misc); /* clears other error flags */ - writew(-TX_BUFF_SZ, &lp->tx_ring[i].length); /* packet length in buffer */ - len -= TX_BUFF_SZ; - } - /* clean out flags */ - writel(readl(&lp->tx_ring[end].base) & ~T_FLAGS, &lp->tx_ring[end].base); - writew(0x0000, &lp->tx_ring[end].misc); /* clears other error flags */ - writew(-len, &lp->tx_ring[end].length); /* packet length in last buff */ - - /* start of packet */ - writel(readl(&lp->tx_ring[entry].base) | T_STP, &lp->tx_ring[entry].base); - /* end of packet */ - writel(readl(&lp->tx_ring[end].base) | T_ENP, &lp->tx_ring[end].base); - - for (i = end; i != entry; --i) { - /* ownership of packet */ - writel(readl(&lp->tx_ring[i].base) | T_OWN, &lp->tx_ring[i].base); - if (i == 0) - i = lp->txRingMask + 1; - } - writel(readl(&lp->tx_ring[entry].base) | T_OWN, &lp->tx_ring[entry].base); - - lp->tx_new = (++end) & lp->txRingMask; /* update current pointers */ - } else { - status = NETDEV_TX_LOCKED; - } - - return status; -} - -static void depca_dbg_open(struct net_device *dev) -{ - struct depca_private *lp = netdev_priv(dev); - u_long ioaddr = dev->base_addr; - struct depca_init *p = &lp->init_block; - int i; - - if (depca_debug > 1) { - /* Do not copy the shadow init block into shared memory */ - /* Debugging should not affect normal operation! */ - /* The shadow init block will get copied across during InitRestartDepca */ - printk("%s: depca open with irq %d\n", dev->name, dev->irq); - printk("Descriptor head addresses (CPU):\n"); - printk(" 0x%lx 0x%lx\n", (u_long) lp->rx_ring, (u_long) lp->tx_ring); - printk("Descriptor addresses (CPU):\nRX: "); - for (i = 0; i < lp->rxRingMask; i++) { - if (i < 3) { - printk("%p ", &lp->rx_ring[i].base); - } - } - printk("...%p\n", &lp->rx_ring[i].base); - printk("TX: "); - for (i = 0; i < lp->txRingMask; i++) { - if (i < 3) { - printk("%p ", &lp->tx_ring[i].base); - } - } - printk("...%p\n", &lp->tx_ring[i].base); - printk("\nDescriptor buffers (Device):\nRX: "); - for (i = 0; i < lp->rxRingMask; i++) { - if (i < 3) { - printk("0x%8.8x ", readl(&lp->rx_ring[i].base)); - } - } - printk("...0x%8.8x\n", readl(&lp->rx_ring[i].base)); - printk("TX: "); - for (i = 0; i < lp->txRingMask; i++) { - if (i < 3) { - printk("0x%8.8x ", readl(&lp->tx_ring[i].base)); - } - } - printk("...0x%8.8x\n", readl(&lp->tx_ring[i].base)); - printk("Initialisation block at 0x%8.8lx(Phys)\n", lp->mem_start); - printk(" mode: 0x%4.4x\n", p->mode); - printk(" physical address: %pM\n", p->phys_addr); - printk(" multicast hash table: "); - for (i = 0; i < (HASH_TABLE_LEN >> 3) - 1; i++) { - printk("%2.2x:", p->mcast_table[i]); - } - printk("%2.2x\n", p->mcast_table[i]); - printk(" rx_ring at: 0x%8.8x\n", p->rx_ring); - printk(" tx_ring at: 0x%8.8x\n", p->tx_ring); - printk("buffers (Phys): 0x%8.8lx\n", lp->mem_start + lp->buffs_offset); - printk("Ring size:\nRX: %d Log2(rxRingMask): 0x%8.8x\n", (int) lp->rxRingMask + 1, lp->rx_rlen); - printk("TX: %d Log2(txRingMask): 0x%8.8x\n", (int) lp->txRingMask + 1, lp->tx_rlen); - outw(CSR2, DEPCA_ADDR); - printk("CSR2&1: 0x%4.4x", inw(DEPCA_DATA)); - outw(CSR1, DEPCA_ADDR); - printk("%4.4x\n", inw(DEPCA_DATA)); - outw(CSR3, DEPCA_ADDR); - printk("CSR3: 0x%4.4x\n", inw(DEPCA_DATA)); - } -} - -/* -** Perform IOCTL call functions here. Some are privileged operations and the -** effective uid is checked in those cases. -** All multicast IOCTLs will not work here and are for testing purposes only. -*/ -static int depca_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct depca_private *lp = netdev_priv(dev); - struct depca_ioctl *ioc = (struct depca_ioctl *) &rq->ifr_ifru; - int i, status = 0; - u_long ioaddr = dev->base_addr; - union { - u8 addr[(HASH_TABLE_LEN * ETH_ALEN)]; - u16 sval[(HASH_TABLE_LEN * ETH_ALEN) >> 1]; - u32 lval[(HASH_TABLE_LEN * ETH_ALEN) >> 2]; - } tmp; - unsigned long flags; - void *buf; - - switch (ioc->cmd) { - case DEPCA_GET_HWADDR: /* Get the hardware address */ - for (i = 0; i < ETH_ALEN; i++) { - tmp.addr[i] = dev->dev_addr[i]; - } - ioc->len = ETH_ALEN; - if (copy_to_user(ioc->data, tmp.addr, ioc->len)) - return -EFAULT; - break; - - case DEPCA_SET_HWADDR: /* Set the hardware address */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (copy_from_user(tmp.addr, ioc->data, ETH_ALEN)) - return -EFAULT; - for (i = 0; i < ETH_ALEN; i++) { - dev->dev_addr[i] = tmp.addr[i]; - } - netif_stop_queue(dev); - while (lp->tx_old != lp->tx_new) - cpu_relax(); /* Wait for the ring to empty */ - - STOP_DEPCA; /* Temporarily stop the depca. */ - depca_init_ring(dev); /* Initialize the descriptor rings */ - LoadCSRs(dev); /* Reload CSR3 */ - InitRestartDepca(dev); /* Resume normal operation. */ - netif_start_queue(dev); /* Unlock the TX ring */ - break; - - case DEPCA_SET_PROM: /* Set Promiscuous Mode */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - netif_stop_queue(dev); - while (lp->tx_old != lp->tx_new) - cpu_relax(); /* Wait for the ring to empty */ - - STOP_DEPCA; /* Temporarily stop the depca. */ - depca_init_ring(dev); /* Initialize the descriptor rings */ - lp->init_block.mode |= PROM; /* Set promiscuous mode */ - - LoadCSRs(dev); /* Reload CSR3 */ - InitRestartDepca(dev); /* Resume normal operation. */ - netif_start_queue(dev); /* Unlock the TX ring */ - break; - - case DEPCA_CLR_PROM: /* Clear Promiscuous Mode */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - netif_stop_queue(dev); - while (lp->tx_old != lp->tx_new) - cpu_relax(); /* Wait for the ring to empty */ - - STOP_DEPCA; /* Temporarily stop the depca. */ - depca_init_ring(dev); /* Initialize the descriptor rings */ - lp->init_block.mode &= ~PROM; /* Clear promiscuous mode */ - - LoadCSRs(dev); /* Reload CSR3 */ - InitRestartDepca(dev); /* Resume normal operation. */ - netif_start_queue(dev); /* Unlock the TX ring */ - break; - - case DEPCA_SAY_BOO: /* Say "Boo!" to the kernel log file */ - if(!capable(CAP_NET_ADMIN)) - return -EPERM; - printk("%s: Boo!\n", dev->name); - break; - - case DEPCA_GET_MCA: /* Get the multicast address table */ - ioc->len = (HASH_TABLE_LEN >> 3); - if (copy_to_user(ioc->data, lp->init_block.mcast_table, ioc->len)) - return -EFAULT; - break; - - case DEPCA_SET_MCA: /* Set a multicast address */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (ioc->len >= HASH_TABLE_LEN) - return -EINVAL; - if (copy_from_user(tmp.addr, ioc->data, ETH_ALEN * ioc->len)) - return -EFAULT; - set_multicast_list(dev); - break; - - case DEPCA_CLR_MCA: /* Clear all multicast addresses */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - set_multicast_list(dev); - break; - - case DEPCA_MCA_EN: /* Enable pass all multicast addressing */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - set_multicast_list(dev); - break; - - case DEPCA_GET_STATS: /* Get the driver statistics */ - ioc->len = sizeof(lp->pktStats); - buf = kmalloc(ioc->len, GFP_KERNEL); - if(!buf) - return -ENOMEM; - spin_lock_irqsave(&lp->lock, flags); - memcpy(buf, &lp->pktStats, ioc->len); - spin_unlock_irqrestore(&lp->lock, flags); - if (copy_to_user(ioc->data, buf, ioc->len)) - status = -EFAULT; - kfree(buf); - break; - - case DEPCA_CLR_STATS: /* Zero out the driver statistics */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - spin_lock_irqsave(&lp->lock, flags); - memset(&lp->pktStats, 0, sizeof(lp->pktStats)); - spin_unlock_irqrestore(&lp->lock, flags); - break; - - case DEPCA_GET_REG: /* Get the DEPCA Registers */ - i = 0; - tmp.sval[i++] = inw(DEPCA_NICSR); - outw(CSR0, DEPCA_ADDR); /* status register */ - tmp.sval[i++] = inw(DEPCA_DATA); - memcpy(&tmp.sval[i], &lp->init_block, sizeof(struct depca_init)); - ioc->len = i + sizeof(struct depca_init); - if (copy_to_user(ioc->data, tmp.addr, ioc->len)) - return -EFAULT; - break; - - default: - return -EOPNOTSUPP; - } - - return status; -} - -static int __init depca_module_init (void) -{ - int err = 0; - -#ifdef CONFIG_EISA - err = eisa_driver_register(&depca_eisa_driver); - if (err) - goto err_eisa; -#endif - err = platform_driver_register(&depca_isa_driver); - if (err) - goto err_eisa; - - depca_platform_probe(); - return 0; - -err_eisa: -#ifdef CONFIG_EISA - eisa_driver_unregister(&depca_eisa_driver); -#endif - return err; -} - -static void __exit depca_module_exit (void) -{ - int i; -#ifdef CONFIG_EISA - eisa_driver_unregister (&depca_eisa_driver); -#endif - platform_driver_unregister (&depca_isa_driver); - - for (i = 0; depca_io_ports[i].iobase; i++) { - if (depca_io_ports[i].device) { - depca_io_ports[i].device->dev.platform_data = NULL; - platform_device_unregister (depca_io_ports[i].device); - depca_io_ports[i].device = NULL; - } - } -} - -module_init (depca_module_init); -module_exit (depca_module_exit); diff --git a/drivers/net/ethernet/amd/depca.h b/drivers/net/ethernet/amd/depca.h deleted file mode 100644 index cdcfe4252c16..000000000000 --- a/drivers/net/ethernet/amd/depca.h +++ /dev/null @@ -1,183 +0,0 @@ -/* - Written 1994 by David C. Davies. - - Copyright 1994 David C. Davies. This software may be used and distributed - according to the terms of the GNU General Public License, incorporated herein by - reference. -*/ - -/* -** I/O addresses. Note that the 2k buffer option is not supported in -** this driver. -*/ -#define DEPCA_NICSR ioaddr+0x00 /* Network interface CSR */ -#define DEPCA_RBI ioaddr+0x02 /* RAM buffer index (2k buffer mode) */ -#define DEPCA_DATA ioaddr+0x04 /* LANCE registers' data port */ -#define DEPCA_ADDR ioaddr+0x06 /* LANCE registers' address port */ -#define DEPCA_HBASE ioaddr+0x08 /* EISA high memory base address reg. */ -#define DEPCA_PROM ioaddr+0x0c /* Ethernet address ROM data port */ -#define DEPCA_CNFG ioaddr+0x0c /* EISA Configuration port */ -#define DEPCA_RBSA ioaddr+0x0e /* RAM buffer starting address (2k buff.) */ - -/* -** These are LANCE registers addressable through DEPCA_ADDR -*/ -#define CSR0 0 -#define CSR1 1 -#define CSR2 2 -#define CSR3 3 - -/* -** NETWORK INTERFACE CSR (NI_CSR) bit definitions -*/ - -#define TO 0x0100 /* Time Out for remote boot */ -#define SHE 0x0080 /* SHadow memory Enable */ -#define BS 0x0040 /* Bank Select */ -#define BUF 0x0020 /* BUFfer size (1->32k, 0->64k) */ -#define RBE 0x0010 /* Remote Boot Enable (1->net boot) */ -#define AAC 0x0008 /* Address ROM Address Counter (1->enable) */ -#define _128KB 0x0008 /* 128kB Network RAM (1->enable) */ -#define IM 0x0004 /* Interrupt Mask (1->mask) */ -#define IEN 0x0002 /* Interrupt tristate ENable (1->enable) */ -#define LED 0x0001 /* LED control */ - -/* -** Control and Status Register 0 (CSR0) bit definitions -*/ - -#define ERR 0x8000 /* Error summary */ -#define BABL 0x4000 /* Babble transmitter timeout error */ -#define CERR 0x2000 /* Collision Error */ -#define MISS 0x1000 /* Missed packet */ -#define MERR 0x0800 /* Memory Error */ -#define RINT 0x0400 /* Receiver Interrupt */ -#define TINT 0x0200 /* Transmit Interrupt */ -#define IDON 0x0100 /* Initialization Done */ -#define INTR 0x0080 /* Interrupt Flag */ -#define INEA 0x0040 /* Interrupt Enable */ -#define RXON 0x0020 /* Receiver on */ -#define TXON 0x0010 /* Transmitter on */ -#define TDMD 0x0008 /* Transmit Demand */ -#define STOP 0x0004 /* Stop */ -#define STRT 0x0002 /* Start */ -#define INIT 0x0001 /* Initialize */ -#define INTM 0xff00 /* Interrupt Mask */ -#define INTE 0xfff0 /* Interrupt Enable */ - -/* -** CONTROL AND STATUS REGISTER 3 (CSR3) -*/ - -#define BSWP 0x0004 /* Byte SWaP */ -#define ACON 0x0002 /* ALE control */ -#define BCON 0x0001 /* Byte CONtrol */ - -/* -** Initialization Block Mode Register -*/ - -#define PROM 0x8000 /* Promiscuous Mode */ -#define EMBA 0x0080 /* Enable Modified Back-off Algorithm */ -#define INTL 0x0040 /* Internal Loopback */ -#define DRTY 0x0020 /* Disable Retry */ -#define COLL 0x0010 /* Force Collision */ -#define DTCR 0x0008 /* Disable Transmit CRC */ -#define LOOP 0x0004 /* Loopback */ -#define DTX 0x0002 /* Disable the Transmitter */ -#define DRX 0x0001 /* Disable the Receiver */ - -/* -** Receive Message Descriptor 1 (RMD1) bit definitions. -*/ - -#define R_OWN 0x80000000 /* Owner bit 0 = host, 1 = lance */ -#define R_ERR 0x4000 /* Error Summary */ -#define R_FRAM 0x2000 /* Framing Error */ -#define R_OFLO 0x1000 /* Overflow Error */ -#define R_CRC 0x0800 /* CRC Error */ -#define R_BUFF 0x0400 /* Buffer Error */ -#define R_STP 0x0200 /* Start of Packet */ -#define R_ENP 0x0100 /* End of Packet */ - -/* -** Transmit Message Descriptor 1 (TMD1) bit definitions. -*/ - -#define T_OWN 0x80000000 /* Owner bit 0 = host, 1 = lance */ -#define T_ERR 0x4000 /* Error Summary */ -#define T_ADD_FCS 0x2000 /* More the 1 retry needed to Xmit */ -#define T_MORE 0x1000 /* >1 retry to transmit packet */ -#define T_ONE 0x0800 /* 1 try needed to transmit the packet */ -#define T_DEF 0x0400 /* Deferred */ -#define T_STP 0x02000000 /* Start of Packet */ -#define T_ENP 0x01000000 /* End of Packet */ -#define T_FLAGS 0xff000000 /* TX Flags Field */ - -/* -** Transmit Message Descriptor 3 (TMD3) bit definitions. -*/ - -#define TMD3_BUFF 0x8000 /* BUFFer error */ -#define TMD3_UFLO 0x4000 /* UnderFLOw error */ -#define TMD3_RES 0x2000 /* REServed */ -#define TMD3_LCOL 0x1000 /* Late COLlision */ -#define TMD3_LCAR 0x0800 /* Loss of CARrier */ -#define TMD3_RTRY 0x0400 /* ReTRY error */ - -/* -** EISA configuration Register (CNFG) bit definitions -*/ - -#define TIMEOUT 0x0100 /* 0:2.5 mins, 1: 30 secs */ -#define REMOTE 0x0080 /* Remote Boot Enable -> 1 */ -#define IRQ11 0x0040 /* Enable -> 1 */ -#define IRQ10 0x0020 /* Enable -> 1 */ -#define IRQ9 0x0010 /* Enable -> 1 */ -#define IRQ5 0x0008 /* Enable -> 1 */ -#define BUFF 0x0004 /* 0: 64kB or 128kB, 1: 32kB */ -#define PADR16 0x0002 /* RAM on 64kB boundary */ -#define PADR17 0x0001 /* RAM on 128kB boundary */ - -/* -** Miscellaneous -*/ -#define HASH_TABLE_LEN 64 /* Bits */ -#define HASH_BITS 0x003f /* 6 LS bits */ - -#define MASK_INTERRUPTS 1 -#define UNMASK_INTERRUPTS 0 - -#define EISA_EN 0x0001 /* Enable EISA bus buffers */ -#define EISA_ID iobase+0x0080 /* ID long word for EISA card */ -#define EISA_CTRL iobase+0x0084 /* Control word for EISA card */ - -/* -** Include the IOCTL stuff -*/ -#include - -struct depca_ioctl { - unsigned short cmd; /* Command to run */ - unsigned short len; /* Length of the data buffer */ - unsigned char __user *data; /* Pointer to the data buffer */ -}; - -/* -** Recognised commands for the driver -*/ -#define DEPCA_GET_HWADDR 0x01 /* Get the hardware address */ -#define DEPCA_SET_HWADDR 0x02 /* Get the hardware address */ -#define DEPCA_SET_PROM 0x03 /* Set Promiscuous Mode */ -#define DEPCA_CLR_PROM 0x04 /* Clear Promiscuous Mode */ -#define DEPCA_SAY_BOO 0x05 /* Say "Boo!" to the kernel log file */ -#define DEPCA_GET_MCA 0x06 /* Get a multicast address */ -#define DEPCA_SET_MCA 0x07 /* Set a multicast address */ -#define DEPCA_CLR_MCA 0x08 /* Clear a multicast address */ -#define DEPCA_MCA_EN 0x09 /* Enable a multicast address group */ -#define DEPCA_GET_STATS 0x0a /* Get the driver statistics */ -#define DEPCA_CLR_STATS 0x0b /* Zero out the driver statistics */ -#define DEPCA_GET_REG 0x0c /* Get the Register contents */ -#define DEPCA_SET_REG 0x0d /* Set the Register contents */ -#define DEPCA_DUMP 0x0f /* Dump the DEPCA Status */ - -- cgit v1.2.3-59-g8ed1b From 0ffd89e48fc10f9665b07615cde40b1775b24570 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 20 Jan 2013 17:14:45 -0500 Subject: drivers/net: delete Digital EtherWorks-3 support. This is another one that makes sense to target for obsolescence, since it (a)appeared pre-1995, and (b)was rather rare, and (c)did not really have any statistically significant active linux user base. Removing this ISA 10Mbit driver support is unlikely to be even noticed by the user base of 3.9+ linux kernels, especially when the documentation clearly indicates the vintage with this text: "...designed to work with all kernels > 1.1.33" Signed-off-by: Paul Gortmaker --- Documentation/networking/00-INDEX | 2 - Documentation/networking/ewrk3.txt | 46 - drivers/net/Space.c | 4 - drivers/net/ethernet/dec/Kconfig | 16 - drivers/net/ethernet/dec/Makefile | 1 - drivers/net/ethernet/dec/ewrk3.c | 1961 ------------------------------------ drivers/net/ethernet/dec/ewrk3.h | 322 ------ 7 files changed, 2352 deletions(-) delete mode 100644 Documentation/networking/ewrk3.txt delete mode 100644 drivers/net/ethernet/dec/ewrk3.c delete mode 100644 drivers/net/ethernet/dec/ewrk3.h (limited to 'Documentation') diff --git a/Documentation/networking/00-INDEX b/Documentation/networking/00-INDEX index 6ec1d6f368af..258d9b92c36f 100644 --- a/Documentation/networking/00-INDEX +++ b/Documentation/networking/00-INDEX @@ -70,8 +70,6 @@ e1000e.txt - README for the Intel Gigabit Ethernet Driver (e1000e). eql.txt - serial IP load balancing -ewrk3.txt - - the Digital EtherWORKS 3 DE203/4/5 Ethernet driver fib_trie.txt - Level Compressed Trie (LC-trie) notes: a structure for routing. filter.txt diff --git a/Documentation/networking/ewrk3.txt b/Documentation/networking/ewrk3.txt deleted file mode 100644 index 90e9e5f16e6b..000000000000 --- a/Documentation/networking/ewrk3.txt +++ /dev/null @@ -1,46 +0,0 @@ -The EtherWORKS 3 driver in this distribution is designed to work with all -kernels > 1.1.33 (approx) and includes tools in the 'ewrk3tools' -subdirectory to allow set up of the card, similar to the MSDOS -'NICSETUP.EXE' tools provided on the DOS drivers disk (type 'make' in that -subdirectory to make the tools). - -The supported cards are DE203, DE204 and DE205. All other cards are NOT -supported - refer to 'depca.c' for running the LANCE based network cards and -'de4x5.c' for the DIGITAL Semiconductor PCI chip based adapters from -Digital. - -The ability to load this driver as a loadable module has been included and -used extensively during the driver development (to save those long reboot -sequences). To utilise this ability, you have to do 8 things: - - 0) have a copy of the loadable modules code installed on your system. - 1) copy ewrk3.c from the /linux/drivers/net directory to your favourite - temporary directory. - 2) edit the source code near line 1898 to reflect the I/O address and - IRQ you're using. - 3) compile ewrk3.c, but include -DMODULE in the command line to ensure - that the correct bits are compiled (see end of source code). - 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a - kernel with the ewrk3 configuration turned off and reboot. - 5) insmod ewrk3.o - [Alan Cox: Changed this so you can insmod ewrk3.o irq=x io=y] - [Adam Kropelin: Multiple cards now supported by irq=x1,x2 io=y1,y2] - 6) run the net startup bits for your new eth?? interface manually - (usually /etc/rc.inet[12] at boot time). - 7) enjoy! - - Note that autoprobing is not allowed in loadable modules - the system is - already up and running and you're messing with interrupts. - - To unload a module, turn off the associated interface - 'ifconfig eth?? down' then 'rmmod ewrk3'. - -The performance we've achieved so far has been measured through the 'ttcp' -tool at 975kB/s. This measures the total TCP stack performance which -includes the card, so don't expect to get much nearer the 1.25MB/s -theoretical Ethernet rate. - - -Enjoy! - -Dave diff --git a/drivers/net/Space.c b/drivers/net/Space.c index 393b0176c131..9c30ea4a1254 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -43,7 +43,6 @@ extern struct net_device *wd_probe(int unit); extern struct net_device *ne_probe(int unit); extern struct net_device *fmv18x_probe(int unit); extern struct net_device *i82596_probe(int unit); -extern struct net_device *ewrk3_probe(int unit); extern struct net_device *ni65_probe(int unit); extern struct net_device *sonic_probe(int unit); extern struct net_device *seeq8005_probe(int unit); @@ -122,9 +121,6 @@ static struct devprobe2 isa_probes[] __initdata = { {cs89x0_probe, 0}, #endif #endif -#ifdef CONFIG_EWRK3 /* DEC EtherWORKS 3 */ - {ewrk3_probe, 0}, -#endif #if defined(CONFIG_MVME16x_NET) || defined(CONFIG_BVME6000_NET) /* Intel I82596 */ {i82596_probe, 0}, #endif diff --git a/drivers/net/ethernet/dec/Kconfig b/drivers/net/ethernet/dec/Kconfig index 37940279ded8..68262aa57d01 100644 --- a/drivers/net/ethernet/dec/Kconfig +++ b/drivers/net/ethernet/dec/Kconfig @@ -17,21 +17,5 @@ config NET_VENDOR_DEC your specific card in the following questions. if NET_VENDOR_DEC - -config EWRK3 - tristate "EtherWORKS 3 (DE203, DE204, DE205) support" - depends on ISA - select CRC32 - ---help--- - This driver supports the DE203, DE204 and DE205 network (Ethernet) - cards. If this is for you, say Y and read - in the kernel source as - well as the Ethernet-HOWTO, available from - . - - To compile this driver as a module, choose M here. The module - will be called ewrk3. - source "drivers/net/ethernet/dec/tulip/Kconfig" - endif # NET_VENDOR_DEC diff --git a/drivers/net/ethernet/dec/Makefile b/drivers/net/ethernet/dec/Makefile index 1b01ed8d42c8..32993fccbbfd 100644 --- a/drivers/net/ethernet/dec/Makefile +++ b/drivers/net/ethernet/dec/Makefile @@ -2,5 +2,4 @@ # Makefile for the Digital Equipment Inc. network device drivers. # -obj-$(CONFIG_EWRK3) += ewrk3.o obj-$(CONFIG_NET_TULIP) += tulip/ diff --git a/drivers/net/ethernet/dec/ewrk3.c b/drivers/net/ethernet/dec/ewrk3.c deleted file mode 100644 index 501517226f63..000000000000 --- a/drivers/net/ethernet/dec/ewrk3.c +++ /dev/null @@ -1,1961 +0,0 @@ -/* ewrk3.c: A DIGITAL EtherWORKS 3 ethernet driver for Linux. - - Written 1994 by David C. Davies. - - Copyright 1994 Digital Equipment Corporation. - - This software may be used and distributed according to the terms of - the GNU General Public License, incorporated herein by reference. - - This driver is written for the Digital Equipment Corporation series - of EtherWORKS ethernet cards: - - DE203 Turbo (BNC) - DE204 Turbo (TP) - DE205 Turbo (TP BNC) - - The driver has been tested on a relatively busy network using the DE205 - card and benchmarked with 'ttcp': it transferred 16M of data at 975kB/s - (7.8Mb/s) to a DECstation 5000/200. - - The author may be reached at davies@maniac.ultranet.com. - - ========================================================================= - This driver has been written substantially from scratch, although its - inheritance of style and stack interface from 'depca.c' and in turn from - Donald Becker's 'lance.c' should be obvious. - - The DE203/4/5 boards all use a new proprietary chip in place of the - LANCE chip used in prior cards (DEPCA, DE100, DE200/1/2, DE210, DE422). - Use the depca.c driver in the standard distribution for the LANCE based - cards from DIGITAL; this driver will not work with them. - - The DE203/4/5 cards have 2 main modes: shared memory and I/O only. I/O - only makes all the card accesses through I/O transactions and no high - (shared) memory is used. This mode provides a >48% performance penalty - and is deprecated in this driver, although allowed to provide initial - setup when hardstrapped. - - The shared memory mode comes in 3 flavours: 2kB, 32kB and 64kB. There is - no point in using any mode other than the 2kB mode - their performances - are virtually identical, although the driver has been tested in the 2kB - and 32kB modes. I would suggest you uncomment the line: - - FORCE_2K_MODE; - - to allow the driver to configure the card as a 2kB card at your current - base address, thus leaving more room to clutter your system box with - other memory hungry boards. - - As many ISA and EISA cards can be supported under this driver as you - wish, limited primarily by the available IRQ lines, rather than by the - available I/O addresses (24 ISA, 16 EISA). I have checked different - configurations of multiple depca cards and ewrk3 cards and have not - found a problem yet (provided you have at least depca.c v0.38) ... - - The board IRQ setting must be at an unused IRQ which is auto-probed - using Donald Becker's autoprobe routines. All these cards are at - {5,10,11,15}. - - No 16MB memory limitation should exist with this driver as DMA is not - used and the common memory area is in low memory on the network card (my - current system has 20MB and I've not had problems yet). - - The ability to load this driver as a loadable module has been included - and used extensively during the driver development (to save those long - reboot sequences). To utilise this ability, you have to do 8 things: - - 0) have a copy of the loadable modules code installed on your system. - 1) copy ewrk3.c from the /linux/drivers/net directory to your favourite - temporary directory. - 2) edit the source code near line 1898 to reflect the I/O address and - IRQ you're using. - 3) compile ewrk3.c, but include -DMODULE in the command line to ensure - that the correct bits are compiled (see end of source code). - 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a - kernel with the ewrk3 configuration turned off and reboot. - 5) insmod ewrk3.o - [Alan Cox: Changed this so you can insmod ewrk3.o irq=x io=y] - [Adam Kropelin: now accepts irq=x1,x2 io=y1,y2 for multiple cards] - 6) run the net startup bits for your new eth?? interface manually - (usually /etc/rc.inet[12] at boot time). - 7) enjoy! - - Note that autoprobing is not allowed in loadable modules - the system is - already up and running and you're messing with interrupts. - - To unload a module, turn off the associated interface - 'ifconfig eth?? down' then 'rmmod ewrk3'. - - Promiscuous mode has been turned off in this driver, but all the - multicast address bits have been turned on. This improved the send - performance on a busy network by about 13%. - - Ioctl's have now been provided (primarily because I wanted to grab some - packet size statistics). They are patterned after 'plipconfig.c' from a - suggestion by Alan Cox. Using these ioctls, you can enable promiscuous - mode, add/delete multicast addresses, change the hardware address, get - packet size distribution statistics and muck around with the control and - status register. I'll add others if and when the need arises. - - TO DO: - ------ - - - Revision History - ---------------- - - Version Date Description - - 0.1 26-aug-94 Initial writing. ALPHA code release. - 0.11 31-aug-94 Fixed: 2k mode memory base calc., - LeMAC version calc., - IRQ vector assignments during autoprobe. - 0.12 31-aug-94 Tested working on LeMAC2 (DE20[345]-AC) card. - Fixed up MCA hash table algorithm. - 0.20 4-sep-94 Added IOCTL functionality. - 0.21 14-sep-94 Added I/O mode. - 0.21axp 15-sep-94 Special version for ALPHA AXP Linux V1.0. - 0.22 16-sep-94 Added more IOCTLs & tidied up. - 0.23 21-sep-94 Added transmit cut through. - 0.24 31-oct-94 Added uid checks in some ioctls. - 0.30 1-nov-94 BETA code release. - 0.31 5-dec-94 Added check/allocate region code. - 0.32 16-jan-95 Broadcast packet fix. - 0.33 10-Feb-95 Fix recognition bug reported by . - 0.40 27-Dec-95 Rationalise MODULE and autoprobe code. - Rewrite for portability & updated. - ALPHA support from - Added verify_area() calls in ewrk3_ioctl() from - suggestion by . - Add new multicasting code. - 0.41 20-Jan-96 Fix IRQ set up problem reported by - . - 0.42 22-Apr-96 Fix alloc_device() bug - 0.43 16-Aug-96 Update alloc_device() to conform to de4x5.c - 0.44 08-Nov-01 use library crc32 functions - 0.45 19-Jul-02 fix unaligned access on alpha - 0.46 10-Oct-02 Multiple NIC support when module - 0.47 18-Oct-02 ethtool support - 0.48 18-Oct-02 cli/sti removal for 2.5 - ioctl locking, signature search cleanup - - ========================================================================= - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "ewrk3.h" - -#define DRV_NAME "ewrk3" -#define DRV_VERSION "0.48" - -static char version[] __initdata = -DRV_NAME ":v" DRV_VERSION " 2002/10/18 davies@maniac.ultranet.com\n"; - -#ifdef EWRK3_DEBUG -static int ewrk3_debug = EWRK3_DEBUG; -#else -static int ewrk3_debug = 1; -#endif - -#define EWRK3_NDA 0xffe0 /* No Device Address */ - -#define PROBE_LENGTH 32 -#define ETH_PROM_SIG 0xAA5500FFUL - -#ifndef EWRK3_SIGNATURE -#define EWRK3_SIGNATURE {"DE203","DE204","DE205",""} -#define EWRK3_STRLEN 8 -#endif - -#ifndef EWRK3_RAM_BASE_ADDRESSES -#define EWRK3_RAM_BASE_ADDRESSES {0xc0000,0xd0000,0x00000} -#endif - -/* - ** Sets up the I/O area for the autoprobe. - */ -#define EWRK3_IO_BASE 0x100 /* Start address for probe search */ -#define EWRK3_IOP_INC 0x20 /* I/O address increment */ -#define EWRK3_TOTAL_SIZE 0x20 /* required I/O address length */ - -#ifndef MAX_NUM_EWRK3S -#define MAX_NUM_EWRK3S 21 -#endif - -#ifndef EWRK3_EISA_IO_PORTS -#define EWRK3_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */ -#endif - -#ifndef MAX_EISA_SLOTS -#define MAX_EISA_SLOTS 16 -#define EISA_SLOT_INC 0x1000 -#endif - -#define QUEUE_PKT_TIMEOUT (1*HZ) /* Jiffies */ - -/* - ** EtherWORKS 3 shared memory window sizes - */ -#define IO_ONLY 0x00 -#define SHMEM_2K 0x800 -#define SHMEM_32K 0x8000 -#define SHMEM_64K 0x10000 - -/* - ** EtherWORKS 3 IRQ ENABLE/DISABLE - */ -#define ENABLE_IRQs { \ - icr |= lp->irq_mask;\ - outb(icr, EWRK3_ICR); /* Enable the IRQs */\ -} - -#define DISABLE_IRQs { \ - icr = inb(EWRK3_ICR);\ - icr &= ~lp->irq_mask;\ - outb(icr, EWRK3_ICR); /* Disable the IRQs */\ -} - -/* - ** EtherWORKS 3 START/STOP - */ -#define START_EWRK3 { \ - csr = inb(EWRK3_CSR);\ - csr &= ~(CSR_TXD|CSR_RXD);\ - outb(csr, EWRK3_CSR); /* Enable the TX and/or RX */\ -} - -#define STOP_EWRK3 { \ - csr = (CSR_TXD|CSR_RXD);\ - outb(csr, EWRK3_CSR); /* Disable the TX and/or RX */\ -} - -/* - ** The EtherWORKS 3 private structure - */ -#define EWRK3_PKT_STAT_SZ 16 -#define EWRK3_PKT_BIN_SZ 128 /* Should be >=100 unless you - increase EWRK3_PKT_STAT_SZ */ - -struct ewrk3_stats { - u32 bins[EWRK3_PKT_STAT_SZ]; - u32 unicast; - u32 multicast; - u32 broadcast; - u32 excessive_collisions; - u32 tx_underruns; - u32 excessive_underruns; -}; - -struct ewrk3_private { - char adapter_name[80]; /* Name exported to /proc/ioports */ - u_long shmem_base; /* Shared memory start address */ - void __iomem *shmem; - u_long shmem_length; /* Shared memory window length */ - struct ewrk3_stats pktStats; /* Private stats counters */ - u_char irq_mask; /* Adapter IRQ mask bits */ - u_char mPage; /* Maximum 2kB Page number */ - u_char lemac; /* Chip rev. level */ - u_char hard_strapped; /* Don't allow a full open */ - u_char txc; /* Transmit cut through */ - void __iomem *mctbl; /* Pointer to the multicast table */ - u_char led_mask; /* Used to reserve LED access for ethtool */ - spinlock_t hw_lock; -}; - -/* - ** Force the EtherWORKS 3 card to be in 2kB MODE - */ -#define FORCE_2K_MODE { \ - shmem_length = SHMEM_2K;\ - outb(((mem_start - 0x80000) >> 11), EWRK3_MBR);\ -} - -/* - ** Public Functions - */ -static int ewrk3_open(struct net_device *dev); -static netdev_tx_t ewrk3_queue_pkt(struct sk_buff *skb, struct net_device *dev); -static irqreturn_t ewrk3_interrupt(int irq, void *dev_id); -static int ewrk3_close(struct net_device *dev); -static void set_multicast_list(struct net_device *dev); -static int ewrk3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static const struct ethtool_ops ethtool_ops_203; -static const struct ethtool_ops ethtool_ops; - -/* - ** Private functions - */ -static int ewrk3_hw_init(struct net_device *dev, u_long iobase); -static void ewrk3_init(struct net_device *dev); -static int ewrk3_rx(struct net_device *dev); -static int ewrk3_tx(struct net_device *dev); -static void ewrk3_timeout(struct net_device *dev); - -static void EthwrkSignature(char *name, char *eeprom_image); -static int DevicePresent(u_long iobase); -static void SetMulticastFilter(struct net_device *dev); -static int EISA_signature(char *name, s32 eisa_id); - -static int Read_EEPROM(u_long iobase, u_char eaddr); -static int Write_EEPROM(short data, u_long iobase, u_char eaddr); -static u_char get_hw_addr(struct net_device *dev, u_char * eeprom_image, char chipType); - -static int ewrk3_probe1(struct net_device *dev, u_long iobase, int irq); -static int isa_probe(struct net_device *dev, u_long iobase); -static int eisa_probe(struct net_device *dev, u_long iobase); - -static u_char irq[MAX_NUM_EWRK3S+1] = {5, 0, 10, 3, 11, 9, 15, 12}; - -static char name[EWRK3_STRLEN + 1]; -static int num_ewrks3s; - -/* - ** Miscellaneous defines... - */ -#define INIT_EWRK3 {\ - outb(EEPROM_INIT, EWRK3_IOPR);\ - mdelay(1);\ -} - -#ifndef MODULE -struct net_device * __init ewrk3_probe(int unit) -{ - struct net_device *dev = alloc_etherdev(sizeof(struct ewrk3_private)); - int err; - - if (!dev) - return ERR_PTR(-ENOMEM); - - if (unit >= 0) { - sprintf(dev->name, "eth%d", unit); - netdev_boot_setup_check(dev); - } - - err = ewrk3_probe1(dev, dev->base_addr, dev->irq); - if (err) - goto out; - return dev; -out: - free_netdev(dev); - return ERR_PTR(err); - -} -#endif - -static int __init ewrk3_probe1(struct net_device *dev, u_long iobase, int irq) -{ - int err; - - dev->base_addr = iobase; - dev->irq = irq; - - /* Address PROM pattern */ - err = isa_probe(dev, iobase); - if (err != 0) - err = eisa_probe(dev, iobase); - - if (err) - return err; - - err = register_netdev(dev); - if (err) - release_region(dev->base_addr, EWRK3_TOTAL_SIZE); - - return err; -} - -static const struct net_device_ops ewrk3_netdev_ops = { - .ndo_open = ewrk3_open, - .ndo_start_xmit = ewrk3_queue_pkt, - .ndo_stop = ewrk3_close, - .ndo_set_rx_mode = set_multicast_list, - .ndo_do_ioctl = ewrk3_ioctl, - .ndo_tx_timeout = ewrk3_timeout, - .ndo_change_mtu = eth_change_mtu, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int __init -ewrk3_hw_init(struct net_device *dev, u_long iobase) -{ - struct ewrk3_private *lp; - int i, status = 0; - u_long mem_start, shmem_length; - u_char cr, cmr, icr, nicsr, lemac, hard_strapped = 0; - u_char eeprom_image[EEPROM_MAX], chksum, eisa_cr = 0; - - /* - ** Stop the EWRK3. Enable the DBR ROM. Disable interrupts and remote boot. - ** This also disables the EISA_ENABLE bit in the EISA Control Register. - */ - if (iobase > 0x400) - eisa_cr = inb(EISA_CR); - INIT_EWRK3; - - nicsr = inb(EWRK3_CSR); - - icr = inb(EWRK3_ICR); - icr &= 0x70; - outb(icr, EWRK3_ICR); /* Disable all the IRQs */ - - if (nicsr != (CSR_TXD | CSR_RXD)) - return -ENXIO; - - /* Check that the EEPROM is alive and well and not living on Pluto... */ - for (chksum = 0, i = 0; i < EEPROM_MAX; i += 2) { - union { - short val; - char c[2]; - } tmp; - - tmp.val = (short) Read_EEPROM(iobase, (i >> 1)); - eeprom_image[i] = tmp.c[0]; - eeprom_image[i + 1] = tmp.c[1]; - chksum += eeprom_image[i] + eeprom_image[i + 1]; - } - - if (chksum != 0) { /* Bad EEPROM Data! */ - printk("%s: Device has a bad on-board EEPROM.\n", dev->name); - return -ENXIO; - } - - EthwrkSignature(name, eeprom_image); - if (*name == '\0') - return -ENXIO; - - dev->base_addr = iobase; - - if (iobase > 0x400) { - outb(eisa_cr, EISA_CR); /* Rewrite the EISA CR */ - } - lemac = eeprom_image[EEPROM_CHIPVER]; - cmr = inb(EWRK3_CMR); - - if (((lemac == LeMAC) && ((cmr & CMR_NO_EEPROM) != CMR_NO_EEPROM)) || - ((lemac == LeMAC2) && !(cmr & CMR_HS))) { - printk("%s: %s at %#4lx", dev->name, name, iobase); - hard_strapped = 1; - } else if ((iobase & 0x0fff) == EWRK3_EISA_IO_PORTS) { - /* EISA slot address */ - printk("%s: %s at %#4lx (EISA slot %ld)", - dev->name, name, iobase, ((iobase >> 12) & 0x0f)); - } else { /* ISA port address */ - printk("%s: %s at %#4lx", dev->name, name, iobase); - } - - printk(", h/w address "); - if (lemac != LeMAC2) - DevicePresent(iobase); /* need after EWRK3_INIT */ - status = get_hw_addr(dev, eeprom_image, lemac); - printk("%pM\n", dev->dev_addr); - - if (status) { - printk(" which has an EEPROM CRC error.\n"); - return -ENXIO; - } - - if (lemac == LeMAC2) { /* Special LeMAC2 CMR things */ - cmr &= ~(CMR_RA | CMR_WB | CMR_LINK | CMR_POLARITY | CMR_0WS); - if (eeprom_image[EEPROM_MISC0] & READ_AHEAD) - cmr |= CMR_RA; - if (eeprom_image[EEPROM_MISC0] & WRITE_BEHIND) - cmr |= CMR_WB; - if (eeprom_image[EEPROM_NETMAN0] & NETMAN_POL) - cmr |= CMR_POLARITY; - if (eeprom_image[EEPROM_NETMAN0] & NETMAN_LINK) - cmr |= CMR_LINK; - if (eeprom_image[EEPROM_MISC0] & _0WS_ENA) - cmr |= CMR_0WS; - } - if (eeprom_image[EEPROM_SETUP] & SETUP_DRAM) - cmr |= CMR_DRAM; - outb(cmr, EWRK3_CMR); - - cr = inb(EWRK3_CR); /* Set up the Control Register */ - cr |= eeprom_image[EEPROM_SETUP] & SETUP_APD; - if (cr & SETUP_APD) - cr |= eeprom_image[EEPROM_SETUP] & SETUP_PS; - cr |= eeprom_image[EEPROM_MISC0] & FAST_BUS; - cr |= eeprom_image[EEPROM_MISC0] & ENA_16; - outb(cr, EWRK3_CR); - - /* - ** Determine the base address and window length for the EWRK3 - ** RAM from the memory base register. - */ - mem_start = inb(EWRK3_MBR); - shmem_length = 0; - if (mem_start != 0) { - if ((mem_start >= 0x0a) && (mem_start <= 0x0f)) { - mem_start *= SHMEM_64K; - shmem_length = SHMEM_64K; - } else if ((mem_start >= 0x14) && (mem_start <= 0x1f)) { - mem_start *= SHMEM_32K; - shmem_length = SHMEM_32K; - } else if ((mem_start >= 0x40) && (mem_start <= 0xff)) { - mem_start = mem_start * SHMEM_2K + 0x80000; - shmem_length = SHMEM_2K; - } else { - return -ENXIO; - } - } - /* - ** See the top of this source code for comments about - ** uncommenting this line. - */ -/* FORCE_2K_MODE; */ - - if (hard_strapped) { - printk(" is hard strapped.\n"); - } else if (mem_start) { - printk(" has a %dk RAM window", (int) (shmem_length >> 10)); - printk(" at 0x%.5lx", mem_start); - } else { - printk(" is in I/O only mode"); - } - - lp = netdev_priv(dev); - lp->shmem_base = mem_start; - lp->shmem = ioremap(mem_start, shmem_length); - if (!lp->shmem) - return -ENOMEM; - lp->shmem_length = shmem_length; - lp->lemac = lemac; - lp->hard_strapped = hard_strapped; - lp->led_mask = CR_LED; - spin_lock_init(&lp->hw_lock); - - lp->mPage = 64; - if (cmr & CMR_DRAM) - lp->mPage <<= 1; /* 2 DRAMS on module */ - - sprintf(lp->adapter_name, "%s (%s)", name, dev->name); - - lp->irq_mask = ICR_TNEM | ICR_TXDM | ICR_RNEM | ICR_RXDM; - - if (!hard_strapped) { - /* - ** Enable EWRK3 board interrupts for autoprobing - */ - icr |= ICR_IE; /* Enable interrupts */ - outb(icr, EWRK3_ICR); - - /* The DMA channel may be passed in on this parameter. */ - dev->dma = 0; - - /* To auto-IRQ we enable the initialization-done and DMA err, - interrupts. For now we will always get a DMA error. */ - if (dev->irq < 2) { -#ifndef MODULE - u_char irqnum; - unsigned long irq_mask; - - - irq_mask = probe_irq_on(); - - /* - ** Trigger a TNE interrupt. - */ - icr |= ICR_TNEM; - outb(1, EWRK3_TDQ); /* Write to the TX done queue */ - outb(icr, EWRK3_ICR); /* Unmask the TXD interrupt */ - - irqnum = irq[((icr & IRQ_SEL) >> 4)]; - - mdelay(20); - dev->irq = probe_irq_off(irq_mask); - if ((dev->irq) && (irqnum == dev->irq)) { - printk(" and uses IRQ%d.\n", dev->irq); - } else { - if (!dev->irq) { - printk(" and failed to detect IRQ line.\n"); - } else if ((irqnum == 1) && (lemac == LeMAC2)) { - printk(" and an illegal IRQ line detected.\n"); - } else { - printk(", but incorrect IRQ line detected.\n"); - } - iounmap(lp->shmem); - return -ENXIO; - } - - DISABLE_IRQs; /* Mask all interrupts */ - -#endif /* MODULE */ - } else { - printk(" and requires IRQ%d.\n", dev->irq); - } - } - - if (ewrk3_debug > 1) { - printk(version); - } - /* The EWRK3-specific entries in the device structure. */ - dev->netdev_ops = &ewrk3_netdev_ops; - if (lp->adapter_name[4] == '3') - SET_ETHTOOL_OPS(dev, ðtool_ops_203); - else - SET_ETHTOOL_OPS(dev, ðtool_ops); - dev->watchdog_timeo = QUEUE_PKT_TIMEOUT; - - dev->mem_start = 0; - - return 0; -} - - -static int ewrk3_open(struct net_device *dev) -{ - struct ewrk3_private *lp = netdev_priv(dev); - u_long iobase = dev->base_addr; - int status = 0; - u_char icr, csr; - - /* - ** Stop the TX and RX... - */ - STOP_EWRK3; - - if (!lp->hard_strapped) { - if (request_irq(dev->irq, (void *) ewrk3_interrupt, 0, "ewrk3", dev)) { - printk("ewrk3_open(): Requested IRQ%d is busy\n", dev->irq); - status = -EAGAIN; - } else { - - /* - ** Re-initialize the EWRK3... - */ - ewrk3_init(dev); - - if (ewrk3_debug > 1) { - printk("%s: ewrk3 open with irq %d\n", dev->name, dev->irq); - printk(" physical address: %pM\n", dev->dev_addr); - if (lp->shmem_length == 0) { - printk(" no shared memory, I/O only mode\n"); - } else { - printk(" start of shared memory: 0x%08lx\n", lp->shmem_base); - printk(" window length: 0x%04lx\n", lp->shmem_length); - } - printk(" # of DRAMS: %d\n", ((inb(EWRK3_CMR) & 0x02) ? 2 : 1)); - printk(" csr: 0x%02x\n", inb(EWRK3_CSR)); - printk(" cr: 0x%02x\n", inb(EWRK3_CR)); - printk(" icr: 0x%02x\n", inb(EWRK3_ICR)); - printk(" cmr: 0x%02x\n", inb(EWRK3_CMR)); - printk(" fmqc: 0x%02x\n", inb(EWRK3_FMQC)); - } - netif_start_queue(dev); - /* - ** Unmask EWRK3 board interrupts - */ - icr = inb(EWRK3_ICR); - ENABLE_IRQs; - - } - } else { - printk(KERN_ERR "%s: ewrk3 available for hard strapped set up only.\n", dev->name); - printk(KERN_ERR " Run the 'ewrk3setup' utility or remove the hard straps.\n"); - return -EINVAL; - } - - return status; -} - -/* - ** Initialize the EtherWORKS 3 operating conditions - */ -static void ewrk3_init(struct net_device *dev) -{ - struct ewrk3_private *lp = netdev_priv(dev); - u_char csr, page; - u_long iobase = dev->base_addr; - int i; - - /* - ** Enable any multicasts - */ - set_multicast_list(dev); - - /* - ** Set hardware MAC address. Address is initialized from the EEPROM - ** during startup but may have since been changed by the user. - */ - for (i=0; idev_addr[i], EWRK3_PAR0 + i); - - /* - ** Clean out any remaining entries in all the queues here - */ - while (inb(EWRK3_TQ)); - while (inb(EWRK3_TDQ)); - while (inb(EWRK3_RQ)); - while (inb(EWRK3_FMQ)); - - /* - ** Write a clean free memory queue - */ - for (page = 1; page < lp->mPage; page++) { /* Write the free page numbers */ - outb(page, EWRK3_FMQ); /* to the Free Memory Queue */ - } - - START_EWRK3; /* Enable the TX and/or RX */ -} - -/* - * Transmit timeout - */ - -static void ewrk3_timeout(struct net_device *dev) -{ - struct ewrk3_private *lp = netdev_priv(dev); - u_char icr, csr; - u_long iobase = dev->base_addr; - - if (!lp->hard_strapped) - { - printk(KERN_WARNING"%s: transmit timed/locked out, status %04x, resetting.\n", - dev->name, inb(EWRK3_CSR)); - - /* - ** Mask all board interrupts - */ - DISABLE_IRQs; - - /* - ** Stop the TX and RX... - */ - STOP_EWRK3; - - ewrk3_init(dev); - - /* - ** Unmask EWRK3 board interrupts - */ - ENABLE_IRQs; - - dev->trans_start = jiffies; /* prevent tx timeout */ - netif_wake_queue(dev); - } -} - -/* - ** Writes a socket buffer to the free page queue - */ -static netdev_tx_t ewrk3_queue_pkt(struct sk_buff *skb, struct net_device *dev) -{ - struct ewrk3_private *lp = netdev_priv(dev); - u_long iobase = dev->base_addr; - void __iomem *buf = NULL; - u_char icr; - u_char page; - - spin_lock_irq (&lp->hw_lock); - DISABLE_IRQs; - - /* if no resources available, exit, request packet be queued */ - if (inb (EWRK3_FMQC) == 0) { - printk (KERN_WARNING "%s: ewrk3_queue_pkt(): No free resources...\n", - dev->name); - printk (KERN_WARNING "%s: ewrk3_queue_pkt(): CSR: %02x ICR: %02x FMQC: %02x\n", - dev->name, inb (EWRK3_CSR), inb (EWRK3_ICR), - inb (EWRK3_FMQC)); - goto err_out; - } - - /* - ** Get a free page from the FMQ - */ - if ((page = inb (EWRK3_FMQ)) >= lp->mPage) { - printk ("ewrk3_queue_pkt(): Invalid free memory page (%d).\n", - (u_char) page); - goto err_out; - } - - - /* - ** Set up shared memory window and pointer into the window - */ - if (lp->shmem_length == IO_ONLY) { - outb (page, EWRK3_IOPR); - } else if (lp->shmem_length == SHMEM_2K) { - buf = lp->shmem; - outb (page, EWRK3_MPR); - } else if (lp->shmem_length == SHMEM_32K) { - buf = (((short) page << 11) & 0x7800) + lp->shmem; - outb ((page >> 4), EWRK3_MPR); - } else if (lp->shmem_length == SHMEM_64K) { - buf = (((short) page << 11) & 0xf800) + lp->shmem; - outb ((page >> 5), EWRK3_MPR); - } else { - printk (KERN_ERR "%s: Oops - your private data area is hosed!\n", - dev->name); - BUG (); - } - - /* - ** Set up the buffer control structures and copy the data from - ** the socket buffer to the shared memory . - */ - if (lp->shmem_length == IO_ONLY) { - int i; - u_char *p = skb->data; - outb ((char) (TCR_QMODE | TCR_PAD | TCR_IFC), EWRK3_DATA); - outb ((char) (skb->len & 0xff), EWRK3_DATA); - outb ((char) ((skb->len >> 8) & 0xff), EWRK3_DATA); - outb ((char) 0x04, EWRK3_DATA); - for (i = 0; i < skb->len; i++) { - outb (*p++, EWRK3_DATA); - } - outb (page, EWRK3_TQ); /* Start sending pkt */ - } else { - writeb ((char) (TCR_QMODE | TCR_PAD | TCR_IFC), buf); /* ctrl byte */ - buf += 1; - writeb ((char) (skb->len & 0xff), buf); /* length (16 bit xfer) */ - buf += 1; - if (lp->txc) { - writeb(((skb->len >> 8) & 0xff) | XCT, buf); - buf += 1; - writeb (0x04, buf); /* index byte */ - buf += 1; - writeb (0x00, (buf + skb->len)); /* Write the XCT flag */ - memcpy_toio (buf, skb->data, PRELOAD); /* Write PRELOAD bytes */ - outb (page, EWRK3_TQ); /* Start sending pkt */ - memcpy_toio (buf + PRELOAD, - skb->data + PRELOAD, - skb->len - PRELOAD); - writeb (0xff, (buf + skb->len)); /* Write the XCT flag */ - } else { - writeb ((skb->len >> 8) & 0xff, buf); - buf += 1; - writeb (0x04, buf); /* index byte */ - buf += 1; - memcpy_toio (buf, skb->data, skb->len); /* Write data bytes */ - outb (page, EWRK3_TQ); /* Start sending pkt */ - } - } - - ENABLE_IRQs; - spin_unlock_irq (&lp->hw_lock); - - dev->stats.tx_bytes += skb->len; - dev_kfree_skb (skb); - - /* Check for free resources: stop Tx queue if there are none */ - if (inb (EWRK3_FMQC) == 0) - netif_stop_queue (dev); - - return NETDEV_TX_OK; - -err_out: - ENABLE_IRQs; - spin_unlock_irq (&lp->hw_lock); - return NETDEV_TX_BUSY; -} - -/* - ** The EWRK3 interrupt handler. - */ -static irqreturn_t ewrk3_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct ewrk3_private *lp; - u_long iobase; - u_char icr, cr, csr; - - lp = netdev_priv(dev); - iobase = dev->base_addr; - - /* get the interrupt information */ - csr = inb(EWRK3_CSR); - - /* - ** Mask the EWRK3 board interrupts and turn on the LED - */ - spin_lock(&lp->hw_lock); - DISABLE_IRQs; - - cr = inb(EWRK3_CR); - cr |= lp->led_mask; - outb(cr, EWRK3_CR); - - if (csr & CSR_RNE) /* Rx interrupt (packet[s] arrived) */ - ewrk3_rx(dev); - - if (csr & CSR_TNE) /* Tx interrupt (packet sent) */ - ewrk3_tx(dev); - - /* - ** Now deal with the TX/RX disable flags. These are set when there - ** are no more resources. If resources free up then enable these - ** interrupts, otherwise mask them - failure to do this will result - ** in the system hanging in an interrupt loop. - */ - if (inb(EWRK3_FMQC)) { /* any resources available? */ - lp->irq_mask |= ICR_TXDM | ICR_RXDM; /* enable the interrupt source */ - csr &= ~(CSR_TXD | CSR_RXD); /* ensure restart of a stalled TX or RX */ - outb(csr, EWRK3_CSR); - netif_wake_queue(dev); - } else { - lp->irq_mask &= ~(ICR_TXDM | ICR_RXDM); /* disable the interrupt source */ - } - - /* Unmask the EWRK3 board interrupts and turn off the LED */ - cr &= ~(lp->led_mask); - outb(cr, EWRK3_CR); - ENABLE_IRQs; - spin_unlock(&lp->hw_lock); - return IRQ_HANDLED; -} - -/* Called with lp->hw_lock held */ -static int ewrk3_rx(struct net_device *dev) -{ - struct ewrk3_private *lp = netdev_priv(dev); - u_long iobase = dev->base_addr; - int i, status = 0; - u_char page; - void __iomem *buf = NULL; - - while (inb(EWRK3_RQC) && !status) { /* Whilst there's incoming data */ - if ((page = inb(EWRK3_RQ)) < lp->mPage) { /* Get next entry's buffer page */ - /* - ** Set up shared memory window and pointer into the window - */ - if (lp->shmem_length == IO_ONLY) { - outb(page, EWRK3_IOPR); - } else if (lp->shmem_length == SHMEM_2K) { - buf = lp->shmem; - outb(page, EWRK3_MPR); - } else if (lp->shmem_length == SHMEM_32K) { - buf = (((short) page << 11) & 0x7800) + lp->shmem; - outb((page >> 4), EWRK3_MPR); - } else if (lp->shmem_length == SHMEM_64K) { - buf = (((short) page << 11) & 0xf800) + lp->shmem; - outb((page >> 5), EWRK3_MPR); - } else { - status = -1; - printk("%s: Oops - your private data area is hosed!\n", dev->name); - } - - if (!status) { - char rx_status; - int pkt_len; - - if (lp->shmem_length == IO_ONLY) { - rx_status = inb(EWRK3_DATA); - pkt_len = inb(EWRK3_DATA); - pkt_len |= ((u_short) inb(EWRK3_DATA) << 8); - } else { - rx_status = readb(buf); - buf += 1; - pkt_len = readw(buf); - buf += 3; - } - - if (!(rx_status & R_ROK)) { /* There was an error. */ - dev->stats.rx_errors++; /* Update the error stats. */ - if (rx_status & R_DBE) - dev->stats.rx_frame_errors++; - if (rx_status & R_CRC) - dev->stats.rx_crc_errors++; - if (rx_status & R_PLL) - dev->stats.rx_fifo_errors++; - } else { - struct sk_buff *skb; - skb = netdev_alloc_skb(dev, - pkt_len + 2); - - if (skb != NULL) { - unsigned char *p; - skb_reserve(skb, 2); /* Align to 16 bytes */ - p = skb_put(skb, pkt_len); - - if (lp->shmem_length == IO_ONLY) { - *p = inb(EWRK3_DATA); /* dummy read */ - for (i = 0; i < pkt_len; i++) { - *p++ = inb(EWRK3_DATA); - } - } else { - memcpy_fromio(p, buf, pkt_len); - } - - for (i = 1; i < EWRK3_PKT_STAT_SZ - 1; i++) { - if (pkt_len < i * EWRK3_PKT_BIN_SZ) { - lp->pktStats.bins[i]++; - i = EWRK3_PKT_STAT_SZ; - } - } - p = skb->data; /* Look at the dest addr */ - if (is_multicast_ether_addr(p)) { - if (is_broadcast_ether_addr(p)) { - lp->pktStats.broadcast++; - } else { - lp->pktStats.multicast++; - } - } else if (ether_addr_equal(p, - dev->dev_addr)) { - lp->pktStats.unicast++; - } - lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */ - if (lp->pktStats.bins[0] == 0) { /* Reset counters */ - memset(&lp->pktStats, 0, sizeof(lp->pktStats)); - } - /* - ** Notify the upper protocol layers that there is another - ** packet to handle - */ - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - - /* - ** Update stats - */ - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - } else { - printk("%s: Insufficient memory; nuking packet.\n", dev->name); - dev->stats.rx_dropped++; /* Really, deferred. */ - break; - } - } - } - /* - ** Return the received buffer to the free memory queue - */ - outb(page, EWRK3_FMQ); - } else { - printk("ewrk3_rx(): Illegal page number, page %d\n", page); - printk("ewrk3_rx(): CSR: %02x ICR: %02x FMQC: %02x\n", inb(EWRK3_CSR), inb(EWRK3_ICR), inb(EWRK3_FMQC)); - } - } - return status; -} - -/* -** Buffer sent - check for TX buffer errors. -** Called with lp->hw_lock held -*/ -static int ewrk3_tx(struct net_device *dev) -{ - struct ewrk3_private *lp = netdev_priv(dev); - u_long iobase = dev->base_addr; - u_char tx_status; - - while ((tx_status = inb(EWRK3_TDQ)) > 0) { /* Whilst there's old buffers */ - if (tx_status & T_VSTS) { /* The status is valid */ - if (tx_status & T_TXE) { - dev->stats.tx_errors++; - if (tx_status & T_NCL) - dev->stats.tx_carrier_errors++; - if (tx_status & T_LCL) - dev->stats.tx_window_errors++; - if (tx_status & T_CTU) { - if ((tx_status & T_COLL) ^ T_XUR) { - lp->pktStats.tx_underruns++; - } else { - lp->pktStats.excessive_underruns++; - } - } else if (tx_status & T_COLL) { - if ((tx_status & T_COLL) ^ T_XCOLL) { - dev->stats.collisions++; - } else { - lp->pktStats.excessive_collisions++; - } - } - } else { - dev->stats.tx_packets++; - } - } - } - - return 0; -} - -static int ewrk3_close(struct net_device *dev) -{ - struct ewrk3_private *lp = netdev_priv(dev); - u_long iobase = dev->base_addr; - u_char icr, csr; - - netif_stop_queue(dev); - - if (ewrk3_debug > 1) { - printk("%s: Shutting down ethercard, status was %2.2x.\n", - dev->name, inb(EWRK3_CSR)); - } - /* - ** We stop the EWRK3 here... mask interrupts and stop TX & RX - */ - DISABLE_IRQs; - - STOP_EWRK3; - - /* - ** Clean out the TX and RX queues here (note that one entry - ** may get added to either the TXD or RX queues if the TX or RX - ** just starts processing a packet before the STOP_EWRK3 command - ** is received. This will be flushed in the ewrk3_open() call). - */ - while (inb(EWRK3_TQ)); - while (inb(EWRK3_TDQ)); - while (inb(EWRK3_RQ)); - - if (!lp->hard_strapped) { - free_irq(dev->irq, dev); - } - return 0; -} - -/* - ** Set or clear the multicast filter for this adapter. - */ -static void set_multicast_list(struct net_device *dev) -{ - struct ewrk3_private *lp = netdev_priv(dev); - u_long iobase = dev->base_addr; - u_char csr; - - csr = inb(EWRK3_CSR); - - if (lp->shmem_length == IO_ONLY) { - lp->mctbl = NULL; - } else { - lp->mctbl = lp->shmem + PAGE0_HTE; - } - - csr &= ~(CSR_PME | CSR_MCE); - if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */ - csr |= CSR_PME; - outb(csr, EWRK3_CSR); - } else { - SetMulticastFilter(dev); - csr |= CSR_MCE; - outb(csr, EWRK3_CSR); - } -} - -/* - ** Calculate the hash code and update the logical address filter - ** from a list of ethernet multicast addresses. - ** Little endian crc one liner from Matt Thomas, DEC. - ** - ** Note that when clearing the table, the broadcast bit must remain asserted - ** to receive broadcast messages. - */ -static void SetMulticastFilter(struct net_device *dev) -{ - struct ewrk3_private *lp = netdev_priv(dev); - struct netdev_hw_addr *ha; - u_long iobase = dev->base_addr; - int i; - char bit, byte; - short __iomem *p = lp->mctbl; - u16 hashcode; - u32 crc; - - spin_lock_irq(&lp->hw_lock); - - if (lp->shmem_length == IO_ONLY) { - outb(0, EWRK3_IOPR); - outw(PAGE0_HTE, EWRK3_PIR1); - } else { - outb(0, EWRK3_MPR); - } - - if (dev->flags & IFF_ALLMULTI) { - for (i = 0; i < (HASH_TABLE_LEN >> 3); i++) { - if (lp->shmem_length == IO_ONLY) { - outb(0xff, EWRK3_DATA); - } else { /* memset didn't work here */ - writew(0xffff, p); - p++; - i++; - } - } - } else { - /* Clear table except for broadcast bit */ - if (lp->shmem_length == IO_ONLY) { - for (i = 0; i < (HASH_TABLE_LEN >> 4) - 1; i++) { - outb(0x00, EWRK3_DATA); - } - outb(0x80, EWRK3_DATA); - i++; /* insert the broadcast bit */ - for (; i < (HASH_TABLE_LEN >> 3); i++) { - outb(0x00, EWRK3_DATA); - } - } else { - memset_io(lp->mctbl, 0, HASH_TABLE_LEN >> 3); - writeb(0x80, lp->mctbl + (HASH_TABLE_LEN >> 4) - 1); - } - - /* Update table */ - netdev_for_each_mc_addr(ha, dev) { - crc = ether_crc_le(ETH_ALEN, ha->addr); - hashcode = crc & ((1 << 9) - 1); /* hashcode is 9 LSb of CRC */ - - byte = hashcode >> 3; /* bit[3-8] -> byte in filter */ - bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */ - - if (lp->shmem_length == IO_ONLY) { - u_char tmp; - - outw(PAGE0_HTE + byte, EWRK3_PIR1); - tmp = inb(EWRK3_DATA); - tmp |= bit; - outw(PAGE0_HTE + byte, EWRK3_PIR1); - outb(tmp, EWRK3_DATA); - } else { - writeb(readb(lp->mctbl + byte) | bit, lp->mctbl + byte); - } - } - } - - spin_unlock_irq(&lp->hw_lock); -} - -/* - ** ISA bus I/O device probe - */ -static int __init isa_probe(struct net_device *dev, u_long ioaddr) -{ - int i = num_ewrks3s, maxSlots; - int ret = -ENODEV; - - u_long iobase; - - if (ioaddr >= 0x400) - goto out; - - if (ioaddr == 0) { /* Autoprobing */ - iobase = EWRK3_IO_BASE; /* Get the first slot address */ - maxSlots = 24; - } else { /* Probe a specific location */ - iobase = ioaddr; - maxSlots = i + 1; - } - - for (; (i < maxSlots) && (dev != NULL); - iobase += EWRK3_IOP_INC, i++) - { - if (request_region(iobase, EWRK3_TOTAL_SIZE, DRV_NAME)) { - if (DevicePresent(iobase) == 0) { - int irq = dev->irq; - ret = ewrk3_hw_init(dev, iobase); - if (!ret) - break; - dev->irq = irq; - } - release_region(iobase, EWRK3_TOTAL_SIZE); - } - } - out: - - return ret; -} - -/* - ** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually - ** the motherboard. - */ -static int __init eisa_probe(struct net_device *dev, u_long ioaddr) -{ - int i, maxSlots; - u_long iobase; - int ret = -ENODEV; - - if (ioaddr < 0x1000) - goto out; - - iobase = ioaddr; - i = (ioaddr >> 12); - maxSlots = i + 1; - - for (i = 1; (i < maxSlots) && (dev != NULL); i++, iobase += EISA_SLOT_INC) { - if (EISA_signature(name, EISA_ID) == 0) { - if (request_region(iobase, EWRK3_TOTAL_SIZE, DRV_NAME) && - DevicePresent(iobase) == 0) { - int irq = dev->irq; - ret = ewrk3_hw_init(dev, iobase); - if (!ret) - break; - dev->irq = irq; - } - release_region(iobase, EWRK3_TOTAL_SIZE); - } - } - - out: - return ret; -} - - -/* - ** Read the EWRK3 EEPROM using this routine - */ -static int Read_EEPROM(u_long iobase, u_char eaddr) -{ - int i; - - outb((eaddr & 0x3f), EWRK3_PIR1); /* set up 6 bits of address info */ - outb(EEPROM_RD, EWRK3_IOPR); /* issue read command */ - for (i = 0; i < 5000; i++) - inb(EWRK3_CSR); /* wait 1msec */ - - return inw(EWRK3_EPROM1); /* 16 bits data return */ -} - -/* - ** Write the EWRK3 EEPROM using this routine - */ -static int Write_EEPROM(short data, u_long iobase, u_char eaddr) -{ - int i; - - outb(EEPROM_WR_EN, EWRK3_IOPR); /* issue write enable command */ - for (i = 0; i < 5000; i++) - inb(EWRK3_CSR); /* wait 1msec */ - outw(data, EWRK3_EPROM1); /* write data to register */ - outb((eaddr & 0x3f), EWRK3_PIR1); /* set up 6 bits of address info */ - outb(EEPROM_WR, EWRK3_IOPR); /* issue write command */ - for (i = 0; i < 75000; i++) - inb(EWRK3_CSR); /* wait 15msec */ - outb(EEPROM_WR_DIS, EWRK3_IOPR); /* issue write disable command */ - for (i = 0; i < 5000; i++) - inb(EWRK3_CSR); /* wait 1msec */ - - return 0; -} - -/* - ** Look for a particular board name in the on-board EEPROM. - */ -static void __init EthwrkSignature(char *name, char *eeprom_image) -{ - int i; - char *signatures[] = EWRK3_SIGNATURE; - - for (i=0; *signatures[i] != '\0'; i++) - if( !strncmp(eeprom_image+EEPROM_PNAME7, signatures[i], strlen(signatures[i])) ) - break; - - if (*signatures[i] != '\0') { - memcpy(name, eeprom_image+EEPROM_PNAME7, EWRK3_STRLEN); - name[EWRK3_STRLEN] = '\0'; - } else - name[0] = '\0'; -} - -/* - ** Look for a special sequence in the Ethernet station address PROM that - ** is common across all EWRK3 products. - ** - ** Search the Ethernet address ROM for the signature. Since the ROM address - ** counter can start at an arbitrary point, the search must include the entire - ** probe sequence length plus the (length_of_the_signature - 1). - ** Stop the search IMMEDIATELY after the signature is found so that the - ** PROM address counter is correctly positioned at the start of the - ** ethernet address for later read out. - */ - -static int __init DevicePresent(u_long iobase) -{ - union { - struct { - u32 a; - u32 b; - } llsig; - char Sig[sizeof(u32) << 1]; - } - dev; - short sigLength; - char data; - int i, j, status = 0; - - dev.llsig.a = ETH_PROM_SIG; - dev.llsig.b = ETH_PROM_SIG; - sigLength = sizeof(u32) << 1; - - for (i = 0, j = 0; j < sigLength && i < PROBE_LENGTH + sigLength - 1; i++) { - data = inb(EWRK3_APROM); - if (dev.Sig[j] == data) { /* track signature */ - j++; - } else { /* lost signature; begin search again */ - if (data == dev.Sig[0]) { - j = 1; - } else { - j = 0; - } - } - } - - if (j != sigLength) { - status = -ENODEV; /* search failed */ - } - return status; -} - -static u_char __init get_hw_addr(struct net_device *dev, u_char * eeprom_image, char chipType) -{ - int i, j, k; - u_short chksum; - u_char crc, lfsr, sd, status = 0; - u_long iobase = dev->base_addr; - u16 tmp; - - if (chipType == LeMAC2) { - for (crc = 0x6a, j = 0; j < ETH_ALEN; j++) { - sd = dev->dev_addr[j] = eeprom_image[EEPROM_PADDR0 + j]; - outb(dev->dev_addr[j], EWRK3_PAR0 + j); - for (k = 0; k < 8; k++, sd >>= 1) { - lfsr = ((((crc & 0x02) >> 1) ^ (crc & 0x01)) ^ (sd & 0x01)) << 7; - crc = (crc >> 1) + lfsr; - } - } - if (crc != eeprom_image[EEPROM_PA_CRC]) - status = -1; - } else { - for (i = 0, k = 0; i < ETH_ALEN;) { - k <<= 1; - if (k > 0xffff) - k -= 0xffff; - - k += (u_char) (tmp = inb(EWRK3_APROM)); - dev->dev_addr[i] = (u_char) tmp; - outb(dev->dev_addr[i], EWRK3_PAR0 + i); - i++; - k += (u_short) ((tmp = inb(EWRK3_APROM)) << 8); - dev->dev_addr[i] = (u_char) tmp; - outb(dev->dev_addr[i], EWRK3_PAR0 + i); - i++; - - if (k > 0xffff) - k -= 0xffff; - } - if (k == 0xffff) - k = 0; - chksum = inb(EWRK3_APROM); - chksum |= (inb(EWRK3_APROM) << 8); - if (k != chksum) - status = -1; - } - - return status; -} - -/* - ** Look for a particular board name in the EISA configuration space - */ -static int __init EISA_signature(char *name, s32 eisa_id) -{ - u_long i; - char *signatures[] = EWRK3_SIGNATURE; - char ManCode[EWRK3_STRLEN]; - union { - s32 ID; - char Id[4]; - } Eisa; - int status = 0; - - *name = '\0'; - for (i = 0; i < 4; i++) { - Eisa.Id[i] = inb(eisa_id + i); - } - - ManCode[0] = (((Eisa.Id[0] >> 2) & 0x1f) + 0x40); - ManCode[1] = (((Eisa.Id[1] & 0xe0) >> 5) + ((Eisa.Id[0] & 0x03) << 3) + 0x40); - ManCode[2] = (((Eisa.Id[2] >> 4) & 0x0f) + 0x30); - ManCode[3] = ((Eisa.Id[2] & 0x0f) + 0x30); - ManCode[4] = (((Eisa.Id[3] >> 4) & 0x0f) + 0x30); - ManCode[5] = '\0'; - - for (i = 0; (*signatures[i] != '\0') && (*name == '\0'); i++) { - if (strstr(ManCode, signatures[i]) != NULL) { - strcpy(name, ManCode); - status = 1; - } - } - - return status; /* return the device name string */ -} - -static void ewrk3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) -{ - int fwrev = Read_EEPROM(dev->base_addr, EEPROM_REVLVL); - - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - snprintf(info->fw_version, sizeof(info->fw_version), "%d", fwrev); - strlcpy(info->bus_info, "N/A", sizeof(info->bus_info)); - info->eedump_len = EEPROM_MAX; -} - -static int ewrk3_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) -{ - struct ewrk3_private *lp = netdev_priv(dev); - unsigned long iobase = dev->base_addr; - u8 cr = inb(EWRK3_CR); - - switch (lp->adapter_name[4]) { - case '3': /* DE203 */ - ecmd->supported = SUPPORTED_BNC; - ecmd->port = PORT_BNC; - break; - - case '4': /* DE204 */ - ecmd->supported = SUPPORTED_TP; - ecmd->port = PORT_TP; - break; - - case '5': /* DE205 */ - ecmd->supported = SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_AUI; - ecmd->autoneg = !(cr & CR_APD); - /* - ** Port is only valid if autoneg is disabled - ** and even then we don't know if AUI is jumpered. - */ - if (!ecmd->autoneg) - ecmd->port = (cr & CR_PSEL) ? PORT_BNC : PORT_TP; - break; - } - - ecmd->supported |= SUPPORTED_10baseT_Half; - ethtool_cmd_speed_set(ecmd, SPEED_10); - ecmd->duplex = DUPLEX_HALF; - return 0; -} - -static int ewrk3_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) -{ - struct ewrk3_private *lp = netdev_priv(dev); - unsigned long iobase = dev->base_addr; - unsigned long flags; - u8 cr; - - /* DE205 is the only card with anything to set */ - if (lp->adapter_name[4] != '5') - return -EOPNOTSUPP; - - /* Sanity-check parameters */ - if (ecmd->speed != SPEED_10) - return -EINVAL; - if (ecmd->port != PORT_TP && ecmd->port != PORT_BNC) - return -EINVAL; /* AUI is not software-selectable */ - if (ecmd->transceiver != XCVR_INTERNAL) - return -EINVAL; - if (ecmd->duplex != DUPLEX_HALF) - return -EINVAL; - if (ecmd->phy_address != 0) - return -EINVAL; - - spin_lock_irqsave(&lp->hw_lock, flags); - cr = inb(EWRK3_CR); - - /* If Autoneg is set, change to Auto Port mode */ - /* Otherwise, disable Auto Port and set port explicitly */ - if (ecmd->autoneg) { - cr &= ~CR_APD; - } else { - cr |= CR_APD; - if (ecmd->port == PORT_TP) - cr &= ~CR_PSEL; /* Force TP */ - else - cr |= CR_PSEL; /* Force BNC */ - } - - /* Commit the changes */ - outb(cr, EWRK3_CR); - spin_unlock_irqrestore(&lp->hw_lock, flags); - return 0; -} - -static u32 ewrk3_get_link(struct net_device *dev) -{ - unsigned long iobase = dev->base_addr; - u8 cmr = inb(EWRK3_CMR); - /* DE203 has BNC only and link status does not apply */ - /* On DE204 this is always valid since TP is the only port. */ - /* On DE205 this reflects TP status even if BNC or AUI is selected. */ - return !(cmr & CMR_LINK); -} - -static int ewrk3_set_phys_id(struct net_device *dev, - enum ethtool_phys_id_state state) -{ - struct ewrk3_private *lp = netdev_priv(dev); - unsigned long iobase = dev->base_addr; - u8 cr; - - spin_lock_irq(&lp->hw_lock); - - switch (state) { - case ETHTOOL_ID_ACTIVE: - /* Prevent ISR from twiddling the LED */ - lp->led_mask = 0; - spin_unlock_irq(&lp->hw_lock); - return 2; /* cycle on/off twice per second */ - - case ETHTOOL_ID_ON: - cr = inb(EWRK3_CR); - outb(cr | CR_LED, EWRK3_CR); - break; - - case ETHTOOL_ID_OFF: - cr = inb(EWRK3_CR); - outb(cr & ~CR_LED, EWRK3_CR); - break; - - case ETHTOOL_ID_INACTIVE: - lp->led_mask = CR_LED; - cr = inb(EWRK3_CR); - outb(cr & ~CR_LED, EWRK3_CR); - } - spin_unlock_irq(&lp->hw_lock); - - return 0; -} - -static const struct ethtool_ops ethtool_ops_203 = { - .get_drvinfo = ewrk3_get_drvinfo, - .get_settings = ewrk3_get_settings, - .set_settings = ewrk3_set_settings, - .set_phys_id = ewrk3_set_phys_id, -}; - -static const struct ethtool_ops ethtool_ops = { - .get_drvinfo = ewrk3_get_drvinfo, - .get_settings = ewrk3_get_settings, - .set_settings = ewrk3_set_settings, - .get_link = ewrk3_get_link, - .set_phys_id = ewrk3_set_phys_id, -}; - -/* - ** Perform IOCTL call functions here. Some are privileged operations and the - ** effective uid is checked in those cases. - */ -static int ewrk3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct ewrk3_private *lp = netdev_priv(dev); - struct ewrk3_ioctl *ioc = (struct ewrk3_ioctl *) &rq->ifr_ifru; - u_long iobase = dev->base_addr; - int i, j, status = 0; - u_char csr; - unsigned long flags; - union ewrk3_addr { - u_char addr[HASH_TABLE_LEN * ETH_ALEN]; - u_short val[(HASH_TABLE_LEN * ETH_ALEN) >> 1]; - }; - - union ewrk3_addr *tmp; - - /* All we handle are private IOCTLs */ - if (cmd != EWRK3IOCTL) - return -EOPNOTSUPP; - - tmp = kmalloc(sizeof(union ewrk3_addr), GFP_KERNEL); - if(tmp==NULL) - return -ENOMEM; - - switch (ioc->cmd) { - case EWRK3_GET_HWADDR: /* Get the hardware address */ - for (i = 0; i < ETH_ALEN; i++) { - tmp->addr[i] = dev->dev_addr[i]; - } - ioc->len = ETH_ALEN; - if (copy_to_user(ioc->data, tmp->addr, ioc->len)) - status = -EFAULT; - break; - - case EWRK3_SET_HWADDR: /* Set the hardware address */ - if (capable(CAP_NET_ADMIN)) { - spin_lock_irqsave(&lp->hw_lock, flags); - csr = inb(EWRK3_CSR); - csr |= (CSR_TXD | CSR_RXD); - outb(csr, EWRK3_CSR); /* Disable the TX and RX */ - spin_unlock_irqrestore(&lp->hw_lock, flags); - - if (copy_from_user(tmp->addr, ioc->data, ETH_ALEN)) { - status = -EFAULT; - break; - } - spin_lock_irqsave(&lp->hw_lock, flags); - for (i = 0; i < ETH_ALEN; i++) { - dev->dev_addr[i] = tmp->addr[i]; - outb(tmp->addr[i], EWRK3_PAR0 + i); - } - - csr = inb(EWRK3_CSR); - csr &= ~(CSR_TXD | CSR_RXD); /* Enable the TX and RX */ - outb(csr, EWRK3_CSR); - spin_unlock_irqrestore(&lp->hw_lock, flags); - } else { - status = -EPERM; - } - - break; - case EWRK3_SET_PROM: /* Set Promiscuous Mode */ - if (capable(CAP_NET_ADMIN)) { - spin_lock_irqsave(&lp->hw_lock, flags); - csr = inb(EWRK3_CSR); - csr |= CSR_PME; - csr &= ~CSR_MCE; - outb(csr, EWRK3_CSR); - spin_unlock_irqrestore(&lp->hw_lock, flags); - } else { - status = -EPERM; - } - - break; - case EWRK3_CLR_PROM: /* Clear Promiscuous Mode */ - if (capable(CAP_NET_ADMIN)) { - spin_lock_irqsave(&lp->hw_lock, flags); - csr = inb(EWRK3_CSR); - csr &= ~CSR_PME; - outb(csr, EWRK3_CSR); - spin_unlock_irqrestore(&lp->hw_lock, flags); - } else { - status = -EPERM; - } - - break; - case EWRK3_GET_MCA: /* Get the multicast address table */ - spin_lock_irqsave(&lp->hw_lock, flags); - if (lp->shmem_length == IO_ONLY) { - outb(0, EWRK3_IOPR); - outw(PAGE0_HTE, EWRK3_PIR1); - for (i = 0; i < (HASH_TABLE_LEN >> 3); i++) { - tmp->addr[i] = inb(EWRK3_DATA); - } - } else { - outb(0, EWRK3_MPR); - memcpy_fromio(tmp->addr, lp->shmem + PAGE0_HTE, (HASH_TABLE_LEN >> 3)); - } - spin_unlock_irqrestore(&lp->hw_lock, flags); - - ioc->len = (HASH_TABLE_LEN >> 3); - if (copy_to_user(ioc->data, tmp->addr, ioc->len)) - status = -EFAULT; - - break; - case EWRK3_SET_MCA: /* Set a multicast address */ - if (capable(CAP_NET_ADMIN)) { - if (ioc->len > HASH_TABLE_LEN) { - status = -EINVAL; - break; - } - if (copy_from_user(tmp->addr, ioc->data, ETH_ALEN * ioc->len)) { - status = -EFAULT; - break; - } - set_multicast_list(dev); - } else { - status = -EPERM; - } - - break; - case EWRK3_CLR_MCA: /* Clear all multicast addresses */ - if (capable(CAP_NET_ADMIN)) { - set_multicast_list(dev); - } else { - status = -EPERM; - } - - break; - case EWRK3_MCA_EN: /* Enable multicast addressing */ - if (capable(CAP_NET_ADMIN)) { - spin_lock_irqsave(&lp->hw_lock, flags); - csr = inb(EWRK3_CSR); - csr |= CSR_MCE; - csr &= ~CSR_PME; - outb(csr, EWRK3_CSR); - spin_unlock_irqrestore(&lp->hw_lock, flags); - } else { - status = -EPERM; - } - - break; - case EWRK3_GET_STATS: { /* Get the driver statistics */ - struct ewrk3_stats *tmp_stats = - kmalloc(sizeof(lp->pktStats), GFP_KERNEL); - if (!tmp_stats) { - status = -ENOMEM; - break; - } - - spin_lock_irqsave(&lp->hw_lock, flags); - memcpy(tmp_stats, &lp->pktStats, sizeof(lp->pktStats)); - spin_unlock_irqrestore(&lp->hw_lock, flags); - - ioc->len = sizeof(lp->pktStats); - if (copy_to_user(ioc->data, tmp_stats, sizeof(lp->pktStats))) - status = -EFAULT; - kfree(tmp_stats); - break; - } - case EWRK3_CLR_STATS: /* Zero out the driver statistics */ - if (capable(CAP_NET_ADMIN)) { - spin_lock_irqsave(&lp->hw_lock, flags); - memset(&lp->pktStats, 0, sizeof(lp->pktStats)); - spin_unlock_irqrestore(&lp->hw_lock,flags); - } else { - status = -EPERM; - } - - break; - case EWRK3_GET_CSR: /* Get the CSR Register contents */ - tmp->addr[0] = inb(EWRK3_CSR); - ioc->len = 1; - if (copy_to_user(ioc->data, tmp->addr, ioc->len)) - status = -EFAULT; - break; - case EWRK3_SET_CSR: /* Set the CSR Register contents */ - if (capable(CAP_NET_ADMIN)) { - if (copy_from_user(tmp->addr, ioc->data, 1)) { - status = -EFAULT; - break; - } - outb(tmp->addr[0], EWRK3_CSR); - } else { - status = -EPERM; - } - - break; - case EWRK3_GET_EEPROM: /* Get the EEPROM contents */ - if (capable(CAP_NET_ADMIN)) { - for (i = 0; i < (EEPROM_MAX >> 1); i++) { - tmp->val[i] = (short) Read_EEPROM(iobase, i); - } - i = EEPROM_MAX; - tmp->addr[i++] = inb(EWRK3_CMR); /* Config/Management Reg. */ - for (j = 0; j < ETH_ALEN; j++) { - tmp->addr[i++] = inb(EWRK3_PAR0 + j); - } - ioc->len = EEPROM_MAX + 1 + ETH_ALEN; - if (copy_to_user(ioc->data, tmp->addr, ioc->len)) - status = -EFAULT; - } else { - status = -EPERM; - } - - break; - case EWRK3_SET_EEPROM: /* Set the EEPROM contents */ - if (capable(CAP_NET_ADMIN)) { - if (copy_from_user(tmp->addr, ioc->data, EEPROM_MAX)) { - status = -EFAULT; - break; - } - for (i = 0; i < (EEPROM_MAX >> 1); i++) { - Write_EEPROM(tmp->val[i], iobase, i); - } - } else { - status = -EPERM; - } - - break; - case EWRK3_GET_CMR: /* Get the CMR Register contents */ - tmp->addr[0] = inb(EWRK3_CMR); - ioc->len = 1; - if (copy_to_user(ioc->data, tmp->addr, ioc->len)) - status = -EFAULT; - break; - case EWRK3_SET_TX_CUT_THRU: /* Set TX cut through mode */ - if (capable(CAP_NET_ADMIN)) { - lp->txc = 1; - } else { - status = -EPERM; - } - - break; - case EWRK3_CLR_TX_CUT_THRU: /* Clear TX cut through mode */ - if (capable(CAP_NET_ADMIN)) { - lp->txc = 0; - } else { - status = -EPERM; - } - - break; - default: - status = -EOPNOTSUPP; - } - kfree(tmp); - return status; -} - -#ifdef MODULE -static struct net_device *ewrk3_devs[MAX_NUM_EWRK3S]; -static int ndevs; -static int io[MAX_NUM_EWRK3S+1] = { 0x300, 0, }; - -module_param_array(io, int, NULL, 0); -module_param_array(irq, byte, NULL, 0); -MODULE_PARM_DESC(io, "EtherWORKS 3 I/O base address(es)"); -MODULE_PARM_DESC(irq, "EtherWORKS 3 IRQ number(s)"); - -static __exit void ewrk3_exit_module(void) -{ - int i; - - for( i=0; ibase_addr, EWRK3_TOTAL_SIZE); - iounmap(lp->shmem); - free_netdev(dev); - } -} - -static __init int ewrk3_init_module(void) -{ - int i=0; - - while( io[i] && irq[i] ) { - struct net_device *dev - = alloc_etherdev(sizeof(struct ewrk3_private)); - - if (!dev) - break; - - if (ewrk3_probe1(dev, io[i], irq[i]) != 0) { - free_netdev(dev); - break; - } - - ewrk3_devs[ndevs++] = dev; - i++; - } - - return ndevs ? 0 : -EIO; -} - - -/* Hack for breakage in new module stuff */ -module_exit(ewrk3_exit_module); -module_init(ewrk3_init_module); -#endif /* MODULE */ -MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/dec/ewrk3.h b/drivers/net/ethernet/dec/ewrk3.h deleted file mode 100644 index 8e0ee906567b..000000000000 --- a/drivers/net/ethernet/dec/ewrk3.h +++ /dev/null @@ -1,322 +0,0 @@ -/* - Written 1994 by David C. Davies. - - Copyright 1994 Digital Equipment Corporation. - - This software may be used and distributed according to the terms of the - GNU General Public License, incorporated herein by reference. - - The author may be reached as davies@wanton.lkg.dec.com or Digital - Equipment Corporation, 550 King Street, Littleton MA 01460. - - ========================================================================= -*/ - -/* -** I/O Address Register Map -*/ -#define EWRK3_CSR iobase+0x00 /* Control and Status Register */ -#define EWRK3_CR iobase+0x01 /* Control Register */ -#define EWRK3_ICR iobase+0x02 /* Interrupt Control Register */ -#define EWRK3_TSR iobase+0x03 /* Transmit Status Register */ -#define EWRK3_RSVD1 iobase+0x04 /* RESERVED */ -#define EWRK3_RSVD2 iobase+0x05 /* RESERVED */ -#define EWRK3_FMQ iobase+0x06 /* Free Memory Queue */ -#define EWRK3_FMQC iobase+0x07 /* Free Memory Queue Counter */ -#define EWRK3_RQ iobase+0x08 /* Receive Queue */ -#define EWRK3_RQC iobase+0x09 /* Receive Queue Counter */ -#define EWRK3_TQ iobase+0x0a /* Transmit Queue */ -#define EWRK3_TQC iobase+0x0b /* Transmit Queue Counter */ -#define EWRK3_TDQ iobase+0x0c /* Transmit Done Queue */ -#define EWRK3_TDQC iobase+0x0d /* Transmit Done Queue Counter */ -#define EWRK3_PIR1 iobase+0x0e /* Page Index Register 1 */ -#define EWRK3_PIR2 iobase+0x0f /* Page Index Register 2 */ -#define EWRK3_DATA iobase+0x10 /* Data Register */ -#define EWRK3_IOPR iobase+0x11 /* I/O Page Register */ -#define EWRK3_IOBR iobase+0x12 /* I/O Base Register */ -#define EWRK3_MPR iobase+0x13 /* Memory Page Register */ -#define EWRK3_MBR iobase+0x14 /* Memory Base Register */ -#define EWRK3_APROM iobase+0x15 /* Address PROM */ -#define EWRK3_EPROM1 iobase+0x16 /* EEPROM Data Register 1 */ -#define EWRK3_EPROM2 iobase+0x17 /* EEPROM Data Register 2 */ -#define EWRK3_PAR0 iobase+0x18 /* Physical Address Register 0 */ -#define EWRK3_PAR1 iobase+0x19 /* Physical Address Register 1 */ -#define EWRK3_PAR2 iobase+0x1a /* Physical Address Register 2 */ -#define EWRK3_PAR3 iobase+0x1b /* Physical Address Register 3 */ -#define EWRK3_PAR4 iobase+0x1c /* Physical Address Register 4 */ -#define EWRK3_PAR5 iobase+0x1d /* Physical Address Register 5 */ -#define EWRK3_CMR iobase+0x1e /* Configuration/Management Register */ - -/* -** Control Page Map -*/ -#define PAGE0_FMQ 0x000 /* Free Memory Queue */ -#define PAGE0_RQ 0x080 /* Receive Queue */ -#define PAGE0_TQ 0x100 /* Transmit Queue */ -#define PAGE0_TDQ 0x180 /* Transmit Done Queue */ -#define PAGE0_HTE 0x200 /* Hash Table Entries */ -#define PAGE0_RSVD 0x240 /* RESERVED */ -#define PAGE0_USRD 0x600 /* User Data */ - -/* -** Control and Status Register bit definitions (EWRK3_CSR) -*/ -#define CSR_RA 0x80 /* Runt Accept */ -#define CSR_PME 0x40 /* Promiscuous Mode Enable */ -#define CSR_MCE 0x20 /* Multicast Enable */ -#define CSR_TNE 0x08 /* TX Done Queue Not Empty */ -#define CSR_RNE 0x04 /* RX Queue Not Empty */ -#define CSR_TXD 0x02 /* TX Disable */ -#define CSR_RXD 0x01 /* RX Disable */ - -/* -** Control Register bit definitions (EWRK3_CR) -*/ -#define CR_APD 0x80 /* Auto Port Disable */ -#define CR_PSEL 0x40 /* Port Select (0->TP port) */ -#define CR_LBCK 0x20 /* LoopBaCK enable */ -#define CR_FDUP 0x10 /* Full DUPlex enable */ -#define CR_FBUS 0x08 /* Fast BUS enable (ISA clk > 8.33MHz) */ -#define CR_EN_16 0x04 /* ENable 16 bit memory accesses */ -#define CR_LED 0x02 /* LED (1-> turn on) */ - -/* -** Interrupt Control Register bit definitions (EWRK3_ICR) -*/ -#define ICR_IE 0x80 /* Interrupt Enable */ -#define ICR_IS 0x60 /* Interrupt Selected */ -#define ICR_TNEM 0x08 /* TNE Mask (0->mask) */ -#define ICR_RNEM 0x04 /* RNE Mask (0->mask) */ -#define ICR_TXDM 0x02 /* TXD Mask (0->mask) */ -#define ICR_RXDM 0x01 /* RXD Mask (0->mask) */ - -/* -** Transmit Status Register bit definitions (EWRK3_TSR) -*/ -#define TSR_NCL 0x80 /* No Carrier Loopback */ -#define TSR_ID 0x40 /* Initially Deferred */ -#define TSR_LCL 0x20 /* Late CoLlision */ -#define TSR_ECL 0x10 /* Excessive CoLlisions */ -#define TSR_RCNTR 0x0f /* Retries CouNTeR */ - -/* -** I/O Page Register bit definitions (EWRK3_IOPR) -*/ -#define EEPROM_INIT 0xc0 /* EEPROM INIT command */ -#define EEPROM_WR_EN 0xc8 /* EEPROM WRITE ENABLE command */ -#define EEPROM_WR 0xd0 /* EEPROM WRITE command */ -#define EEPROM_WR_DIS 0xd8 /* EEPROM WRITE DISABLE command */ -#define EEPROM_RD 0xe0 /* EEPROM READ command */ - -/* -** I/O Base Register bit definitions (EWRK3_IOBR) -*/ -#define EISA_REGS_EN 0x20 /* Enable EISA ID and Control Registers */ -#define EISA_IOB 0x1f /* Compare bits for I/O Base Address */ - -/* -** I/O Configuration/Management Register bit definitions (EWRK3_CMR) -*/ -#define CMR_RA 0x80 /* Read Ahead */ -#define CMR_WB 0x40 /* Write Behind */ -#define CMR_LINK 0x20 /* 0->TP */ -#define CMR_POLARITY 0x10 /* Informational */ -#define CMR_NO_EEPROM 0x0c /* NO_EEPROM<1:0> pin status */ -#define CMR_HS 0x08 /* Hard Strapped pin status (LeMAC2) */ -#define CMR_PNP 0x04 /* Plug 'n Play */ -#define CMR_DRAM 0x02 /* 0-> 1DRAM, 1-> 2 DRAM on board */ -#define CMR_0WS 0x01 /* Zero Wait State */ - -/* -** MAC Receive Status Register bit definitions -*/ - -#define R_ROK 0x80 /* Receive OK summary */ -#define R_IAM 0x10 /* Individual Address Match */ -#define R_MCM 0x08 /* MultiCast Match */ -#define R_DBE 0x04 /* Dribble Bit Error */ -#define R_CRC 0x02 /* CRC error */ -#define R_PLL 0x01 /* Phase Lock Lost */ - -/* -** MAC Transmit Control Register bit definitions -*/ - -#define TCR_SQEE 0x40 /* SQE Enable - look for heartbeat */ -#define TCR_SED 0x20 /* Stop when Error Detected */ -#define TCR_QMODE 0x10 /* Q_MODE */ -#define TCR_LAB 0x08 /* Less Aggressive Backoff */ -#define TCR_PAD 0x04 /* PAD Runt Packets */ -#define TCR_IFC 0x02 /* Insert Frame Check */ -#define TCR_ISA 0x01 /* Insert Source Address */ - -/* -** MAC Transmit Status Register bit definitions -*/ - -#define T_VSTS 0x80 /* Valid STatuS */ -#define T_CTU 0x40 /* Cut Through Used */ -#define T_SQE 0x20 /* Signal Quality Error */ -#define T_NCL 0x10 /* No Carrier Loopback */ -#define T_LCL 0x08 /* Late Collision */ -#define T_ID 0x04 /* Initially Deferred */ -#define T_COLL 0x03 /* COLLision status */ -#define T_XCOLL 0x03 /* Excessive Collisions */ -#define T_MCOLL 0x02 /* Multiple Collisions */ -#define T_OCOLL 0x01 /* One Collision */ -#define T_NOCOLL 0x00 /* No Collisions */ -#define T_XUR 0x03 /* Excessive Underruns */ -#define T_TXE 0x7f /* TX Errors */ - -/* -** EISA Configuration Register bit definitions -*/ - -#define EISA_ID iobase + 0x0c80 /* EISA ID Registers */ -#define EISA_ID0 iobase + 0x0c80 /* EISA ID Register 0 */ -#define EISA_ID1 iobase + 0x0c81 /* EISA ID Register 1 */ -#define EISA_ID2 iobase + 0x0c82 /* EISA ID Register 2 */ -#define EISA_ID3 iobase + 0x0c83 /* EISA ID Register 3 */ -#define EISA_CR iobase + 0x0c84 /* EISA Control Register */ - -/* -** EEPROM BYTES -*/ -#define EEPROM_MEMB 0x00 -#define EEPROM_IOB 0x01 -#define EEPROM_EISA_ID0 0x02 -#define EEPROM_EISA_ID1 0x03 -#define EEPROM_EISA_ID2 0x04 -#define EEPROM_EISA_ID3 0x05 -#define EEPROM_MISC0 0x06 -#define EEPROM_MISC1 0x07 -#define EEPROM_PNAME7 0x08 -#define EEPROM_PNAME6 0x09 -#define EEPROM_PNAME5 0x0a -#define EEPROM_PNAME4 0x0b -#define EEPROM_PNAME3 0x0c -#define EEPROM_PNAME2 0x0d -#define EEPROM_PNAME1 0x0e -#define EEPROM_PNAME0 0x0f -#define EEPROM_SWFLAGS 0x10 -#define EEPROM_HWCAT 0x11 -#define EEPROM_NETMAN2 0x12 -#define EEPROM_REVLVL 0x13 -#define EEPROM_NETMAN0 0x14 -#define EEPROM_NETMAN1 0x15 -#define EEPROM_CHIPVER 0x16 -#define EEPROM_SETUP 0x17 -#define EEPROM_PADDR0 0x18 -#define EEPROM_PADDR1 0x19 -#define EEPROM_PADDR2 0x1a -#define EEPROM_PADDR3 0x1b -#define EEPROM_PADDR4 0x1c -#define EEPROM_PADDR5 0x1d -#define EEPROM_PA_CRC 0x1e -#define EEPROM_CHKSUM 0x1f - -/* -** EEPROM bytes for checksumming -*/ -#define EEPROM_MAX 32 /* bytes */ - -/* -** EEPROM MISCELLANEOUS FLAGS -*/ -#define RBE_SHADOW 0x0100 /* Remote Boot Enable Shadow */ -#define READ_AHEAD 0x0080 /* Read Ahead feature */ -#define IRQ_SEL2 0x0070 /* IRQ line selection (LeMAC2) */ -#define IRQ_SEL 0x0060 /* IRQ line selection */ -#define FAST_BUS 0x0008 /* ISA Bus speeds > 8.33MHz */ -#define ENA_16 0x0004 /* Enables 16 bit memory transfers */ -#define WRITE_BEHIND 0x0002 /* Write Behind feature */ -#define _0WS_ENA 0x0001 /* Zero Wait State Enable */ - -/* -** EEPROM NETWORK MANAGEMENT FLAGS -*/ -#define NETMAN_POL 0x04 /* Polarity defeat */ -#define NETMAN_LINK 0x02 /* Link defeat */ -#define NETMAN_CCE 0x01 /* Custom Counters Enable */ - -/* -** EEPROM SW FLAGS -*/ -#define SW_SQE 0x10 /* Signal Quality Error */ -#define SW_LAB 0x08 /* Less Aggressive Backoff */ -#define SW_INIT 0x04 /* Initialized */ -#define SW_TIMEOUT 0x02 /* 0:2.5 mins, 1: 30 secs */ -#define SW_REMOTE 0x01 /* Remote Boot Enable -> 1 */ - -/* -** EEPROM SETUP FLAGS -*/ -#define SETUP_APD 0x80 /* AutoPort Disable */ -#define SETUP_PS 0x40 /* Port Select */ -#define SETUP_MP 0x20 /* MultiPort */ -#define SETUP_1TP 0x10 /* 1 port, TP */ -#define SETUP_1COAX 0x00 /* 1 port, Coax */ -#define SETUP_DRAM 0x02 /* Number of DRAMS on board */ - -/* -** EEPROM MANAGEMENT FLAGS -*/ -#define MGMT_CCE 0x01 /* Custom Counters Enable */ - -/* -** EEPROM VERSIONS -*/ -#define LeMAC 0x11 -#define LeMAC2 0x12 - -/* -** Miscellaneous -*/ - -#define EEPROM_WAIT_TIME 1000 /* Number of microseconds */ -#define EISA_EN 0x0001 /* Enable EISA bus buffers */ - -#define HASH_TABLE_LEN 512 /* Bits */ - -#define XCT 0x80 /* Transmit Cut Through */ -#define PRELOAD 16 /* 4 long words */ - -#define MASK_INTERRUPTS 1 -#define UNMASK_INTERRUPTS 0 - -#define EEPROM_OFFSET(a) ((u_short)((u_long)(a))) - -/* -** Include the IOCTL stuff -*/ -#include - -#define EWRK3IOCTL SIOCDEVPRIVATE - -struct ewrk3_ioctl { - unsigned short cmd; /* Command to run */ - unsigned short len; /* Length of the data buffer */ - unsigned char __user *data; /* Pointer to the data buffer */ -}; - -/* -** Recognised commands for the driver -*/ -#define EWRK3_GET_HWADDR 0x01 /* Get the hardware address */ -#define EWRK3_SET_HWADDR 0x02 /* Get the hardware address */ -#define EWRK3_SET_PROM 0x03 /* Set Promiscuous Mode */ -#define EWRK3_CLR_PROM 0x04 /* Clear Promiscuous Mode */ -#define EWRK3_SAY_BOO 0x05 /* Say "Boo!" to the kernel log file */ -#define EWRK3_GET_MCA 0x06 /* Get a multicast address */ -#define EWRK3_SET_MCA 0x07 /* Set a multicast address */ -#define EWRK3_CLR_MCA 0x08 /* Clear a multicast address */ -#define EWRK3_MCA_EN 0x09 /* Enable a multicast address group */ -#define EWRK3_GET_STATS 0x0a /* Get the driver statistics */ -#define EWRK3_CLR_STATS 0x0b /* Zero out the driver statistics */ -#define EWRK3_GET_CSR 0x0c /* Get the CSR Register contents */ -#define EWRK3_SET_CSR 0x0d /* Set the CSR Register contents */ -#define EWRK3_GET_EEPROM 0x0e /* Get the EEPROM contents */ -#define EWRK3_SET_EEPROM 0x0f /* Set the EEPROM contents */ -#define EWRK3_GET_CMR 0x10 /* Get the CMR Register contents */ -#define EWRK3_CLR_TX_CUT_THRU 0x11 /* Clear the TX cut through mode */ -#define EWRK3_SET_TX_CUT_THRU 0x12 /* Set the TX cut through mode */ -- cgit v1.2.3-59-g8ed1b From 2475ff9d2c6ea3bbfed55c4635426c371f9ad327 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Tue, 23 Oct 2012 14:55:08 +0100 Subject: arm64: Add simple earlyprintk support This patch adds support for "earlyprintk=" parameter on the kernel command line. The format is: earlyprintk=[,][,] where is the name of the (UART) device, e.g. "pl011", is the I/O address. The aren't currently used. The mapping of the earlyprintk device is done very early during kernel boot and there are restrictions on which functions it can call. A special early_io_map() function is added which creates the mapping from the pre-defined EARLY_IOBASE to the device I/O address passed via the kernel parameter. The pgd entry corresponding to EARLY_IOBASE is pre-populated in head.S during kernel boot. Only PL011 is currently supported and it is assumed that the interface is already initialised by the boot loader before the kernel is started. Signed-off-by: Catalin Marinas Acked-by: Arnd Bergmann --- Documentation/arm64/memory.txt | 2 + arch/arm64/Kconfig.debug | 9 +++ arch/arm64/include/asm/io.h | 3 + arch/arm64/include/asm/memory.h | 1 + arch/arm64/include/asm/mmu.h | 1 + arch/arm64/kernel/Makefile | 1 + arch/arm64/kernel/early_printk.c | 118 +++++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/head.S | 12 +++- arch/arm64/mm/mmu.c | 42 ++++++++++++++ 9 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 arch/arm64/kernel/early_printk.c (limited to 'Documentation') diff --git a/Documentation/arm64/memory.txt b/Documentation/arm64/memory.txt index d758702fc03c..5f583af0a6e1 100644 --- a/Documentation/arm64/memory.txt +++ b/Documentation/arm64/memory.txt @@ -35,6 +35,8 @@ ffffffbc00000000 ffffffbdffffffff 8GB vmemmap ffffffbe00000000 ffffffbffbbfffff ~8GB [guard, future vmmemap] +ffffffbffbc00000 ffffffbffbdfffff 2MB earlyprintk device + ffffffbffbe00000 ffffffbffbe0ffff 64KB PCI I/O space ffffffbbffff0000 ffffffbcffffffff ~2MB [guard] diff --git a/arch/arm64/Kconfig.debug b/arch/arm64/Kconfig.debug index d7553f2bda66..79871cd78da8 100644 --- a/arch/arm64/Kconfig.debug +++ b/arch/arm64/Kconfig.debug @@ -24,4 +24,13 @@ config DEBUG_STACK_USAGE Enables the display of the minimum amount of free stack which each task has ever had available in the sysrq-T output. +config EARLY_PRINTK + bool "Early printk support" + default y + help + Say Y here if you want to have an early console using the + earlyprintk=[,][,] kernel parameter. It + is assumed that the early console device has been initialised + by the boot loader prior to starting the Linux kernel. + endmenu diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h index d2f05a608274..57f12c991de2 100644 --- a/arch/arm64/include/asm/io.h +++ b/arch/arm64/include/asm/io.h @@ -230,6 +230,9 @@ extern void __iounmap(volatile void __iomem *addr); #define ioremap_wc(addr, size) __ioremap((addr), (size), __pgprot(PROT_NORMAL_NC)) #define iounmap __iounmap +#define PROT_SECT_DEFAULT (PMD_TYPE_SECT | PMD_SECT_AF) +#define PROT_SECT_DEVICE_nGnRE (PROT_SECT_DEFAULT | PTE_PXN | PTE_UXN | PMD_ATTRINDX(MT_DEVICE_nGnRE)) + #define ARCH_HAS_IOREMAP_WC #include diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h index 1cac16a001cb..381f556b664e 100644 --- a/arch/arm64/include/asm/memory.h +++ b/arch/arm64/include/asm/memory.h @@ -43,6 +43,7 @@ #define PAGE_OFFSET UL(0xffffffc000000000) #define MODULES_END (PAGE_OFFSET) #define MODULES_VADDR (MODULES_END - SZ_64M) +#define EARLYCON_IOBASE (MODULES_VADDR - SZ_4M) #define VA_BITS (39) #define TASK_SIZE_64 (UL(1) << VA_BITS) diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h index d4f7fd5b9e33..2494fc01896a 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -26,5 +26,6 @@ typedef struct { extern void paging_init(void); extern void setup_mm_for_reboot(void); +extern void __iomem *early_io_map(phys_addr_t phys, unsigned long virt); #endif diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 74239c31e25a..a1cace45e970 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -17,6 +17,7 @@ arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o arm64-obj-$(CONFIG_SMP) += smp.o arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o +arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-y += $(arm64-obj-y) vdso/ obj-m += $(arm64-obj-m) diff --git a/arch/arm64/kernel/early_printk.c b/arch/arm64/kernel/early_printk.c new file mode 100644 index 000000000000..7e320a2edb9b --- /dev/null +++ b/arch/arm64/kernel/early_printk.c @@ -0,0 +1,118 @@ +/* + * Earlyprintk support. + * + * Copyright (C) 2012 ARM Ltd. + * Author: Catalin Marinas + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include +#include + +#include + +static void __iomem *early_base; +static void (*printch)(char ch); + +/* + * PL011 single character TX. + */ +static void pl011_printch(char ch) +{ + while (readl_relaxed(early_base + UART01x_FR) & UART01x_FR_TXFF) + ; + writeb_relaxed(ch, early_base + UART01x_DR); + while (readl_relaxed(early_base + UART01x_FR) & UART01x_FR_BUSY) + ; +} + +struct earlycon_match { + const char *name; + void (*printch)(char ch); +}; + +static const struct earlycon_match earlycon_match[] __initconst = { + { .name = "pl011", .printch = pl011_printch, }, + {} +}; + +static void early_write(struct console *con, const char *s, unsigned n) +{ + while (n-- > 0) { + if (*s == '\n') + printch('\r'); + printch(*s); + s++; + } +} + +static struct console early_console = { + .name = "earlycon", + .write = early_write, + .flags = CON_PRINTBUFFER | CON_BOOT, + .index = -1, +}; + +/* + * Parse earlyprintk=... parameter in the format: + * + * [,][,] + * + * and register the early console. It is assumed that the UART has been + * initialised by the bootloader already. + */ +static int __init setup_early_printk(char *buf) +{ + const struct earlycon_match *match = earlycon_match; + phys_addr_t paddr = 0; + + if (!buf) { + pr_warning("No earlyprintk arguments passed.\n"); + return 0; + } + + while (match->name) { + size_t len = strlen(match->name); + if (!strncmp(buf, match->name, len)) { + buf += len; + break; + } + match++; + } + if (!match->name) { + pr_warning("Unknown earlyprintk arguments: %s\n", buf); + return 0; + } + + /* I/O address */ + if (!strncmp(buf, ",0x", 3)) { + char *e; + paddr = simple_strtoul(buf + 1, &e, 16); + buf = e; + } + /* no options parsing yet */ + + if (paddr) + early_base = early_io_map(paddr, EARLYCON_IOBASE); + + printch = match->printch; + register_console(&early_console); + + return 0; +} + +early_param("earlyprintk", setup_early_printk); diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S index 368ad1f7c36c..0a0a49756826 100644 --- a/arch/arm64/kernel/head.S +++ b/arch/arm64/kernel/head.S @@ -82,10 +82,8 @@ #ifdef CONFIG_ARM64_64K_PAGES #define MM_MMUFLAGS PTE_ATTRINDX(MT_NORMAL) | PTE_FLAGS -#define IO_MMUFLAGS PTE_ATTRINDX(MT_DEVICE_nGnRE) | PTE_XN | PTE_FLAGS #else #define MM_MMUFLAGS PMD_ATTRINDX(MT_NORMAL) | PMD_FLAGS -#define IO_MMUFLAGS PMD_ATTRINDX(MT_DEVICE_nGnRE) | PMD_SECT_XN | PMD_FLAGS #endif /* @@ -368,6 +366,7 @@ ENDPROC(__calc_phys_offset) * - identity mapping to enable the MMU (low address, TTBR0) * - first few MB of the kernel linear mapping to jump to once the MMU has * been enabled, including the FDT blob (TTBR1) + * - UART mapping if CONFIG_EARLY_PRINTK is enabled (TTBR1) */ __create_page_tables: pgtbl x25, x26, x24 // idmap_pg_dir and swapper_pg_dir addresses @@ -420,6 +419,15 @@ __create_page_tables: sub x6, x6, #1 // inclusive range create_block_map x0, x7, x3, x5, x6 1: +#ifdef CONFIG_EARLY_PRINTK + /* + * Create the pgd entry for the UART mapping. The full mapping is done + * later based earlyprintk kernel parameter. + */ + ldr x5, =EARLYCON_IOBASE // UART virtual address + add x0, x26, #2 * PAGE_SIZE // section table address + create_pgd_entry x26, x0, x5, x6, x7 +#endif ret ENDPROC(__create_page_tables) .ltorg diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index a6885d896ab6..f4dd585898c5 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -251,6 +252,47 @@ static void __init create_mapping(phys_addr_t phys, unsigned long virt, } while (pgd++, addr = next, addr != end); } +#ifdef CONFIG_EARLY_PRINTK +/* + * Create an early I/O mapping using the pgd/pmd entries already populated + * in head.S as this function is called too early to allocated any memory. The + * mapping size is 2MB with 4KB pages or 64KB or 64KB pages. + */ +void __iomem * __init early_io_map(phys_addr_t phys, unsigned long virt) +{ + unsigned long size, mask; + bool page64k = IS_ENABLED(ARM64_64K_PAGES); + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + + /* + * No early pte entries with !ARM64_64K_PAGES configuration, so using + * sections (pmd). + */ + size = page64k ? PAGE_SIZE : SECTION_SIZE; + mask = ~(size - 1); + + pgd = pgd_offset_k(virt); + pud = pud_offset(pgd, virt); + if (pud_none(*pud)) + return NULL; + pmd = pmd_offset(pud, virt); + + if (page64k) { + if (pmd_none(*pmd)) + return NULL; + pte = pte_offset_kernel(pmd, virt); + set_pte(pte, __pte((phys & mask) | PROT_DEVICE_nGnRE)); + } else { + set_pmd(pmd, __pmd((phys & mask) | PROT_SECT_DEVICE_nGnRE)); + } + + return (void __iomem *)((virt & mask) + (phys & ~mask)); +} +#endif + static void __init map_mem(void) { struct memblock_region *reg; -- cgit v1.2.3-59-g8ed1b From 2724680bceee94eac391552863771af105a7355c Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki / 吉藤英明 Date: Tue, 22 Jan 2013 05:20:05 +0000 Subject: neigh: Keep neighbour cache entries if number of them is small enough. Since we have removed NCE (Neighbour Cache Entry) reference from routing entries, the only refcnt holders of an NCE are its timer (if running) and its owner table, in usual cases. As a result, neigh_periodic_work() purges NCEs over and over again even for gateways. It does not make sense to purge entries, if number of them is very small, so keep them. The minimum number of entries to keep is specified by gc_thresh1. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 5 +++++ net/core/neighbour.c | 4 ++++ 2 files changed, 9 insertions(+) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 497656473b1c..19ac1802bfd4 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -26,6 +26,11 @@ route/max_size - INTEGER Maximum number of routes allowed in the kernel. Increase this when using large numbers of interfaces and/or routes. +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 + neigh/default/gc_thresh3 - INTEGER Maximum number of neighbor entries allowed. Increase this when using large numbers of interfaces and when communicating diff --git a/net/core/neighbour.c b/net/core/neighbour.c index c815f285e5ab..7bd0eedb357f 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -778,6 +778,9 @@ static void neigh_periodic_work(struct work_struct *work) nht = rcu_dereference_protected(tbl->nht, lockdep_is_held(&tbl->lock)); + if (atomic_read(&tbl->entries) < tbl->gc_thresh1) + goto out; + /* * periodically recompute ReachableTime from random function */ @@ -832,6 +835,7 @@ next_elt: nht = rcu_dereference_protected(tbl->nht, lockdep_is_held(&tbl->lock)); } +out: /* Cycle through all hash buckets every base_reachable_time/2 ticks. * ARP entry timeouts range from 1/2 base_reachable_time to 3/2 * base_reachable_time. -- cgit v1.2.3-59-g8ed1b From ab78029ecc347debbd737f06688d788bd9d60c1d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 22 Jan 2013 10:56:14 -0700 Subject: drivers/pinctrl: grab default handles from device core This makes the device core auto-grab the pinctrl handle and set the "default" (PINCTRL_STATE_DEFAULT) state for every device that is present in the device model right before probe. This will account for the lion's share of embedded silicon devcies. A modification of the semantics for pinctrl_get() is also done: previously if the pinctrl handle for a certain device was already taken, the pinctrl core would return an error. Now, since the core may have already default-grabbed the handle and set its state to "default", if the handle was already taken, this will be disregarded and the located, previously instanitated handle will be returned to the caller. This way all code in drivers explicitly requesting their pinctrl handlers will still be functional, and drivers that want to explicitly retrieve and switch their handles can still do that. But if the desired functionality is just boilerplate of this type in the probe() function: struct pinctrl *p; p = devm_pinctrl_get_select_default(&dev); if (IS_ERR(p)) { if (PTR_ERR(p) == -EPROBE_DEFER) return -EPROBE_DEFER; dev_warn(&dev, "no pinctrl handle\n"); } The discussion began with the addition of such boilerplate to the omap4 keypad driver: http://marc.info/?l=linux-input&m=135091157719300&w=2 A previous approach using notifiers was discussed: http://marc.info/?l=linux-kernel&m=135263661110528&w=2 This failed because it could not handle deferred probes. This patch alone does not solve the entire dilemma faced: whether code should be distributed into the drivers or if it should be centralized to e.g. a PM domain. But it solves the immediate issue of the addition of boilerplate to a lot of drivers that just want to grab the default state. As mentioned, they can later explicitly retrieve the handle and set different states, and this could as well be done by e.g. PM domains as it is only related to a certain struct device * pointer. ChangeLog v4->v5 (Stephen): - Simplified the devicecore grab code. - Deleted a piece of documentation recommending that pins be mapped to a device rather than hogged. ChangeLog v3->v4 (Linus): - Drop overzealous NULL checks. - Move kref initialization to pinctrl_create(). - Seeking Tested-by from Stephen Warren so we do not disturb the Tegra platform. - Seeking ACK on this from Greg (and others who like it) so I can merge it through the pinctrl subsystem. ChangeLog v2->v3 (Linus): - Abstain from using IS_ERR_OR_NULL() in the driver core, Russell recently sent a patch to remove it. Handle the NULL case explicitly even though it's a bogus case. - Make sure we handle probe deferral correctly in the device core file. devm_kfree() the container on error so we don't waste memory for devices without pinctrl handles. - Introduce reference counting into the pinctrl core using so that we don't release pinctrl handles that have been obtained for two or more places. ChangeLog v1->v2 (Linus): - Only store a pointer in the device struct, and only allocate this if it's really used by the device. Cc: Felipe Balbi Cc: Benoit Cousson Cc: Dmitry Torokhov Cc: Thomas Petazzoni Cc: Mitch Bradley Cc: Ulf Hansson Cc: Rafael J. Wysocki Cc: Jean-Christophe PLAGNIOL-VILLARD Cc: Rickard Andersson Cc: Russell King Reviewed-by: Mark Brown Acked-by: Greg Kroah-Hartman Signed-off-by: Linus Walleij [swarren: fixed and simplified error-handling in pinctrl_bind_pins(), to correctly handle deferred probe. Removed admonition from docs not to use pinctrl hogs for devices] Signed-off-by: Stephen Warren Signed-off-by: Linus Walleij --- Documentation/pinctrl.txt | 18 +++++++++-- drivers/base/Makefile | 1 + drivers/base/dd.c | 7 +++++ drivers/base/pinctrl.c | 69 +++++++++++++++++++++++++++++++++++++++++ drivers/pinctrl/core.c | 30 +++++++++++++++--- drivers/pinctrl/core.h | 3 ++ include/linux/device.h | 7 +++++ include/linux/pinctrl/devinfo.h | 45 +++++++++++++++++++++++++++ 8 files changed, 173 insertions(+), 7 deletions(-) create mode 100644 drivers/base/pinctrl.c create mode 100644 include/linux/pinctrl/devinfo.h (limited to 'Documentation') diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index da40efbef6ec..a2b57e0a1db0 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -972,6 +972,18 @@ pinmux core. Pin control requests from drivers ================================= +When a device driver is about to probe the device core will automatically +attempt to issue pinctrl_get_select_default() on these devices. +This way driver writers do not need to add any of the boilerplate code +of the type found below. However when doing fine-grained state selection +and not using the "default" state, you may have to do some device driver +handling of the pinctrl handles and states. + +So if you just want to put the pins for a certain device into the default +state and be done with it, there is nothing you need to do besides +providing the proper mapping table. The device core will take care of +the rest. + Generally it is discouraged to let individual drivers get and enable pin control. So if possible, handle the pin control in platform code or some other place where you have access to all the affected struct device * pointers. In @@ -1097,9 +1109,9 @@ situations that can be electrically unpleasant, you will certainly want to mux in and bias pins in a certain way before the GPIO subsystems starts to deal with them. -The above can be hidden: using pinctrl hogs, the pin control driver may be -setting up the config and muxing for the pins when it is probing, -nevertheless orthogonal to the GPIO subsystem. +The above can be hidden: using the device core, the pinctrl core may be +setting up the config and muxing for the pins right before the device is +probing, nevertheless orthogonal to the GPIO subsystem. But there are also situations where it makes sense for the GPIO subsystem to communicate directly with with the pinctrl subsystem, using the latter diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 5aa2d703d19f..4e22ce3ed73d 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -21,6 +21,7 @@ endif obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o obj-$(CONFIG_REGMAP) += regmap/ obj-$(CONFIG_SOC_BUS) += soc.o +obj-$(CONFIG_PINCTRL) += pinctrl.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG diff --git a/drivers/base/dd.c b/drivers/base/dd.c index e3bbed8a617c..656310156dde 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "base.h" #include "power/power.h" @@ -269,6 +270,12 @@ static int really_probe(struct device *dev, struct device_driver *drv) WARN_ON(!list_empty(&dev->devres_head)); dev->driver = drv; + + /* If using pinctrl, bind pins now before probing */ + ret = pinctrl_bind_pins(dev); + if (ret) + goto probe_failed; + if (driver_sysfs_add(dev)) { printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n", __func__, dev_name(dev)); diff --git a/drivers/base/pinctrl.c b/drivers/base/pinctrl.c new file mode 100644 index 000000000000..67a274e86727 --- /dev/null +++ b/drivers/base/pinctrl.c @@ -0,0 +1,69 @@ +/* + * Driver core interface to the pinctrl subsystem. + * + * Copyright (C) 2012 ST-Ericsson SA + * Written on behalf of Linaro for ST-Ericsson + * Based on bits of regulator core, gpio core and clk core + * + * Author: Linus Walleij + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include + +/** + * pinctrl_bind_pins() - called by the device core before probe + * @dev: the device that is just about to probe + */ +int pinctrl_bind_pins(struct device *dev) +{ + int ret; + + dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL); + if (!dev->pins) + return -ENOMEM; + + dev->pins->p = devm_pinctrl_get(dev); + if (IS_ERR(dev->pins->p)) { + dev_dbg(dev, "no pinctrl handle\n"); + ret = PTR_ERR(dev->pins->p); + goto cleanup_alloc; + } + + dev->pins->default_state = pinctrl_lookup_state(dev->pins->p, + PINCTRL_STATE_DEFAULT); + if (IS_ERR(dev->pins->default_state)) { + dev_dbg(dev, "no default pinctrl state\n"); + ret = 0; + goto cleanup_get; + } + + ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state); + if (ret) { + dev_dbg(dev, "failed to activate default pinctrl state\n"); + goto cleanup_get; + } + + return 0; + + /* + * If no pinctrl handle or default state was found for this device, + * let's explicitly free the pin container in the device, there is + * no point in keeping it around. + */ +cleanup_get: + devm_pinctrl_put(dev->pins->p); +cleanup_alloc: + devm_kfree(dev, dev->pins); + dev->pins = NULL; + + /* Only return deferrals */ + if (ret != -EPROBE_DEFER) + ret = 0; + + return ret; +} diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index e6373f30aeeb..5a2fe9aae20f 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -14,6 +14,7 @@ #define pr_fmt(fmt) "pinctrl core: " fmt #include +#include #include #include #include @@ -727,6 +728,8 @@ static struct pinctrl *create_pinctrl(struct device *dev) return ERR_PTR(ret); } + kref_init(&p->users); + /* Add the pinctrl handle to the global list */ list_add_tail(&p->node, &pinctrl_list); @@ -740,9 +743,17 @@ static struct pinctrl *pinctrl_get_locked(struct device *dev) if (WARN_ON(!dev)) return ERR_PTR(-EINVAL); + /* + * See if somebody else (such as the device core) has already + * obtained a handle to the pinctrl for this device. In that case, + * return another pointer to it. + */ p = find_pinctrl(dev); - if (p != NULL) - return ERR_PTR(-EBUSY); + if (p != NULL) { + dev_dbg(dev, "obtain a copy of previously claimed pinctrl\n"); + kref_get(&p->users); + return p; + } return create_pinctrl(dev); } @@ -798,13 +809,24 @@ static void pinctrl_put_locked(struct pinctrl *p, bool inlist) } /** - * pinctrl_put() - release a previously claimed pinctrl handle + * pinctrl_release() - release the pinctrl handle + * @kref: the kref in the pinctrl being released + */ +void pinctrl_release(struct kref *kref) +{ + struct pinctrl *p = container_of(kref, struct pinctrl, users); + + pinctrl_put_locked(p, true); +} + +/** + * pinctrl_put() - decrease use count on a previously claimed pinctrl handle * @p: the pinctrl handle to release */ void pinctrl_put(struct pinctrl *p) { mutex_lock(&pinctrl_mutex); - pinctrl_put_locked(p, true); + kref_put(&p->users, pinctrl_release); mutex_unlock(&pinctrl_mutex); } EXPORT_SYMBOL_GPL(pinctrl_put); diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h index 232a9f6db4aa..fdd350d639f6 100644 --- a/drivers/pinctrl/core.h +++ b/drivers/pinctrl/core.h @@ -9,6 +9,7 @@ * License terms: GNU General Public License (GPL) version 2 */ +#include #include #include #include @@ -58,6 +59,7 @@ struct pinctrl_dev { * @state: the current state * @dt_maps: the mapping table chunks dynamically parsed from device tree for * this device, if any + * @users: reference count */ struct pinctrl { struct list_head node; @@ -65,6 +67,7 @@ struct pinctrl { struct list_head states; struct pinctrl_state *state; struct list_head dt_maps; + struct kref users; }; /** diff --git a/include/linux/device.h b/include/linux/device.h index 43dcda937ddf..001f6637aa47 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -620,6 +621,8 @@ struct acpi_dev_node { * @pm_domain: Provide callbacks that are executed during system suspend, * hibernation, system resume and during runtime PM transitions * along with subsystem-level and driver-level callbacks. + * @pins: For device pin management. + * See Documentation/pinctrl.txt for details. * @numa_node: NUMA node this device is close to. * @dma_mask: Dma mask (if dma'ble device). * @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all @@ -672,6 +675,10 @@ struct device { struct dev_pm_info power; struct dev_pm_domain *pm_domain; +#ifdef CONFIG_PINCTRL + struct dev_pin_info *pins; +#endif + #ifdef CONFIG_NUMA int numa_node; /* NUMA node this device is close to */ #endif diff --git a/include/linux/pinctrl/devinfo.h b/include/linux/pinctrl/devinfo.h new file mode 100644 index 000000000000..6e5f8a985ea7 --- /dev/null +++ b/include/linux/pinctrl/devinfo.h @@ -0,0 +1,45 @@ +/* + * Per-device information from the pin control system. + * This is the stuff that get included into the device + * core. + * + * Copyright (C) 2012 ST-Ericsson SA + * Written on behalf of Linaro for ST-Ericsson + * This interface is used in the core to keep track of pins. + * + * Author: Linus Walleij + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef PINCTRL_DEVINFO_H +#define PINCTRL_DEVINFO_H + +#ifdef CONFIG_PINCTRL + +/* The device core acts as a consumer toward pinctrl */ +#include + +/** + * struct dev_pin_info - pin state container for devices + * @p: pinctrl handle for the containing device + * @default_state: the default state for the handle, if found + */ +struct dev_pin_info { + struct pinctrl *p; + struct pinctrl_state *default_state; +}; + +extern int pinctrl_bind_pins(struct device *dev); + +#else + +/* Stubs if we're not using pinctrl */ + +static inline int pinctrl_bind_pins(struct device *dev) +{ + return 0; +} + +#endif /* CONFIG_PINCTRL */ +#endif /* PINCTRL_DEVINFO_H */ -- cgit v1.2.3-59-g8ed1b From 749cf76c5a363e1383108a914ea09530bfa0bd43 Mon Sep 17 00:00:00 2001 From: Christoffer Dall Date: Sun, 20 Jan 2013 18:28:06 -0500 Subject: KVM: ARM: Initial skeleton to compile KVM support Targets KVM support for Cortex A-15 processors. Contains all the framework components, make files, header files, some tracing functionality, and basic user space API. Only supported core is Cortex-A15 for now. Most functionality is in arch/arm/kvm/* or arch/arm/include/asm/kvm_*.h. Reviewed-by: Will Deacon Reviewed-by: Marcelo Tosatti Signed-off-by: Rusty Russell Signed-off-by: Marc Zyngier Signed-off-by: Christoffer Dall --- Documentation/virtual/kvm/api.txt | 57 +++++- arch/arm/Kconfig | 2 + arch/arm/Makefile | 1 + arch/arm/include/asm/kvm_arm.h | 24 +++ arch/arm/include/asm/kvm_asm.h | 58 ++++++ arch/arm/include/asm/kvm_coproc.h | 24 +++ arch/arm/include/asm/kvm_emulate.h | 50 ++++++ arch/arm/include/asm/kvm_host.h | 114 ++++++++++++ arch/arm/include/uapi/asm/kvm.h | 106 +++++++++++ arch/arm/kvm/Kconfig | 55 ++++++ arch/arm/kvm/Makefile | 21 +++ arch/arm/kvm/arm.c | 350 +++++++++++++++++++++++++++++++++++++ arch/arm/kvm/coproc.c | 23 +++ arch/arm/kvm/emulate.c | 155 ++++++++++++++++ arch/arm/kvm/guest.c | 221 +++++++++++++++++++++++ arch/arm/kvm/init.S | 19 ++ arch/arm/kvm/interrupts.S | 19 ++ arch/arm/kvm/mmu.c | 17 ++ arch/arm/kvm/reset.c | 74 ++++++++ arch/arm/kvm/trace.h | 52 ++++++ include/uapi/linux/kvm.h | 7 + 21 files changed, 1445 insertions(+), 4 deletions(-) create mode 100644 arch/arm/include/asm/kvm_arm.h create mode 100644 arch/arm/include/asm/kvm_asm.h create mode 100644 arch/arm/include/asm/kvm_coproc.h create mode 100644 arch/arm/include/asm/kvm_emulate.h create mode 100644 arch/arm/include/asm/kvm_host.h create mode 100644 arch/arm/include/uapi/asm/kvm.h create mode 100644 arch/arm/kvm/Kconfig create mode 100644 arch/arm/kvm/Makefile create mode 100644 arch/arm/kvm/arm.c create mode 100644 arch/arm/kvm/coproc.c create mode 100644 arch/arm/kvm/emulate.c create mode 100644 arch/arm/kvm/guest.c create mode 100644 arch/arm/kvm/init.S create mode 100644 arch/arm/kvm/interrupts.S create mode 100644 arch/arm/kvm/mmu.c create mode 100644 arch/arm/kvm/reset.c create mode 100644 arch/arm/kvm/trace.h (limited to 'Documentation') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index a4df5535996b..4237c27ea612 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -293,7 +293,7 @@ kvm_run' (see below). 4.11 KVM_GET_REGS Capability: basic -Architectures: all +Architectures: all except ARM Type: vcpu ioctl Parameters: struct kvm_regs (out) Returns: 0 on success, -1 on error @@ -314,7 +314,7 @@ struct kvm_regs { 4.12 KVM_SET_REGS Capability: basic -Architectures: all +Architectures: all except ARM Type: vcpu ioctl Parameters: struct kvm_regs (in) Returns: 0 on success, -1 on error @@ -600,7 +600,7 @@ struct kvm_fpu { 4.24 KVM_CREATE_IRQCHIP Capability: KVM_CAP_IRQCHIP -Architectures: x86, ia64 +Architectures: x86, ia64, ARM Type: vm ioctl Parameters: none Returns: 0 on success, -1 on error @@ -608,7 +608,8 @@ Returns: 0 on success, -1 on error Creates an interrupt controller model in the kernel. On x86, creates a virtual ioapic, a virtual PIC (two PICs, nested), and sets up future vcpus to have a local APIC. IRQ routing for GSIs 0-15 is set to both PIC and IOAPIC; GSI 16-23 -only go to the IOAPIC. On ia64, a IOSAPIC is created. +only go to the IOAPIC. On ia64, a IOSAPIC is created. On ARM, a GIC is +created. 4.25 KVM_IRQ_LINE @@ -1775,6 +1776,14 @@ registers, find a list below: PPC | KVM_REG_PPC_VPA_DTL | 128 PPC | KVM_REG_PPC_EPCR | 32 +ARM registers are mapped using the lower 32 bits. The upper 16 of that +is the register group type, or coprocessor number: + +ARM core registers have the following id bit patterns: + 0x4002 0000 0010 + + + 4.69 KVM_GET_ONE_REG Capability: KVM_CAP_ONE_REG @@ -2127,6 +2136,46 @@ written, then `n_invalid' invalid entries, invalidating any previously valid entries found. +4.77 KVM_ARM_VCPU_INIT + +Capability: basic +Architectures: arm +Type: vcpu ioctl +Parameters: struct struct kvm_vcpu_init (in) +Returns: 0 on success; -1 on error +Errors: +  EINVAL:    the target is unknown, or the combination of features is invalid. +  ENOENT:    a features bit specified is unknown. + +This tells KVM what type of CPU to present to the guest, and what +optional features it should have.  This will cause a reset of the cpu +registers to their initial values.  If this is not called, KVM_RUN will +return ENOEXEC for that vcpu. + +Note that because some registers reflect machine topology, all vcpus +should be created before this ioctl is invoked. + + +4.78 KVM_GET_REG_LIST + +Capability: basic +Architectures: arm +Type: vcpu ioctl +Parameters: struct kvm_reg_list (in/out) +Returns: 0 on success; -1 on error +Errors: +  E2BIG:     the reg index list is too big to fit in the array specified by +             the user (the number required will be written into n). + +struct kvm_reg_list { + __u64 n; /* number of registers in reg[] */ + __u64 reg[0]; +}; + +This ioctl returns the guest registers that are supported for the +KVM_GET_ONE_REG/KVM_SET_ONE_REG calls. + + 5. The kvm_run structure ------------------------ diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 67874b82a4ed..e0627cdbcda5 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -2322,3 +2322,5 @@ source "security/Kconfig" source "crypto/Kconfig" source "lib/Kconfig" + +source "arch/arm/kvm/Kconfig" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 30c443c406f3..4bcd2d6b0535 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -252,6 +252,7 @@ core-$(CONFIG_FPE_NWFPE) += arch/arm/nwfpe/ core-$(CONFIG_FPE_FASTFPE) += $(FASTFPE_OBJ) core-$(CONFIG_VFP) += arch/arm/vfp/ core-$(CONFIG_XEN) += arch/arm/xen/ +core-$(CONFIG_KVM_ARM_HOST) += arch/arm/kvm/ # If we have a machine-specific directory, then include it in the build. core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/ diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h new file mode 100644 index 000000000000..dc678e193417 --- /dev/null +++ b/arch/arm/include/asm/kvm_arm.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARM_KVM_ARM_H__ +#define __ARM_KVM_ARM_H__ + +#include + +#endif /* __ARM_KVM_ARM_H__ */ diff --git a/arch/arm/include/asm/kvm_asm.h b/arch/arm/include/asm/kvm_asm.h new file mode 100644 index 000000000000..f9993e5fb695 --- /dev/null +++ b/arch/arm/include/asm/kvm_asm.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARM_KVM_ASM_H__ +#define __ARM_KVM_ASM_H__ + +/* 0 is reserved as an invalid value. */ +#define c0_MPIDR 1 /* MultiProcessor ID Register */ +#define c0_CSSELR 2 /* Cache Size Selection Register */ +#define c1_SCTLR 3 /* System Control Register */ +#define c1_ACTLR 4 /* Auxilliary Control Register */ +#define c1_CPACR 5 /* Coprocessor Access Control */ +#define c2_TTBR0 6 /* Translation Table Base Register 0 */ +#define c2_TTBR0_high 7 /* TTBR0 top 32 bits */ +#define c2_TTBR1 8 /* Translation Table Base Register 1 */ +#define c2_TTBR1_high 9 /* TTBR1 top 32 bits */ +#define c2_TTBCR 10 /* Translation Table Base Control R. */ +#define c3_DACR 11 /* Domain Access Control Register */ +#define c5_DFSR 12 /* Data Fault Status Register */ +#define c5_IFSR 13 /* Instruction Fault Status Register */ +#define c5_ADFSR 14 /* Auxilary Data Fault Status R */ +#define c5_AIFSR 15 /* Auxilary Instrunction Fault Status R */ +#define c6_DFAR 16 /* Data Fault Address Register */ +#define c6_IFAR 17 /* Instruction Fault Address Register */ +#define c9_L2CTLR 18 /* Cortex A15 L2 Control Register */ +#define c10_PRRR 19 /* Primary Region Remap Register */ +#define c10_NMRR 20 /* Normal Memory Remap Register */ +#define c12_VBAR 21 /* Vector Base Address Register */ +#define c13_CID 22 /* Context ID Register */ +#define c13_TID_URW 23 /* Thread ID, User R/W */ +#define c13_TID_URO 24 /* Thread ID, User R/O */ +#define c13_TID_PRIV 25 /* Thread ID, Privileged */ +#define NR_CP15_REGS 26 /* Number of regs (incl. invalid) */ + +#define ARM_EXCEPTION_RESET 0 +#define ARM_EXCEPTION_UNDEFINED 1 +#define ARM_EXCEPTION_SOFTWARE 2 +#define ARM_EXCEPTION_PREF_ABORT 3 +#define ARM_EXCEPTION_DATA_ABORT 4 +#define ARM_EXCEPTION_IRQ 5 +#define ARM_EXCEPTION_FIQ 6 + +#endif /* __ARM_KVM_ASM_H__ */ diff --git a/arch/arm/include/asm/kvm_coproc.h b/arch/arm/include/asm/kvm_coproc.h new file mode 100644 index 000000000000..b6d023deb426 --- /dev/null +++ b/arch/arm/include/asm/kvm_coproc.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2012 Rusty Russell IBM Corporation + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARM_KVM_COPROC_H__ +#define __ARM_KVM_COPROC_H__ +#include + +void kvm_reset_coprocs(struct kvm_vcpu *vcpu); + +#endif /* __ARM_KVM_COPROC_H__ */ diff --git a/arch/arm/include/asm/kvm_emulate.h b/arch/arm/include/asm/kvm_emulate.h new file mode 100644 index 000000000000..17dad674b90f --- /dev/null +++ b/arch/arm/include/asm/kvm_emulate.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARM_KVM_EMULATE_H__ +#define __ARM_KVM_EMULATE_H__ + +#include +#include + +u32 *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num); +u32 *vcpu_spsr(struct kvm_vcpu *vcpu); + +static inline u32 *vcpu_pc(struct kvm_vcpu *vcpu) +{ + return (u32 *)&vcpu->arch.regs.usr_regs.ARM_pc; +} + +static inline u32 *vcpu_cpsr(struct kvm_vcpu *vcpu) +{ + return (u32 *)&vcpu->arch.regs.usr_regs.ARM_cpsr; +} + +static inline bool mode_has_spsr(struct kvm_vcpu *vcpu) +{ + unsigned long cpsr_mode = vcpu->arch.regs.usr_regs.ARM_cpsr & MODE_MASK; + return (cpsr_mode > USR_MODE && cpsr_mode < SYSTEM_MODE); +} + +static inline bool vcpu_mode_priv(struct kvm_vcpu *vcpu) +{ + unsigned long cpsr_mode = vcpu->arch.regs.usr_regs.ARM_cpsr & MODE_MASK; + return cpsr_mode > USR_MODE;; +} + +#endif /* __ARM_KVM_EMULATE_H__ */ diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h new file mode 100644 index 000000000000..0d9938a20751 --- /dev/null +++ b/arch/arm/include/asm/kvm_host.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARM_KVM_HOST_H__ +#define __ARM_KVM_HOST_H__ + +#include +#include + +#define KVM_MAX_VCPUS CONFIG_KVM_ARM_MAX_VCPUS +#define KVM_MEMORY_SLOTS 32 +#define KVM_PRIVATE_MEM_SLOTS 4 +#define KVM_COALESCED_MMIO_PAGE_OFFSET 1 + +#define KVM_VCPU_MAX_FEATURES 0 + +/* We don't currently support large pages. */ +#define KVM_HPAGE_GFN_SHIFT(x) 0 +#define KVM_NR_PAGE_SIZES 1 +#define KVM_PAGES_PER_HPAGE(x) (1UL<<31) + +struct kvm_vcpu; +u32 *kvm_vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num, u32 mode); +int kvm_target_cpu(void); +int kvm_reset_vcpu(struct kvm_vcpu *vcpu); +void kvm_reset_coprocs(struct kvm_vcpu *vcpu); + +struct kvm_arch { + /* VTTBR value associated with below pgd and vmid */ + u64 vttbr; + + /* + * Anything that is not used directly from assembly code goes + * here. + */ + + /* The VMID generation used for the virt. memory system */ + u64 vmid_gen; + u32 vmid; + + /* Stage-2 page table */ + pgd_t *pgd; +}; + +#define KVM_NR_MEM_OBJS 40 + +/* + * We don't want allocation failures within the mmu code, so we preallocate + * enough memory for a single page fault in a cache. + */ +struct kvm_mmu_memory_cache { + int nobjs; + void *objects[KVM_NR_MEM_OBJS]; +}; + +struct kvm_vcpu_arch { + struct kvm_regs regs; + + int target; /* Processor target */ + DECLARE_BITMAP(features, KVM_VCPU_MAX_FEATURES); + + /* System control coprocessor (cp15) */ + u32 cp15[NR_CP15_REGS]; + + /* The CPU type we expose to the VM */ + u32 midr; + + /* Exception Information */ + u32 hsr; /* Hyp Syndrome Register */ + u32 hxfar; /* Hyp Data/Inst Fault Address Register */ + u32 hpfar; /* Hyp IPA Fault Address Register */ + + /* Interrupt related fields */ + u32 irq_lines; /* IRQ and FIQ levels */ + + /* Hyp exception information */ + u32 hyp_pc; /* PC when exception was taken from Hyp mode */ + + /* Cache some mmu pages needed inside spinlock regions */ + struct kvm_mmu_memory_cache mmu_page_cache; +}; + +struct kvm_vm_stat { + u32 remote_tlb_flush; +}; + +struct kvm_vcpu_stat { + u32 halt_wakeup; +}; + +struct kvm_vcpu_init; +int kvm_vcpu_set_target(struct kvm_vcpu *vcpu, + const struct kvm_vcpu_init *init); +unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu); +int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices); +struct kvm_one_reg; +int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg); +int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg); +#endif /* __ARM_KVM_HOST_H__ */ diff --git a/arch/arm/include/uapi/asm/kvm.h b/arch/arm/include/uapi/asm/kvm.h new file mode 100644 index 000000000000..1083327b5fcd --- /dev/null +++ b/arch/arm/include/uapi/asm/kvm.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARM_KVM_H__ +#define __ARM_KVM_H__ + +#include +#include + +#define __KVM_HAVE_GUEST_DEBUG + +#define KVM_REG_SIZE(id) \ + (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT)) + +/* Valid for svc_regs, abt_regs, und_regs, irq_regs in struct kvm_regs */ +#define KVM_ARM_SVC_sp svc_regs[0] +#define KVM_ARM_SVC_lr svc_regs[1] +#define KVM_ARM_SVC_spsr svc_regs[2] +#define KVM_ARM_ABT_sp abt_regs[0] +#define KVM_ARM_ABT_lr abt_regs[1] +#define KVM_ARM_ABT_spsr abt_regs[2] +#define KVM_ARM_UND_sp und_regs[0] +#define KVM_ARM_UND_lr und_regs[1] +#define KVM_ARM_UND_spsr und_regs[2] +#define KVM_ARM_IRQ_sp irq_regs[0] +#define KVM_ARM_IRQ_lr irq_regs[1] +#define KVM_ARM_IRQ_spsr irq_regs[2] + +/* Valid only for fiq_regs in struct kvm_regs */ +#define KVM_ARM_FIQ_r8 fiq_regs[0] +#define KVM_ARM_FIQ_r9 fiq_regs[1] +#define KVM_ARM_FIQ_r10 fiq_regs[2] +#define KVM_ARM_FIQ_fp fiq_regs[3] +#define KVM_ARM_FIQ_ip fiq_regs[4] +#define KVM_ARM_FIQ_sp fiq_regs[5] +#define KVM_ARM_FIQ_lr fiq_regs[6] +#define KVM_ARM_FIQ_spsr fiq_regs[7] + +struct kvm_regs { + struct pt_regs usr_regs;/* R0_usr - R14_usr, PC, CPSR */ + __u32 svc_regs[3]; /* SP_svc, LR_svc, SPSR_svc */ + __u32 abt_regs[3]; /* SP_abt, LR_abt, SPSR_abt */ + __u32 und_regs[3]; /* SP_und, LR_und, SPSR_und */ + __u32 irq_regs[3]; /* SP_irq, LR_irq, SPSR_irq */ + __u32 fiq_regs[8]; /* R8_fiq - R14_fiq, SPSR_fiq */ +}; + +/* Supported Processor Types */ +#define KVM_ARM_TARGET_CORTEX_A15 0 +#define KVM_ARM_NUM_TARGETS 1 + +struct kvm_vcpu_init { + __u32 target; + __u32 features[7]; +}; + +struct kvm_sregs { +}; + +struct kvm_fpu { +}; + +struct kvm_guest_debug_arch { +}; + +struct kvm_debug_exit_arch { +}; + +struct kvm_sync_regs { +}; + +struct kvm_arch_memory_slot { +}; + +/* If you need to interpret the index values, here is the key: */ +#define KVM_REG_ARM_COPROC_MASK 0x000000000FFF0000 +#define KVM_REG_ARM_COPROC_SHIFT 16 +#define KVM_REG_ARM_32_OPC2_MASK 0x0000000000000007 +#define KVM_REG_ARM_32_OPC2_SHIFT 0 +#define KVM_REG_ARM_OPC1_MASK 0x0000000000000078 +#define KVM_REG_ARM_OPC1_SHIFT 3 +#define KVM_REG_ARM_CRM_MASK 0x0000000000000780 +#define KVM_REG_ARM_CRM_SHIFT 7 +#define KVM_REG_ARM_32_CRN_MASK 0x0000000000007800 +#define KVM_REG_ARM_32_CRN_SHIFT 11 + +/* Normal registers are mapped as coprocessor 16. */ +#define KVM_REG_ARM_CORE (0x0010 << KVM_REG_ARM_COPROC_SHIFT) +#define KVM_REG_ARM_CORE_REG(name) (offsetof(struct kvm_regs, name) / 4) + +#endif /* __ARM_KVM_H__ */ diff --git a/arch/arm/kvm/Kconfig b/arch/arm/kvm/Kconfig new file mode 100644 index 000000000000..4a01b6fbf380 --- /dev/null +++ b/arch/arm/kvm/Kconfig @@ -0,0 +1,55 @@ +# +# KVM configuration +# + +source "virt/kvm/Kconfig" + +menuconfig VIRTUALIZATION + bool "Virtualization" + ---help--- + Say Y here to get to see options for using your Linux host to run + other operating systems inside virtual machines (guests). + This option alone does not add any kernel code. + + If you say N, all options in this submenu will be skipped and + disabled. + +if VIRTUALIZATION + +config KVM + bool "Kernel-based Virtual Machine (KVM) support" + select PREEMPT_NOTIFIERS + select ANON_INODES + select KVM_MMIO + select KVM_ARM_HOST + depends on ARM_VIRT_EXT && ARM_LPAE + ---help--- + Support hosting virtualized guest machines. You will also + need to select one or more of the processor modules below. + + This module provides access to the hardware capabilities through + a character device node named /dev/kvm. + + If unsure, say N. + +config KVM_ARM_HOST + bool "KVM host support for ARM cpus." + depends on KVM + depends on MMU + ---help--- + Provides host support for ARM processors. + +config KVM_ARM_MAX_VCPUS + int "Number maximum supported virtual CPUs per VM" + depends on KVM_ARM_HOST + default 4 + help + Static number of max supported virtual CPUs per VM. + + If you choose a high number, the vcpu structures will be quite + large, so only choose a reasonable number that you expect to + actually use. + +source drivers/virtio/Kconfig + +endif # VIRTUALIZATION diff --git a/arch/arm/kvm/Makefile b/arch/arm/kvm/Makefile new file mode 100644 index 000000000000..dfc293f277b3 --- /dev/null +++ b/arch/arm/kvm/Makefile @@ -0,0 +1,21 @@ +# +# Makefile for Kernel-based Virtual Machine module +# + +plus_virt := $(call as-instr,.arch_extension virt,+virt) +ifeq ($(plus_virt),+virt) + plus_virt_def := -DREQUIRES_VIRT=1 +endif + +ccflags-y += -Ivirt/kvm -Iarch/arm/kvm +CFLAGS_arm.o := -I. $(plus_virt_def) +CFLAGS_mmu.o := -I. + +AFLAGS_init.o := -Wa,-march=armv7-a$(plus_virt) +AFLAGS_interrupts.o := -Wa,-march=armv7-a$(plus_virt) + +kvm-arm-y = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o) + +obj-y += kvm-arm.o init.o interrupts.o +obj-y += arm.o guest.o mmu.o emulate.o reset.o +obj-y += coproc.o diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c new file mode 100644 index 000000000000..d3506b4001aa --- /dev/null +++ b/arch/arm/kvm/arm.c @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CREATE_TRACE_POINTS +#include "trace.h" + +#include +#include +#include +#include +#include + +#ifdef REQUIRES_VIRT +__asm__(".arch_extension virt"); +#endif + +int kvm_arch_hardware_enable(void *garbage) +{ + return 0; +} + +int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) +{ + return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE; +} + +void kvm_arch_hardware_disable(void *garbage) +{ +} + +int kvm_arch_hardware_setup(void) +{ + return 0; +} + +void kvm_arch_hardware_unsetup(void) +{ +} + +void kvm_arch_check_processor_compat(void *rtn) +{ + *(int *)rtn = 0; +} + +void kvm_arch_sync_events(struct kvm *kvm) +{ +} + +int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) +{ + if (type) + return -EINVAL; + + return 0; +} + +int kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf) +{ + return VM_FAULT_SIGBUS; +} + +void kvm_arch_free_memslot(struct kvm_memory_slot *free, + struct kvm_memory_slot *dont) +{ +} + +int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages) +{ + return 0; +} + +void kvm_arch_destroy_vm(struct kvm *kvm) +{ + int i; + + for (i = 0; i < KVM_MAX_VCPUS; ++i) { + if (kvm->vcpus[i]) { + kvm_arch_vcpu_free(kvm->vcpus[i]); + kvm->vcpus[i] = NULL; + } + } +} + +int kvm_dev_ioctl_check_extension(long ext) +{ + int r; + switch (ext) { + case KVM_CAP_USER_MEMORY: + case KVM_CAP_SYNC_MMU: + case KVM_CAP_DESTROY_MEMORY_REGION_WORKS: + case KVM_CAP_ONE_REG: + r = 1; + break; + case KVM_CAP_COALESCED_MMIO: + r = KVM_COALESCED_MMIO_PAGE_OFFSET; + break; + case KVM_CAP_NR_VCPUS: + r = num_online_cpus(); + break; + case KVM_CAP_MAX_VCPUS: + r = KVM_MAX_VCPUS; + break; + default: + r = 0; + break; + } + return r; +} + +long kvm_arch_dev_ioctl(struct file *filp, + unsigned int ioctl, unsigned long arg) +{ + return -EINVAL; +} + +int kvm_arch_set_memory_region(struct kvm *kvm, + struct kvm_userspace_memory_region *mem, + struct kvm_memory_slot old, + int user_alloc) +{ + return 0; +} + +int kvm_arch_prepare_memory_region(struct kvm *kvm, + struct kvm_memory_slot *memslot, + struct kvm_memory_slot old, + struct kvm_userspace_memory_region *mem, + int user_alloc) +{ + return 0; +} + +void kvm_arch_commit_memory_region(struct kvm *kvm, + struct kvm_userspace_memory_region *mem, + struct kvm_memory_slot old, + int user_alloc) +{ +} + +void kvm_arch_flush_shadow_all(struct kvm *kvm) +{ +} + +void kvm_arch_flush_shadow_memslot(struct kvm *kvm, + struct kvm_memory_slot *slot) +{ +} + +struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id) +{ + int err; + struct kvm_vcpu *vcpu; + + vcpu = kmem_cache_zalloc(kvm_vcpu_cache, GFP_KERNEL); + if (!vcpu) { + err = -ENOMEM; + goto out; + } + + err = kvm_vcpu_init(vcpu, kvm, id); + if (err) + goto free_vcpu; + + return vcpu; +free_vcpu: + kmem_cache_free(kvm_vcpu_cache, vcpu); +out: + return ERR_PTR(err); +} + +int kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu) +{ + return 0; +} + +void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu) +{ +} + +void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) +{ + kvm_arch_vcpu_free(vcpu); +} + +int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu) +{ + return 0; +} + +int __attribute_const__ kvm_target_cpu(void) +{ + unsigned long implementor = read_cpuid_implementor(); + unsigned long part_number = read_cpuid_part_number(); + + if (implementor != ARM_CPU_IMP_ARM) + return -EINVAL; + + switch (part_number) { + case ARM_CPU_PART_CORTEX_A15: + return KVM_ARM_TARGET_CORTEX_A15; + default: + return -EINVAL; + } +} + +int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) +{ + return 0; +} + +void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) +{ +} + +void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) +{ +} + +void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) +{ +} + +int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, + struct kvm_guest_debug *dbg) +{ + return -EINVAL; +} + + +int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu, + struct kvm_mp_state *mp_state) +{ + return -EINVAL; +} + +int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, + struct kvm_mp_state *mp_state) +{ + return -EINVAL; +} + +int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) +{ + return 0; +} + +int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + return -EINVAL; +} + +long kvm_arch_vcpu_ioctl(struct file *filp, + unsigned int ioctl, unsigned long arg) +{ + struct kvm_vcpu *vcpu = filp->private_data; + void __user *argp = (void __user *)arg; + + switch (ioctl) { + case KVM_ARM_VCPU_INIT: { + struct kvm_vcpu_init init; + + if (copy_from_user(&init, argp, sizeof(init))) + return -EFAULT; + + return kvm_vcpu_set_target(vcpu, &init); + + } + case KVM_SET_ONE_REG: + case KVM_GET_ONE_REG: { + struct kvm_one_reg reg; + if (copy_from_user(®, argp, sizeof(reg))) + return -EFAULT; + if (ioctl == KVM_SET_ONE_REG) + return kvm_arm_set_reg(vcpu, ®); + else + return kvm_arm_get_reg(vcpu, ®); + } + case KVM_GET_REG_LIST: { + struct kvm_reg_list __user *user_list = argp; + struct kvm_reg_list reg_list; + unsigned n; + + if (copy_from_user(®_list, user_list, sizeof(reg_list))) + return -EFAULT; + n = reg_list.n; + reg_list.n = kvm_arm_num_regs(vcpu); + if (copy_to_user(user_list, ®_list, sizeof(reg_list))) + return -EFAULT; + if (n < reg_list.n) + return -E2BIG; + return kvm_arm_copy_reg_indices(vcpu, user_list->reg); + } + default: + return -EINVAL; + } +} + +int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log) +{ + return -EINVAL; +} + +long kvm_arch_vm_ioctl(struct file *filp, + unsigned int ioctl, unsigned long arg) +{ + return -EINVAL; +} + +int kvm_arch_init(void *opaque) +{ + return 0; +} + +/* NOP: Compiling as a module not supported */ +void kvm_arch_exit(void) +{ +} + +static int arm_init(void) +{ + int rc = kvm_init(NULL, sizeof(struct kvm_vcpu), 0, THIS_MODULE); + return rc; +} + +module_init(arm_init); diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c new file mode 100644 index 000000000000..0c433558591c --- /dev/null +++ b/arch/arm/kvm/coproc.c @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Authors: Rusty Russell + * Christoffer Dall + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include + +void kvm_reset_coprocs(struct kvm_vcpu *vcpu) +{ +} diff --git a/arch/arm/kvm/emulate.c b/arch/arm/kvm/emulate.c new file mode 100644 index 000000000000..3eadc25e95de --- /dev/null +++ b/arch/arm/kvm/emulate.c @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include + +#define VCPU_NR_MODES 6 +#define VCPU_REG_OFFSET_USR 0 +#define VCPU_REG_OFFSET_FIQ 1 +#define VCPU_REG_OFFSET_IRQ 2 +#define VCPU_REG_OFFSET_SVC 3 +#define VCPU_REG_OFFSET_ABT 4 +#define VCPU_REG_OFFSET_UND 5 +#define REG_OFFSET(_reg) \ + (offsetof(struct kvm_regs, _reg) / sizeof(u32)) + +#define USR_REG_OFFSET(_num) REG_OFFSET(usr_regs.uregs[_num]) + +static const unsigned long vcpu_reg_offsets[VCPU_NR_MODES][15] = { + /* USR/SYS Registers */ + [VCPU_REG_OFFSET_USR] = { + USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), + USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), + USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), + USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), + USR_REG_OFFSET(12), USR_REG_OFFSET(13), USR_REG_OFFSET(14), + }, + + /* FIQ Registers */ + [VCPU_REG_OFFSET_FIQ] = { + USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), + USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), + USR_REG_OFFSET(6), USR_REG_OFFSET(7), + REG_OFFSET(fiq_regs[0]), /* r8 */ + REG_OFFSET(fiq_regs[1]), /* r9 */ + REG_OFFSET(fiq_regs[2]), /* r10 */ + REG_OFFSET(fiq_regs[3]), /* r11 */ + REG_OFFSET(fiq_regs[4]), /* r12 */ + REG_OFFSET(fiq_regs[5]), /* r13 */ + REG_OFFSET(fiq_regs[6]), /* r14 */ + }, + + /* IRQ Registers */ + [VCPU_REG_OFFSET_IRQ] = { + USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), + USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), + USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), + USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), + USR_REG_OFFSET(12), + REG_OFFSET(irq_regs[0]), /* r13 */ + REG_OFFSET(irq_regs[1]), /* r14 */ + }, + + /* SVC Registers */ + [VCPU_REG_OFFSET_SVC] = { + USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), + USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), + USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), + USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), + USR_REG_OFFSET(12), + REG_OFFSET(svc_regs[0]), /* r13 */ + REG_OFFSET(svc_regs[1]), /* r14 */ + }, + + /* ABT Registers */ + [VCPU_REG_OFFSET_ABT] = { + USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), + USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), + USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), + USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), + USR_REG_OFFSET(12), + REG_OFFSET(abt_regs[0]), /* r13 */ + REG_OFFSET(abt_regs[1]), /* r14 */ + }, + + /* UND Registers */ + [VCPU_REG_OFFSET_UND] = { + USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), + USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), + USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), + USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), + USR_REG_OFFSET(12), + REG_OFFSET(und_regs[0]), /* r13 */ + REG_OFFSET(und_regs[1]), /* r14 */ + }, +}; + +/* + * Return a pointer to the register number valid in the current mode of + * the virtual CPU. + */ +u32 *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num) +{ + u32 *reg_array = (u32 *)&vcpu->arch.regs; + u32 mode = *vcpu_cpsr(vcpu) & MODE_MASK; + + switch (mode) { + case USR_MODE...SVC_MODE: + mode &= ~MODE32_BIT; /* 0 ... 3 */ + break; + + case ABT_MODE: + mode = VCPU_REG_OFFSET_ABT; + break; + + case UND_MODE: + mode = VCPU_REG_OFFSET_UND; + break; + + case SYSTEM_MODE: + mode = VCPU_REG_OFFSET_USR; + break; + + default: + BUG(); + } + + return reg_array + vcpu_reg_offsets[mode][reg_num]; +} + +/* + * Return the SPSR for the current mode of the virtual CPU. + */ +u32 *vcpu_spsr(struct kvm_vcpu *vcpu) +{ + u32 mode = *vcpu_cpsr(vcpu) & MODE_MASK; + switch (mode) { + case SVC_MODE: + return &vcpu->arch.regs.KVM_ARM_SVC_spsr; + case ABT_MODE: + return &vcpu->arch.regs.KVM_ARM_ABT_spsr; + case UND_MODE: + return &vcpu->arch.regs.KVM_ARM_UND_spsr; + case IRQ_MODE: + return &vcpu->arch.regs.KVM_ARM_IRQ_spsr; + case FIQ_MODE: + return &vcpu->arch.regs.KVM_ARM_FIQ_spsr; + default: + BUG(); + } +} diff --git a/arch/arm/kvm/guest.c b/arch/arm/kvm/guest.c new file mode 100644 index 000000000000..a12eb229021d --- /dev/null +++ b/arch/arm/kvm/guest.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VM_STAT(x) { #x, offsetof(struct kvm, stat.x), KVM_STAT_VM } +#define VCPU_STAT(x) { #x, offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU } + +struct kvm_stats_debugfs_item debugfs_entries[] = { + { NULL } +}; + +int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu) +{ + return 0; +} + +static u64 core_reg_offset_from_id(u64 id) +{ + return id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_ARM_CORE); +} + +static int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) +{ + u32 __user *uaddr = (u32 __user *)(long)reg->addr; + struct kvm_regs *regs = &vcpu->arch.regs; + u64 off; + + if (KVM_REG_SIZE(reg->id) != 4) + return -ENOENT; + + /* Our ID is an index into the kvm_regs struct. */ + off = core_reg_offset_from_id(reg->id); + if (off >= sizeof(*regs) / KVM_REG_SIZE(reg->id)) + return -ENOENT; + + return put_user(((u32 *)regs)[off], uaddr); +} + +static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) +{ + u32 __user *uaddr = (u32 __user *)(long)reg->addr; + struct kvm_regs *regs = &vcpu->arch.regs; + u64 off, val; + + if (KVM_REG_SIZE(reg->id) != 4) + return -ENOENT; + + /* Our ID is an index into the kvm_regs struct. */ + off = core_reg_offset_from_id(reg->id); + if (off >= sizeof(*regs) / KVM_REG_SIZE(reg->id)) + return -ENOENT; + + if (get_user(val, uaddr) != 0) + return -EFAULT; + + if (off == KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr)) { + unsigned long mode = val & MODE_MASK; + switch (mode) { + case USR_MODE: + case FIQ_MODE: + case IRQ_MODE: + case SVC_MODE: + case ABT_MODE: + case UND_MODE: + break; + default: + return -EINVAL; + } + } + + ((u32 *)regs)[off] = val; + return 0; +} + +int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) +{ + return -EINVAL; +} + +int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) +{ + return -EINVAL; +} + +static unsigned long num_core_regs(void) +{ + return sizeof(struct kvm_regs) / sizeof(u32); +} + +/** + * kvm_arm_num_regs - how many registers do we present via KVM_GET_ONE_REG + * + * This is for all registers. + */ +unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu) +{ + return num_core_regs(); +} + +/** + * kvm_arm_copy_reg_indices - get indices of all registers. + * + * We do core registers right here, then we apppend coproc regs. + */ +int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices) +{ + unsigned int i; + const u64 core_reg = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_CORE; + + for (i = 0; i < sizeof(struct kvm_regs)/sizeof(u32); i++) { + if (put_user(core_reg | i, uindices)) + return -EFAULT; + uindices++; + } + + return 0; +} + +int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) +{ + /* We currently use nothing arch-specific in upper 32 bits */ + if ((reg->id & ~KVM_REG_SIZE_MASK) >> 32 != KVM_REG_ARM >> 32) + return -EINVAL; + + /* Register group 16 means we want a core register. */ + if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE) + return get_core_reg(vcpu, reg); + + return -EINVAL; +} + +int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) +{ + /* We currently use nothing arch-specific in upper 32 bits */ + if ((reg->id & ~KVM_REG_SIZE_MASK) >> 32 != KVM_REG_ARM >> 32) + return -EINVAL; + + /* Register group 16 means we set a core register. */ + if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE) + return set_core_reg(vcpu, reg); + + return -EINVAL; +} + +int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu, + struct kvm_sregs *sregs) +{ + return -EINVAL; +} + +int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu, + struct kvm_sregs *sregs) +{ + return -EINVAL; +} + +int kvm_vcpu_set_target(struct kvm_vcpu *vcpu, + const struct kvm_vcpu_init *init) +{ + unsigned int i; + + /* We can only do a cortex A15 for now. */ + if (init->target != kvm_target_cpu()) + return -EINVAL; + + vcpu->arch.target = init->target; + bitmap_zero(vcpu->arch.features, KVM_VCPU_MAX_FEATURES); + + /* -ENOENT for unknown features, -EINVAL for invalid combinations. */ + for (i = 0; i < sizeof(init->features) * 8; i++) { + if (test_bit(i, (void *)init->features)) { + if (i >= KVM_VCPU_MAX_FEATURES) + return -ENOENT; + set_bit(i, vcpu->arch.features); + } + } + + /* Now we know what it is, we can reset it. */ + return kvm_reset_vcpu(vcpu); +} + +int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) +{ + return -EINVAL; +} + +int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) +{ + return -EINVAL; +} + +int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu, + struct kvm_translation *tr) +{ + return -EINVAL; +} diff --git a/arch/arm/kvm/init.S b/arch/arm/kvm/init.S new file mode 100644 index 000000000000..1dc8926e26d2 --- /dev/null +++ b/arch/arm/kvm/init.S @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include diff --git a/arch/arm/kvm/interrupts.S b/arch/arm/kvm/interrupts.S new file mode 100644 index 000000000000..1dc8926e26d2 --- /dev/null +++ b/arch/arm/kvm/interrupts.S @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c new file mode 100644 index 000000000000..10ed4643269f --- /dev/null +++ b/arch/arm/kvm/mmu.c @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ diff --git a/arch/arm/kvm/reset.c b/arch/arm/kvm/reset.c new file mode 100644 index 000000000000..b80256b554cd --- /dev/null +++ b/arch/arm/kvm/reset.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/****************************************************************************** + * Cortex-A15 Reset Values + */ + +static const int a15_max_cpu_idx = 3; + +static struct kvm_regs a15_regs_reset = { + .usr_regs.ARM_cpsr = SVC_MODE | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT, +}; + + +/******************************************************************************* + * Exported reset function + */ + +/** + * kvm_reset_vcpu - sets core registers and cp15 registers to reset value + * @vcpu: The VCPU pointer + * + * This function finds the right table above and sets the registers on the + * virtual CPU struct to their architectually defined reset values. + */ +int kvm_reset_vcpu(struct kvm_vcpu *vcpu) +{ + struct kvm_regs *cpu_reset; + + switch (vcpu->arch.target) { + case KVM_ARM_TARGET_CORTEX_A15: + if (vcpu->vcpu_id > a15_max_cpu_idx) + return -EINVAL; + cpu_reset = &a15_regs_reset; + vcpu->arch.midr = read_cpuid_id(); + break; + default: + return -ENODEV; + } + + /* Reset core registers */ + memcpy(&vcpu->arch.regs, cpu_reset, sizeof(vcpu->arch.regs)); + + /* Reset CP15 registers */ + kvm_reset_coprocs(vcpu); + + return 0; +} diff --git a/arch/arm/kvm/trace.h b/arch/arm/kvm/trace.h new file mode 100644 index 000000000000..f8869c19c0a3 --- /dev/null +++ b/arch/arm/kvm/trace.h @@ -0,0 +1,52 @@ +#if !defined(_TRACE_KVM_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_KVM_H + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM kvm + +/* + * Tracepoints for entry/exit to guest + */ +TRACE_EVENT(kvm_entry, + TP_PROTO(unsigned long vcpu_pc), + TP_ARGS(vcpu_pc), + + TP_STRUCT__entry( + __field( unsigned long, vcpu_pc ) + ), + + TP_fast_assign( + __entry->vcpu_pc = vcpu_pc; + ), + + TP_printk("PC: 0x%08lx", __entry->vcpu_pc) +); + +TRACE_EVENT(kvm_exit, + TP_PROTO(unsigned long vcpu_pc), + TP_ARGS(vcpu_pc), + + TP_STRUCT__entry( + __field( unsigned long, vcpu_pc ) + ), + + TP_fast_assign( + __entry->vcpu_pc = vcpu_pc; + ), + + TP_printk("PC: 0x%08lx", __entry->vcpu_pc) +); + + + +#endif /* _TRACE_KVM_H */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH arch/arm/kvm +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +/* This part must be outside protection */ +#include diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index e6e5d4b13708..24978d525c6e 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -764,6 +764,11 @@ struct kvm_dirty_tlb { #define KVM_REG_SIZE_U512 0x0060000000000000ULL #define KVM_REG_SIZE_U1024 0x0070000000000000ULL +struct kvm_reg_list { + __u64 n; /* number of regs */ + __u64 reg[0]; +}; + struct kvm_one_reg { __u64 id; __u64 addr; @@ -932,6 +937,8 @@ struct kvm_s390_ucas_mapping { #define KVM_SET_ONE_REG _IOW(KVMIO, 0xac, struct kvm_one_reg) /* VM is being stopped by host */ #define KVM_KVMCLOCK_CTRL _IO(KVMIO, 0xad) +#define KVM_ARM_VCPU_INIT _IOW(KVMIO, 0xae, struct kvm_vcpu_init) +#define KVM_GET_REG_LIST _IOWR(KVMIO, 0xb0, struct kvm_reg_list) #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) #define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1) -- cgit v1.2.3-59-g8ed1b From 86ce85352f0da7e1431ad8efcb04323819a620e7 Mon Sep 17 00:00:00 2001 From: Christoffer Dall Date: Sun, 20 Jan 2013 18:28:08 -0500 Subject: KVM: ARM: Inject IRQs and FIQs from userspace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All interrupt injection is now based on the VM ioctl KVM_IRQ_LINE. This works semantically well for the GIC as we in fact raise/lower a line on a machine component (the gic). The IOCTL uses the follwing struct. struct kvm_irq_level { union { __u32 irq; /* GSI */ __s32 status; /* not used for KVM_IRQ_LEVEL */ }; __u32 level; /* 0 or 1 */ }; ARM can signal an interrupt either at the CPU level, or at the in-kernel irqchip (GIC), and for in-kernel irqchip can tell the GIC to use PPIs designated for specific cpus. The irq field is interpreted like this:  bits: | 31 ... 24 | 23 ... 16 | 15 ... 0 | field: | irq_type | vcpu_index | irq_number | The irq_type field has the following values: - irq_type[0]: out-of-kernel GIC: irq_number 0 is IRQ, irq_number 1 is FIQ - irq_type[1]: in-kernel GIC: SPI, irq_number between 32 and 1019 (incl.) (the vcpu_index field is ignored) - irq_type[2]: in-kernel GIC: PPI, irq_number between 16 and 31 (incl.) The irq_number thus corresponds to the irq ID in as in the GICv2 specs. This is documented in Documentation/kvm/api.txt. Reviewed-by: Will Deacon Reviewed-by: Marcelo Tosatti Signed-off-by: Christoffer Dall --- Documentation/virtual/kvm/api.txt | 25 ++++++++++++--- arch/arm/include/asm/kvm_arm.h | 1 + arch/arm/include/uapi/asm/kvm.h | 21 +++++++++++++ arch/arm/kvm/arm.c | 65 +++++++++++++++++++++++++++++++++++++++ arch/arm/kvm/trace.h | 25 +++++++++++++++ include/uapi/linux/kvm.h | 1 + 6 files changed, 134 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 4237c27ea612..505049299298 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -615,15 +615,32 @@ created. 4.25 KVM_IRQ_LINE Capability: KVM_CAP_IRQCHIP -Architectures: x86, ia64 +Architectures: x86, ia64, arm Type: vm ioctl Parameters: struct kvm_irq_level Returns: 0 on success, -1 on error Sets the level of a GSI input to the interrupt controller model in the kernel. -Requires that an interrupt controller model has been previously created with -KVM_CREATE_IRQCHIP. Note that edge-triggered interrupts require the level -to be set to 1 and then back to 0. +On some architectures it is required that an interrupt controller model has +been previously created with KVM_CREATE_IRQCHIP. Note that edge-triggered +interrupts require the level to be set to 1 and then back to 0. + +ARM can signal an interrupt either at the CPU level, or at the in-kernel irqchip +(GIC), and for in-kernel irqchip can tell the GIC to use PPIs designated for +specific cpus. The irq field is interpreted like this: + +  bits: | 31 ... 24 | 23 ... 16 | 15 ... 0 | + field: | irq_type | vcpu_index | irq_id | + +The irq_type field has the following values: +- irq_type[0]: out-of-kernel GIC: irq_id 0 is IRQ, irq_id 1 is FIQ +- irq_type[1]: in-kernel GIC: SPI, irq_id between 32 and 1019 (incl.) + (the vcpu_index field is ignored) +- irq_type[2]: in-kernel GIC: PPI, irq_id between 16 and 31 (incl.) + +(The irq_id field thus corresponds nicely to the IRQ ID in the ARM GIC specs) + +In both cases, level is used to raise/lower the line. struct kvm_irq_level { union { diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h index 8875b3f605a7..d64b5250ad4e 100644 --- a/arch/arm/include/asm/kvm_arm.h +++ b/arch/arm/include/asm/kvm_arm.h @@ -68,6 +68,7 @@ #define HCR_GUEST_MASK (HCR_TSC | HCR_TSW | HCR_TWI | HCR_VM | HCR_BSU_IS | \ HCR_FB | HCR_TAC | HCR_AMO | HCR_IMO | HCR_FMO | \ HCR_SWIO | HCR_TIDCP) +#define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF) /* Hyp System Control Register (HSCTLR) bits */ #define HSCTLR_TE (1 << 30) diff --git a/arch/arm/include/uapi/asm/kvm.h b/arch/arm/include/uapi/asm/kvm.h index 1083327b5fcd..53f45f146875 100644 --- a/arch/arm/include/uapi/asm/kvm.h +++ b/arch/arm/include/uapi/asm/kvm.h @@ -23,6 +23,7 @@ #include #define __KVM_HAVE_GUEST_DEBUG +#define __KVM_HAVE_IRQ_LINE #define KVM_REG_SIZE(id) \ (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT)) @@ -103,4 +104,24 @@ struct kvm_arch_memory_slot { #define KVM_REG_ARM_CORE (0x0010 << KVM_REG_ARM_COPROC_SHIFT) #define KVM_REG_ARM_CORE_REG(name) (offsetof(struct kvm_regs, name) / 4) +/* KVM_IRQ_LINE irq field index values */ +#define KVM_ARM_IRQ_TYPE_SHIFT 24 +#define KVM_ARM_IRQ_TYPE_MASK 0xff +#define KVM_ARM_IRQ_VCPU_SHIFT 16 +#define KVM_ARM_IRQ_VCPU_MASK 0xff +#define KVM_ARM_IRQ_NUM_SHIFT 0 +#define KVM_ARM_IRQ_NUM_MASK 0xffff + +/* irq_type field */ +#define KVM_ARM_IRQ_TYPE_CPU 0 +#define KVM_ARM_IRQ_TYPE_SPI 1 +#define KVM_ARM_IRQ_TYPE_PPI 2 + +/* out-of-kernel GIC cpu interrupt injection irq_number field */ +#define KVM_ARM_IRQ_CPU_IRQ 0 +#define KVM_ARM_IRQ_CPU_FIQ 1 + +/* Highest supported SPI, from VGIC_NR_IRQS */ +#define KVM_ARM_IRQ_GIC_MAX 127 + #endif /* __ARM_KVM_H__ */ diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index d810afb6cb84..2101152c3a4b 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #define CREATE_TRACE_POINTS @@ -284,6 +285,7 @@ void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { + vcpu->cpu = cpu; } void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) @@ -319,6 +321,69 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) return -EINVAL; } +static int vcpu_interrupt_line(struct kvm_vcpu *vcpu, int number, bool level) +{ + int bit_index; + bool set; + unsigned long *ptr; + + if (number == KVM_ARM_IRQ_CPU_IRQ) + bit_index = __ffs(HCR_VI); + else /* KVM_ARM_IRQ_CPU_FIQ */ + bit_index = __ffs(HCR_VF); + + ptr = (unsigned long *)&vcpu->arch.irq_lines; + if (level) + set = test_and_set_bit(bit_index, ptr); + else + set = test_and_clear_bit(bit_index, ptr); + + /* + * If we didn't change anything, no need to wake up or kick other CPUs + */ + if (set == level) + return 0; + + /* + * The vcpu irq_lines field was updated, wake up sleeping VCPUs and + * trigger a world-switch round on the running physical CPU to set the + * virtual IRQ/FIQ fields in the HCR appropriately. + */ + kvm_vcpu_kick(vcpu); + + return 0; +} + +int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level) +{ + u32 irq = irq_level->irq; + unsigned int irq_type, vcpu_idx, irq_num; + int nrcpus = atomic_read(&kvm->online_vcpus); + struct kvm_vcpu *vcpu = NULL; + bool level = irq_level->level; + + irq_type = (irq >> KVM_ARM_IRQ_TYPE_SHIFT) & KVM_ARM_IRQ_TYPE_MASK; + vcpu_idx = (irq >> KVM_ARM_IRQ_VCPU_SHIFT) & KVM_ARM_IRQ_VCPU_MASK; + irq_num = (irq >> KVM_ARM_IRQ_NUM_SHIFT) & KVM_ARM_IRQ_NUM_MASK; + + trace_kvm_irq_line(irq_type, vcpu_idx, irq_num, irq_level->level); + + if (irq_type != KVM_ARM_IRQ_TYPE_CPU) + return -EINVAL; + + if (vcpu_idx >= nrcpus) + return -EINVAL; + + vcpu = kvm_get_vcpu(kvm, vcpu_idx); + if (!vcpu) + return -EINVAL; + + if (irq_num > KVM_ARM_IRQ_CPU_FIQ) + return -EINVAL; + + return vcpu_interrupt_line(vcpu, irq_num, level); +} + long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { diff --git a/arch/arm/kvm/trace.h b/arch/arm/kvm/trace.h index 862b2cc12fbe..105d1f79909a 100644 --- a/arch/arm/kvm/trace.h +++ b/arch/arm/kvm/trace.h @@ -39,6 +39,31 @@ TRACE_EVENT(kvm_exit, TP_printk("PC: 0x%08lx", __entry->vcpu_pc) ); +TRACE_EVENT(kvm_irq_line, + TP_PROTO(unsigned int type, int vcpu_idx, int irq_num, int level), + TP_ARGS(type, vcpu_idx, irq_num, level), + + TP_STRUCT__entry( + __field( unsigned int, type ) + __field( int, vcpu_idx ) + __field( int, irq_num ) + __field( int, level ) + ), + + TP_fast_assign( + __entry->type = type; + __entry->vcpu_idx = vcpu_idx; + __entry->irq_num = irq_num; + __entry->level = level; + ), + + TP_printk("Inject %s interrupt (%d), vcpu->idx: %d, num: %d, level: %d", + (__entry->type == KVM_ARM_IRQ_TYPE_CPU) ? "CPU" : + (__entry->type == KVM_ARM_IRQ_TYPE_PPI) ? "VGIC PPI" : + (__entry->type == KVM_ARM_IRQ_TYPE_SPI) ? "VGIC SPI" : "UNKNOWN", + __entry->type, __entry->vcpu_idx, __entry->irq_num, __entry->level) +); + TRACE_EVENT(kvm_unmap_hva, TP_PROTO(unsigned long hva), TP_ARGS(hva), diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 24978d525c6e..dc63665e73ad 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -115,6 +115,7 @@ struct kvm_irq_level { * ACPI gsi notion of irq. * For IA-64 (APIC model) IOAPIC0: irq 0-23; IOAPIC1: irq 24-47.. * For X86 (standard AT mode) PIC0/1: irq 0-15. IOAPIC0: 0-23.. + * For ARM: See Documentation/virtual/kvm/api.txt */ union { __u32 irq; -- cgit v1.2.3-59-g8ed1b From 1138245ccf9652429630c09fb068e9b12c56c3d3 Mon Sep 17 00:00:00 2001 From: Christoffer Dall Date: Sun, 20 Jan 2013 18:28:10 -0500 Subject: KVM: ARM: User space API for getting/setting co-proc registers The following three ioctls are implemented: - KVM_GET_REG_LIST - KVM_GET_ONE_REG - KVM_SET_ONE_REG Now we have a table for all the cp15 registers, we can drive a generic API. The register IDs carry the following encoding: ARM registers are mapped using the lower 32 bits. The upper 16 of that is the register group type, or coprocessor number: ARM 32-bit CP15 registers have the following id bit patterns: 0x4002 0000 000F ARM 64-bit CP15 registers have the following id bit patterns: 0x4003 0000 000F For futureproofing, we need to tell QEMU about the CP15 registers the host lets the guest access. It will need this information to restore a current guest on a future CPU or perhaps a future KVM which allow some of these to be changed. We use a separate table for these, as they're only for the userspace API. Reviewed-by: Will Deacon Reviewed-by: Marcelo Tosatti Signed-off-by: Rusty Russell Signed-off-by: Christoffer Dall --- Documentation/virtual/kvm/api.txt | 5 + arch/arm/include/asm/kvm_coproc.h | 9 ++ arch/arm/include/asm/kvm_host.h | 4 + arch/arm/kvm/coproc.c | 327 ++++++++++++++++++++++++++++++++++++++ arch/arm/kvm/guest.c | 9 +- 5 files changed, 350 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 505049299298..0e2287406464 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -1799,6 +1799,11 @@ is the register group type, or coprocessor number: ARM core registers have the following id bit patterns: 0x4002 0000 0010 +ARM 32-bit CP15 registers have the following id bit patterns: + 0x4002 0000 000F + +ARM 64-bit CP15 registers have the following id bit patterns: + 0x4003 0000 000F 4.69 KVM_GET_ONE_REG diff --git a/arch/arm/include/asm/kvm_coproc.h b/arch/arm/include/asm/kvm_coproc.h index bd1ace030495..4917c2f7e459 100644 --- a/arch/arm/include/asm/kvm_coproc.h +++ b/arch/arm/include/asm/kvm_coproc.h @@ -34,5 +34,14 @@ int kvm_handle_cp14_load_store(struct kvm_vcpu *vcpu, struct kvm_run *run); int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run); int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run); int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run); + +unsigned long kvm_arm_num_guest_msrs(struct kvm_vcpu *vcpu); +int kvm_arm_copy_msrindices(struct kvm_vcpu *vcpu, u64 __user *uindices); void kvm_coproc_table_init(void); + +struct kvm_one_reg; +int kvm_arm_copy_coproc_indices(struct kvm_vcpu *vcpu, u64 __user *uindices); +int kvm_arm_coproc_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *); +int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *); +unsigned long kvm_arm_num_coproc_regs(struct kvm_vcpu *vcpu); #endif /* __ARM_KVM_COPROC_H__ */ diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index e1d4168d4f19..ed79043d7921 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h @@ -27,6 +27,7 @@ #define KVM_MEMORY_SLOTS 32 #define KVM_PRIVATE_MEM_SLOTS 4 #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 +#define KVM_HAVE_ONE_REG #define KVM_VCPU_MAX_FEATURES 0 @@ -137,6 +138,9 @@ int kvm_unmap_hva_range(struct kvm *kvm, unsigned long start, unsigned long end); void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte); +unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu); +int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices); + /* We do not have shadow page tables, hence the empty hooks */ static inline int kvm_age_hva(struct kvm *kvm, unsigned long hva) { diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c index 722efe3b1675..95a0f5e5c1fc 100644 --- a/arch/arm/kvm/coproc.c +++ b/arch/arm/kvm/coproc.c @@ -18,6 +18,7 @@ */ #include #include +#include #include #include #include @@ -347,6 +348,328 @@ int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run) return emulate_cp15(vcpu, ¶ms); } +/****************************************************************************** + * Userspace API + *****************************************************************************/ + +static bool index_to_params(u64 id, struct coproc_params *params) +{ + switch (id & KVM_REG_SIZE_MASK) { + case KVM_REG_SIZE_U32: + /* Any unused index bits means it's not valid. */ + if (id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK + | KVM_REG_ARM_COPROC_MASK + | KVM_REG_ARM_32_CRN_MASK + | KVM_REG_ARM_CRM_MASK + | KVM_REG_ARM_OPC1_MASK + | KVM_REG_ARM_32_OPC2_MASK)) + return false; + + params->is_64bit = false; + params->CRn = ((id & KVM_REG_ARM_32_CRN_MASK) + >> KVM_REG_ARM_32_CRN_SHIFT); + params->CRm = ((id & KVM_REG_ARM_CRM_MASK) + >> KVM_REG_ARM_CRM_SHIFT); + params->Op1 = ((id & KVM_REG_ARM_OPC1_MASK) + >> KVM_REG_ARM_OPC1_SHIFT); + params->Op2 = ((id & KVM_REG_ARM_32_OPC2_MASK) + >> KVM_REG_ARM_32_OPC2_SHIFT); + return true; + case KVM_REG_SIZE_U64: + /* Any unused index bits means it's not valid. */ + if (id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK + | KVM_REG_ARM_COPROC_MASK + | KVM_REG_ARM_CRM_MASK + | KVM_REG_ARM_OPC1_MASK)) + return false; + params->is_64bit = true; + params->CRm = ((id & KVM_REG_ARM_CRM_MASK) + >> KVM_REG_ARM_CRM_SHIFT); + params->Op1 = ((id & KVM_REG_ARM_OPC1_MASK) + >> KVM_REG_ARM_OPC1_SHIFT); + params->Op2 = 0; + params->CRn = 0; + return true; + default: + return false; + } +} + +/* Decode an index value, and find the cp15 coproc_reg entry. */ +static const struct coproc_reg *index_to_coproc_reg(struct kvm_vcpu *vcpu, + u64 id) +{ + size_t num; + const struct coproc_reg *table, *r; + struct coproc_params params; + + /* We only do cp15 for now. */ + if ((id & KVM_REG_ARM_COPROC_MASK) >> KVM_REG_ARM_COPROC_SHIFT != 15) + return NULL; + + if (!index_to_params(id, ¶ms)) + return NULL; + + table = get_target_table(vcpu->arch.target, &num); + r = find_reg(¶ms, table, num); + if (!r) + r = find_reg(¶ms, cp15_regs, ARRAY_SIZE(cp15_regs)); + + /* Not saved in the cp15 array? */ + if (r && !r->reg) + r = NULL; + + return r; +} + +/* + * These are the invariant cp15 registers: we let the guest see the host + * versions of these, so they're part of the guest state. + * + * A future CPU may provide a mechanism to present different values to + * the guest, or a future kvm may trap them. + */ +/* Unfortunately, there's no register-argument for mrc, so generate. */ +#define FUNCTION_FOR32(crn, crm, op1, op2, name) \ + static void get_##name(struct kvm_vcpu *v, \ + const struct coproc_reg *r) \ + { \ + u32 val; \ + \ + asm volatile("mrc p15, " __stringify(op1) \ + ", %0, c" __stringify(crn) \ + ", c" __stringify(crm) \ + ", " __stringify(op2) "\n" : "=r" (val)); \ + ((struct coproc_reg *)r)->val = val; \ + } + +FUNCTION_FOR32(0, 0, 0, 0, MIDR) +FUNCTION_FOR32(0, 0, 0, 1, CTR) +FUNCTION_FOR32(0, 0, 0, 2, TCMTR) +FUNCTION_FOR32(0, 0, 0, 3, TLBTR) +FUNCTION_FOR32(0, 0, 0, 6, REVIDR) +FUNCTION_FOR32(0, 1, 0, 0, ID_PFR0) +FUNCTION_FOR32(0, 1, 0, 1, ID_PFR1) +FUNCTION_FOR32(0, 1, 0, 2, ID_DFR0) +FUNCTION_FOR32(0, 1, 0, 3, ID_AFR0) +FUNCTION_FOR32(0, 1, 0, 4, ID_MMFR0) +FUNCTION_FOR32(0, 1, 0, 5, ID_MMFR1) +FUNCTION_FOR32(0, 1, 0, 6, ID_MMFR2) +FUNCTION_FOR32(0, 1, 0, 7, ID_MMFR3) +FUNCTION_FOR32(0, 2, 0, 0, ID_ISAR0) +FUNCTION_FOR32(0, 2, 0, 1, ID_ISAR1) +FUNCTION_FOR32(0, 2, 0, 2, ID_ISAR2) +FUNCTION_FOR32(0, 2, 0, 3, ID_ISAR3) +FUNCTION_FOR32(0, 2, 0, 4, ID_ISAR4) +FUNCTION_FOR32(0, 2, 0, 5, ID_ISAR5) +FUNCTION_FOR32(0, 0, 1, 1, CLIDR) +FUNCTION_FOR32(0, 0, 1, 7, AIDR) + +/* ->val is filled in by kvm_invariant_coproc_table_init() */ +static struct coproc_reg invariant_cp15[] = { + { CRn( 0), CRm( 0), Op1( 0), Op2( 0), is32, NULL, get_MIDR }, + { CRn( 0), CRm( 0), Op1( 0), Op2( 1), is32, NULL, get_CTR }, + { CRn( 0), CRm( 0), Op1( 0), Op2( 2), is32, NULL, get_TCMTR }, + { CRn( 0), CRm( 0), Op1( 0), Op2( 3), is32, NULL, get_TLBTR }, + { CRn( 0), CRm( 0), Op1( 0), Op2( 6), is32, NULL, get_REVIDR }, + + { CRn( 0), CRm( 1), Op1( 0), Op2( 0), is32, NULL, get_ID_PFR0 }, + { CRn( 0), CRm( 1), Op1( 0), Op2( 1), is32, NULL, get_ID_PFR1 }, + { CRn( 0), CRm( 1), Op1( 0), Op2( 2), is32, NULL, get_ID_DFR0 }, + { CRn( 0), CRm( 1), Op1( 0), Op2( 3), is32, NULL, get_ID_AFR0 }, + { CRn( 0), CRm( 1), Op1( 0), Op2( 4), is32, NULL, get_ID_MMFR0 }, + { CRn( 0), CRm( 1), Op1( 0), Op2( 5), is32, NULL, get_ID_MMFR1 }, + { CRn( 0), CRm( 1), Op1( 0), Op2( 6), is32, NULL, get_ID_MMFR2 }, + { CRn( 0), CRm( 1), Op1( 0), Op2( 7), is32, NULL, get_ID_MMFR3 }, + + { CRn( 0), CRm( 2), Op1( 0), Op2( 0), is32, NULL, get_ID_ISAR0 }, + { CRn( 0), CRm( 2), Op1( 0), Op2( 1), is32, NULL, get_ID_ISAR1 }, + { CRn( 0), CRm( 2), Op1( 0), Op2( 2), is32, NULL, get_ID_ISAR2 }, + { CRn( 0), CRm( 2), Op1( 0), Op2( 3), is32, NULL, get_ID_ISAR3 }, + { CRn( 0), CRm( 2), Op1( 0), Op2( 4), is32, NULL, get_ID_ISAR4 }, + { CRn( 0), CRm( 2), Op1( 0), Op2( 5), is32, NULL, get_ID_ISAR5 }, + + { CRn( 0), CRm( 0), Op1( 1), Op2( 1), is32, NULL, get_CLIDR }, + { CRn( 0), CRm( 0), Op1( 1), Op2( 7), is32, NULL, get_AIDR }, +}; + +static int reg_from_user(void *val, const void __user *uaddr, u64 id) +{ + /* This Just Works because we are little endian. */ + if (copy_from_user(val, uaddr, KVM_REG_SIZE(id)) != 0) + return -EFAULT; + return 0; +} + +static int reg_to_user(void __user *uaddr, const void *val, u64 id) +{ + /* This Just Works because we are little endian. */ + if (copy_to_user(uaddr, val, KVM_REG_SIZE(id)) != 0) + return -EFAULT; + return 0; +} + +static int get_invariant_cp15(u64 id, void __user *uaddr) +{ + struct coproc_params params; + const struct coproc_reg *r; + + if (!index_to_params(id, ¶ms)) + return -ENOENT; + + r = find_reg(¶ms, invariant_cp15, ARRAY_SIZE(invariant_cp15)); + if (!r) + return -ENOENT; + + return reg_to_user(uaddr, &r->val, id); +} + +static int set_invariant_cp15(u64 id, void __user *uaddr) +{ + struct coproc_params params; + const struct coproc_reg *r; + int err; + u64 val = 0; /* Make sure high bits are 0 for 32-bit regs */ + + if (!index_to_params(id, ¶ms)) + return -ENOENT; + r = find_reg(¶ms, invariant_cp15, ARRAY_SIZE(invariant_cp15)); + if (!r) + return -ENOENT; + + err = reg_from_user(&val, uaddr, id); + if (err) + return err; + + /* This is what we mean by invariant: you can't change it. */ + if (r->val != val) + return -EINVAL; + + return 0; +} + +int kvm_arm_coproc_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) +{ + const struct coproc_reg *r; + void __user *uaddr = (void __user *)(long)reg->addr; + + r = index_to_coproc_reg(vcpu, reg->id); + if (!r) + return get_invariant_cp15(reg->id, uaddr); + + /* Note: copies two regs if size is 64 bit. */ + return reg_to_user(uaddr, &vcpu->arch.cp15[r->reg], reg->id); +} + +int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) +{ + const struct coproc_reg *r; + void __user *uaddr = (void __user *)(long)reg->addr; + + r = index_to_coproc_reg(vcpu, reg->id); + if (!r) + return set_invariant_cp15(reg->id, uaddr); + + /* Note: copies two regs if size is 64 bit */ + return reg_from_user(&vcpu->arch.cp15[r->reg], uaddr, reg->id); +} + +static u64 cp15_to_index(const struct coproc_reg *reg) +{ + u64 val = KVM_REG_ARM | (15 << KVM_REG_ARM_COPROC_SHIFT); + if (reg->is_64) { + val |= KVM_REG_SIZE_U64; + val |= (reg->Op1 << KVM_REG_ARM_OPC1_SHIFT); + val |= (reg->CRm << KVM_REG_ARM_CRM_SHIFT); + } else { + val |= KVM_REG_SIZE_U32; + val |= (reg->Op1 << KVM_REG_ARM_OPC1_SHIFT); + val |= (reg->Op2 << KVM_REG_ARM_32_OPC2_SHIFT); + val |= (reg->CRm << KVM_REG_ARM_CRM_SHIFT); + val |= (reg->CRn << KVM_REG_ARM_32_CRN_SHIFT); + } + return val; +} + +static bool copy_reg_to_user(const struct coproc_reg *reg, u64 __user **uind) +{ + if (!*uind) + return true; + + if (put_user(cp15_to_index(reg), *uind)) + return false; + + (*uind)++; + return true; +} + +/* Assumed ordered tables, see kvm_coproc_table_init. */ +static int walk_cp15(struct kvm_vcpu *vcpu, u64 __user *uind) +{ + const struct coproc_reg *i1, *i2, *end1, *end2; + unsigned int total = 0; + size_t num; + + /* We check for duplicates here, to allow arch-specific overrides. */ + i1 = get_target_table(vcpu->arch.target, &num); + end1 = i1 + num; + i2 = cp15_regs; + end2 = cp15_regs + ARRAY_SIZE(cp15_regs); + + BUG_ON(i1 == end1 || i2 == end2); + + /* Walk carefully, as both tables may refer to the same register. */ + while (i1 || i2) { + int cmp = cmp_reg(i1, i2); + /* target-specific overrides generic entry. */ + if (cmp <= 0) { + /* Ignore registers we trap but don't save. */ + if (i1->reg) { + if (!copy_reg_to_user(i1, &uind)) + return -EFAULT; + total++; + } + } else { + /* Ignore registers we trap but don't save. */ + if (i2->reg) { + if (!copy_reg_to_user(i2, &uind)) + return -EFAULT; + total++; + } + } + + if (cmp <= 0 && ++i1 == end1) + i1 = NULL; + if (cmp >= 0 && ++i2 == end2) + i2 = NULL; + } + return total; +} + +unsigned long kvm_arm_num_coproc_regs(struct kvm_vcpu *vcpu) +{ + return ARRAY_SIZE(invariant_cp15) + + walk_cp15(vcpu, (u64 __user *)NULL); +} + +int kvm_arm_copy_coproc_indices(struct kvm_vcpu *vcpu, u64 __user *uindices) +{ + unsigned int i; + int err; + + /* Then give them all the invariant registers' indices. */ + for (i = 0; i < ARRAY_SIZE(invariant_cp15); i++) { + if (put_user(cp15_to_index(&invariant_cp15[i]), uindices)) + return -EFAULT; + uindices++; + } + + err = walk_cp15(vcpu, uindices); + if (err > 0) + err = 0; + return err; +} + void kvm_coproc_table_init(void) { unsigned int i; @@ -354,6 +677,10 @@ void kvm_coproc_table_init(void) /* Make sure tables are unique and in order. */ for (i = 1; i < ARRAY_SIZE(cp15_regs); i++) BUG_ON(cmp_reg(&cp15_regs[i-1], &cp15_regs[i]) >= 0); + + /* We abuse the reset function to overwrite the table itself. */ + for (i = 0; i < ARRAY_SIZE(invariant_cp15); i++) + invariant_cp15[i].reset(NULL, &invariant_cp15[i]); } /** diff --git a/arch/arm/kvm/guest.c b/arch/arm/kvm/guest.c index a12eb229021d..2339d9609d36 100644 --- a/arch/arm/kvm/guest.c +++ b/arch/arm/kvm/guest.c @@ -26,6 +26,7 @@ #include #include #include +#include #define VM_STAT(x) { #x, offsetof(struct kvm, stat.x), KVM_STAT_VM } #define VCPU_STAT(x) { #x, offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU } @@ -119,7 +120,7 @@ static unsigned long num_core_regs(void) */ unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu) { - return num_core_regs(); + return num_core_regs() + kvm_arm_num_coproc_regs(vcpu); } /** @@ -138,7 +139,7 @@ int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices) uindices++; } - return 0; + return kvm_arm_copy_coproc_indices(vcpu, uindices); } int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) @@ -151,7 +152,7 @@ int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE) return get_core_reg(vcpu, reg); - return -EINVAL; + return kvm_arm_coproc_get_reg(vcpu, reg); } int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) @@ -164,7 +165,7 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE) return set_core_reg(vcpu, reg); - return -EINVAL; + return kvm_arm_coproc_set_reg(vcpu, reg); } int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu, -- cgit v1.2.3-59-g8ed1b From c27581ed32275897651a84043b04ea3ccdd644e0 Mon Sep 17 00:00:00 2001 From: Christoffer Dall Date: Sun, 20 Jan 2013 18:28:10 -0500 Subject: KVM: ARM: Demux CCSIDR in the userspace API The Cache Size Selection Register (CSSELR) selects the current Cache Size ID Register (CCSIDR). You write which cache you are interested in to CSSELR, and read the information out of CCSIDR. Which cache numbers are valid is known by reading the Cache Level ID Register (CLIDR). To export this state to userspace, we add a KVM_REG_ARM_DEMUX numberspace (17), which uses 8 bits to represent which register is being demultiplexed (0 for CCSIDR), and the lower 8 bits to represent this demultiplexing (in our case, the CSSELR value, which is 4 bits). Reviewed-by: Will Deacon Reviewed-by: Marcelo Tosatti Signed-off-by: Rusty Russell Signed-off-by: Christoffer Dall --- Documentation/virtual/kvm/api.txt | 2 + arch/arm/include/uapi/asm/kvm.h | 9 +++ arch/arm/kvm/coproc.c | 164 +++++++++++++++++++++++++++++++++++++- 3 files changed, 172 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 0e2287406464..94f17a30edd3 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -1805,6 +1805,8 @@ ARM 32-bit CP15 registers have the following id bit patterns: ARM 64-bit CP15 registers have the following id bit patterns: 0x4003 0000 000F +ARM CCSIDR registers are demultiplexed by CSSELR value: + 0x4002 0000 0011 00 4.69 KVM_GET_ONE_REG diff --git a/arch/arm/include/uapi/asm/kvm.h b/arch/arm/include/uapi/asm/kvm.h index 53f45f146875..71ae27ec0599 100644 --- a/arch/arm/include/uapi/asm/kvm.h +++ b/arch/arm/include/uapi/asm/kvm.h @@ -104,6 +104,15 @@ struct kvm_arch_memory_slot { #define KVM_REG_ARM_CORE (0x0010 << KVM_REG_ARM_COPROC_SHIFT) #define KVM_REG_ARM_CORE_REG(name) (offsetof(struct kvm_regs, name) / 4) +/* Some registers need more space to represent values. */ +#define KVM_REG_ARM_DEMUX (0x0011 << KVM_REG_ARM_COPROC_SHIFT) +#define KVM_REG_ARM_DEMUX_ID_MASK 0x000000000000FF00 +#define KVM_REG_ARM_DEMUX_ID_SHIFT 8 +#define KVM_REG_ARM_DEMUX_ID_CCSIDR (0x00 << KVM_REG_ARM_DEMUX_ID_SHIFT) +#define KVM_REG_ARM_DEMUX_VAL_MASK 0x00000000000000FF +#define KVM_REG_ARM_DEMUX_VAL_SHIFT 0 + + /* KVM_IRQ_LINE irq field index values */ #define KVM_ARM_IRQ_TYPE_SHIFT 24 #define KVM_ARM_IRQ_TYPE_MASK 0xff diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c index 95a0f5e5c1fc..1827b643af15 100644 --- a/arch/arm/kvm/coproc.c +++ b/arch/arm/kvm/coproc.c @@ -35,6 +35,12 @@ * Co-processor emulation *****************************************************************************/ +/* 3 bits per cache level, as per CLIDR, but non-existent caches always 0 */ +static u32 cache_levels; + +/* CSSELR values; used to index KVM_REG_ARM_DEMUX_ID_CCSIDR */ +#define CSSELR_MAX 12 + int kvm_handle_cp10_id(struct kvm_vcpu *vcpu, struct kvm_run *run) { kvm_inject_undefined(vcpu); @@ -548,11 +554,113 @@ static int set_invariant_cp15(u64 id, void __user *uaddr) return 0; } +static bool is_valid_cache(u32 val) +{ + u32 level, ctype; + + if (val >= CSSELR_MAX) + return -ENOENT; + + /* Bottom bit is Instruction or Data bit. Next 3 bits are level. */ + level = (val >> 1); + ctype = (cache_levels >> (level * 3)) & 7; + + switch (ctype) { + case 0: /* No cache */ + return false; + case 1: /* Instruction cache only */ + return (val & 1); + case 2: /* Data cache only */ + case 4: /* Unified cache */ + return !(val & 1); + case 3: /* Separate instruction and data caches */ + return true; + default: /* Reserved: we can't know instruction or data. */ + return false; + } +} + +/* Which cache CCSIDR represents depends on CSSELR value. */ +static u32 get_ccsidr(u32 csselr) +{ + u32 ccsidr; + + /* Make sure noone else changes CSSELR during this! */ + local_irq_disable(); + /* Put value into CSSELR */ + asm volatile("mcr p15, 2, %0, c0, c0, 0" : : "r" (csselr)); + isb(); + /* Read result out of CCSIDR */ + asm volatile("mrc p15, 1, %0, c0, c0, 0" : "=r" (ccsidr)); + local_irq_enable(); + + return ccsidr; +} + +static int demux_c15_get(u64 id, void __user *uaddr) +{ + u32 val; + u32 __user *uval = uaddr; + + /* Fail if we have unknown bits set. */ + if (id & ~(KVM_REG_ARCH_MASK|KVM_REG_SIZE_MASK|KVM_REG_ARM_COPROC_MASK + | ((1 << KVM_REG_ARM_COPROC_SHIFT)-1))) + return -ENOENT; + + switch (id & KVM_REG_ARM_DEMUX_ID_MASK) { + case KVM_REG_ARM_DEMUX_ID_CCSIDR: + if (KVM_REG_SIZE(id) != 4) + return -ENOENT; + val = (id & KVM_REG_ARM_DEMUX_VAL_MASK) + >> KVM_REG_ARM_DEMUX_VAL_SHIFT; + if (!is_valid_cache(val)) + return -ENOENT; + + return put_user(get_ccsidr(val), uval); + default: + return -ENOENT; + } +} + +static int demux_c15_set(u64 id, void __user *uaddr) +{ + u32 val, newval; + u32 __user *uval = uaddr; + + /* Fail if we have unknown bits set. */ + if (id & ~(KVM_REG_ARCH_MASK|KVM_REG_SIZE_MASK|KVM_REG_ARM_COPROC_MASK + | ((1 << KVM_REG_ARM_COPROC_SHIFT)-1))) + return -ENOENT; + + switch (id & KVM_REG_ARM_DEMUX_ID_MASK) { + case KVM_REG_ARM_DEMUX_ID_CCSIDR: + if (KVM_REG_SIZE(id) != 4) + return -ENOENT; + val = (id & KVM_REG_ARM_DEMUX_VAL_MASK) + >> KVM_REG_ARM_DEMUX_VAL_SHIFT; + if (!is_valid_cache(val)) + return -ENOENT; + + if (get_user(newval, uval)) + return -EFAULT; + + /* This is also invariant: you can't change it. */ + if (newval != get_ccsidr(val)) + return -EINVAL; + return 0; + default: + return -ENOENT; + } +} + int kvm_arm_coproc_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) { const struct coproc_reg *r; void __user *uaddr = (void __user *)(long)reg->addr; + if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX) + return demux_c15_get(reg->id, uaddr); + r = index_to_coproc_reg(vcpu, reg->id); if (!r) return get_invariant_cp15(reg->id, uaddr); @@ -566,6 +674,9 @@ int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) const struct coproc_reg *r; void __user *uaddr = (void __user *)(long)reg->addr; + if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX) + return demux_c15_set(reg->id, uaddr); + r = index_to_coproc_reg(vcpu, reg->id); if (!r) return set_invariant_cp15(reg->id, uaddr); @@ -574,6 +685,33 @@ int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) return reg_from_user(&vcpu->arch.cp15[r->reg], uaddr, reg->id); } +static unsigned int num_demux_regs(void) +{ + unsigned int i, count = 0; + + for (i = 0; i < CSSELR_MAX; i++) + if (is_valid_cache(i)) + count++; + + return count; +} + +static int write_demux_regids(u64 __user *uindices) +{ + u64 val = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_DEMUX; + unsigned int i; + + val |= KVM_REG_ARM_DEMUX_ID_CCSIDR; + for (i = 0; i < CSSELR_MAX; i++) { + if (!is_valid_cache(i)) + continue; + if (put_user(val | i, uindices)) + return -EFAULT; + uindices++; + } + return 0; +} + static u64 cp15_to_index(const struct coproc_reg *reg) { u64 val = KVM_REG_ARM | (15 << KVM_REG_ARM_COPROC_SHIFT); @@ -649,6 +787,7 @@ static int walk_cp15(struct kvm_vcpu *vcpu, u64 __user *uind) unsigned long kvm_arm_num_coproc_regs(struct kvm_vcpu *vcpu) { return ARRAY_SIZE(invariant_cp15) + + num_demux_regs() + walk_cp15(vcpu, (u64 __user *)NULL); } @@ -665,9 +804,11 @@ int kvm_arm_copy_coproc_indices(struct kvm_vcpu *vcpu, u64 __user *uindices) } err = walk_cp15(vcpu, uindices); - if (err > 0) - err = 0; - return err; + if (err < 0) + return err; + uindices += err; + + return write_demux_regids(uindices); } void kvm_coproc_table_init(void) @@ -681,6 +822,23 @@ void kvm_coproc_table_init(void) /* We abuse the reset function to overwrite the table itself. */ for (i = 0; i < ARRAY_SIZE(invariant_cp15); i++) invariant_cp15[i].reset(NULL, &invariant_cp15[i]); + + /* + * CLIDR format is awkward, so clean it up. See ARM B4.1.20: + * + * If software reads the Cache Type fields from Ctype1 + * upwards, once it has seen a value of 0b000, no caches + * exist at further-out levels of the hierarchy. So, for + * example, if Ctype3 is the first Cache Type field with a + * value of 0b000, the values of Ctype4 to Ctype7 must be + * ignored. + */ + asm volatile("mrc p15, 1, %0, c0, c0, 1" : "=r" (cache_levels)); + for (i = 0; i < 7; i++) + if (((cache_levels >> (i*3)) & 7) == 0) + break; + /* Clear all higher bits. */ + cache_levels &= (1 << (i*3))-1; } /** -- cgit v1.2.3-59-g8ed1b From 4fe21e4c6def3c6a8f609893b4d5c72bc186d0d5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 20 Jan 2013 18:28:11 -0500 Subject: KVM: ARM: VFP userspace interface We use space #18 for floating point regs. Reviewed-by: Will Deacon Reviewed-by: Marcelo Tosatti Signed-off-by: Rusty Russell Signed-off-by: Christoffer Dall --- Documentation/virtual/kvm/api.txt | 6 ++ arch/arm/include/uapi/asm/kvm.h | 12 +++ arch/arm/kvm/coproc.c | 178 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+) (limited to 'Documentation') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 94f17a30edd3..38066a7a74e1 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -1808,6 +1808,12 @@ ARM 64-bit CP15 registers have the following id bit patterns: ARM CCSIDR registers are demultiplexed by CSSELR value: 0x4002 0000 0011 00 +ARM 32-bit VFP control registers have the following id bit patterns: + 0x4002 0000 0012 1 + +ARM 64-bit FP registers have the following id bit patterns: + 0x4002 0000 0012 0 + 4.69 KVM_GET_ONE_REG Capability: KVM_CAP_ONE_REG diff --git a/arch/arm/include/uapi/asm/kvm.h b/arch/arm/include/uapi/asm/kvm.h index 71ae27ec0599..bbb6b2328004 100644 --- a/arch/arm/include/uapi/asm/kvm.h +++ b/arch/arm/include/uapi/asm/kvm.h @@ -112,6 +112,18 @@ struct kvm_arch_memory_slot { #define KVM_REG_ARM_DEMUX_VAL_MASK 0x00000000000000FF #define KVM_REG_ARM_DEMUX_VAL_SHIFT 0 +/* VFP registers: we could overload CP10 like ARM does, but that's ugly. */ +#define KVM_REG_ARM_VFP (0x0012 << KVM_REG_ARM_COPROC_SHIFT) +#define KVM_REG_ARM_VFP_MASK 0x000000000000FFFF +#define KVM_REG_ARM_VFP_BASE_REG 0x0 +#define KVM_REG_ARM_VFP_FPSID 0x1000 +#define KVM_REG_ARM_VFP_FPSCR 0x1001 +#define KVM_REG_ARM_VFP_MVFR1 0x1006 +#define KVM_REG_ARM_VFP_MVFR0 0x1007 +#define KVM_REG_ARM_VFP_FPEXC 0x1008 +#define KVM_REG_ARM_VFP_FPINST 0x1009 +#define KVM_REG_ARM_VFP_FPINST2 0x100A + /* KVM_IRQ_LINE irq field index values */ #define KVM_ARM_IRQ_TYPE_SHIFT 24 diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c index 1827b643af15..d782638c7ec0 100644 --- a/arch/arm/kvm/coproc.c +++ b/arch/arm/kvm/coproc.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include "../vfp/vfpinstr.h" #include "trace.h" #include "coproc.h" @@ -653,6 +655,170 @@ static int demux_c15_set(u64 id, void __user *uaddr) } } +#ifdef CONFIG_VFPv3 +static const int vfp_sysregs[] = { KVM_REG_ARM_VFP_FPEXC, + KVM_REG_ARM_VFP_FPSCR, + KVM_REG_ARM_VFP_FPINST, + KVM_REG_ARM_VFP_FPINST2, + KVM_REG_ARM_VFP_MVFR0, + KVM_REG_ARM_VFP_MVFR1, + KVM_REG_ARM_VFP_FPSID }; + +static unsigned int num_fp_regs(void) +{ + if (((fmrx(MVFR0) & MVFR0_A_SIMD_MASK) >> MVFR0_A_SIMD_BIT) == 2) + return 32; + else + return 16; +} + +static unsigned int num_vfp_regs(void) +{ + /* Normal FP regs + control regs. */ + return num_fp_regs() + ARRAY_SIZE(vfp_sysregs); +} + +static int copy_vfp_regids(u64 __user *uindices) +{ + unsigned int i; + const u64 u32reg = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP; + const u64 u64reg = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP; + + for (i = 0; i < num_fp_regs(); i++) { + if (put_user((u64reg | KVM_REG_ARM_VFP_BASE_REG) + i, + uindices)) + return -EFAULT; + uindices++; + } + + for (i = 0; i < ARRAY_SIZE(vfp_sysregs); i++) { + if (put_user(u32reg | vfp_sysregs[i], uindices)) + return -EFAULT; + uindices++; + } + + return num_vfp_regs(); +} + +static int vfp_get_reg(const struct kvm_vcpu *vcpu, u64 id, void __user *uaddr) +{ + u32 vfpid = (id & KVM_REG_ARM_VFP_MASK); + u32 val; + + /* Fail if we have unknown bits set. */ + if (id & ~(KVM_REG_ARCH_MASK|KVM_REG_SIZE_MASK|KVM_REG_ARM_COPROC_MASK + | ((1 << KVM_REG_ARM_COPROC_SHIFT)-1))) + return -ENOENT; + + if (vfpid < num_fp_regs()) { + if (KVM_REG_SIZE(id) != 8) + return -ENOENT; + return reg_to_user(uaddr, &vcpu->arch.vfp_guest.fpregs[vfpid], + id); + } + + /* FP control registers are all 32 bit. */ + if (KVM_REG_SIZE(id) != 4) + return -ENOENT; + + switch (vfpid) { + case KVM_REG_ARM_VFP_FPEXC: + return reg_to_user(uaddr, &vcpu->arch.vfp_guest.fpexc, id); + case KVM_REG_ARM_VFP_FPSCR: + return reg_to_user(uaddr, &vcpu->arch.vfp_guest.fpscr, id); + case KVM_REG_ARM_VFP_FPINST: + return reg_to_user(uaddr, &vcpu->arch.vfp_guest.fpinst, id); + case KVM_REG_ARM_VFP_FPINST2: + return reg_to_user(uaddr, &vcpu->arch.vfp_guest.fpinst2, id); + case KVM_REG_ARM_VFP_MVFR0: + val = fmrx(MVFR0); + return reg_to_user(uaddr, &val, id); + case KVM_REG_ARM_VFP_MVFR1: + val = fmrx(MVFR1); + return reg_to_user(uaddr, &val, id); + case KVM_REG_ARM_VFP_FPSID: + val = fmrx(FPSID); + return reg_to_user(uaddr, &val, id); + default: + return -ENOENT; + } +} + +static int vfp_set_reg(struct kvm_vcpu *vcpu, u64 id, const void __user *uaddr) +{ + u32 vfpid = (id & KVM_REG_ARM_VFP_MASK); + u32 val; + + /* Fail if we have unknown bits set. */ + if (id & ~(KVM_REG_ARCH_MASK|KVM_REG_SIZE_MASK|KVM_REG_ARM_COPROC_MASK + | ((1 << KVM_REG_ARM_COPROC_SHIFT)-1))) + return -ENOENT; + + if (vfpid < num_fp_regs()) { + if (KVM_REG_SIZE(id) != 8) + return -ENOENT; + return reg_from_user(&vcpu->arch.vfp_guest.fpregs[vfpid], + uaddr, id); + } + + /* FP control registers are all 32 bit. */ + if (KVM_REG_SIZE(id) != 4) + return -ENOENT; + + switch (vfpid) { + case KVM_REG_ARM_VFP_FPEXC: + return reg_from_user(&vcpu->arch.vfp_guest.fpexc, uaddr, id); + case KVM_REG_ARM_VFP_FPSCR: + return reg_from_user(&vcpu->arch.vfp_guest.fpscr, uaddr, id); + case KVM_REG_ARM_VFP_FPINST: + return reg_from_user(&vcpu->arch.vfp_guest.fpinst, uaddr, id); + case KVM_REG_ARM_VFP_FPINST2: + return reg_from_user(&vcpu->arch.vfp_guest.fpinst2, uaddr, id); + /* These are invariant. */ + case KVM_REG_ARM_VFP_MVFR0: + if (reg_from_user(&val, uaddr, id)) + return -EFAULT; + if (val != fmrx(MVFR0)) + return -EINVAL; + return 0; + case KVM_REG_ARM_VFP_MVFR1: + if (reg_from_user(&val, uaddr, id)) + return -EFAULT; + if (val != fmrx(MVFR1)) + return -EINVAL; + return 0; + case KVM_REG_ARM_VFP_FPSID: + if (reg_from_user(&val, uaddr, id)) + return -EFAULT; + if (val != fmrx(FPSID)) + return -EINVAL; + return 0; + default: + return -ENOENT; + } +} +#else /* !CONFIG_VFPv3 */ +static unsigned int num_vfp_regs(void) +{ + return 0; +} + +static int copy_vfp_regids(u64 __user *uindices) +{ + return 0; +} + +static int vfp_get_reg(const struct kvm_vcpu *vcpu, u64 id, void __user *uaddr) +{ + return -ENOENT; +} + +static int vfp_set_reg(struct kvm_vcpu *vcpu, u64 id, const void __user *uaddr) +{ + return -ENOENT; +} +#endif /* !CONFIG_VFPv3 */ + int kvm_arm_coproc_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) { const struct coproc_reg *r; @@ -661,6 +827,9 @@ int kvm_arm_coproc_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX) return demux_c15_get(reg->id, uaddr); + if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_VFP) + return vfp_get_reg(vcpu, reg->id, uaddr); + r = index_to_coproc_reg(vcpu, reg->id); if (!r) return get_invariant_cp15(reg->id, uaddr); @@ -677,6 +846,9 @@ int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX) return demux_c15_set(reg->id, uaddr); + if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_VFP) + return vfp_set_reg(vcpu, reg->id, uaddr); + r = index_to_coproc_reg(vcpu, reg->id); if (!r) return set_invariant_cp15(reg->id, uaddr); @@ -788,6 +960,7 @@ unsigned long kvm_arm_num_coproc_regs(struct kvm_vcpu *vcpu) { return ARRAY_SIZE(invariant_cp15) + num_demux_regs() + + num_vfp_regs() + walk_cp15(vcpu, (u64 __user *)NULL); } @@ -808,6 +981,11 @@ int kvm_arm_copy_coproc_indices(struct kvm_vcpu *vcpu, u64 __user *uindices) return err; uindices += err; + err = copy_vfp_regids(uindices); + if (err < 0) + return err; + uindices += err; + return write_demux_regids(uindices); } -- cgit v1.2.3-59-g8ed1b From aa024c2f35a07cc32e48c5f62a5807be01c09249 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Sun, 20 Jan 2013 18:28:13 -0500 Subject: KVM: ARM: Power State Coordination Interface implementation Implement the PSCI specification (ARM DEN 0022A) to control virtual CPUs being "powered" on or off. PSCI/KVM is detected using the KVM_CAP_ARM_PSCI capability. A virtual CPU can now be initialized in a "powered off" state, using the KVM_ARM_VCPU_POWER_OFF feature flag. The guest can use either SMC or HVC to execute a PSCI function. Reviewed-by: Will Deacon Signed-off-by: Marc Zyngier Signed-off-by: Christoffer Dall --- Documentation/virtual/kvm/api.txt | 4 ++ arch/arm/include/asm/kvm_emulate.h | 10 ++++ arch/arm/include/asm/kvm_host.h | 5 +- arch/arm/include/asm/kvm_psci.h | 23 ++++++++ arch/arm/include/uapi/asm/kvm.h | 16 ++++++ arch/arm/kvm/Makefile | 2 +- arch/arm/kvm/arm.c | 30 ++++++++++- arch/arm/kvm/psci.c | 108 +++++++++++++++++++++++++++++++++++++ include/uapi/linux/kvm.h | 1 + 9 files changed, 195 insertions(+), 4 deletions(-) create mode 100644 arch/arm/include/asm/kvm_psci.h create mode 100644 arch/arm/kvm/psci.c (limited to 'Documentation') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 38066a7a74e1..c25439a58274 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -2185,6 +2185,10 @@ return ENOEXEC for that vcpu. Note that because some registers reflect machine topology, all vcpus should be created before this ioctl is invoked. +Possible features: + - KVM_ARM_VCPU_POWER_OFF: Starts the CPU in a power-off state. + Depends on KVM_CAP_ARM_PSCI. + 4.78 KVM_GET_REG_LIST diff --git a/arch/arm/include/asm/kvm_emulate.h b/arch/arm/include/asm/kvm_emulate.h index 4c1a073280be..fd611996bfb5 100644 --- a/arch/arm/include/asm/kvm_emulate.h +++ b/arch/arm/include/asm/kvm_emulate.h @@ -32,6 +32,11 @@ void kvm_inject_undefined(struct kvm_vcpu *vcpu); void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr); void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr); +static inline bool vcpu_mode_is_32bit(struct kvm_vcpu *vcpu) +{ + return 1; +} + static inline u32 *vcpu_pc(struct kvm_vcpu *vcpu) { return (u32 *)&vcpu->arch.regs.usr_regs.ARM_pc; @@ -42,6 +47,11 @@ static inline u32 *vcpu_cpsr(struct kvm_vcpu *vcpu) return (u32 *)&vcpu->arch.regs.usr_regs.ARM_cpsr; } +static inline void vcpu_set_thumb(struct kvm_vcpu *vcpu) +{ + *vcpu_cpsr(vcpu) |= PSR_T_BIT; +} + static inline bool mode_has_spsr(struct kvm_vcpu *vcpu) { unsigned long cpsr_mode = vcpu->arch.regs.usr_regs.ARM_cpsr & MODE_MASK; diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index e65fc967a71d..98b4d1a72923 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h @@ -30,7 +30,7 @@ #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 #define KVM_HAVE_ONE_REG -#define KVM_VCPU_MAX_FEATURES 0 +#define KVM_VCPU_MAX_FEATURES 1 /* We don't currently support large pages. */ #define KVM_HPAGE_GFN_SHIFT(x) 0 @@ -100,6 +100,9 @@ struct kvm_vcpu_arch { int last_pcpu; cpumask_t require_dcache_flush; + /* Don't run the guest on this vcpu */ + bool pause; + /* IO related fields */ struct kvm_decode mmio_decode; diff --git a/arch/arm/include/asm/kvm_psci.h b/arch/arm/include/asm/kvm_psci.h new file mode 100644 index 000000000000..9a83d98bf170 --- /dev/null +++ b/arch/arm/include/asm/kvm_psci.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2012 - ARM Ltd + * Author: Marc Zyngier + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __ARM_KVM_PSCI_H__ +#define __ARM_KVM_PSCI_H__ + +bool kvm_psci_call(struct kvm_vcpu *vcpu); + +#endif /* __ARM_KVM_PSCI_H__ */ diff --git a/arch/arm/include/uapi/asm/kvm.h b/arch/arm/include/uapi/asm/kvm.h index bbb6b2328004..3303ff5adbf3 100644 --- a/arch/arm/include/uapi/asm/kvm.h +++ b/arch/arm/include/uapi/asm/kvm.h @@ -65,6 +65,8 @@ struct kvm_regs { #define KVM_ARM_TARGET_CORTEX_A15 0 #define KVM_ARM_NUM_TARGETS 1 +#define KVM_ARM_VCPU_POWER_OFF 0 /* CPU is started in OFF state */ + struct kvm_vcpu_init { __u32 target; __u32 features[7]; @@ -145,4 +147,18 @@ struct kvm_arch_memory_slot { /* Highest supported SPI, from VGIC_NR_IRQS */ #define KVM_ARM_IRQ_GIC_MAX 127 +/* PSCI interface */ +#define KVM_PSCI_FN_BASE 0x95c1ba5e +#define KVM_PSCI_FN(n) (KVM_PSCI_FN_BASE + (n)) + +#define KVM_PSCI_FN_CPU_SUSPEND KVM_PSCI_FN(0) +#define KVM_PSCI_FN_CPU_OFF KVM_PSCI_FN(1) +#define KVM_PSCI_FN_CPU_ON KVM_PSCI_FN(2) +#define KVM_PSCI_FN_MIGRATE KVM_PSCI_FN(3) + +#define KVM_PSCI_RET_SUCCESS 0 +#define KVM_PSCI_RET_NI ((unsigned long)-1) +#define KVM_PSCI_RET_INVAL ((unsigned long)-2) +#define KVM_PSCI_RET_DENIED ((unsigned long)-3) + #endif /* __ARM_KVM_H__ */ diff --git a/arch/arm/kvm/Makefile b/arch/arm/kvm/Makefile index 1e45cd97a7fc..ea27987bd07f 100644 --- a/arch/arm/kvm/Makefile +++ b/arch/arm/kvm/Makefile @@ -18,4 +18,4 @@ kvm-arm-y = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o) obj-y += kvm-arm.o init.o interrupts.o obj-y += arm.o guest.o mmu.o emulate.o reset.o -obj-y += coproc.o coproc_a15.o mmio.o +obj-y += coproc.o coproc_a15.o mmio.o psci.o diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index 8680b9ffd2ae..2d30e3afdaf9 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #ifdef REQUIRES_VIRT @@ -160,6 +161,7 @@ int kvm_dev_ioctl_check_extension(long ext) case KVM_CAP_SYNC_MMU: case KVM_CAP_DESTROY_MEMORY_REGION_WORKS: case KVM_CAP_ONE_REG: + case KVM_CAP_ARM_PSCI: r = 1; break; case KVM_CAP_COALESCED_MMIO: @@ -443,14 +445,18 @@ static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run) trace_kvm_hvc(*vcpu_pc(vcpu), *vcpu_reg(vcpu, 0), vcpu->arch.hsr & HSR_HVC_IMM_MASK); + if (kvm_psci_call(vcpu)) + return 1; + kvm_inject_undefined(vcpu); return 1; } static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run) { - /* We don't support SMC; don't do that. */ - kvm_debug("smc: at %08x", *vcpu_pc(vcpu)); + if (kvm_psci_call(vcpu)) + return 1; + kvm_inject_undefined(vcpu); return 1; } @@ -589,9 +595,26 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu) return 0; vcpu->arch.has_run_once = true; + + /* + * Handle the "start in power-off" case by calling into the + * PSCI code. + */ + if (test_and_clear_bit(KVM_ARM_VCPU_POWER_OFF, vcpu->arch.features)) { + *vcpu_reg(vcpu, 0) = KVM_PSCI_FN_CPU_OFF; + kvm_psci_call(vcpu); + } + return 0; } +static void vcpu_pause(struct kvm_vcpu *vcpu) +{ + wait_queue_head_t *wq = kvm_arch_vcpu_wq(vcpu); + + wait_event_interruptible(*wq, !vcpu->arch.pause); +} + /** * kvm_arch_vcpu_ioctl_run - the main VCPU run function to execute guest code * @vcpu: The VCPU pointer @@ -635,6 +658,9 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) update_vttbr(vcpu->kvm); + if (vcpu->arch.pause) + vcpu_pause(vcpu); + local_irq_disable(); /* diff --git a/arch/arm/kvm/psci.c b/arch/arm/kvm/psci.c new file mode 100644 index 000000000000..7ee5bb7a3667 --- /dev/null +++ b/arch/arm/kvm/psci.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2012 - ARM Ltd + * Author: Marc Zyngier + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include +#include + +/* + * This is an implementation of the Power State Coordination Interface + * as described in ARM document number ARM DEN 0022A. + */ + +static void kvm_psci_vcpu_off(struct kvm_vcpu *vcpu) +{ + vcpu->arch.pause = true; +} + +static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu) +{ + struct kvm *kvm = source_vcpu->kvm; + struct kvm_vcpu *vcpu; + wait_queue_head_t *wq; + unsigned long cpu_id; + phys_addr_t target_pc; + + cpu_id = *vcpu_reg(source_vcpu, 1); + if (vcpu_mode_is_32bit(source_vcpu)) + cpu_id &= ~((u32) 0); + + if (cpu_id >= atomic_read(&kvm->online_vcpus)) + return KVM_PSCI_RET_INVAL; + + target_pc = *vcpu_reg(source_vcpu, 2); + + vcpu = kvm_get_vcpu(kvm, cpu_id); + + wq = kvm_arch_vcpu_wq(vcpu); + if (!waitqueue_active(wq)) + return KVM_PSCI_RET_INVAL; + + kvm_reset_vcpu(vcpu); + + /* Gracefully handle Thumb2 entry point */ + if (vcpu_mode_is_32bit(vcpu) && (target_pc & 1)) { + target_pc &= ~((phys_addr_t) 1); + vcpu_set_thumb(vcpu); + } + + *vcpu_pc(vcpu) = target_pc; + vcpu->arch.pause = false; + smp_mb(); /* Make sure the above is visible */ + + wake_up_interruptible(wq); + + return KVM_PSCI_RET_SUCCESS; +} + +/** + * kvm_psci_call - handle PSCI call if r0 value is in range + * @vcpu: Pointer to the VCPU struct + * + * Handle PSCI calls from guests through traps from HVC or SMC instructions. + * The calling convention is similar to SMC calls to the secure world where + * the function number is placed in r0 and this function returns true if the + * function number specified in r0 is withing the PSCI range, and false + * otherwise. + */ +bool kvm_psci_call(struct kvm_vcpu *vcpu) +{ + unsigned long psci_fn = *vcpu_reg(vcpu, 0) & ~((u32) 0); + unsigned long val; + + switch (psci_fn) { + case KVM_PSCI_FN_CPU_OFF: + kvm_psci_vcpu_off(vcpu); + val = KVM_PSCI_RET_SUCCESS; + break; + case KVM_PSCI_FN_CPU_ON: + val = kvm_psci_vcpu_on(vcpu); + break; + case KVM_PSCI_FN_CPU_SUSPEND: + case KVM_PSCI_FN_MIGRATE: + val = KVM_PSCI_RET_NI; + break; + + default: + return false; + } + + *vcpu_reg(vcpu, 0) = val; + return true; +} diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index dc63665e73ad..7f2360a46fc2 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -636,6 +636,7 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_IRQFD_RESAMPLE 82 #define KVM_CAP_PPC_BOOKE_WATCHDOG 83 #define KVM_CAP_PPC_HTAB_FD 84 +#define KVM_CAP_ARM_PSCI 87 #ifdef KVM_CAP_IRQ_ROUTING -- cgit v1.2.3-59-g8ed1b From 836aedb1414d4724b2ec68dd19810960c593720c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 24 Jan 2013 12:49:59 +0100 Subject: ACPI / PM: Expose power states of ACPI devices to user space Make it possible to retrieve the current power state of a device with ACPI power management from user space via sysfs by adding two new attributes, power_state and real_power_state, to the sysfs directory associated with the struct acpi_device object representing the device's ACPI node. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman --- .../ABI/testing/sysfs-devices-power_state | 20 +++++++++ .../ABI/testing/sysfs-devices-real_power_state | 23 ++++++++++ drivers/acpi/scan.c | 49 +++++++++++++++++++++- 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/sysfs-devices-power_state create mode 100644 Documentation/ABI/testing/sysfs-devices-real_power_state (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-devices-power_state b/Documentation/ABI/testing/sysfs-devices-power_state new file mode 100644 index 000000000000..7ad9546748f0 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-power_state @@ -0,0 +1,20 @@ +What: /sys/devices/.../power_state +Date: January 2013 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../power_state attribute is only present for + device objects representing ACPI device nodes that provide power + management methods. + + If present, it contains a string representing the current ACPI + power state of the given device node. Its possible values, + "D0", "D1", "D2", "D3hot", and "D3cold", reflect the power state + names defined by the ACPI specification (ACPI 4 and above). + + If the device node uses shared ACPI power resources, this state + determines a list of power resources required not to be turned + off. However, some power resources needed by the device node in + higher-power (lower-number) states may also be ON because of + some other devices using them at the moment. + + This attribute is read-only. diff --git a/Documentation/ABI/testing/sysfs-devices-real_power_state b/Documentation/ABI/testing/sysfs-devices-real_power_state new file mode 100644 index 000000000000..8b3527c82a7d --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-real_power_state @@ -0,0 +1,23 @@ +What: /sys/devices/.../real_power_state +Date: January 2013 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../real_power_state attribute is only present + for device objects representing ACPI device nodes that provide + power management methods and use ACPI power resources for power + management. + + If present, it contains a string representing the real ACPI + power state of the given device node as returned by the _PSC + control method or inferred from the configuration of power + resources. Its possible values, "D0", "D1", "D2", "D3hot", and + "D3cold", reflect the power state names defined by the ACPI + specification (ACPI 4 and above). + + In some situations the value of this attribute may be different + from the value of the /sys/devices/.../power_state attribute for + the same device object. If that happens, some shared power + resources used by the device node are only ON because of some + other devices using them at the moment. + + This attribute is read-only. diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8b3b18846c8c..9761d589f3f5 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -178,6 +178,32 @@ err_out: } EXPORT_SYMBOL(acpi_bus_hot_remove_device); +static ssize_t real_power_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_device *adev = to_acpi_device(dev); + int state; + int ret; + + ret = acpi_device_get_power(adev, &state); + if (ret) + return ret; + + return sprintf(buf, "%s\n", acpi_power_state_string(state)); +} + +static DEVICE_ATTR(real_power_state, 0444, real_power_state_show, NULL); + +static ssize_t power_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_device *adev = to_acpi_device(dev); + + return sprintf(buf, "%s\n", acpi_power_state_string(adev->power.state)); +} + +static DEVICE_ATTR(power_state, 0444, power_state_show, NULL); + static ssize_t acpi_eject_store(struct device *d, struct device_attribute *attr, const char *buf, size_t count) @@ -369,8 +395,22 @@ static int acpi_device_setup_files(struct acpi_device *dev) * hot-removal function from userland. */ status = acpi_get_handle(dev->handle, "_EJ0", &temp); - if (ACPI_SUCCESS(status)) + if (ACPI_SUCCESS(status)) { result = device_create_file(&dev->dev, &dev_attr_eject); + if (result) + return result; + } + + if (dev->flags.power_manageable) { + result = device_create_file(&dev->dev, &dev_attr_power_state); + if (result) + return result; + + if (dev->power.flags.power_resources) + result = device_create_file(&dev->dev, + &dev_attr_real_power_state); + } + end: return result; } @@ -380,6 +420,13 @@ static void acpi_device_remove_files(struct acpi_device *dev) acpi_status status; acpi_handle temp; + if (dev->flags.power_manageable) { + device_remove_file(&dev->dev, &dev_attr_power_state); + if (dev->power.flags.power_resources) + device_remove_file(&dev->dev, + &dev_attr_real_power_state); + } + /* * If device has _STR, remove 'description' file */ -- cgit v1.2.3-59-g8ed1b From b1c0f99bfb89cd9b42e3119ab822a8102fa87909 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 24 Jan 2013 12:50:09 +0100 Subject: ACPI / PM: Expose current status of ACPI power resources Since ACPI power resources are going to be used more extensively on new hardware platforms, it becomes necessary for user space (powertop in particular) to observe some properties of those resources for diagnostics purposes. For this reason, expose the current status of each ACPI power resource to user space via sysfs by adding a new resource_in_use attribute to the sysfs directory representing the given power resource. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman --- .../ABI/testing/sysfs-devices-resource_in_use | 12 ++++++++++ drivers/acpi/power.c | 26 +++++++++++++++++++++- drivers/acpi/scan.c | 3 +++ include/acpi/acpi_bus.h | 1 + 4 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/sysfs-devices-resource_in_use (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-devices-resource_in_use b/Documentation/ABI/testing/sysfs-devices-resource_in_use new file mode 100644 index 000000000000..b4a3bc5922a3 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-resource_in_use @@ -0,0 +1,12 @@ +What: /sys/devices/.../resource_in_use +Date: January 2013 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../resource_in_use attribute is only present + for device objects representing ACPI power resources. + + If present, it contains a number (0 or 1) representing the + current status of the given power resource (0 means that the + resource is not in use and therefore it has been turned off). + + This attribute is read-only. diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 3f16dd4db23e..946720a4db57 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -87,6 +87,12 @@ static DEFINE_MUTEX(power_resource_list_lock); Power Resource Management -------------------------------------------------------------------------- */ +static inline +struct acpi_power_resource *to_power_resource(struct acpi_device *device) +{ + return container_of(device, struct acpi_power_resource, device); +} + static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle) { struct acpi_device *device; @@ -94,7 +100,7 @@ static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle) if (acpi_bus_get_device(handle, &device)) return NULL; - return container_of(device, struct acpi_power_resource, device); + return to_power_resource(device); } static int acpi_power_resources_list_add(acpi_handle handle, @@ -678,6 +684,21 @@ static void acpi_release_power_resource(struct device *dev) kfree(resource); } +static ssize_t acpi_power_in_use_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct acpi_power_resource *resource; + + resource = to_power_resource(to_acpi_device(dev)); + return sprintf(buf, "%u\n", !!resource->ref_count); +} +static DEVICE_ATTR(resource_in_use, 0444, acpi_power_in_use_show, NULL); + +static void acpi_power_sysfs_remove(struct acpi_device *device) +{ + device_remove_file(&device->dev, &dev_attr_resource_in_use); +} + int acpi_add_power_resource(acpi_handle handle) { struct acpi_power_resource *resource; @@ -725,6 +746,9 @@ int acpi_add_power_resource(acpi_handle handle) if (result) goto err; + if (!device_create_file(&device->dev, &dev_attr_resource_in_use)) + device->remove = acpi_power_sysfs_remove; + mutex_lock(&power_resource_list_lock); list_add(&resource->list_node, &acpi_power_resource_list); mutex_unlock(&power_resource_list_lock); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 9761d589f3f5..9801837876b7 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -791,6 +791,9 @@ static void acpi_device_unregister(struct acpi_device *device) acpi_power_add_remove_device(device, false); acpi_device_remove_files(device); + if (device->remove) + device->remove(device); + device_del(&device->dev); /* * Drop the reference counts of all power resources the device depends diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index fca1b9cb27d9..aef56a9f4e70 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -280,6 +280,7 @@ struct acpi_device { struct mutex physical_node_lock; DECLARE_BITMAP(physical_node_id_bitmap, ACPI_MAX_PHYSICAL_NODE); struct list_head power_dependent; + void (*remove)(struct acpi_device *); }; static inline void *acpi_driver_data(struct acpi_device *d) -- cgit v1.2.3-59-g8ed1b From 08261d87f7d1b6253ab3223756625a5c74532293 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Mon, 19 Nov 2012 16:02:10 +0100 Subject: PCI/MSI: Enable multiple MSIs with pci_enable_msi_block_auto() The new function pci_enable_msi_block_auto() tries to allocate maximum possible number of MSIs up to the number the device supports. It generalizes a pattern when pci_enable_msi_block() is contiguously called until it succeeds or fails. Opposite to pci_enable_msi_block() which takes the number of MSIs to allocate as a input parameter, pci_enable_msi_block_auto() could be used by device drivers to obtain the number of assigned MSIs and the number of MSIs the device supports. Signed-off-by: Alexander Gordeev Acked-by: Bjorn Helgaas Cc: Suresh Siddha Cc: Yinghai Lu Cc: Matthew Wilcox Cc: Jeff Garzik Cc: Linus Torvalds Cc: Andrew Morton Cc: Peter Zijlstra Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/c3de2419df94a0f95ca1a6f755afc421486455e6.1353324359.git.agordeev@redhat.com Signed-off-by: Ingo Molnar --- Documentation/PCI/MSI-HOWTO.txt | 37 ++++++++++++++++++++++++++++++++----- drivers/pci/msi.c | 26 ++++++++++++++++++++++++++ include/linux/pci.h | 7 +++++++ 3 files changed, 65 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/PCI/MSI-HOWTO.txt b/Documentation/PCI/MSI-HOWTO.txt index 53e6fca146d7..a09178086c30 100644 --- a/Documentation/PCI/MSI-HOWTO.txt +++ b/Documentation/PCI/MSI-HOWTO.txt @@ -127,15 +127,42 @@ on the number of vectors that can be allocated; pci_enable_msi_block() returns as soon as it finds any constraint that doesn't allow the call to succeed. -4.2.3 pci_disable_msi +4.2.3 pci_enable_msi_block_auto + +int pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *count) + +This variation on pci_enable_msi() call allows a device driver to request +the maximum possible number of MSIs. The MSI specification only allows +interrupts to be allocated in powers of two, up to a maximum of 2^5 (32). + +If this function returns a positive number, it indicates that it has +succeeded and the returned value is the number of allocated interrupts. In +this case, the function enables MSI on this device and updates dev->irq to +be the lowest of the new interrupts assigned to it. The other interrupts +assigned to the device are in the range dev->irq to dev->irq + returned +value - 1. + +If this function returns a negative number, it indicates an error and +the driver should not attempt to request any more MSI interrupts for +this device. + +If the device driver needs to know the number of interrupts the device +supports it can pass the pointer count where that number is stored. The +device driver must decide what action to take if pci_enable_msi_block_auto() +succeeds, but returns a value less than the number of interrupts supported. +If the device driver does not need to know the number of interrupts +supported, it can set the pointer count to NULL. + +4.2.4 pci_disable_msi void pci_disable_msi(struct pci_dev *dev) This function should be used to undo the effect of pci_enable_msi() or -pci_enable_msi_block(). Calling it restores dev->irq to the pin-based -interrupt number and frees the previously allocated message signaled -interrupt(s). The interrupt may subsequently be assigned to another -device, so drivers should not cache the value of dev->irq. +pci_enable_msi_block() or pci_enable_msi_block_auto(). Calling it restores +dev->irq to the pin-based interrupt number and frees the previously +allocated message signaled interrupt(s). The interrupt may subsequently be +assigned to another device, so drivers should not cache the value of +dev->irq. Before calling this function, a device driver must always call free_irq() on any interrupt for which it previously called request_irq(). diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 5099636a6e5f..00cc78c7aa04 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -845,6 +845,32 @@ int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) } EXPORT_SYMBOL(pci_enable_msi_block); +int pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *maxvec) +{ + int ret, pos, nvec; + u16 msgctl; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + if (!pos) + return -EINVAL; + + pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl); + ret = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1); + + if (maxvec) + *maxvec = ret; + + do { + nvec = ret; + ret = pci_enable_msi_block(dev, nvec); + } while (ret > 0); + + if (ret < 0) + return ret; + return nvec; +} +EXPORT_SYMBOL(pci_enable_msi_block_auto); + void pci_msi_shutdown(struct pci_dev *dev) { struct msi_desc *desc; diff --git a/include/linux/pci.h b/include/linux/pci.h index 15472d691ee6..6fa4dd2a3b9e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1101,6 +1101,12 @@ static inline int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) return -1; } +static inline int +pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *maxvec) +{ + return -1; +} + static inline void pci_msi_shutdown(struct pci_dev *dev) { } static inline void pci_disable_msi(struct pci_dev *dev) @@ -1132,6 +1138,7 @@ static inline int pci_msi_enabled(void) } #else extern int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec); +extern int pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *maxvec); extern void pci_msi_shutdown(struct pci_dev *dev); extern void pci_disable_msi(struct pci_dev *dev); extern int pci_msix_table_size(struct pci_dev *dev); -- cgit v1.2.3-59-g8ed1b From 425140af9e7040adc84a1b3f2c198eaf6eebc5aa Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Thu, 24 Jan 2013 08:18:35 +0400 Subject: of: Add vendor prefix for Cirrus Logic This patch adds a device tree vendor prefix for Cirrus Logic, Inc. Signed-off-by: Alexander Shiyan --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 902b1b1f568e..723bd8d62c79 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -14,6 +14,7 @@ bosch Bosch Sensortec GmbH brcm Broadcom Corporation cavium Cavium, Inc. chrp Common Hardware Reference Platform +cirrus Cirrus Logic, Inc. cortina Cortina Systems, Inc. dallas Maxim Integrated Products (formerly Dallas Semiconductor) denx Denx Software Engineering -- cgit v1.2.3-59-g8ed1b From 7d0291256ca99cbb6124f63228003329e7a64b21 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 4 Jan 2013 16:18:14 -0500 Subject: x86: Add TS-5500 platform support The Technologic Systems TS-5500 is an x86-based (AMD Elan SC520) single board computer. This driver registers most of its devices and exposes sysfs attributes for information such as jumpers' state or presence of some of its options. This driver currently registers the TS-5500 platform, its on-board LED, 2 pin blocks (GPIO) and its analog/digital converter. It can be extended to support other Technologic Systems products, such as the TS-5600. Signed-off-by: Vivien Didelot Acked-by: Thomas Gleixner Cc: Savoir-faire Linux Inc. Link: http://lkml.kernel.org/r/1357334294-12760-1-git-send-email-vivien.didelot@savoirfairelinux.com Signed-off-by: Ingo Molnar --- Documentation/ABI/testing/sysfs-platform-ts5500 | 47 ++++ MAINTAINERS | 5 + arch/x86/Kconfig | 9 + arch/x86/platform/Makefile | 1 + arch/x86/platform/ts5500/Makefile | 1 + arch/x86/platform/ts5500/ts5500.c | 339 ++++++++++++++++++++++++ 6 files changed, 402 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-platform-ts5500 create mode 100644 arch/x86/platform/ts5500/Makefile create mode 100644 arch/x86/platform/ts5500/ts5500.c (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-platform-ts5500 b/Documentation/ABI/testing/sysfs-platform-ts5500 new file mode 100644 index 000000000000..c88375a537a1 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-ts5500 @@ -0,0 +1,47 @@ +What: /sys/devices/platform/ts5500/adc +Date: January 2013 +KernelVersion: 3.7 +Contact: "Savoir-faire Linux Inc." +Description: + Indicates the presence of an A/D Converter. If it is present, + it will display "1", otherwise "0". + +What: /sys/devices/platform/ts5500/ereset +Date: January 2013 +KernelVersion: 3.7 +Contact: "Savoir-faire Linux Inc." +Description: + Indicates the presence of an external reset. If it is present, + it will display "1", otherwise "0". + +What: /sys/devices/platform/ts5500/id +Date: January 2013 +KernelVersion: 3.7 +Contact: "Savoir-faire Linux Inc." +Description: + Product ID of the TS board. TS-5500 ID is 0x60. + +What: /sys/devices/platform/ts5500/jumpers +Date: January 2013 +KernelVersion: 3.7 +Contact: "Savoir-faire Linux Inc." +Description: + Bitfield showing the jumpers' state. If a jumper is present, + the corresponding bit is set. For instance, 0x0e means jumpers + 2, 3 and 4 are set. + +What: /sys/devices/platform/ts5500/rs485 +Date: January 2013 +KernelVersion: 3.7 +Contact: "Savoir-faire Linux Inc." +Description: + Indicates the presence of the RS485 option. If it is present, + it will display "1", otherwise "0". + +What: /sys/devices/platform/ts5500/sram +Date: January 2013 +KernelVersion: 3.7 +Contact: "Savoir-faire Linux Inc." +Description: + Indicates the presence of the SRAM option. If it is present, + it will display "1", otherwise "0". diff --git a/MAINTAINERS b/MAINTAINERS index 3105c4868c4e..261704012240 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7543,6 +7543,11 @@ F: drivers/net/team/ F: include/linux/if_team.h F: include/uapi/linux/if_team.h +TECHNOLOGIC SYSTEMS TS-5500 PLATFORM SUPPORT +M: Savoir-faire Linux Inc. +S: Maintained +F: arch/x86/platform/ts5500/ + TECHNOTREND USB IR RECEIVER M: Sean Young L: linux-media@vger.kernel.org diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 46fb28c93f76..0709e34ce8f9 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2199,6 +2199,15 @@ config GEOS ---help--- This option enables system support for the Traverse Technologies GEOS. +config TS5500 + bool "Technologic Systems TS-5500 platform support" + depends on MELAN + select CHECK_SIGNATURE + select NEW_LEDS + select LEDS_CLASS + ---help--- + This option enables system support for the Technologic Systems TS-5500. + endif # X86_32 config AMD_NB diff --git a/arch/x86/platform/Makefile b/arch/x86/platform/Makefile index bfe917f4f69a..01e0231a113e 100644 --- a/arch/x86/platform/Makefile +++ b/arch/x86/platform/Makefile @@ -8,5 +8,6 @@ obj-y += mrst/ obj-y += olpc/ obj-y += scx200/ obj-y += sfi/ +obj-y += ts5500/ obj-y += visws/ obj-y += uv/ diff --git a/arch/x86/platform/ts5500/Makefile b/arch/x86/platform/ts5500/Makefile new file mode 100644 index 000000000000..c54e348c96a7 --- /dev/null +++ b/arch/x86/platform/ts5500/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_TS5500) += ts5500.o diff --git a/arch/x86/platform/ts5500/ts5500.c b/arch/x86/platform/ts5500/ts5500.c new file mode 100644 index 000000000000..39febb214e8c --- /dev/null +++ b/arch/x86/platform/ts5500/ts5500.c @@ -0,0 +1,339 @@ +/* + * Technologic Systems TS-5500 Single Board Computer support + * + * Copyright (C) 2013 Savoir-faire Linux Inc. + * Vivien Didelot + * + * 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 driver registers the Technologic Systems TS-5500 Single Board Computer + * (SBC) and its devices, and exposes information to userspace such as jumpers' + * state or available options. For further information about sysfs entries, see + * Documentation/ABI/testing/sysfs-platform-ts5500. + * + * This code actually supports the TS-5500 platform, but it may be extended to + * support similar Technologic Systems x86-based platforms, such as the TS-5600. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Product code register */ +#define TS5500_PRODUCT_CODE_ADDR 0x74 +#define TS5500_PRODUCT_CODE 0x60 /* TS-5500 product code */ + +/* SRAM/RS-485/ADC options, and RS-485 RTS/Automatic RS-485 flags register */ +#define TS5500_SRAM_RS485_ADC_ADDR 0x75 +#define TS5500_SRAM BIT(0) /* SRAM option */ +#define TS5500_RS485 BIT(1) /* RS-485 option */ +#define TS5500_ADC BIT(2) /* A/D converter option */ +#define TS5500_RS485_RTS BIT(6) /* RTS for RS-485 */ +#define TS5500_RS485_AUTO BIT(7) /* Automatic RS-485 */ + +/* External Reset/Industrial Temperature Range options register */ +#define TS5500_ERESET_ITR_ADDR 0x76 +#define TS5500_ERESET BIT(0) /* External Reset option */ +#define TS5500_ITR BIT(1) /* Indust. Temp. Range option */ + +/* LED/Jumpers register */ +#define TS5500_LED_JP_ADDR 0x77 +#define TS5500_LED BIT(0) /* LED flag */ +#define TS5500_JP1 BIT(1) /* Automatic CMOS */ +#define TS5500_JP2 BIT(2) /* Enable Serial Console */ +#define TS5500_JP3 BIT(3) /* Write Enable Drive A */ +#define TS5500_JP4 BIT(4) /* Fast Console (115K baud) */ +#define TS5500_JP5 BIT(5) /* User Jumper */ +#define TS5500_JP6 BIT(6) /* Console on COM1 (req. JP2) */ +#define TS5500_JP7 BIT(7) /* Undocumented (Unused) */ + +/* A/D Converter registers */ +#define TS5500_ADC_CONV_BUSY_ADDR 0x195 /* Conversion state register */ +#define TS5500_ADC_CONV_BUSY BIT(0) +#define TS5500_ADC_CONV_INIT_LSB_ADDR 0x196 /* Start conv. / LSB register */ +#define TS5500_ADC_CONV_MSB_ADDR 0x197 /* MSB register */ +#define TS5500_ADC_CONV_DELAY 12 /* usec */ + +/** + * struct ts5500_sbc - TS-5500 board description + * @id: Board product ID. + * @sram: Flag for SRAM option. + * @rs485: Flag for RS-485 option. + * @adc: Flag for Analog/Digital converter option. + * @ereset: Flag for External Reset option. + * @itr: Flag for Industrial Temperature Range option. + * @jumpers: Bitfield for jumpers' state. + */ +struct ts5500_sbc { + int id; + bool sram; + bool rs485; + bool adc; + bool ereset; + bool itr; + u8 jumpers; +}; + +/* Board signatures in BIOS shadow RAM */ +static const struct { + const char * const string; + const ssize_t offset; +} ts5500_signatures[] __initdata = { + { "TS-5x00 AMD Elan", 0xb14 }, +}; + +static int __init ts5500_check_signature(void) +{ + void __iomem *bios; + int i, ret = -ENODEV; + + bios = ioremap(0xf0000, 0x10000); + if (!bios) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(ts5500_signatures); i++) { + if (check_signature(bios + ts5500_signatures[i].offset, + ts5500_signatures[i].string, + strlen(ts5500_signatures[i].string))) { + ret = 0; + break; + } + } + + iounmap(bios); + return ret; +} + +static int __init ts5500_detect_config(struct ts5500_sbc *sbc) +{ + u8 tmp; + int ret = 0; + + if (!request_region(TS5500_PRODUCT_CODE_ADDR, 4, "ts5500")) + return -EBUSY; + + tmp = inb(TS5500_PRODUCT_CODE_ADDR); + if (tmp != TS5500_PRODUCT_CODE) { + pr_err("This platform is not a TS-5500 (found ID 0x%x)\n", tmp); + ret = -ENODEV; + goto cleanup; + } + sbc->id = tmp; + + tmp = inb(TS5500_SRAM_RS485_ADC_ADDR); + sbc->sram = tmp & TS5500_SRAM; + sbc->rs485 = tmp & TS5500_RS485; + sbc->adc = tmp & TS5500_ADC; + + tmp = inb(TS5500_ERESET_ITR_ADDR); + sbc->ereset = tmp & TS5500_ERESET; + sbc->itr = tmp & TS5500_ITR; + + tmp = inb(TS5500_LED_JP_ADDR); + sbc->jumpers = tmp & ~TS5500_LED; + +cleanup: + release_region(TS5500_PRODUCT_CODE_ADDR, 4); + return ret; +} + +static ssize_t ts5500_show_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ts5500_sbc *sbc = dev_get_drvdata(dev); + + return sprintf(buf, "0x%.2x\n", sbc->id); +} + +static ssize_t ts5500_show_jumpers(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ts5500_sbc *sbc = dev_get_drvdata(dev); + + return sprintf(buf, "0x%.2x\n", sbc->jumpers >> 1); +} + +#define TS5500_SHOW(field) \ + static ssize_t ts5500_show_##field(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ + { \ + struct ts5500_sbc *sbc = dev_get_drvdata(dev); \ + return sprintf(buf, "%d\n", sbc->field); \ + } + +TS5500_SHOW(sram) +TS5500_SHOW(rs485) +TS5500_SHOW(adc) +TS5500_SHOW(ereset) +TS5500_SHOW(itr) + +static DEVICE_ATTR(id, S_IRUGO, ts5500_show_id, NULL); +static DEVICE_ATTR(jumpers, S_IRUGO, ts5500_show_jumpers, NULL); +static DEVICE_ATTR(sram, S_IRUGO, ts5500_show_sram, NULL); +static DEVICE_ATTR(rs485, S_IRUGO, ts5500_show_rs485, NULL); +static DEVICE_ATTR(adc, S_IRUGO, ts5500_show_adc, NULL); +static DEVICE_ATTR(ereset, S_IRUGO, ts5500_show_ereset, NULL); +static DEVICE_ATTR(itr, S_IRUGO, ts5500_show_itr, NULL); + +static struct attribute *ts5500_attributes[] = { + &dev_attr_id.attr, + &dev_attr_jumpers.attr, + &dev_attr_sram.attr, + &dev_attr_rs485.attr, + &dev_attr_adc.attr, + &dev_attr_ereset.attr, + &dev_attr_itr.attr, + NULL +}; + +static const struct attribute_group ts5500_attr_group = { + .attrs = ts5500_attributes, +}; + +static struct resource ts5500_dio1_resource[] = { + DEFINE_RES_IRQ_NAMED(7, "DIO1 interrupt"), +}; + +static struct platform_device ts5500_dio1_pdev = { + .name = "ts5500-dio1", + .id = -1, + .resource = ts5500_dio1_resource, + .num_resources = 1, +}; + +static struct resource ts5500_dio2_resource[] = { + DEFINE_RES_IRQ_NAMED(6, "DIO2 interrupt"), +}; + +static struct platform_device ts5500_dio2_pdev = { + .name = "ts5500-dio2", + .id = -1, + .resource = ts5500_dio2_resource, + .num_resources = 1, +}; + +static void ts5500_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + outb(!!brightness, TS5500_LED_JP_ADDR); +} + +static enum led_brightness ts5500_led_get(struct led_classdev *led_cdev) +{ + return (inb(TS5500_LED_JP_ADDR) & TS5500_LED) ? LED_FULL : LED_OFF; +} + +static struct led_classdev ts5500_led_cdev = { + .name = "ts5500:green:", + .brightness_set = ts5500_led_set, + .brightness_get = ts5500_led_get, +}; + +static int ts5500_adc_convert(u8 ctrl) +{ + u8 lsb, msb; + + /* Start conversion (ensure the 3 MSB are set to 0) */ + outb(ctrl & 0x1f, TS5500_ADC_CONV_INIT_LSB_ADDR); + + /* + * The platform has CPLD logic driving the A/D converter. + * The conversion must complete within 11 microseconds, + * otherwise we have to re-initiate a conversion. + */ + udelay(TS5500_ADC_CONV_DELAY); + if (inb(TS5500_ADC_CONV_BUSY_ADDR) & TS5500_ADC_CONV_BUSY) + return -EBUSY; + + /* Read the raw data */ + lsb = inb(TS5500_ADC_CONV_INIT_LSB_ADDR); + msb = inb(TS5500_ADC_CONV_MSB_ADDR); + + return (msb << 8) | lsb; +} + +static struct max197_platform_data ts5500_adc_pdata = { + .convert = ts5500_adc_convert, +}; + +static struct platform_device ts5500_adc_pdev = { + .name = "max197", + .id = -1, + .dev = { + .platform_data = &ts5500_adc_pdata, + }, +}; + +static int __init ts5500_init(void) +{ + struct platform_device *pdev; + struct ts5500_sbc *sbc; + int err; + + /* + * There is no DMI available or PCI bridge subvendor info, + * only the BIOS provides a 16-bit identification call. + * It is safer to find a signature in the BIOS shadow RAM. + */ + err = ts5500_check_signature(); + if (err) + return err; + + pdev = platform_device_register_simple("ts5500", -1, NULL, 0); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + sbc = devm_kzalloc(&pdev->dev, sizeof(struct ts5500_sbc), GFP_KERNEL); + if (!sbc) { + err = -ENOMEM; + goto error; + } + + err = ts5500_detect_config(sbc); + if (err) + goto error; + + platform_set_drvdata(pdev, sbc); + + err = sysfs_create_group(&pdev->dev.kobj, &ts5500_attr_group); + if (err) + goto error; + + ts5500_dio1_pdev.dev.parent = &pdev->dev; + if (platform_device_register(&ts5500_dio1_pdev)) + dev_warn(&pdev->dev, "DIO1 block registration failed\n"); + ts5500_dio2_pdev.dev.parent = &pdev->dev; + if (platform_device_register(&ts5500_dio2_pdev)) + dev_warn(&pdev->dev, "DIO2 block registration failed\n"); + + if (led_classdev_register(&pdev->dev, &ts5500_led_cdev)) + dev_warn(&pdev->dev, "LED registration failed\n"); + + if (sbc->adc) { + ts5500_adc_pdev.dev.parent = &pdev->dev; + if (platform_device_register(&ts5500_adc_pdev)) + dev_warn(&pdev->dev, "ADC registration failed\n"); + } + + return 0; +error: + platform_device_unregister(pdev); + return err; +} +device_initcall(ts5500_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Savoir-faire Linux Inc. "); +MODULE_DESCRIPTION("Technologic Systems TS-5500 platform driver"); -- cgit v1.2.3-59-g8ed1b From 18a387099b3e3fd901cc706f708b163aa45347b6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 25 Jan 2013 21:51:32 +0100 Subject: ACPI / PM: Expose lists of device power resources to user space Since ACPI power resources are going to be used more extensively on new hardware platforms, it is necessary to allow user space (powertop in particular) to look at the lists of power resources corresponding to different power states of devices for diagnostics and control purposes. For this reason, for each power state of an ACPI device node using power resources create a special attribute group under the device node's directory in sysfs containing links to sysfs directories representing the power resources in that list. The names of the new attribute groups are "power_resources_", where is the state name i.e. "D0", "D1", "D2", or "D3hot". Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman --- .../ABI/testing/sysfs-devices-power_resources_D0 | 13 +++ .../ABI/testing/sysfs-devices-power_resources_D1 | 14 +++ .../ABI/testing/sysfs-devices-power_resources_D2 | 14 +++ .../testing/sysfs-devices-power_resources_D3hot | 14 +++ drivers/acpi/power.c | 104 ++++++++++++++++++--- 5 files changed, 146 insertions(+), 13 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-devices-power_resources_D0 create mode 100644 Documentation/ABI/testing/sysfs-devices-power_resources_D1 create mode 100644 Documentation/ABI/testing/sysfs-devices-power_resources_D2 create mode 100644 Documentation/ABI/testing/sysfs-devices-power_resources_D3hot (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-devices-power_resources_D0 b/Documentation/ABI/testing/sysfs-devices-power_resources_D0 new file mode 100644 index 000000000000..73b77a6be196 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-power_resources_D0 @@ -0,0 +1,13 @@ +What: /sys/devices/.../power_resources_D0/ +Date: January 2013 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../power_resources_D0/ directory is only + present for device objects representing ACPI device nodes that + use ACPI power resources for power management. + + If present, it contains symbolic links to device directories + representing ACPI power resources that need to be turned on for + the given device node to be in ACPI power state D0. The names + of the links are the same as the names of the directories they + point to. diff --git a/Documentation/ABI/testing/sysfs-devices-power_resources_D1 b/Documentation/ABI/testing/sysfs-devices-power_resources_D1 new file mode 100644 index 000000000000..30c20703fb8c --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-power_resources_D1 @@ -0,0 +1,14 @@ +What: /sys/devices/.../power_resources_D1/ +Date: January 2013 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../power_resources_D1/ directory is only + present for device objects representing ACPI device nodes that + use ACPI power resources for power management and support ACPI + power state D1. + + If present, it contains symbolic links to device directories + representing ACPI power resources that need to be turned on for + the given device node to be in ACPI power state D1. The names + of the links are the same as the names of the directories they + point to. diff --git a/Documentation/ABI/testing/sysfs-devices-power_resources_D2 b/Documentation/ABI/testing/sysfs-devices-power_resources_D2 new file mode 100644 index 000000000000..fd9d84b421e1 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-power_resources_D2 @@ -0,0 +1,14 @@ +What: /sys/devices/.../power_resources_D2/ +Date: January 2013 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../power_resources_D2/ directory is only + present for device objects representing ACPI device nodes that + use ACPI power resources for power management and support ACPI + power state D2. + + If present, it contains symbolic links to device directories + representing ACPI power resources that need to be turned on for + the given device node to be in ACPI power state D2. The names + of the links are the same as the names of the directories they + point to. diff --git a/Documentation/ABI/testing/sysfs-devices-power_resources_D3hot b/Documentation/ABI/testing/sysfs-devices-power_resources_D3hot new file mode 100644 index 000000000000..3df32c20addf --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-power_resources_D3hot @@ -0,0 +1,14 @@ +What: /sys/devices/.../power_resources_D3hot/ +Date: January 2013 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../power_resources_D3hot/ directory is only + present for device objects representing ACPI device nodes that + use ACPI power resources for power management and support ACPI + power state D3hot. + + If present, it contains symbolic links to device directories + representing ACPI power resources that need to be turned on for + the given device node to be in ACPI power state D3hot. The + names of the links are the same as the names of the directories + they point to. diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 946720a4db57..9466f56b938f 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include "sleep.h" @@ -417,24 +418,101 @@ static void acpi_power_remove_dependent(struct acpi_power_resource *resource, } } -void acpi_power_add_remove_device(struct acpi_device *adev, bool add) +static struct attribute *attrs[] = { + NULL, +}; + +static struct attribute_group attr_groups[] = { + [ACPI_STATE_D0] = { + .name = "power_resources_D0", + .attrs = attrs, + }, + [ACPI_STATE_D1] = { + .name = "power_resources_D1", + .attrs = attrs, + }, + [ACPI_STATE_D2] = { + .name = "power_resources_D2", + .attrs = attrs, + }, + [ACPI_STATE_D3_HOT] = { + .name = "power_resources_D3hot", + .attrs = attrs, + }, +}; + +static void acpi_power_hide_list(struct acpi_device *adev, int state) +{ + struct acpi_device_power_state *ps = &adev->power.states[state]; + struct acpi_power_resource_entry *entry; + + if (list_empty(&ps->resources)) + return; + + list_for_each_entry_reverse(entry, &ps->resources, node) { + struct acpi_device *res_dev = &entry->resource->device; + + sysfs_remove_link_from_group(&adev->dev.kobj, + attr_groups[state].name, + dev_name(&res_dev->dev)); + } + sysfs_remove_group(&adev->dev.kobj, &attr_groups[state]); +} + +static void acpi_power_expose_list(struct acpi_device *adev, int state) { - if (adev->power.flags.power_resources) { - struct acpi_device_power_state *ps; - struct acpi_power_resource_entry *entry; - - ps = &adev->power.states[ACPI_STATE_D0]; - list_for_each_entry(entry, &ps->resources, node) { - struct acpi_power_resource *resource = entry->resource; - - if (add) - acpi_power_add_dependent(resource, adev); - else - acpi_power_remove_dependent(resource, adev); + struct acpi_device_power_state *ps = &adev->power.states[state]; + struct acpi_power_resource_entry *entry; + int ret; + + if (list_empty(&ps->resources)) + return; + + ret = sysfs_create_group(&adev->dev.kobj, &attr_groups[state]); + if (ret) + return; + + list_for_each_entry(entry, &ps->resources, node) { + struct acpi_device *res_dev = &entry->resource->device; + + ret = sysfs_add_link_to_group(&adev->dev.kobj, + attr_groups[state].name, + &res_dev->dev.kobj, + dev_name(&res_dev->dev)); + if (ret) { + acpi_power_hide_list(adev, state); + break; } } } +void acpi_power_add_remove_device(struct acpi_device *adev, bool add) +{ + struct acpi_device_power_state *ps; + struct acpi_power_resource_entry *entry; + int state; + + if (!adev->power.flags.power_resources) + return; + + ps = &adev->power.states[ACPI_STATE_D0]; + list_for_each_entry(entry, &ps->resources, node) { + struct acpi_power_resource *resource = entry->resource; + + if (add) + acpi_power_add_dependent(resource, adev); + else + acpi_power_remove_dependent(resource, adev); + } + + for (state = ACPI_STATE_D0; state <= ACPI_STATE_D3_HOT; state++) { + if (add) + acpi_power_expose_list(adev, state); + else + acpi_power_hide_list(adev, state); + } +} + int acpi_power_min_system_level(struct list_head *list) { struct acpi_power_resource_entry *entry; -- cgit v1.2.3-59-g8ed1b From 43720bd6014327ac454434496cb953edcdb9f8d6 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Fri, 11 Jan 2013 13:43:45 +0100 Subject: PM / tracing: remove deprecated power trace API The text in Documentation said it would be removed in 2.6.41; the text in the Kconfig said removal in the 3.1 release. Either way you look at it, we are well past both, so push it off a cliff. Note that the POWER_CSTATE and the POWER_PSTATE are part of the legacy tracing API. Remove all tracepoints which use these flags. As can be seen from context, most already have a trace entry via trace_cpu_idle anyways. Also, the cpufreq/cpufreq.c PSTATE one is actually unpaired, as compared to the CSTATE ones which all have a clear start/stop. As part of this, the trace_power_frequency also becomes orphaned, so it too is deleted. Signed-off-by: Paul Gortmaker Acked-by: Steven Rostedt Signed-off-by: Rafael J. Wysocki --- Documentation/trace/events-power.txt | 27 +---------- arch/arm/mach-omap2/pm34xx.c | 2 - arch/x86/kernel/process.c | 6 --- drivers/cpufreq/cpufreq.c | 1 - drivers/cpuidle/cpuidle.c | 2 - include/trace/events/power.h | 92 ------------------------------------ kernel/trace/Kconfig | 15 ------ kernel/trace/power-traces.c | 3 -- 8 files changed, 1 insertion(+), 147 deletions(-) (limited to 'Documentation') diff --git a/Documentation/trace/events-power.txt b/Documentation/trace/events-power.txt index cf794af22855..e1498ff8cf94 100644 --- a/Documentation/trace/events-power.txt +++ b/Documentation/trace/events-power.txt @@ -17,7 +17,7 @@ Cf. include/trace/events/power.h for the events definitions. 1. Power state switch events ============================ -1.1 New trace API +1.1 Trace API ----------------- A 'cpu' event class gathers the CPU-related events: cpuidle and @@ -41,31 +41,6 @@ The event which has 'state=4294967295' in the trace is very important to the use space tools which are using it to detect the end of the current state, and so to correctly draw the states diagrams and to calculate accurate statistics etc. -1.2 DEPRECATED trace API ------------------------- - -A new Kconfig option CONFIG_EVENT_POWER_TRACING_DEPRECATED with the default value of -'y' has been created. This allows the legacy trace power API to be used conjointly -with the new trace API. -The Kconfig option, the old trace API (in include/trace/events/power.h) and the -old trace points will disappear in a future release (namely 2.6.41). - -power_start "type=%lu state=%lu cpu_id=%lu" -power_frequency "type=%lu state=%lu cpu_id=%lu" -power_end "cpu_id=%lu" - -The 'type' parameter takes one of those macros: - . POWER_NONE = 0, - . POWER_CSTATE = 1, /* C-State */ - . POWER_PSTATE = 2, /* Frequency change or DVFS */ - -The 'state' parameter is set depending on the type: - . Target C-state for type=POWER_CSTATE, - . Target frequency for type=POWER_PSTATE, - -power_end is used to indicate the exit of a state, corresponding to the latest -power_start event. - 2. Clocks events ================ The clock events are used for clock enable/disable and for diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index 7be3622cfc85..2d93d8b23835 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -351,12 +351,10 @@ static void omap3_pm_idle(void) if (omap_irq_pending()) goto out; - trace_power_start(POWER_CSTATE, 1, smp_processor_id()); trace_cpu_idle(1, smp_processor_id()); omap_sram_idle(); - trace_power_end(smp_processor_id()); trace_cpu_idle(PWR_EVENT_EXIT, smp_processor_id()); out: diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 2ed787f15bf0..dcfc1f410dc4 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -375,7 +375,6 @@ void cpu_idle(void) */ void default_idle(void) { - trace_power_start_rcuidle(POWER_CSTATE, 1, smp_processor_id()); trace_cpu_idle_rcuidle(1, smp_processor_id()); current_thread_info()->status &= ~TS_POLLING; /* @@ -389,7 +388,6 @@ void default_idle(void) else local_irq_enable(); current_thread_info()->status |= TS_POLLING; - trace_power_end_rcuidle(smp_processor_id()); trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, smp_processor_id()); } #ifdef CONFIG_APM_MODULE @@ -423,7 +421,6 @@ void stop_this_cpu(void *dummy) static void mwait_idle(void) { if (!need_resched()) { - trace_power_start_rcuidle(POWER_CSTATE, 1, smp_processor_id()); trace_cpu_idle_rcuidle(1, smp_processor_id()); if (this_cpu_has(X86_FEATURE_CLFLUSH_MONITOR)) clflush((void *)¤t_thread_info()->flags); @@ -434,7 +431,6 @@ static void mwait_idle(void) __sti_mwait(0, 0); else local_irq_enable(); - trace_power_end_rcuidle(smp_processor_id()); trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, smp_processor_id()); } else local_irq_enable(); @@ -447,12 +443,10 @@ static void mwait_idle(void) */ static void poll_idle(void) { - trace_power_start_rcuidle(POWER_CSTATE, 0, smp_processor_id()); trace_cpu_idle_rcuidle(0, smp_processor_id()); local_irq_enable(); while (!need_resched()) cpu_relax(); - trace_power_end_rcuidle(smp_processor_id()); trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, smp_processor_id()); } diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 1f93dbd72355..99faadf454ec 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -294,7 +294,6 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) adjust_jiffies(CPUFREQ_POSTCHANGE, freqs); pr_debug("FREQ: %lu - CPU: %lu", (unsigned long)freqs->new, (unsigned long)freqs->cpu); - trace_power_frequency(POWER_PSTATE, freqs->new, freqs->cpu); trace_cpu_frequency(freqs->new, freqs->cpu); srcu_notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_POSTCHANGE, freqs); diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index e1f6860e069c..eba69290e074 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -144,7 +144,6 @@ int cpuidle_idle_call(void) return 0; } - trace_power_start_rcuidle(POWER_CSTATE, next_state, dev->cpu); trace_cpu_idle_rcuidle(next_state, dev->cpu); if (cpuidle_state_is_coupled(dev, drv, next_state)) @@ -153,7 +152,6 @@ int cpuidle_idle_call(void) else entered_state = cpuidle_enter_state(dev, drv, next_state); - trace_power_end_rcuidle(dev->cpu); trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); /* give the governor an opportunity to reflect on the outcome */ diff --git a/include/trace/events/power.h b/include/trace/events/power.h index 0c9783841a30..427acab5d69a 100644 --- a/include/trace/events/power.h +++ b/include/trace/events/power.h @@ -99,98 +99,6 @@ DEFINE_EVENT(wakeup_source, wakeup_source_deactivate, TP_ARGS(name, state) ); -#ifdef CONFIG_EVENT_POWER_TRACING_DEPRECATED - -/* - * The power events are used for cpuidle & suspend (power_start, power_end) - * and for cpufreq (power_frequency) - */ -DECLARE_EVENT_CLASS(power, - - TP_PROTO(unsigned int type, unsigned int state, unsigned int cpu_id), - - TP_ARGS(type, state, cpu_id), - - TP_STRUCT__entry( - __field( u64, type ) - __field( u64, state ) - __field( u64, cpu_id ) - ), - - TP_fast_assign( - __entry->type = type; - __entry->state = state; - __entry->cpu_id = cpu_id; - ), - - TP_printk("type=%lu state=%lu cpu_id=%lu", (unsigned long)__entry->type, - (unsigned long)__entry->state, (unsigned long)__entry->cpu_id) -); - -DEFINE_EVENT(power, power_start, - - TP_PROTO(unsigned int type, unsigned int state, unsigned int cpu_id), - - TP_ARGS(type, state, cpu_id) -); - -DEFINE_EVENT(power, power_frequency, - - TP_PROTO(unsigned int type, unsigned int state, unsigned int cpu_id), - - TP_ARGS(type, state, cpu_id) -); - -TRACE_EVENT(power_end, - - TP_PROTO(unsigned int cpu_id), - - TP_ARGS(cpu_id), - - TP_STRUCT__entry( - __field( u64, cpu_id ) - ), - - TP_fast_assign( - __entry->cpu_id = cpu_id; - ), - - TP_printk("cpu_id=%lu", (unsigned long)__entry->cpu_id) - -); - -/* Deprecated dummy functions must be protected against multi-declartion */ -#ifndef _PWR_EVENT_AVOID_DOUBLE_DEFINING_DEPRECATED -#define _PWR_EVENT_AVOID_DOUBLE_DEFINING_DEPRECATED - -enum { - POWER_NONE = 0, - POWER_CSTATE = 1, - POWER_PSTATE = 2, -}; -#endif /* _PWR_EVENT_AVOID_DOUBLE_DEFINING_DEPRECATED */ - -#else /* CONFIG_EVENT_POWER_TRACING_DEPRECATED */ - -#ifndef _PWR_EVENT_AVOID_DOUBLE_DEFINING_DEPRECATED -#define _PWR_EVENT_AVOID_DOUBLE_DEFINING_DEPRECATED -enum { - POWER_NONE = 0, - POWER_CSTATE = 1, - POWER_PSTATE = 2, -}; - -/* These dummy declaration have to be ripped out when the deprecated - events get removed */ -static inline void trace_power_start(u64 type, u64 state, u64 cpuid) {}; -static inline void trace_power_end(u64 cpuid) {}; -static inline void trace_power_start_rcuidle(u64 type, u64 state, u64 cpuid) {}; -static inline void trace_power_end_rcuidle(u64 cpuid) {}; -static inline void trace_power_frequency(u64 type, u64 state, u64 cpuid) {}; -#endif /* _PWR_EVENT_AVOID_DOUBLE_DEFINING_DEPRECATED */ - -#endif /* CONFIG_EVENT_POWER_TRACING_DEPRECATED */ - /* * The clock events are used for clock enable/disable and for * clock rate change diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 5d89335a485f..ad0a067ad4b3 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -78,21 +78,6 @@ config EVENT_TRACING select CONTEXT_SWITCH_TRACER bool -config EVENT_POWER_TRACING_DEPRECATED - depends on EVENT_TRACING - bool "Deprecated power event trace API, to be removed" - default y - help - Provides old power event types: - C-state/idle accounting events: - power:power_start - power:power_end - and old cpufreq accounting event: - power:power_frequency - This is for userspace compatibility - and will vanish after 5 kernel iterations, - namely 3.1. - config CONTEXT_SWITCH_TRACER bool diff --git a/kernel/trace/power-traces.c b/kernel/trace/power-traces.c index f55fcf61b223..1c71382b283d 100644 --- a/kernel/trace/power-traces.c +++ b/kernel/trace/power-traces.c @@ -13,8 +13,5 @@ #define CREATE_TRACE_POINTS #include -#ifdef EVENT_POWER_TRACING_DEPRECATED -EXPORT_TRACEPOINT_SYMBOL_GPL(power_start); -#endif EXPORT_TRACEPOINT_SYMBOL_GPL(cpu_idle); -- cgit v1.2.3-59-g8ed1b From fbadc58dd3a52c330c8f3926aa93011bf9d91fa0 Mon Sep 17 00:00:00 2001 From: ShuoX Liu Date: Wed, 23 Jan 2013 21:49:37 +0100 Subject: PM / Runtime: Add new helper function: pm_runtime_active() This boolean function simply returns whether or not the runtime status of the device is 'active'. The typical scenario is driver calls pm_runtime_get firstly, then check pm_runtime_active in atomic environment. Also add entry to Documentation/power/runtime.txt Signed-off-by: Yanmin Zhang Signed-off-by: ShuoX Liu Signed-off-by: Rafael J. Wysocki --- Documentation/power/runtime_pm.txt | 4 ++++ include/linux/pm_runtime.h | 7 +++++++ 2 files changed, 11 insertions(+) (limited to 'Documentation') diff --git a/Documentation/power/runtime_pm.txt b/Documentation/power/runtime_pm.txt index 03591a750f99..6c9f5d9aa115 100644 --- a/Documentation/power/runtime_pm.txt +++ b/Documentation/power/runtime_pm.txt @@ -426,6 +426,10 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h: 'power.runtime_error' is set or 'power.disable_depth' is greater than zero) + bool pm_runtime_active(struct device *dev); + - return true if the device's runtime PM status is 'active' or its + 'power.disable_depth' field is not equal to zero, or false otherwise + bool pm_runtime_suspended(struct device *dev); - return true if the device's runtime PM status is 'suspended' and its 'power.disable_depth' field is equal to zero, or false otherwise diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index f271860c78d5..c785c215abfc 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -80,6 +80,12 @@ static inline bool pm_runtime_suspended(struct device *dev) && !dev->power.disable_depth; } +static inline bool pm_runtime_active(struct device *dev) +{ + return dev->power.runtime_status == RPM_ACTIVE + || dev->power.disable_depth; +} + static inline bool pm_runtime_status_suspended(struct device *dev) { return dev->power.runtime_status == RPM_SUSPENDED; @@ -132,6 +138,7 @@ static inline void pm_runtime_put_noidle(struct device *dev) {} static inline bool device_run_wake(struct device *dev) { return false; } static inline void device_set_run_wake(struct device *dev, bool enable) {} static inline bool pm_runtime_suspended(struct device *dev) { return false; } +static inline bool pm_runtime_active(struct device *dev) { return true; } static inline bool pm_runtime_status_suspended(struct device *dev) { return false; } static inline bool pm_runtime_enabled(struct device *dev) { return false; } -- cgit v1.2.3-59-g8ed1b From c25fb816298138a4b12c606f0aaa018bdd3cc09c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 9 Jan 2013 08:12:46 -0800 Subject: hwmon: Retire SENSORS_LIMIT SENSORS_LIMIT and clamp_val have the same functionality, so retire SENSORS_LIMIT as it is no longer needed. Signed-off-by: Guenter Roeck Acked-by: Jean Delvare --- Documentation/hwmon/sysfs-interface | 8 ++++---- include/linux/hwmon.h | 12 ------------ 2 files changed, 4 insertions(+), 16 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface index 1f4dd855a299..79f8257dd790 100644 --- a/Documentation/hwmon/sysfs-interface +++ b/Documentation/hwmon/sysfs-interface @@ -722,14 +722,14 @@ add/subtract if it has been divided before the add/subtract. What to do if a value is found to be invalid, depends on the type of the sysfs attribute that is being set. If it is a continuous setting like a tempX_max or inX_max attribute, then the value should be clamped to its -limits using SENSORS_LIMIT(value, min_limit, max_limit). If it is not -continuous like for example a tempX_type, then when an invalid value is -written, -EINVAL should be returned. +limits using clamp_val(value, min_limit, max_limit). If it is not continuous +like for example a tempX_type, then when an invalid value is written, +-EINVAL should be returned. Example1, temp1_max, register is a signed 8 bit value (-128 - 127 degrees): long v = simple_strtol(buf, NULL, 10) / 1000; - v = SENSORS_LIMIT(v, -128, 127); + v = clamp_val(v, -128, 127); /* write v to register */ Example2, fan divider setting, valid values 2, 4 and 8: diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h index 82b29ae6ebb0..b2514f70d591 100644 --- a/include/linux/hwmon.h +++ b/include/linux/hwmon.h @@ -20,16 +20,4 @@ struct device *hwmon_device_register(struct device *dev); void hwmon_device_unregister(struct device *dev); -/* Scale user input to sensible values */ -static inline int SENSORS_LIMIT(long value, long low, long high) -{ - if (value < low) - return low; - else if (value > high) - return high; - else - return value; -} - #endif - -- cgit v1.2.3-59-g8ed1b From 9e3970fba9ea43bc2c215f71f78bcf766c1775e7 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 16 Nov 2012 21:55:24 -0800 Subject: hwmon: (coretemp) Document and add support for additional CPU models Cc: Fenghua Yu Signed-off-by: Guenter Roeck --- Documentation/hwmon/coretemp | 9 +++++++++ drivers/hwmon/coretemp.c | 5 +++-- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/coretemp b/Documentation/hwmon/coretemp index 3374c085678d..fec5a9bf755f 100644 --- a/Documentation/hwmon/coretemp +++ b/Documentation/hwmon/coretemp @@ -66,6 +66,7 @@ Process Processor TjMax(C) i5 3470T 91 32nm Core i3/i5/i7 Processors + i7 2600 98 i7 660UM/640/620, 640LM/620, 620M, 610E 105 i5 540UM/520/430, 540M/520/450/430 105 i3 330E, 370M/350/330 90 rPGA, 105 BGA @@ -79,7 +80,10 @@ Process Processor TjMax(C) P4505/P4500 90 32nm Atom Processors + S1260/1220 95 + S1240 102 Z2460 90 + Z2760 90 D2700/2550/2500 100 N2850/2800/2650/2600 100 @@ -98,6 +102,7 @@ Process Processor TjMax(C) 45nm Atom Processors D525/510/425/410 100 + K525/510/425/410 100 Z670/650 90 Z560/550/540/530P/530/520PT/520/515/510PT/510P 90 Z510/500 90 @@ -107,7 +112,11 @@ Process Processor TjMax(C) 330/230 125 E680/660/640/620 90 E680T/660T/640T/620T 110 + E665C/645C 90 + E665CT/645CT 110 CE4170/4150/4110 110 + CE4200 series unknown + CE5300 series unknown 45nm Core2 Processors Solo ULV SU3500/3300 100 diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index d64923d63537..3f1e297663ad 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -198,7 +198,7 @@ struct tjmax { static const struct tjmax __cpuinitconst tjmax_table[] = { { "CPU 230", 100000 }, /* Model 0x1c, stepping 2 */ { "CPU 330", 125000 }, /* Model 0x1c, stepping 2 */ - { "CPU CE4110", 110000 }, /* Model 0x1c, stepping 10 */ + { "CPU CE4110", 110000 }, /* Model 0x1c, stepping 10 Sodaville */ { "CPU CE4150", 110000 }, /* Model 0x1c, stepping 10 */ { "CPU CE4170", 110000 }, /* Model 0x1c, stepping 10 */ }; @@ -212,7 +212,7 @@ struct tjmax_model { #define ANY 0xff static const struct tjmax_model __cpuinitconst tjmax_model_table[] = { - { 0x1c, 10, 100000 }, /* D4xx, N4xx, D5xx, N5xx */ + { 0x1c, 10, 100000 }, /* D4xx, K4xx, N4xx, D5xx, K5xx, N5xx */ { 0x1c, ANY, 90000 }, /* Z5xx, N2xx, possibly others * Note: Also matches 230 and 330, * which are covered by tjmax_table @@ -222,6 +222,7 @@ static const struct tjmax_model __cpuinitconst tjmax_model_table[] = { * is undetectable by software */ { 0x27, ANY, 90000 }, /* Atom Medfield (Z2460) */ + { 0x35, ANY, 90000 }, /* Atom Clover Trail/Cloverview (Z2760) */ { 0x36, ANY, 100000 }, /* Atom Cedar Trail/Cedarview (N2xxx, D2xxx) */ }; -- cgit v1.2.3-59-g8ed1b From 6116ad94e1205b33a4c96530474844cd2a1ac21d Mon Sep 17 00:00:00 2001 From: "Vishwanathrao Badarkhe, Manish" Date: Thu, 24 Jan 2013 16:25:18 +0530 Subject: regulator: tps6507x: add device tree support. Add device tree based initialization support for TI's tps6507x regulators. Add device tree binding document for TI's tps6507x using datasheet: http://www.ti.com/lit/ds/symlink/tps65070.pdf Signed-off-by: Vishwanathrao Badarkhe, Manish Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/mfd/tps6507x.txt | 91 +++++++++++++++++++++ drivers/regulator/tps6507x-regulator.c | 92 ++++++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100755 Documentation/devicetree/bindings/mfd/tps6507x.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/mfd/tps6507x.txt b/Documentation/devicetree/bindings/mfd/tps6507x.txt new file mode 100755 index 000000000000..8fffa3c5ed40 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/tps6507x.txt @@ -0,0 +1,91 @@ +TPS6507x Power Management Integrated Circuit + +Required properties: +- compatible: "ti,tps6507x" +- reg: I2C slave address +- regulators: This is the list of child nodes that specify the regulator + initialization data for defined regulators. Not all regulators for the + given device need to be present. The definition for each of these nodes + is defined using the standard binding for regulators found at + Documentation/devicetree/bindings/regulator/regulator.txt. + The regulator is matched with the regulator-compatible. + + The valid regulator-compatible values are: + tps6507x: vdcdc1, vdcdc2, vdcdc3, vldo1, vldo2 +- xxx-supply: Input voltage supply regulator. + These entries are required if regulators are enabled for a device. + Missing of these properties can cause the regulator registration + fails. + If some of input supply is powered through battery or always-on + supply then also it is require to have these parameters with proper + node handle of always on power supply. + tps6507x: + vindcdc1_2-supply: VDCDC1 and VDCDC2 input. + vindcdc3-supply : VDCDC3 input. + vldo1_2-supply : VLDO1 and VLDO2 input. + +Regulator Optional properties: +- defdcdc_default: It's property of DCDC2 and DCDC3 regulators. + 0: If defdcdc pin of DCDC2/DCDC3 is pulled to GND. + 1: If defdcdc pin of DCDC2/DCDC3 is driven HIGH. + If this property is not defined, it defaults to 0 (not enabled). + +Example: + + pmu: tps6507x@48 { + compatible = "ti,tps6507x"; + reg = <0x48>; + + vindcdc1_2-supply = <&vbat>; + vindcdc3-supply = <...>; + vinldo1_2-supply = <...>; + + regulators { + #address-cells = <1>; + #size-cells = <0>; + + vdcdc1_reg: regulator@0 { + regulator-compatible = "VDCDC1"; + reg = <0>; + regulator-min-microvolt = <3150000>; + regulator-max-microvolt = <3450000>; + regulator-always-on; + regulator-boot-on; + }; + vdcdc2_reg: regulator@1 { + regulator-compatible = "VDCDC2"; + reg = <1>; + regulator-min-microvolt = <1710000>; + regulator-max-microvolt = <3450000>; + regulator-always-on; + regulator-boot-on; + defdcdc_default = <1>; + }; + vdcdc3_reg: regulator@2 { + regulator-compatible = "VDCDC3"; + reg = <2>; + regulator-min-microvolt = <950000> + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-boot-on; + defdcdc_default = <1>; + }; + ldo1_reg: regulator@3 { + regulator-compatible = "LDO1"; + reg = <3>; + regulator-min-microvolt = <1710000>; + regulator-max-microvolt = <1890000>; + regulator-always-on; + regulator-boot-on; + }; + ldo2_reg: regulator@4 { + regulator-compatible = "LDO2"; + reg = <4>; + regulator-min-microvolt = <1140000>; + regulator-max-microvolt = <1320000>; + regulator-always-on; + regulator-boot-on; + }; + }; + + }; diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index 0233cfb56560..afdeb65b6a4d 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -23,8 +23,10 @@ #include #include #include +#include #include #include +#include /* DCDC's */ #define TPS6507X_DCDC_1 0 @@ -356,6 +358,80 @@ static struct regulator_ops tps6507x_pmic_ops = { .list_voltage = regulator_list_voltage_table, }; +#ifdef CONFIG_OF +static struct of_regulator_match tps6507x_matches[] = { + { .name = "VDCDC1"}, + { .name = "VDCDC2"}, + { .name = "VDCDC3"}, + { .name = "LDO1"}, + { .name = "LDO2"}, +}; + +static struct tps6507x_board *tps6507x_parse_dt_reg_data( + struct platform_device *pdev, + struct of_regulator_match **tps6507x_reg_matches) +{ + struct tps6507x_board *tps_board; + struct device_node *np = pdev->dev.parent->of_node; + struct device_node *regulators; + struct of_regulator_match *matches; + static struct regulator_init_data *reg_data; + int idx = 0, count, ret; + + tps_board = devm_kzalloc(&pdev->dev, sizeof(*tps_board), + GFP_KERNEL); + if (!tps_board) { + dev_err(&pdev->dev, "Failure to alloc pdata for regulators.\n"); + return NULL; + } + + regulators = of_find_node_by_name(np, "regulators"); + if (!regulators) { + dev_err(&pdev->dev, "regulator node not found\n"); + return NULL; + } + + count = ARRAY_SIZE(tps6507x_matches); + matches = tps6507x_matches; + + ret = of_regulator_match(pdev->dev.parent, regulators, matches, count); + if (ret < 0) { + dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", + ret); + return NULL; + } + + *tps6507x_reg_matches = matches; + + reg_data = devm_kzalloc(&pdev->dev, (sizeof(struct regulator_init_data) + * TPS6507X_NUM_REGULATOR), GFP_KERNEL); + if (!reg_data) { + dev_err(&pdev->dev, "Failure to alloc init data for regulators.\n"); + return NULL; + } + + tps_board->tps6507x_pmic_init_data = reg_data; + + for (idx = 0; idx < count; idx++) { + if (!matches[idx].init_data || !matches[idx].of_node) + continue; + + memcpy(®_data[idx], matches[idx].init_data, + sizeof(struct regulator_init_data)); + + } + + return tps_board; +} +#else +static inline struct tps6507x_board *tps6507x_parse_dt_reg_data( + struct platform_device *pdev, + struct of_regulator_match **tps6507x_reg_matches) +{ + *tps6507x_reg_matches = NULL; + return NULL; +} +#endif static int tps6507x_pmic_probe(struct platform_device *pdev) { struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent); @@ -365,8 +441,10 @@ static int tps6507x_pmic_probe(struct platform_device *pdev) struct regulator_dev *rdev; struct tps6507x_pmic *tps; struct tps6507x_board *tps_board; + struct of_regulator_match *tps6507x_reg_matches = NULL; int i; int error; + unsigned int prop; /** * tps_board points to pmic related constants @@ -374,6 +452,9 @@ static int tps6507x_pmic_probe(struct platform_device *pdev) */ tps_board = dev_get_platdata(tps6507x_dev->dev); + if (!tps_board && tps6507x_dev->dev->of_node) + tps_board = tps6507x_parse_dt_reg_data(pdev, + &tps6507x_reg_matches); if (!tps_board) return -EINVAL; @@ -415,6 +496,17 @@ static int tps6507x_pmic_probe(struct platform_device *pdev) config.init_data = init_data; config.driver_data = tps; + if (tps6507x_reg_matches) { + error = of_property_read_u32( + tps6507x_reg_matches[i].of_node, + "ti,defdcdc_default", &prop); + + if (!error) + tps->info[i]->defdcdc_default = prop; + + config.of_node = tps6507x_reg_matches[i].of_node; + } + rdev = regulator_register(&tps->desc[i], &config); if (IS_ERR(rdev)) { dev_err(tps6507x_dev->dev, -- cgit v1.2.3-59-g8ed1b From eb7ccb8184ab36b61fb596bd8d573e22e04d6266 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 24 Jan 2013 17:09:45 +0100 Subject: DT: add vendor prefixes for Renesas and Toshiba Signed-off-by: Guennadi Liakhovetski Signed-off-by: Rob Herring --- Documentation/devicetree/bindings/vendor-prefixes.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 723bd8d62c79..4519dff3f506 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -43,6 +43,7 @@ powervr PowerVR (deprecated, use img) qcom Qualcomm, Inc. ramtron Ramtron International realtek Realtek Semiconductor Corp. +renesas Renesas Electronics Corporation samsung Samsung Semiconductor sbs Smart Battery System schindler Schindler @@ -53,6 +54,7 @@ snps Synopsys, Inc. st STMicroelectronics stericsson ST-Ericsson ti Texas Instruments +toshiba Toshiba Corporation via VIA Technologies, Inc. wlf Wolfson Microelectronics wm Wondermedia Technologies, Inc. -- cgit v1.2.3-59-g8ed1b From ca589f9469641916f4f9bd6a820012a27102ef63 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 30 Jan 2013 14:27:29 +0100 Subject: ACPI / scan: Introduce struct acpi_scan_handler Introduce struct acpi_scan_handler for representing objects that will do configuration tasks depending on ACPI device nodes' hardware IDs (HIDs). Currently, those tasks are done either directly by the ACPI namespace scanning code or by ACPI device drivers designed specifically for this purpose. None of the above is desirable, however, because doing that directly in the namespace scanning code makes that code overly complicated and difficult to follow and doing that in "special" device drivers leads to a great deal of confusion about their role and to confusing interactions with the driver core (for example, sysfs directories are created for those drivers, but they are completely unnecessary and only increase the kernel's memory footprint in vain). Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu Acked-by: Yasuaki Ishimatsu Acked-by: Toshi Kani --- Documentation/acpi/scan_handlers.txt | 77 ++++++++++++++++++++++++++++++++++++ drivers/acpi/scan.c | 60 ++++++++++++++++++++++++---- include/acpi/acpi_bus.h | 14 +++++++ 3 files changed, 144 insertions(+), 7 deletions(-) create mode 100644 Documentation/acpi/scan_handlers.txt (limited to 'Documentation') diff --git a/Documentation/acpi/scan_handlers.txt b/Documentation/acpi/scan_handlers.txt new file mode 100644 index 000000000000..3246ccf15992 --- /dev/null +++ b/Documentation/acpi/scan_handlers.txt @@ -0,0 +1,77 @@ +ACPI Scan Handlers + +Copyright (C) 2012, Intel Corporation +Author: Rafael J. Wysocki + +During system initialization and ACPI-based device hot-add, the ACPI namespace +is scanned in search of device objects that generally represent various pieces +of hardware. This causes a struct acpi_device object to be created and +registered with the driver core for every device object in the ACPI namespace +and the hierarchy of those struct acpi_device objects reflects the namespace +layout (i.e. parent device objects in the namespace are represented by parent +struct acpi_device objects and analogously for their children). Those struct +acpi_device objects are referred to as "device nodes" in what follows, but they +should not be confused with struct device_node objects used by the Device Trees +parsing code (although their role is analogous to the role of those objects). + +During ACPI-based device hot-remove device nodes representing pieces of hardware +being removed are unregistered and deleted. + +The core ACPI namespace scanning code in drivers/acpi/scan.c carries out basic +initialization of device nodes, such as retrieving common configuration +information from the device objects represented by them and populating them with +appropriate data, but some of them require additional handling after they have +been registered. For example, if the given device node represents a PCI host +bridge, its registration should cause the PCI bus under that bridge to be +enumerated and PCI devices on that bus to be registered with the driver core. +Similarly, if the device node represents a PCI interrupt link, it is necessary +to configure that link so that the kernel can use it. + +Those additional configuration tasks usually depend on the type of the hardware +component represented by the given device node which can be determined on the +basis of the device node's hardware ID (HID). They are performed by objects +called ACPI scan handlers represented by the following structure: + +struct acpi_scan_handler { + const struct acpi_device_id *ids; + struct list_head list_node; + int (*attach)(struct acpi_device *dev, const struct acpi_device_id *id); + void (*detach)(struct acpi_device *dev); +}; + +where ids is the list of IDs of device nodes the given handler is supposed to +take care of, list_node is the hook to the global list of ACPI scan handlers +maintained by the ACPI core and the .attach() and .detach() callbacks are +executed, respectively, after registration of new device nodes and before +unregistration of device nodes the handler attached to previously. + +The namespace scanning function, acpi_bus_scan(), first registers all of the +device nodes in the given namespace scope with the driver core. Then, it tries +to match a scan handler against each of them using the ids arrays of the +available scan handlers. If a matching scan handler is found, its .attach() +callback is executed for the given device node. If that callback returns 1, +that means that the handler has claimed the device node and is now responsible +for carrying out any additional configuration tasks related to it. It also will +be responsible for preparing the device node for unregistration in that case. +The device node's handler field is then populated with the address of the scan +handler that has claimed it. + +If the .attach() callback returns 0, it means that the device node is not +interesting to the given scan handler and may be matched against the next scan +handler in the list. If it returns a (negative) error code, that means that +the namespace scan should be terminated due to a serious error. The error code +returned should then reflect the type of the error. + +The namespace trimming function, acpi_bus_trim(), first executes .detach() +callbacks from the scan handlers of all device nodes in the given namespace +scope (if they have scan handlers). Next, it unregisters all of the device +nodes in that scope. + +ACPI scan handlers can be added to the list maintained by the ACPI core with the +help of the acpi_scan_add_handler() function taking a pointer to the new scan +handler as an argument. The order in which scan handlers are added to the list +is the order in which they are matched against device nodes during namespace +scans. + +All scan handles must be added to the list before acpi_bus_scan() is run for the +first time and they cannot be removed from it. diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index fe9f2c926663..1453cd0672fb 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -53,6 +53,7 @@ static const struct acpi_device_id acpi_platform_device_ids[] = { static LIST_HEAD(acpi_device_list); static LIST_HEAD(acpi_bus_id_list); static DEFINE_MUTEX(acpi_scan_lock); +static LIST_HEAD(acpi_scan_handlers_list); DEFINE_MUTEX(acpi_device_lock); LIST_HEAD(acpi_wakeup_device_list); @@ -62,6 +63,15 @@ struct acpi_device_bus_id{ struct list_head node; }; +int acpi_scan_add_handler(struct acpi_scan_handler *handler) +{ + if (!handler || !handler->attach) + return -EINVAL; + + list_add_tail(&handler->list_node, &acpi_scan_handlers_list); + 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: @@ -1570,20 +1580,42 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, return AE_OK; } +static int acpi_scan_attach_handler(struct acpi_device *device) +{ + struct acpi_scan_handler *handler; + int ret = 0; + + list_for_each_entry(handler, &acpi_scan_handlers_list, list_node) { + const struct acpi_device_id *id; + + id = __acpi_match_device(device, handler->ids); + if (!id) + continue; + + ret = handler->attach(device, id); + if (ret > 0) { + device->handler = handler; + break; + } else if (ret < 0) { + break; + } + } + return ret; +} + static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, void *not_used, void **ret_not_used) { const struct acpi_device_id *id; - acpi_status status = AE_OK; struct acpi_device *device; unsigned long long sta_not_used; - int type_not_used; + int ret; /* * Ignore errors ignored by acpi_bus_check_add() to avoid terminating * namespace walks prematurely. */ - if (acpi_bus_type_and_status(handle, &type_not_used, &sta_not_used)) + if (acpi_bus_type_and_status(handle, &ret, &sta_not_used)) return AE_OK; if (acpi_bus_get_device(handle, &device)) @@ -1593,10 +1625,15 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, if (id) { /* This is a known good platform device. */ acpi_create_platform_device(device, id->driver_data); - } else if (device_attach(&device->dev) < 0) { - status = AE_CTRL_DEPTH; + return AE_OK; } - return status; + + ret = acpi_scan_attach_handler(device); + if (ret) + return ret > 0 ? AE_OK : AE_CTRL_DEPTH; + + ret = device_attach(&device->dev); + return ret >= 0 ? AE_OK : AE_CTRL_DEPTH; } /** @@ -1639,8 +1676,17 @@ static acpi_status acpi_bus_device_detach(acpi_handle handle, u32 lvl_not_used, struct acpi_device *device = NULL; if (!acpi_bus_get_device(handle, &device)) { + struct acpi_scan_handler *dev_handler = device->handler; + device->removal_type = ACPI_BUS_REMOVAL_EJECT; - device_release_driver(&device->dev); + if (dev_handler) { + if (dev_handler->detach) + dev_handler->detach(device); + + device->handler = NULL; + } else { + device_release_driver(&device->dev); + } } return AE_OK; } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index ad0a86ac5cce..41850cb21730 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -83,6 +83,18 @@ enum acpi_bus_device_type { struct acpi_driver; struct acpi_device; +/* + * ACPI Scan Handler + * ----------------- + */ + +struct acpi_scan_handler { + const struct acpi_device_id *ids; + struct list_head list_node; + int (*attach)(struct acpi_device *dev, const struct acpi_device_id *id); + void (*detach)(struct acpi_device *dev); +}; + /* * ACPI Driver * ----------- @@ -269,6 +281,7 @@ struct acpi_device { struct acpi_device_wakeup wakeup; struct acpi_device_perf performance; struct acpi_device_dir dir; + struct acpi_scan_handler *handler; struct acpi_driver *driver; void *driver_data; struct device dev; @@ -382,6 +395,7 @@ int acpi_bus_receive_event(struct acpi_bus_event *event); static inline int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data) { return 0; } #endif +int acpi_scan_add_handler(struct acpi_scan_handler *handler); int acpi_bus_register_driver(struct acpi_driver *driver); void acpi_bus_unregister_driver(struct acpi_driver *driver); int acpi_bus_scan(acpi_handle handle); -- cgit v1.2.3-59-g8ed1b From c1043fcda1b9e8e5144cfdaee7be262c50dbdead Mon Sep 17 00:00:00 2001 From: Hiraku Toyooka Date: Wed, 26 Dec 2012 11:53:09 +0900 Subject: tracing: Add documentation of snapshot utility This patch adds snapshot description in ftrace documentation. This description includes what the snapshot is and how to use it. Link: http://lkml.kernel.org/r/20121226025309.3252.150.stgit@liselsia Cc: Rob Landley Signed-off-by: Hiraku Toyooka Signed-off-by: Steven Rostedt --- Documentation/trace/ftrace.txt | 83 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) (limited to 'Documentation') diff --git a/Documentation/trace/ftrace.txt b/Documentation/trace/ftrace.txt index 6f51fed45f2d..53d6a3c51d87 100644 --- a/Documentation/trace/ftrace.txt +++ b/Documentation/trace/ftrace.txt @@ -1842,6 +1842,89 @@ an error. # cat buffer_size_kb 85 +Snapshot +-------- +CONFIG_TRACER_SNAPSHOT makes a generic snapshot feature +available to all non latency tracers. (Latency tracers which +record max latency, such as "irqsoff" or "wakeup", can't use +this feature, since those are already using the snapshot +mechanism internally.) + +Snapshot preserves a current trace buffer at a particular point +in time without stopping tracing. Ftrace swaps the current +buffer with a spare buffer, and tracing continues in the new +current (=previous spare) buffer. + +The following debugfs files in "tracing" are related to this +feature: + + snapshot: + + This is used to take a snapshot and to read the output + of the snapshot. Echo 1 into this file to allocate a + spare buffer and to take a snapshot (swap), then read + the snapshot from this file in the same format as + "trace" (described above in the section "The File + System"). Both reads snapshot and tracing are executable + in parallel. When the spare buffer is allocated, echoing + 0 frees it, and echoing else (positive) values clear the + snapshot contents. + More details are shown in the table below. + + status\input | 0 | 1 | else | + --------------+------------+------------+------------+ + not allocated |(do nothing)| alloc+swap | EINVAL | + --------------+------------+------------+------------+ + allocated | free | swap | clear | + --------------+------------+------------+------------+ + +Here is an example of using the snapshot feature. + + # echo 1 > events/sched/enable + # echo 1 > snapshot + # cat snapshot +# tracer: nop +# +# entries-in-buffer/entries-written: 71/71 #P:8 +# +# _-----=> irqs-off +# / _----=> need-resched +# | / _---=> hardirq/softirq +# || / _--=> preempt-depth +# ||| / delay +# TASK-PID CPU# |||| TIMESTAMP FUNCTION +# | | | |||| | | + -0 [005] d... 2440.603828: sched_switch: prev_comm=swapper/5 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=snapshot-test-2 next_pid=2242 next_prio=120 + sleep-2242 [005] d... 2440.603846: sched_switch: prev_comm=snapshot-test-2 prev_pid=2242 prev_prio=120 prev_state=R ==> next_comm=kworker/5:1 next_pid=60 next_prio=120 +[...] + -0 [002] d... 2440.707230: sched_switch: prev_comm=swapper/2 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=snapshot-test-2 next_pid=2229 next_prio=120 + + # cat trace +# tracer: nop +# +# entries-in-buffer/entries-written: 77/77 #P:8 +# +# _-----=> irqs-off +# / _----=> need-resched +# | / _---=> hardirq/softirq +# || / _--=> preempt-depth +# ||| / delay +# TASK-PID CPU# |||| TIMESTAMP FUNCTION +# | | | |||| | | + -0 [007] d... 2440.707395: sched_switch: prev_comm=swapper/7 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=snapshot-test-2 next_pid=2243 next_prio=120 + snapshot-test-2-2229 [002] d... 2440.707438: sched_switch: prev_comm=snapshot-test-2 prev_pid=2229 prev_prio=120 prev_state=S ==> next_comm=swapper/2 next_pid=0 next_prio=120 +[...] + + +If you try to use this snapshot feature when current tracer is +one of the latency tracers, you will get the following results. + + # echo wakeup > current_tracer + # echo 1 > snapshot +bash: echo: write error: Device or resource busy + # cat snapshot +cat: snapshot: Device or resource busy + ----------- More details can be found in the source code, in the -- cgit v1.2.3-59-g8ed1b From 9ee417c07479b9a87d0808dd3c8b4ce3925983f1 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Thu, 31 Jan 2013 11:23:53 -0500 Subject: regulators: anatop: add set_voltage_time_sel interface some of anatop's regulators(cpu, vddpu and vddsoc) have register settings about LDO's step time, which will impact the LDO ramp up speed, need to use set_voltage_time_sel interface to add necessary delay everytime LDOs' voltage is increased. offset 0x170: bit [24-25]: cpu bit [26-27]: vddpu bit [28-29]: vddsoc field definition: 0'b00: 64 cycles of 24M clock; 0'b01: 128 cycles of 24M clock; 0'b02: 256 cycles of 24M clock; 0'b03: 512 cycles of 24M clock; Signed-off-by: Anson Huang Acked-by: Shawn Guo Signed-off-by: Mark Brown --- .../bindings/regulator/anatop-regulator.txt | 8 +++++ drivers/regulator/anatop-regulator.c | 41 ++++++++++++++++++++++ 2 files changed, 49 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/regulator/anatop-regulator.txt b/Documentation/devicetree/bindings/regulator/anatop-regulator.txt index 357758cb6e92..758eae24082a 100644 --- a/Documentation/devicetree/bindings/regulator/anatop-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/anatop-regulator.txt @@ -9,6 +9,11 @@ Required properties: - anatop-min-voltage: Minimum voltage of this regulator - anatop-max-voltage: Maximum voltage of this regulator +Optional properties: +- anatop-delay-reg-offset: Anatop MFD step time register offset +- anatop-delay-bit-shift: Bit shift for the step time register +- anatop-delay-bit-width: Number of bits used in the step time register + Any property defined as part of the core regulator binding, defined in regulator.txt, can also be used. @@ -23,6 +28,9 @@ Example: anatop-reg-offset = <0x140>; anatop-vol-bit-shift = <9>; anatop-vol-bit-width = <5>; + anatop-delay-reg-offset = <0x170>; + anatop-delay-bit-shift = <24>; + anatop-delay-bit-width = <2>; anatop-min-bit-val = <1>; anatop-min-voltage = <725000>; anatop-max-voltage = <1300000>; diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c index 8f39cac661d2..0df9c6a97604 100644 --- a/drivers/regulator/anatop-regulator.c +++ b/drivers/regulator/anatop-regulator.c @@ -31,12 +31,18 @@ #include #include +#define LDO_RAMP_UP_UNIT_IN_CYCLES 64 /* 64 cycles per step */ +#define LDO_RAMP_UP_FREQ_IN_MHZ 24 /* cycle based on 24M OSC */ + struct anatop_regulator { const char *name; u32 control_reg; struct regmap *anatop; int vol_bit_shift; int vol_bit_width; + u32 delay_reg; + int delay_bit_shift; + int delay_bit_width; int min_bit_val; int min_voltage; int max_voltage; @@ -55,6 +61,32 @@ static int anatop_regmap_set_voltage_sel(struct regulator_dev *reg, return regulator_set_voltage_sel_regmap(reg, selector); } +static int anatop_regmap_set_voltage_time_sel(struct regulator_dev *reg, + unsigned int old_sel, + unsigned int new_sel) +{ + struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg); + u32 val; + int ret = 0; + + /* check whether need to care about LDO ramp up speed */ + if (anatop_reg->delay_bit_width && new_sel > old_sel) { + /* + * the delay for LDO ramp up time is + * based on the register setting, we need + * to calculate how many steps LDO need to + * ramp up, and how much delay needed. (us) + */ + regmap_read(anatop_reg->anatop, anatop_reg->delay_reg, &val); + val = (val >> anatop_reg->delay_bit_shift) & + ((1 << anatop_reg->delay_bit_width) - 1); + ret = (new_sel - old_sel) * ((LDO_RAMP_UP_UNIT_IN_CYCLES << + val) / LDO_RAMP_UP_FREQ_IN_MHZ + 1); + } + + return ret; +} + static int anatop_regmap_get_voltage_sel(struct regulator_dev *reg) { struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg); @@ -67,6 +99,7 @@ static int anatop_regmap_get_voltage_sel(struct regulator_dev *reg) static struct regulator_ops anatop_rops = { .set_voltage_sel = anatop_regmap_set_voltage_sel, + .set_voltage_time_sel = anatop_regmap_set_voltage_time_sel, .get_voltage_sel = anatop_regmap_get_voltage_sel, .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, @@ -143,6 +176,14 @@ static int anatop_regulator_probe(struct platform_device *pdev) goto anatop_probe_end; } + /* read LDO ramp up setting, only for core reg */ + of_property_read_u32(np, "anatop-delay-reg-offset", + &sreg->delay_reg); + of_property_read_u32(np, "anatop-delay-bit-width", + &sreg->delay_bit_width); + of_property_read_u32(np, "anatop-delay-bit-shift", + &sreg->delay_bit_shift); + rdesc->n_voltages = (sreg->max_voltage - sreg->min_voltage) / 25000 + 1 + sreg->min_bit_val; rdesc->min_uV = sreg->min_voltage; -- cgit v1.2.3-59-g8ed1b From 1c53a270724df91276d28d66f8e5a302fc6a5d74 Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Tue, 22 Jan 2013 22:24:54 -0800 Subject: perf/POWER7: Make generic event translations available in sysfs Make the generic perf events in POWER7 available via sysfs. $ ls /sys/bus/event_source/devices/cpu/events branch-instructions branch-misses cache-misses cache-references cpu-cycles instructions stalled-cycles-backend stalled-cycles-frontend $ cat /sys/bus/event_source/devices/cpu/events/cache-misses event=0x400f0 This patch is based on commits that implement this functionality on x86. Eg: commit a47473939db20e3961b200eb00acf5fcf084d755 Author: Jiri Olsa Date: Wed Oct 10 14:53:11 2012 +0200 perf/x86: Make hardware event translations available in sysfs Changelog:[v2] [Jiri Osla] Drop EVENT_ID() macro since it is only used once. Signed-off-by: Sukadev Bhattiprolu Cc: Andi Kleen Cc: Anton Blanchard Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Robert Richter Cc: Stephane Eranian Cc: linuxppc-dev@ozlabs.org Link: http://lkml.kernel.org/r/20130123062454.GD13720@us.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- Documentation/ABI/stable/sysfs-devices-cpu-events | 0 arch/powerpc/include/asm/perf_event_server.h | 23 +++++++++++++++ arch/powerpc/perf/core-book3s.c | 12 ++++++++ arch/powerpc/perf/power7-pmu.c | 34 +++++++++++++++++++++++ 4 files changed, 69 insertions(+) create mode 100644 Documentation/ABI/stable/sysfs-devices-cpu-events (limited to 'Documentation') diff --git a/Documentation/ABI/stable/sysfs-devices-cpu-events b/Documentation/ABI/stable/sysfs-devices-cpu-events new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/arch/powerpc/include/asm/perf_event_server.h b/arch/powerpc/include/asm/perf_event_server.h index 9710be3a2d17..b9b6c557bd0c 100644 --- a/arch/powerpc/include/asm/perf_event_server.h +++ b/arch/powerpc/include/asm/perf_event_server.h @@ -11,6 +11,7 @@ #include #include +#include #define MAX_HWEVENTS 8 #define MAX_EVENT_ALTERNATIVES 8 @@ -35,6 +36,7 @@ struct power_pmu { void (*disable_pmc)(unsigned int pmc, unsigned long mmcr[]); int (*limited_pmc_event)(u64 event_id); u32 flags; + const struct attribute_group **attr_groups; int n_generic; int *generic_events; int (*cache_events)[PERF_COUNT_HW_CACHE_MAX] @@ -109,3 +111,24 @@ extern unsigned long perf_instruction_pointer(struct pt_regs *regs); * If an event_id is not subject to the constraint expressed by a particular * field, then it will have 0 in both the mask and value for that field. */ + +extern ssize_t power_events_sysfs_show(struct device *dev, + struct device_attribute *attr, char *page); + +/* + * EVENT_VAR() is same as PMU_EVENT_VAR with a suffix. + * + * Having a suffix allows us to have aliases in sysfs - eg: the generic + * event 'cpu-cycles' can have two entries in sysfs: 'cpu-cycles' and + * 'PM_CYC' where the latter is the name by which the event is known in + * POWER CPU specification. + */ +#define EVENT_VAR(_id, _suffix) event_attr_##_id##_suffix +#define EVENT_PTR(_id, _suffix) &EVENT_VAR(_id, _suffix) + +#define EVENT_ATTR(_name, _id, _suffix) \ + PMU_EVENT_ATTR(_name, EVENT_VAR(_id, _suffix), PME_PM_##_id, \ + power_events_sysfs_show) + +#define GENERIC_EVENT_ATTR(_name, _id) EVENT_ATTR(_name, _id, _g) +#define GENERIC_EVENT_PTR(_id) EVENT_PTR(_id, _g) diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index aa2465e21f1a..fa476d50791f 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -1305,6 +1305,16 @@ static int power_pmu_event_idx(struct perf_event *event) return event->hw.idx; } +ssize_t power_events_sysfs_show(struct device *dev, + struct device_attribute *attr, char *page) +{ + struct perf_pmu_events_attr *pmu_attr; + + pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); + + return sprintf(page, "event=0x%02llx\n", pmu_attr->id); +} + struct pmu power_pmu = { .pmu_enable = power_pmu_enable, .pmu_disable = power_pmu_disable, @@ -1537,6 +1547,8 @@ int __cpuinit register_power_pmu(struct power_pmu *pmu) pr_info("%s performance monitor hardware support registered\n", pmu->name); + power_pmu.attr_groups = ppmu->attr_groups; + #ifdef MSR_HV /* * Use FCHV to ignore kernel events if MSR.HV is set. diff --git a/arch/powerpc/perf/power7-pmu.c b/arch/powerpc/perf/power7-pmu.c index eebb36de429f..269bf2464a36 100644 --- a/arch/powerpc/perf/power7-pmu.c +++ b/arch/powerpc/perf/power7-pmu.c @@ -374,6 +374,39 @@ static int power7_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { }, }; + +GENERIC_EVENT_ATTR(cpu-cycles, CYC); +GENERIC_EVENT_ATTR(stalled-cycles-frontend, GCT_NOSLOT_CYC); +GENERIC_EVENT_ATTR(stalled-cycles-backend, CMPLU_STALL); +GENERIC_EVENT_ATTR(instructions, INST_CMPL); +GENERIC_EVENT_ATTR(cache-references, LD_REF_L1); +GENERIC_EVENT_ATTR(cache-misses, LD_MISS_L1); +GENERIC_EVENT_ATTR(branch-instructions, BRU_FIN); +GENERIC_EVENT_ATTR(branch-misses, BRU_MPRED); + +static struct attribute *power7_events_attr[] = { + GENERIC_EVENT_PTR(CYC), + GENERIC_EVENT_PTR(GCT_NOSLOT_CYC), + GENERIC_EVENT_PTR(CMPLU_STALL), + GENERIC_EVENT_PTR(INST_CMPL), + GENERIC_EVENT_PTR(LD_REF_L1), + GENERIC_EVENT_PTR(LD_MISS_L1), + GENERIC_EVENT_PTR(BRU_FIN), + GENERIC_EVENT_PTR(BRU_MPRED), + NULL +}; + + +static struct attribute_group power7_pmu_events_group = { + .name = "events", + .attrs = power7_events_attr, +}; + +static const struct attribute_group *power7_pmu_attr_groups[] = { + &power7_pmu_events_group, + NULL, +}; + static struct power_pmu power7_pmu = { .name = "POWER7", .n_counter = 6, @@ -385,6 +418,7 @@ static struct power_pmu power7_pmu = { .get_alternatives = power7_get_alternatives, .disable_pmc = power7_disable_pmc, .flags = PPMU_ALT_SIPR, + .attr_groups = power7_pmu_attr_groups, .n_generic = ARRAY_SIZE(power7_generic_events), .generic_events = power7_generic_events, .cache_events = &power7_cache_events, -- cgit v1.2.3-59-g8ed1b From 2ac3634a7e1c8eedc961030c87c5c36ebd5bbf8e Mon Sep 17 00:00:00 2001 From: Sukadev Bhattiprolu Date: Tue, 22 Jan 2013 22:26:45 -0800 Subject: perf: Document the ABI of perf sysfs entries This patchset addes two new sets of files to sysfs for POWER architecture. - perf event config format in /sys/devices/cpu/format/event - generic and POWER-specific perf events in /sys/devices/cpu/events/ The format of the first file is already documented in: sysfs-bus-event_source-devices-format Document the format of the second set of files '/sys/devices/cpu/events/*' which would also become part of the ABI. Changelog[v4]: [Jiri Olsa]: Mention that multiple event= like terms can be specified in the 'events' file. [Jiri Olsa]: Remove the documentation for the 'config format' file as it is already documented in 'Documentation/ABI/testing/'. [Jiri Olsa]: Move ABI documentation from 'stable/' to 'testing/' Changelog[v3]: [Greg KH] Include ABI documentation. Signed-off-by: Sukadev Bhattiprolu Acked-by: Jiri Olsa Cc: Andi Kleen Cc: Anton Blanchard Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Robert Richter Cc: Stephane Eranian Cc: linuxppc-dev@ozlabs.org Link: http://lkml.kernel.org/r/20130123062645.GG13720@us.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- Documentation/ABI/stable/sysfs-devices-cpu-events | 0 .../testing/sysfs-bus-event_source-devices-events | 62 ++++++++++++++++++++++ 2 files changed, 62 insertions(+) delete mode 100644 Documentation/ABI/stable/sysfs-devices-cpu-events create mode 100644 Documentation/ABI/testing/sysfs-bus-event_source-devices-events (limited to 'Documentation') diff --git a/Documentation/ABI/stable/sysfs-devices-cpu-events b/Documentation/ABI/stable/sysfs-devices-cpu-events deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/Documentation/ABI/testing/sysfs-bus-event_source-devices-events b/Documentation/ABI/testing/sysfs-bus-event_source-devices-events new file mode 100644 index 000000000000..0adeb524c0d4 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-event_source-devices-events @@ -0,0 +1,62 @@ +What: /sys/devices/cpu/events/ + /sys/devices/cpu/events/branch-misses + /sys/devices/cpu/events/cache-references + /sys/devices/cpu/events/cache-misses + /sys/devices/cpu/events/stalled-cycles-frontend + /sys/devices/cpu/events/branch-instructions + /sys/devices/cpu/events/stalled-cycles-backend + /sys/devices/cpu/events/instructions + /sys/devices/cpu/events/cpu-cycles + +Date: 2013/01/08 + +Contact: Linux kernel mailing list + +Description: Generic performance monitoring events + + A collection of performance monitoring events that may be + supported by many/most CPUs. These events can be monitored + using the 'perf(1)' tool. + + The contents of each file would look like: + + event=0xNNNN + + where 'N' is a hex digit and the number '0xNNNN' shows the + "raw code" for the perf event identified by the file's + "basename". + + +What: /sys/devices/cpu/events/PM_LD_MISS_L1 + /sys/devices/cpu/events/PM_LD_REF_L1 + /sys/devices/cpu/events/PM_CYC + /sys/devices/cpu/events/PM_BRU_FIN + /sys/devices/cpu/events/PM_GCT_NOSLOT_CYC + /sys/devices/cpu/events/PM_BRU_MPRED + /sys/devices/cpu/events/PM_INST_CMPL + /sys/devices/cpu/events/PM_CMPLU_STALL + +Date: 2013/01/08 + +Contact: Linux kernel mailing list + Linux Powerpc mailing list + +Description: POWER-systems specific performance monitoring events + + A collection of performance monitoring events that may be + supported by the POWER CPU. These events can be monitored + using the 'perf(1)' tool. + + These events may not be supported by other CPUs. + + The contents of each file would look like: + + event=0xNNNN + + where 'N' is a hex digit and the number '0xNNNN' shows the + "raw code" for the perf event identified by the file's + "basename". + + Further, multiple terms like 'event=0xNNNN' can be specified + and separated with comma. All available terms are defined in + the /sys/bus/event_source/devices//format file. -- cgit v1.2.3-59-g8ed1b From a786a7c0ad44985548118fd2370c792c0da36891 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 30 Jan 2013 21:49:29 -0500 Subject: wanrouter: completely decouple obsolete code from kernel. The original suggestion to delete wanrouter started earlier with the mainline commit f0d1b3c2bcc5de8a17af5f2274f7fcde8292b5fc ("net/wanrouter: Deprecate and schedule for removal") in May 2012. More importantly, Dan Carpenter found[1] that the driver had a fundamental breakage introduced back in 2008, with commit 7be6065b39c3 ("netdevice wanrouter: Convert directly reference of netdev->priv"). So we know with certainty that the code hasn't been used by anyone willing to at least take the effort to send an e-mail report of breakage for at least 4 years. This commit does a decouple of the wanrouter subsystem, by going after the Makefile/Kconfig and similar files, so that these mainline files that we are keeping do not have the big wanrouter file/driver deletion commit tied into their history. Once this commit is in place, we then can remove the obsolete cyclomx drivers and similar that have a dependency on CONFIG_WAN_ROUTER_DRIVERS. [1] http://www.spinics.net/lists/netdev/msg218670.html Originally-by: Joe Perches Cc: Dan Carpenter Cc: Arnaldo Carvalho de Melo Signed-off-by: Paul Gortmaker --- Documentation/ioctl/ioctl-number.txt | 2 +- Documentation/magic-number.txt | 2 +- Documentation/zh_CN/magic-number.txt | 2 +- drivers/isdn/i4l/isdn_x25iface.h | 1 - drivers/net/wan/Kconfig | 54 ------------------------------------ drivers/net/wan/Makefile | 5 ---- net/Kconfig | 1 - net/Makefile | 1 - net/socket.c | 1 - 9 files changed, 3 insertions(+), 66 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 2152b0e7237d..3210540f8bd3 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -179,7 +179,7 @@ Code Seq#(hex) Include File Comments 'V' C0 media/davinci/vpfe_capture.h conflict! 'V' C0 media/si4713.h conflict! 'W' 00-1F linux/watchdog.h conflict! -'W' 00-1F linux/wanrouter.h conflict! +'W' 00-1F linux/wanrouter.h conflict! (pre 3.9) 'W' 00-3F sound/asound.h conflict! 'X' all fs/xfs/xfs_fs.h conflict! and fs/xfs/linux-2.6/xfs_ioctl32.h diff --git a/Documentation/magic-number.txt b/Documentation/magic-number.txt index 82761a31d64d..76d80a64bbe1 100644 --- a/Documentation/magic-number.txt +++ b/Documentation/magic-number.txt @@ -122,7 +122,7 @@ SLAB_C_MAGIC 0x4f17a36d kmem_cache mm/slab.c COW_MAGIC 0x4f4f4f4d cow_header_v1 arch/um/drivers/ubd_user.c I810_CARD_MAGIC 0x5072696E i810_card sound/oss/i810_audio.c TRIDENT_CARD_MAGIC 0x5072696E trident_card sound/oss/trident.c -ROUTER_MAGIC 0x524d4157 wan_device include/linux/wanrouter.h +ROUTER_MAGIC 0x524d4157 wan_device [in wanrouter.h pre 3.9] SCC_MAGIC 0x52696368 gs_port drivers/char/scc.h SAVEKMSG_MAGIC1 0x53415645 savekmsg arch/*/amiga/config.c GDA_MAGIC 0x58464552 gda arch/mips/include/asm/sn/gda.h diff --git a/Documentation/zh_CN/magic-number.txt b/Documentation/zh_CN/magic-number.txt index 4263022f5002..2ebe539f5450 100644 --- a/Documentation/zh_CN/magic-number.txt +++ b/Documentation/zh_CN/magic-number.txt @@ -122,7 +122,7 @@ SLAB_C_MAGIC 0x4f17a36d kmem_cache mm/slab.c COW_MAGIC 0x4f4f4f4d cow_header_v1 arch/um/drivers/ubd_user.c I810_CARD_MAGIC 0x5072696E i810_card sound/oss/i810_audio.c TRIDENT_CARD_MAGIC 0x5072696E trident_card sound/oss/trident.c -ROUTER_MAGIC 0x524d4157 wan_device include/linux/wanrouter.h +ROUTER_MAGIC 0x524d4157 wan_device [in wanrouter.h pre 3.9] SCC_MAGIC 0x52696368 gs_port drivers/char/scc.h SAVEKMSG_MAGIC1 0x53415645 savekmsg arch/*/amiga/config.c GDA_MAGIC 0x58464552 gda arch/mips/include/asm/sn/gda.h diff --git a/drivers/isdn/i4l/isdn_x25iface.h b/drivers/isdn/i4l/isdn_x25iface.h index 0b26e3b336e7..ca08e082cf7c 100644 --- a/drivers/isdn/i4l/isdn_x25iface.h +++ b/drivers/isdn/i4l/isdn_x25iface.h @@ -19,7 +19,6 @@ #endif #include -#include #include #include diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig index d58431e99f73..0c077b0f7a2b 100644 --- a/drivers/net/wan/Kconfig +++ b/drivers/net/wan/Kconfig @@ -356,60 +356,6 @@ config SDLA To compile this driver as a module, choose M here: the module will be called sdla. -# Wan router core. -config WAN_ROUTER_DRIVERS - tristate "WAN router drivers" - depends on WAN_ROUTER - ---help--- - Connect LAN to WAN via Linux box. - - Select driver your card and remember to say Y to "Wan Router." - You will need the wan-tools package which is available from - . - - Note that the answer to this question won't directly affect the - kernel except for how subordinate drivers may be built: - saying N will just cause the configurator to skip all - the questions about WAN router drivers. - - If unsure, say N. - -config CYCLADES_SYNC - tristate "Cyclom 2X(tm) cards (EXPERIMENTAL)" - depends on WAN_ROUTER_DRIVERS && (PCI || ISA) - ---help--- - Cyclom 2X from Cyclades Corporation is an - intelligent multiprotocol WAN adapter with data transfer rates up to - 512 Kbps. These cards support the X.25 and SNA related protocols. - - While no documentation is available at this time please grab the - wanconfig tarball in - (with minor changes - to make it compile with the current wanrouter include files; efforts - are being made to use the original package available at - ). - - Feel free to contact me or the cycsyn-devel mailing list at - and for - additional details, I hope to have documentation available as soon as - possible. (Cyclades Brazil is writing the Documentation). - - The next questions will ask you about the protocols you want the - driver to support (for now only X.25 is supported). - - If you have one or more of these cards, say Y to this option. - - To compile this driver as a module, choose M here: the - module will be called cyclomx. - -config CYCLOMX_X25 - bool "Cyclom 2X X.25 support (EXPERIMENTAL)" - depends on CYCLADES_SYNC - help - Connect a Cyclom 2X card to an X.25 network. - - Enabling X.25 support will enlarge your kernel by about 11 kB. - # X.25 network drivers config LAPBETHER tristate "LAPB over Ethernet driver (EXPERIMENTAL)" diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile index df70248e2fda..c135ef47cbca 100644 --- a/drivers/net/wan/Makefile +++ b/drivers/net/wan/Makefile @@ -5,10 +5,6 @@ # Rewritten to use lists instead of if-statements. # -cyclomx-y := cycx_main.o -cyclomx-$(CONFIG_CYCLOMX_X25) += cycx_x25.o -cyclomx-objs := $(cyclomx-y) - obj-$(CONFIG_HDLC) += hdlc.o obj-$(CONFIG_HDLC_RAW) += hdlc_raw.o obj-$(CONFIG_HDLC_RAW_ETH) += hdlc_raw_eth.o @@ -28,7 +24,6 @@ obj-$(CONFIG_LANMEDIA) += lmc/ obj-$(CONFIG_DLCI) += dlci.o obj-$(CONFIG_SDLA) += sdla.o -obj-$(CONFIG_CYCLADES_SYNC) += cycx_drv.o cyclomx.o obj-$(CONFIG_LAPBETHER) += lapbether.o obj-$(CONFIG_SBNI) += sbni.o obj-$(CONFIG_N2) += n2.o diff --git a/net/Kconfig b/net/Kconfig index 3cc5be0fe420..c31348e70aad 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -209,7 +209,6 @@ source "net/ipx/Kconfig" source "drivers/net/appletalk/Kconfig" source "net/x25/Kconfig" source "net/lapb/Kconfig" -source "net/wanrouter/Kconfig" source "net/phonet/Kconfig" source "net/ieee802154/Kconfig" source "net/mac802154/Kconfig" diff --git a/net/Makefile b/net/Makefile index 4f4ee083064c..c5aa8b3b49dc 100644 --- a/net/Makefile +++ b/net/Makefile @@ -26,7 +26,6 @@ obj-$(CONFIG_BRIDGE) += bridge/ obj-$(CONFIG_NET_DSA) += dsa/ obj-$(CONFIG_IPX) += ipx/ obj-$(CONFIG_ATALK) += appletalk/ -obj-$(CONFIG_WAN_ROUTER) += wanrouter/ obj-$(CONFIG_X25) += x25/ obj-$(CONFIG_LAPB) += lapb/ obj-$(CONFIG_NETROM) += netrom/ diff --git a/net/socket.c b/net/socket.c index 2ca51c719ef9..5c4d82c05293 100644 --- a/net/socket.c +++ b/net/socket.c @@ -69,7 +69,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3-59-g8ed1b From 951fc5f45836988c7df1d05c7f4658f331e7a920 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 31 Jan 2013 02:03:53 +0000 Subject: cpufreq: Update Documentation for cpus and related_cpus Documentation related to cpus and related_cpus is confusing and not very clear. Over that CPUFreq core has seen much changes recently. Lets update documentation and comments for cpus and related_cpus. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- Documentation/cpu-freq/cpu-drivers.txt | 6 ++++++ Documentation/cpu-freq/user-guide.txt | 8 ++++---- include/linux/cpufreq.h | 6 ++++-- 3 files changed, 14 insertions(+), 6 deletions(-) (limited to 'Documentation') diff --git a/Documentation/cpu-freq/cpu-drivers.txt b/Documentation/cpu-freq/cpu-drivers.txt index c436096351f8..72f70b16d299 100644 --- a/Documentation/cpu-freq/cpu-drivers.txt +++ b/Documentation/cpu-freq/cpu-drivers.txt @@ -111,6 +111,12 @@ policy->governor must contain the "default policy" for For setting some of these values, 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() +routine must initialize policy->cpus with mask of all possible cpus (Online + +Offline) that share the clock. Then the core would copy this mask onto +policy->related_cpus and will reset policy->cpus to carry only online cpus. + 1.3 verify ------------ diff --git a/Documentation/cpu-freq/user-guide.txt b/Documentation/cpu-freq/user-guide.txt index 04f6b32993e6..ff2f28332cc4 100644 --- a/Documentation/cpu-freq/user-guide.txt +++ b/Documentation/cpu-freq/user-guide.txt @@ -190,11 +190,11 @@ scaling_max_freq show the current "policy limits" (in first set scaling_max_freq, then scaling_min_freq. -affected_cpus : List of CPUs that require software coordination - of frequency. +affected_cpus : List of Online CPUs that require software + coordination of frequency. -related_cpus : List of CPUs that need some sort of frequency - coordination, whether software or hardware. +related_cpus : List of Online + Offline CPUs that need software + coordination of frequency. scaling_driver : Hardware driver for cpufreq. diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 5fdc6c6e3f8a..753b198750cf 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -89,8 +89,10 @@ struct cpufreq_real_policy { }; struct cpufreq_policy { - cpumask_var_t cpus; /* CPUs requiring sw coordination */ - cpumask_var_t related_cpus; /* CPUs with any coordination */ + /* CPUs sharing clock, require sw coordination */ + cpumask_var_t cpus; /* Online CPUs only */ + cpumask_var_t related_cpus; /* Online + Offline CPUs */ + unsigned int shared_type; /* ANY or ALL affected CPUs should set cpufreq */ unsigned int cpu; /* cpu nr of CPU managing this policy */ -- cgit v1.2.3-59-g8ed1b From 26aec009f6b61c077c6de1a96cca7a5132851dbe Mon Sep 17 00:00:00 2001 From: Amit Daniel Kachhap Date: Sun, 3 Feb 2013 15:49:47 -0800 Subject: regulator: add device tree support for s5m8767 This device tree support is added for PMIC block of S5m8767 multi function driver. The usage detail is added in the device tree documentation section. This change is tested on exynos5250 based arndale platform by regulator voltage set/get API's. Reviewed-by: Thomas Abraham Signed-off-by: Amit Daniel Kachhap Tested-by: Sachin Kamat Signed-off-by: Mark Brown --- .../bindings/regulator/s5m8767-regulator.txt | 152 +++++++++++++++++ drivers/mfd/sec-core.c | 75 ++++++++- drivers/regulator/s5m8767.c | 186 ++++++++++++++++++++- include/linux/mfd/samsung/core.h | 11 +- 4 files changed, 417 insertions(+), 7 deletions(-) create mode 100644 Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt b/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt new file mode 100644 index 000000000000..a35ff99003a5 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt @@ -0,0 +1,152 @@ +* Samsung S5M8767 Voltage and Current Regulator + +The Samsung S5M8767 is a multi-function device which includes volatage and +current regulators, rtc, charger controller and other sub-blocks. It is +interfaced to the host controller using a i2c interface. Each sub-block is +addressed by the host system using different i2c slave address. This document +describes the bindings for 'pmic' sub-block of s5m8767. + +Required properties: +- compatible: Should be "samsung,s5m8767-pmic". +- reg: Specifies the i2c slave address of the pmic block. It should be 0x66. + +- s5m8767,pmic-buck2-dvs-voltage: A set of 8 voltage values in micro-volt (uV) + units for buck2 when changing voltage using gpio dvs. Refer to [1] below + for additional information. + +- s5m8767,pmic-buck3-dvs-voltage: A set of 8 voltage values in micro-volt (uV) + units for buck3 when changing voltage using gpio dvs. Refer to [1] below + for additional information. + +- s5m8767,pmic-buck4-dvs-voltage: A set of 8 voltage values in micro-volt (uV) + units for buck4 when changing voltage using gpio dvs. Refer to [1] below + for additional information. + +- s5m8767,pmic-buck-ds-gpios: GPIO specifiers for three host gpio's used + for selecting GPIO DVS lines. It is one-to-one mapped to dvs gpio lines. + +[1] If none of the 's5m8767,pmic-buck[2/3/4]-uses-gpio-dvs' optional + property is specified, the 's5m8767,pmic-buck[2/3/4]-dvs-voltage' + property should specify atleast one voltage level (which would be a + safe operating voltage). + + If either of the 's5m8767,pmic-buck[2/3/4]-uses-gpio-dvs' optional + property is specified, then all the eight voltage values for the + 's5m8767,pmic-buck[2/3/4]-dvs-voltage' should be specified. + +Optional properties: +- interrupt-parent: Specifies the phandle of the interrupt controller to which + the interrupts from s5m8767 are delivered to. +- interrupts: Interrupt specifiers for two interrupt sources. + - First interrupt specifier is for 'irq1' interrupt. + - Second interrupt specifier is for 'alert' interrupt. +- s5m8767,pmic-buck2-uses-gpio-dvs: 'buck2' can be controlled by gpio dvs. +- s5m8767,pmic-buck3-uses-gpio-dvs: 'buck3' can be controlled by gpio dvs. +- s5m8767,pmic-buck4-uses-gpio-dvs: 'buck4' can be controlled by gpio dvs. + +Additional properties required if either of the optional properties are used: + +- s5m8767,pmic-buck234-default-dvs-idx: Default voltage setting selected from + the possible 8 options selectable by the dvs gpios. The value of this + property should be between 0 and 7. If not specified or if out of range, the + default value of this property is set to 0. + +- s5m8767,pmic-buck-dvs-gpios: GPIO specifiers for three host gpio's used + for dvs. The format of the gpio specifier depends in the gpio controller. + +Regulators: The regulators of s5m8767 that have to be instantiated should be +included in a sub-node named 'regulators'. Regulator nodes included in this +sub-node should be of the format as listed below. + + regulator_name { + ldo1_reg: LDO1 { + regulator-name = "VDD_ALIVE_1.0V"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + regulator-always-on; + regulator-boot-on; + op_mode = <1>; /* Normal Mode */ + }; + }; +The above regulator entries are defined in regulator bindings documentation +except op_mode description. + - op_mode: describes the different operating modes of the LDO's with + power mode change in SOC. The different possible values are, + 0 - always off mode + 1 - on in normal mode + 2 - low power mode + 3 - suspend mode + +The following are the names of the regulators that the s5m8767 pmic block +supports. Note: The 'n' in LDOn and BUCKn represents the LDO or BUCK number +as per the datasheet of s5m8767. + + - LDOn + - valid values for n are 1 to 28 + - Example: LDO0, LD01, LDO28 + - BUCKn + - valid values for n are 1 to 9. + - Example: BUCK1, BUCK2, BUCK9 + +The bindings inside the regulator nodes use the standard regulator bindings +which are documented elsewhere. + +Example: + + s5m8767_pmic@66 { + compatible = "samsung,s5m8767-pmic"; + reg = <0x66>; + + s5m8767,pmic-buck2-uses-gpio-dvs; + s5m8767,pmic-buck3-uses-gpio-dvs; + s5m8767,pmic-buck4-uses-gpio-dvs; + + s5m8767,pmic-buck-default-dvs-idx = <0>; + + s5m8767,pmic-buck-dvs-gpios = <&gpx0 0 1 0 0>, /* DVS1 */ + <&gpx0 1 1 0 0>, /* DVS2 */ + <&gpx0 2 1 0 0>; /* DVS3 */ + + s5m8767,pmic-buck-ds-gpios = <&gpx2 3 1 0 0>, /* SET1 */ + <&gpx2 4 1 0 0>, /* SET2 */ + <&gpx2 5 1 0 0>; /* SET3 */ + + s5m8767,pmic-buck2-dvs-voltage = <1350000>, <1300000>, + <1250000>, <1200000>, + <1150000>, <1100000>, + <1000000>, <950000>; + + s5m8767,pmic-buck3-dvs-voltage = <1100000>, <1100000>, + <1100000>, <1100000>, + <1000000>, <1000000>, + <1000000>, <1000000>; + + s5m8767,pmic-buck4-dvs-voltage = <1200000>, <1200000>, + <1200000>, <1200000>, + <1200000>, <1200000>, + <1200000>, <1200000>; + + regulators { + ldo1_reg: LDO1 { + regulator-name = "VDD_ABB_3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + op_mode = <1>; /* Normal Mode */ + }; + + ldo2_reg: LDO2 { + regulator-name = "VDD_ALIVE_1.1V"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + regulator-always-on; + }; + + buck1_reg: BUCK1 { + regulator-name = "VDD_MIF_1.2V"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-boot-on; + }; + }; + }; diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c index 49d361a618d0..ae029834d0a0 100644 --- a/drivers/mfd/sec-core.c +++ b/drivers/mfd/sec-core.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,15 @@ static struct mfd_cell s2mps11_devs[] = { }, }; +#ifdef CONFIG_OF +static struct of_device_id sec_dt_match[] = { + { .compatible = "samsung,s5m8767-pmic", + .data = (void *)S5M8767X, + }, + {}, +}; +#endif + int sec_reg_read(struct sec_pmic_dev *sec_pmic, u8 reg, void *dest) { return regmap_read(sec_pmic->regmap, reg, dest); @@ -95,6 +105,57 @@ static struct regmap_config sec_regmap_config = { .val_bits = 8, }; + +#ifdef CONFIG_OF +/* + * Only the common platform data elements for s5m8767 are parsed here from the + * device tree. Other sub-modules of s5m8767 such as pmic, rtc , charger and + * others have to parse their own platform data elements from device tree. + * + * The s5m8767 platform data structure is instantiated here and the drivers for + * the sub-modules need not instantiate another instance while parsing their + * platform data. + */ +static struct sec_platform_data *sec_pmic_i2c_parse_dt_pdata( + struct device *dev) +{ + struct sec_platform_data *pd; + + pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); + if (!pd) { + dev_err(dev, "could not allocate memory for pdata\n"); + return ERR_PTR(-ENOMEM); + } + + /* + * ToDo: the 'wakeup' member in the platform data is more of a linux + * specfic information. Hence, there is no binding for that yet and + * not parsed here. + */ + + return pd; +} +#else +static struct sec_platform_data *sec_i2c_parse_dt_pdata( + struct device *dev) +{ + return 0; +} +#endif + +static inline int sec_i2c_get_driver_data(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ +#ifdef CONFIG_OF + if (i2c->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(sec_dt_match, i2c->dev.of_node); + return (int)match->data; + } +#endif + return (int)id->driver_data; +} + static int sec_pmic_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -111,13 +172,22 @@ static int sec_pmic_probe(struct i2c_client *i2c, sec_pmic->dev = &i2c->dev; sec_pmic->i2c = i2c; sec_pmic->irq = i2c->irq; - sec_pmic->type = id->driver_data; - + sec_pmic->type = sec_i2c_get_driver_data(i2c, id); + + if (sec_pmic->dev->of_node) { + pdata = sec_pmic_i2c_parse_dt_pdata(sec_pmic->dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + return ret; + } + pdata->device_type = sec_pmic->type; + } if (pdata) { sec_pmic->device_type = pdata->device_type; sec_pmic->ono = pdata->ono; sec_pmic->irq_base = pdata->irq_base; sec_pmic->wakeup = pdata->wakeup; + sec_pmic->pdata = pdata; } sec_pmic->regmap = devm_regmap_init_i2c(i2c, &sec_regmap_config); @@ -192,6 +262,7 @@ static struct i2c_driver sec_pmic_driver = { .driver = { .name = "sec_pmic", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(sec_dt_match), }, .probe = sec_pmic_probe, .remove = sec_pmic_remove, diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index aa0ccef5c31c..1250cef43d7b 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,9 @@ #include #include #include +#include + +#define S5M8767_OPMODE_NORMAL_MODE 0x1 struct s5m8767_info { struct device *dev; @@ -474,15 +478,194 @@ static struct regulator_desc regulators[] = { s5m8767_regulator_desc(BUCK9), }; +#ifdef CONFIG_OF +static int s5m8767_pmic_dt_parse_dvs_gpio(struct sec_pmic_dev *iodev, + struct sec_platform_data *pdata, + struct device_node *pmic_np) +{ + int i, gpio; + + for (i = 0; i < 3; i++) { + gpio = of_get_named_gpio(pmic_np, + "s5m8767,pmic-buck-dvs-gpios", i); + if (!gpio_is_valid(gpio)) { + dev_err(iodev->dev, "invalid gpio[%d]: %d\n", i, gpio); + return -EINVAL; + } + pdata->buck_gpios[i] = gpio; + } + return 0; +} + +static int s5m8767_pmic_dt_parse_ds_gpio(struct sec_pmic_dev *iodev, + struct sec_platform_data *pdata, + struct device_node *pmic_np) +{ + int i, gpio; + + for (i = 0; i < 3; i++) { + gpio = of_get_named_gpio(pmic_np, + "s5m8767,pmic-buck-ds-gpios", i); + if (!gpio_is_valid(gpio)) { + dev_err(iodev->dev, "invalid gpio[%d]: %d\n", i, gpio); + return -EINVAL; + } + pdata->buck_ds[i] = gpio; + } + return 0; +} + +static int s5m8767_pmic_dt_parse_pdata(struct sec_pmic_dev *iodev, + struct sec_platform_data *pdata) +{ + struct device_node *pmic_np, *regulators_np, *reg_np; + struct sec_regulator_data *rdata; + struct sec_opmode_data *rmode; + unsigned int i, dvs_voltage_nr = 1, ret; + + pmic_np = iodev->dev->of_node; + if (!pmic_np) { + dev_err(iodev->dev, "could not find pmic sub-node\n"); + return -ENODEV; + } + + regulators_np = of_find_node_by_name(pmic_np, "regulators"); + if (!regulators_np) { + dev_err(iodev->dev, "could not find regulators sub-node\n"); + return -EINVAL; + } + + /* count the number of regulators to be supported in pmic */ + pdata->num_regulators = 0; + for_each_child_of_node(regulators_np, reg_np) + pdata->num_regulators++; + + rdata = devm_kzalloc(iodev->dev, sizeof(*rdata) * + pdata->num_regulators, GFP_KERNEL); + if (!rdata) { + dev_err(iodev->dev, + "could not allocate memory for regulator data\n"); + return -ENOMEM; + } + + rmode = devm_kzalloc(iodev->dev, sizeof(*rmode) * + pdata->num_regulators, GFP_KERNEL); + if (!rdata) { + dev_err(iodev->dev, + "could not allocate memory for regulator mode\n"); + return -ENOMEM; + } + + pdata->regulators = rdata; + pdata->opmode = rmode; + for_each_child_of_node(regulators_np, reg_np) { + for (i = 0; i < ARRAY_SIZE(regulators); i++) + if (!of_node_cmp(reg_np->name, regulators[i].name)) + break; + + if (i == ARRAY_SIZE(regulators)) { + dev_warn(iodev->dev, + "don't know how to configure regulator %s\n", + reg_np->name); + continue; + } + + rdata->id = i; + rdata->initdata = of_get_regulator_init_data( + iodev->dev, reg_np); + rdata->reg_node = reg_np; + rdata++; + rmode->id = i; + if (of_property_read_u32(reg_np, "op_mode", + &rmode->mode)) { + dev_warn(iodev->dev, + "no op_mode property property at %s\n", + reg_np->full_name); + + rmode->mode = S5M8767_OPMODE_NORMAL_MODE; + } + rmode++; + } + + if (of_get_property(pmic_np, "s5m8767,pmic-buck2-uses-gpio-dvs", NULL)) + pdata->buck2_gpiodvs = true; + + if (of_get_property(pmic_np, "s5m8767,pmic-buck3-uses-gpio-dvs", NULL)) + pdata->buck3_gpiodvs = true; + + if (of_get_property(pmic_np, "s5m8767,pmic-buck4-uses-gpio-dvs", NULL)) + pdata->buck4_gpiodvs = true; + + if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs || + pdata->buck4_gpiodvs) { + ret = s5m8767_pmic_dt_parse_dvs_gpio(iodev, pdata, pmic_np); + if (ret) + return -EINVAL; + + if (of_property_read_u32(pmic_np, + "s5m8767,pmic-buck-default-dvs-idx", + &pdata->buck_default_idx)) { + pdata->buck_default_idx = 0; + } else { + if (pdata->buck_default_idx >= 8) { + pdata->buck_default_idx = 0; + dev_info(iodev->dev, + "invalid value for default dvs index, use 0\n"); + } + } + dvs_voltage_nr = 8; + } + + ret = s5m8767_pmic_dt_parse_ds_gpio(iodev, pdata, pmic_np); + if (ret) + return -EINVAL; + + if (of_property_read_u32_array(pmic_np, + "s5m8767,pmic-buck2-dvs-voltage", + pdata->buck2_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck2 voltages not specified\n"); + return -EINVAL; + } + + if (of_property_read_u32_array(pmic_np, + "s5m8767,pmic-buck3-dvs-voltage", + pdata->buck3_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck3 voltages not specified\n"); + return -EINVAL; + } + + if (of_property_read_u32_array(pmic_np, + "s5m8767,pmic-buck4-dvs-voltage", + pdata->buck4_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck4 voltages not specified\n"); + return -EINVAL; + } + + return 0; +} +#else +static int s5m8767_pmic_dt_parse_pdata(struct sec_pmic_dev *iodev, + struct sec_platform_data *pdata) +{ + return 0; +} +#endif /* CONFIG_OF */ + static int s5m8767_pmic_probe(struct platform_device *pdev) { struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct sec_platform_data *pdata = dev_get_platdata(iodev->dev); + struct sec_platform_data *pdata = iodev->pdata; struct regulator_config config = { }; struct regulator_dev **rdev; struct s5m8767_info *s5m8767; int i, ret, size, buck_init; + if (iodev->dev->of_node) { + ret = s5m8767_pmic_dt_parse_pdata(iodev, pdata); + if (ret) + return ret; + } + if (!pdata) { dev_err(pdev->dev.parent, "Platform data not supplied\n"); return -ENODEV; @@ -726,6 +909,7 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) config.init_data = pdata->regulators[i].initdata; config.driver_data = s5m8767; config.regmap = iodev->regmap; + config.of_node = pdata->regulators[i].reg_node; rdev[i] = regulator_register(®ulators[id], &config); if (IS_ERR(rdev[i])) { diff --git a/include/linux/mfd/samsung/core.h b/include/linux/mfd/samsung/core.h index b50c38f8bc48..f0f4de3b4ccc 100644 --- a/include/linux/mfd/samsung/core.h +++ b/include/linux/mfd/samsung/core.h @@ -26,6 +26,7 @@ enum sec_device_type { /** * struct sec_pmic_dev - s5m87xx master device for sub-drivers * @dev: master device of the chip (can be used to access platform data) + * @pdata: pointer to private data used to pass platform data to child * @i2c: i2c client private data for regulator * @rtc: i2c client private data for rtc * @iolock: mutex for serializing io access @@ -39,6 +40,7 @@ enum sec_device_type { */ struct sec_pmic_dev { struct device *dev; + struct sec_platform_data *pdata; struct regmap *regmap; struct i2c_client *i2c; struct i2c_client *rtc; @@ -82,11 +84,11 @@ struct sec_platform_data { int buck_gpios[3]; int buck_ds[3]; - int buck2_voltage[8]; + unsigned int buck2_voltage[8]; bool buck2_gpiodvs; - int buck3_voltage[8]; + unsigned int buck3_voltage[8]; bool buck3_gpiodvs; - int buck4_voltage[8]; + unsigned int buck4_voltage[8]; bool buck4_gpiodvs; int buck_set1; @@ -127,6 +129,7 @@ struct sec_platform_data { struct sec_regulator_data { int id; struct regulator_init_data *initdata; + struct device_node *reg_node; }; /* @@ -136,7 +139,7 @@ struct sec_regulator_data { */ struct sec_opmode_data { int id; - int mode; + unsigned int mode; }; /* -- cgit v1.2.3-59-g8ed1b From 577ae39ddb037242964f5fe87fd50b0b89e3263b Mon Sep 17 00:00:00 2001 From: Jitendra Kalsaria Date: Mon, 4 Feb 2013 12:33:07 +0000 Subject: qlcnic: Updating copyright information. We recently refactored the driver source, this patch will take care of updating copyright date and adding it to newly added files. Signed-off-by: Jitendra Kalsaria Signed-off-by: David S. Miller --- Documentation/networking/LICENSE.qlcnic | 2 +- drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 2 +- drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 7 +++++++ drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h | 7 +++++++ drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c | 7 +++++++ drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c | 7 +++++++ drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c | 2 +- drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c | 2 +- drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h | 2 +- drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c | 2 +- drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h | 7 +++++++ drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c | 2 +- drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c | 7 +++++++ drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 2 +- drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c | 6 ++++++ drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c | 7 +++++++ 16 files changed, 63 insertions(+), 8 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/LICENSE.qlcnic b/Documentation/networking/LICENSE.qlcnic index e7fb2c6023bc..2ae3b64983ab 100644 --- a/Documentation/networking/LICENSE.qlcnic +++ b/Documentation/networking/LICENSE.qlcnic @@ -1,4 +1,4 @@ -Copyright (c) 2009-2011 QLogic Corporation +Copyright (c) 2009-2013 QLogic Corporation QLogic Linux qlcnic NIC Driver You may modify and redistribute the device driver code under the diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 457ca8ec9e73..3a840e741982 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -1,6 +1,6 @@ /* * QLogic qlcnic NIC Driver - * Copyright (c) 2009-2010 QLogic Corporation + * Copyright (c) 2009-2013 QLogic Corporation * * See LICENSE.qlcnic for copyright and licensing details. */ diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index 65233c846537..cdcbbb0e40cb 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -1,3 +1,10 @@ +/* + * QLogic qlcnic NIC Driver + * Copyright (c) 2009-2013 QLogic Corporation + * + * See LICENSE.qlcnic for copyright and licensing details. + */ + #include "qlcnic.h" #include #include diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h index 2b44eb14d525..b62017bae368 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h @@ -1,3 +1,10 @@ +/* + * QLogic qlcnic NIC Driver + * Copyright (c) 2009-2013 QLogic Corporation + * + * See LICENSE.qlcnic for copyright and licensing details. + */ + #ifndef __QLCNIC_83XX_HW_H #define __QLCNIC_83XX_HW_H diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c index 8163e5b338e4..f6470027d0fc 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c @@ -1,3 +1,10 @@ +/* + * QLogic qlcnic NIC Driver + * Copyright (c) 2009-2013 QLogic Corporation + * + * See LICENSE.qlcnic for copyright and licensing details. + */ + #include "qlcnic.h" #include "qlcnic_hw.h" diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c index b3ef33a67229..b0c3de9ede03 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c @@ -1,3 +1,10 @@ +/* + * QLogic qlcnic NIC Driver + * Copyright (c) 2009-2013 QLogic Corporation + * + * See LICENSE.qlcnic for copyright and licensing details. + */ + #include "qlcnic.h" #include "qlcnic_hw.h" diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c index 7372964d3a76..4a3bd64b7fb0 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c @@ -1,6 +1,6 @@ /* * QLogic qlcnic NIC Driver - * Copyright (c) 2009-2010 QLogic Corporation + * Copyright (c) 2009-2013 QLogic Corporation * * See LICENSE.qlcnic for copyright and licensing details. */ diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c index f65fd7b6f9ae..6320d551c63d 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c @@ -1,6 +1,6 @@ /* * QLogic qlcnic NIC Driver - * Copyright (c) 2009-2010 QLogic Corporation + * Copyright (c) 2009-2013 QLogic Corporation * * See LICENSE.qlcnic for copyright and licensing details. */ diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h index 74f771122985..44197ca1456c 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h @@ -1,6 +1,6 @@ /* * QLogic qlcnic NIC Driver - * Copyright (c) 2009-2010 QLogic Corporation + * Copyright (c) 2009-2013 QLogic Corporation * * See LICENSE.qlcnic for copyright and licensing details. */ diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c index 4ee92b254fc0..51716ab8739a 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c @@ -1,6 +1,6 @@ /* * QLogic qlcnic NIC Driver - * Copyright (c) 2009-2010 QLogic Corporation + * Copyright (c) 2009-2013 QLogic Corporation * * See LICENSE.qlcnic for copyright and licensing details. */ diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h index 9673e2bcff06..ed52b9cfa15d 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h @@ -1,3 +1,10 @@ +/* + * QLogic qlcnic NIC Driver + * Copyright (c) 2009-2013 QLogic Corporation + * + * See LICENSE.qlcnic for copyright and licensing details. + */ + #ifndef __QLCNIC_HW_H #define __QLCNIC_HW_H diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c index 10ad25d7aa0e..d28336fc65ab 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c @@ -1,6 +1,6 @@ /* * QLogic qlcnic NIC Driver - * Copyright (c) 2009-2010 QLogic Corporation + * Copyright (c) 2009-2013 QLogic Corporation * * See LICENSE.qlcnic for copyright and licensing details. */ diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c index d00f62832742..bb4311e9aea9 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c @@ -1,3 +1,10 @@ +/* + * QLogic qlcnic NIC Driver + * Copyright (c) 2009-2013 QLogic Corporation + * + * See LICENSE.qlcnic for copyright and licensing details. + */ + #include #include #include diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index fb0255a90ce4..d0aef32d0390 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -1,6 +1,6 @@ /* * QLogic qlcnic NIC Driver - * Copyright (c) 2009-2010 QLogic Corporation + * Copyright (c) 2009-2013 QLogic Corporation * * See LICENSE.qlcnic for copyright and licensing details. */ diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c index 8dbc8e76bc74..abbd22c814a6 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c @@ -1,3 +1,9 @@ +/* + * QLogic qlcnic NIC Driver + * Copyright (c) 2009-2013 QLogic Corporation + * + * See LICENSE.qlcnic for copyright and licensing details. + */ #include "qlcnic.h" #include "qlcnic_hdr.h" diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c index 504506349ac1..987fb6f8adc3 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c @@ -1,3 +1,10 @@ +/* + * QLogic qlcnic NIC Driver + * Copyright (c) 2009-2013 QLogic Corporation + * + * See LICENSE.qlcnic for copyright and licensing details. + */ + #include #include #include -- cgit v1.2.3-59-g8ed1b From cf9c86efecf9510e62388fd174cf607671c59fa3 Mon Sep 17 00:00:00 2001 From: Bastian Hecht Date: Wed, 12 Dec 2012 12:54:48 +0100 Subject: spi/sh-msiof: Add device tree parsing to driver This adds the capability to retrieve setup data from the device tree node. The usage of platform data is still available. Signed-off-by: Bastian Hecht Signed-off-by: Grant Likely --- Documentation/devicetree/bindings/spi/sh-msiof.txt | 12 +++++ drivers/spi/spi-sh-msiof.c | 56 +++++++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/spi/sh-msiof.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/spi/sh-msiof.txt b/Documentation/devicetree/bindings/spi/sh-msiof.txt new file mode 100644 index 000000000000..e6222106ca36 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/sh-msiof.txt @@ -0,0 +1,12 @@ +Renesas MSIOF spi controller + +Required properties: +- compatible : "renesas,sh-msiof" for SuperH or + "renesas,sh-mobile-msiof" for SH Mobile series +- reg : Offset and length of the register set for the device +- interrupts : interrupt line used by MSIOF + +Optional properties: +- num-cs : total number of chip-selects +- renesas,tx-fifo-size : Overrides the default tx fifo size given in words +- renesas,rx-fifo-size : Overrides the default rx fifo size given in words diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 96358d0eabb7..8b40d0884f8b 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -592,6 +593,37 @@ static u32 sh_msiof_spi_txrx_word(struct spi_device *spi, unsigned nsecs, return 0; } +#ifdef CONFIG_OF +static struct sh_msiof_spi_info *sh_msiof_spi_parse_dt(struct device *dev) +{ + struct sh_msiof_spi_info *info; + struct device_node *np = dev->of_node; + u32 num_cs = 0; + + info = devm_kzalloc(dev, sizeof(struct sh_msiof_spi_info), GFP_KERNEL); + if (!info) { + dev_err(dev, "failed to allocate setup data\n"); + return NULL; + } + + /* Parse the MSIOF properties */ + of_property_read_u32(np, "num-cs", &num_cs); + of_property_read_u32(np, "renesas,tx-fifo-size", + &info->tx_fifo_override); + of_property_read_u32(np, "renesas,rx-fifo-size", + &info->rx_fifo_override); + + info->num_chipselect = num_cs; + + return info; +} +#else +static struct sh_msiof_spi_info *sh_msiof_spi_parse_dt(struct device *dev) +{ + return NULL; +} +#endif + static int sh_msiof_spi_probe(struct platform_device *pdev) { struct resource *r; @@ -610,7 +642,17 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) p = spi_master_get_devdata(master); platform_set_drvdata(pdev, p); - p->info = pdev->dev.platform_data; + if (pdev->dev.of_node) + p->info = sh_msiof_spi_parse_dt(&pdev->dev); + else + p->info = pdev->dev.platform_data; + + if (!p->info) { + dev_err(&pdev->dev, "failed to obtain device info\n"); + ret = -ENXIO; + goto err1; + } + init_completion(&p->done); p->clk = clk_get(&pdev->dev, NULL); @@ -715,6 +757,17 @@ static int sh_msiof_spi_runtime_nop(struct device *dev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id sh_msiof_match[] = { + { .compatible = "renesas,sh-msiof", }, + { .compatible = "renesas,sh-mobile-msiof", }, + {}, +}; +MODULE_DEVICE_TABLE(of, sh_msiof_match); +#else +#define sh_msiof_match NULL +#endif + static struct dev_pm_ops sh_msiof_spi_dev_pm_ops = { .runtime_suspend = sh_msiof_spi_runtime_nop, .runtime_resume = sh_msiof_spi_runtime_nop, @@ -727,6 +780,7 @@ static struct platform_driver sh_msiof_spi_drv = { .name = "spi_sh_msiof", .owner = THIS_MODULE, .pm = &sh_msiof_spi_dev_pm_ops, + .of_match_table = sh_msiof_match, }, }; module_platform_driver(sh_msiof_spi_drv); -- cgit v1.2.3-59-g8ed1b From ca2eb5679f8ddffff60156af42595df44a315ef0 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 5 Feb 2013 07:25:17 +0000 Subject: tcp: remove Appropriate Byte Count support TCP Appropriate Byte Count was added by me, but later disabled. There is no point in maintaining it since it is a potential source of bugs and Linux already implements other better window protection heuristics. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 11 ----------- include/linux/tcp.h | 1 - include/net/tcp.h | 1 - kernel/sysctl_binary.c | 1 - net/ipv4/sysctl_net_ipv4.c | 7 ------- net/ipv4/tcp.c | 1 - net/ipv4/tcp_cong.c | 30 +----------------------------- net/ipv4/tcp_input.c | 15 --------------- net/ipv4/tcp_minisocks.c | 1 - 9 files changed, 1 insertion(+), 67 deletions(-) (limited to 'Documentation') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 19ac1802bfd4..dc2dc87d2557 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -130,17 +130,6 @@ somaxconn - INTEGER Defaults to 128. See also tcp_max_syn_backlog for additional tuning for TCP sockets. -tcp_abc - INTEGER - Controls Appropriate Byte Count (ABC) defined in RFC3465. - ABC is a way of increasing congestion window (cwnd) more slowly - in response to partial acknowledgments. - Possible values are: - 0 increase cwnd once per acknowledgment (no ABC) - 1 increase cwnd once per acknowledgment of full sized segment - 2 allow increase cwnd by two if acknowledgment is - of two segments to compensate for delayed acknowledgments. - Default: 0 (off) - tcp_abort_on_overflow - BOOLEAN If listening service is too slow to accept new connections, reset them. Default state is FALSE. It means that if overflow diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 4e1d2283e3cc..6d0d46138ae8 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -246,7 +246,6 @@ struct tcp_sock { u32 sacked_out; /* SACK'd packets */ u32 fackets_out; /* FACK'd packets */ u32 tso_deferred; - u32 bytes_acked; /* Appropriate Byte Counting - RFC3465 */ /* from STCP, retrans queue hinting */ struct sk_buff* lost_skb_hint; diff --git a/include/net/tcp.h b/include/net/tcp.h index 614af8b7758e..23f2e98d4b65 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -279,7 +279,6 @@ extern int sysctl_tcp_dma_copybreak; extern int sysctl_tcp_nometrics_save; extern int sysctl_tcp_moderate_rcvbuf; extern int sysctl_tcp_tso_win_divisor; -extern int sysctl_tcp_abc; extern int sysctl_tcp_mtu_probing; extern int sysctl_tcp_base_mss; extern int sysctl_tcp_workaround_signed_windows; diff --git a/kernel/sysctl_binary.c b/kernel/sysctl_binary.c index 5a6384450501..b669ca1fa103 100644 --- a/kernel/sysctl_binary.c +++ b/kernel/sysctl_binary.c @@ -387,7 +387,6 @@ static const struct bin_table bin_net_ipv4_table[] = { { CTL_INT, NET_TCP_MODERATE_RCVBUF, "tcp_moderate_rcvbuf" }, { CTL_INT, NET_TCP_TSO_WIN_DIVISOR, "tcp_tso_win_divisor" }, { CTL_STR, NET_TCP_CONG_CONTROL, "tcp_congestion_control" }, - { CTL_INT, NET_TCP_ABC, "tcp_abc" }, { CTL_INT, NET_TCP_MTU_PROBING, "tcp_mtu_probing" }, { CTL_INT, NET_TCP_BASE_MSS, "tcp_base_mss" }, { CTL_INT, NET_IPV4_TCP_WORKAROUND_SIGNED_WINDOWS, "tcp_workaround_signed_windows" }, diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 2622707602d1..960fd29d9b8e 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -632,13 +632,6 @@ static struct ctl_table ipv4_table[] = { .maxlen = TCP_CA_NAME_MAX, .proc_handler = proc_tcp_congestion_control, }, - { - .procname = "tcp_abc", - .data = &sysctl_tcp_abc, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, { .procname = "tcp_mtu_probing", .data = &sysctl_tcp_mtu_probing, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 3ec1f69c5ceb..2c7e5963c2ea 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2289,7 +2289,6 @@ int tcp_disconnect(struct sock *sk, int flags) tp->packets_out = 0; tp->snd_ssthresh = TCP_INFINITE_SSTHRESH; tp->snd_cwnd_cnt = 0; - tp->bytes_acked = 0; tp->window_clamp = 0; tcp_set_ca_state(sk, TCP_CA_Open); tcp_clear_retrans(tp); diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index cdf2e707bb10..019c2389a341 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -317,28 +317,11 @@ void tcp_slow_start(struct tcp_sock *tp) snd_cwnd = 1U; } - /* RFC3465: ABC Slow start - * Increase only after a full MSS of bytes is acked - * - * TCP sender SHOULD increase cwnd by the number of - * previously unacknowledged bytes ACKed by each incoming - * acknowledgment, provided the increase is not more than L - */ - if (sysctl_tcp_abc && tp->bytes_acked < tp->mss_cache) - return; - if (sysctl_tcp_max_ssthresh > 0 && tp->snd_cwnd > sysctl_tcp_max_ssthresh) cnt = sysctl_tcp_max_ssthresh >> 1; /* limited slow start */ else cnt = snd_cwnd; /* exponential increase */ - /* RFC3465: ABC - * We MAY increase by 2 if discovered delayed ack - */ - if (sysctl_tcp_abc > 1 && tp->bytes_acked >= 2*tp->mss_cache) - cnt <<= 1; - tp->bytes_acked = 0; - tp->snd_cwnd_cnt += cnt; while (tp->snd_cwnd_cnt >= snd_cwnd) { tp->snd_cwnd_cnt -= snd_cwnd; @@ -378,20 +361,9 @@ void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) /* In "safe" area, increase. */ if (tp->snd_cwnd <= tp->snd_ssthresh) tcp_slow_start(tp); - /* In dangerous area, increase slowly. */ - else if (sysctl_tcp_abc) { - /* RFC3465: Appropriate Byte Count - * increase once for each full cwnd acked - */ - if (tp->bytes_acked >= tp->snd_cwnd*tp->mss_cache) { - tp->bytes_acked -= tp->snd_cwnd*tp->mss_cache; - if (tp->snd_cwnd < tp->snd_cwnd_clamp) - tp->snd_cwnd++; - } - } else { + else tcp_cong_avoid_ai(tp, tp->snd_cwnd); - } } EXPORT_SYMBOL_GPL(tcp_reno_cong_avoid); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index e376aa9591bc..f56bd1082f54 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -98,7 +98,6 @@ 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_abc __read_mostly; int sysctl_tcp_early_retrans __read_mostly = 2; #define FLAG_DATA 0x01 /* Incoming frame contained data. */ @@ -2007,7 +2006,6 @@ static void tcp_enter_frto_loss(struct sock *sk, int allowed_segments, int flag) tp->snd_cwnd_cnt = 0; tp->snd_cwnd_stamp = tcp_time_stamp; tp->frto_counter = 0; - tp->bytes_acked = 0; tp->reordering = min_t(unsigned int, tp->reordering, sysctl_tcp_reordering); @@ -2056,7 +2054,6 @@ void tcp_enter_loss(struct sock *sk, int how) tp->snd_cwnd_cnt = 0; tp->snd_cwnd_stamp = tcp_time_stamp; - tp->bytes_acked = 0; tcp_clear_retrans_partial(tp); if (tcp_is_reno(tp)) @@ -2684,7 +2681,6 @@ static void tcp_init_cwnd_reduction(struct sock *sk, const bool set_ssthresh) struct tcp_sock *tp = tcp_sk(sk); tp->high_seq = tp->snd_nxt; - tp->bytes_acked = 0; tp->snd_cwnd_cnt = 0; tp->prior_cwnd = tp->snd_cwnd; tp->prr_delivered = 0; @@ -2735,7 +2731,6 @@ void tcp_enter_cwr(struct sock *sk, const int set_ssthresh) struct tcp_sock *tp = tcp_sk(sk); tp->prior_ssthresh = 0; - tp->bytes_acked = 0; if (inet_csk(sk)->icsk_ca_state < TCP_CA_CWR) { tp->undo_marker = 0; tcp_init_cwnd_reduction(sk, set_ssthresh); @@ -3417,7 +3412,6 @@ 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; - tp->bytes_acked = 0; TCP_ECN_queue_cwr(tp); tcp_moderate_cwnd(tp); } @@ -3609,15 +3603,6 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) if (after(ack, prior_snd_una)) flag |= FLAG_SND_UNA_ADVANCED; - if (sysctl_tcp_abc) { - if (icsk->icsk_ca_state < TCP_CA_CWR) - tp->bytes_acked += ack - prior_snd_una; - else if (icsk->icsk_ca_state == TCP_CA_Loss) - /* we assume just one segment left network */ - tp->bytes_acked += min(ack - prior_snd_una, - tp->mss_cache); - } - prior_fackets = tp->fackets_out; prior_in_flight = tcp_packets_in_flight(tp); diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index f35f2dfb6401..f0409287b5f4 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -446,7 +446,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->bytes_acked = 0; newtp->frto_counter = 0; newtp->frto_highmark = 0; -- cgit v1.2.3-59-g8ed1b From f21ccfa07070228e0c32dad05d1cb0d19128517c Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Mon, 14 Jan 2013 15:14:56 +0900 Subject: documentation/devicetree: Fix typos Correct spelling typos within Documentation/devicetree Signed-off-by: Masanari Iida Signed-off-by: Grant Likely --- Documentation/devicetree/bindings/arm/atmel-aic.txt | 2 +- Documentation/devicetree/bindings/arm/gic.txt | 4 ++-- Documentation/devicetree/bindings/arm/omap/omap.txt | 6 +++--- Documentation/devicetree/bindings/mips/cavium/dma-engine.txt | 2 +- Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt | 6 +++--- Documentation/devicetree/bindings/mmc/samsung-sdhci.txt | 2 +- Documentation/devicetree/bindings/powerpc/fsl/srio.txt | 4 ++-- Documentation/devicetree/bindings/regulator/tps62360-regulator.txt | 4 ++-- Documentation/devicetree/bindings/rtc/s3c-rtc.txt | 2 +- Documentation/devicetree/bindings/watchdog/samsung-wdt.txt | 2 +- 10 files changed, 17 insertions(+), 17 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/atmel-aic.txt b/Documentation/devicetree/bindings/arm/atmel-aic.txt index 19078bf5cca8..ad031211b5b8 100644 --- a/Documentation/devicetree/bindings/arm/atmel-aic.txt +++ b/Documentation/devicetree/bindings/arm/atmel-aic.txt @@ -4,7 +4,7 @@ Required properties: - compatible: Should be "atmel,-aic" - interrupt-controller: Identifies the node as an interrupt controller. - interrupt-parent: For single AIC system, it is an empty property. -- #interrupt-cells: The number of cells to define the interrupts. It sould be 3. +- #interrupt-cells: The number of cells to define the interrupts. It should be 3. The first cell is the IRQ number (aka "Peripheral IDentifier" on datasheet). The second cell is used to specify flags: bits[3:0] trigger type and level flags: diff --git a/Documentation/devicetree/bindings/arm/gic.txt b/Documentation/devicetree/bindings/arm/gic.txt index 62eb8df1e08d..3dfb0c0384f5 100644 --- a/Documentation/devicetree/bindings/arm/gic.txt +++ b/Documentation/devicetree/bindings/arm/gic.txt @@ -42,7 +42,7 @@ Main node required properties: Optional - interrupts : Interrupt source of the parent interrupt controller on - secondary GICs, or VGIC maintainance interrupt on primary GIC (see + secondary GICs, or VGIC maintenance interrupt on primary GIC (see below). - cpu-offset : per-cpu offset within the distributor and cpu interface @@ -74,7 +74,7 @@ Required properties: virtual interface control register base and size. The 2nd additional region is the GIC virtual cpu interface register base and size. -- interrupts : VGIC maintainance interrupt. +- interrupts : VGIC maintenance interrupt. Example: diff --git a/Documentation/devicetree/bindings/arm/omap/omap.txt b/Documentation/devicetree/bindings/arm/omap/omap.txt index d0051a750587..f8288ea1b530 100644 --- a/Documentation/devicetree/bindings/arm/omap/omap.txt +++ b/Documentation/devicetree/bindings/arm/omap/omap.txt @@ -39,16 +39,16 @@ Boards: - OMAP3 Tobi with Overo : Commercial expansion board with daughter board compatible = "ti,omap3-tobi", "ti,omap3-overo", "ti,omap3" -- OMAP4 SDP : Software Developement Board +- OMAP4 SDP : Software Development Board compatible = "ti,omap4-sdp", "ti,omap4430" - OMAP4 PandaBoard : Low cost community board compatible = "ti,omap4-panda", "ti,omap4430" -- OMAP3 EVM : Software Developement Board for OMAP35x, AM/DM37x +- OMAP3 EVM : Software Development Board for OMAP35x, AM/DM37x compatible = "ti,omap3-evm", "ti,omap3" -- AM335X EVM : Software Developement Board for AM335x +- AM335X EVM : Software Development Board for AM335x compatible = "ti,am335x-evm", "ti,am33xx", "ti,omap3" - AM335X Bone : Low cost community board diff --git a/Documentation/devicetree/bindings/mips/cavium/dma-engine.txt b/Documentation/devicetree/bindings/mips/cavium/dma-engine.txt index cb4291e3b1d1..a5bdff400002 100644 --- a/Documentation/devicetree/bindings/mips/cavium/dma-engine.txt +++ b/Documentation/devicetree/bindings/mips/cavium/dma-engine.txt @@ -1,7 +1,7 @@ * DMA Engine. The Octeon DMA Engine transfers between the Boot Bus and main memory. -The DMA Engine will be refered to by phandle by any device that is +The DMA Engine will be referred to by phandle by any device that is connected to it. Properties: diff --git a/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt index 792768953330..898b22294ba9 100644 --- a/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt +++ b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt @@ -11,11 +11,11 @@ Required Properties: * compatible: should be - "samsung,exynos4210-dw-mshc": for controllers with Samsung Exynos4210 - specific extentions. + specific extensions. - "samsung,exynos4412-dw-mshc": for controllers with Samsung Exynos4412 - specific extentions. + specific extensions. - "samsung,exynos5250-dw-mshc": for controllers with Samsung Exynos5250 - specific extentions. + specific extensions. * samsung,dw-mshc-ciu-div: Specifies the divider value for the card interface unit (ciu) clock. This property is applicable only for Exynos5 SoC's and diff --git a/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt b/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt index 97e9e315400d..3b3a1ee055ff 100644 --- a/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt +++ b/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt @@ -55,5 +55,5 @@ Example: }; Note: This example shows both SoC specific and board specific properties - in a single device node. The properties can be actually be seperated + in a single device node. The properties can be actually be separated into SoC specific node and board specific node. diff --git a/Documentation/devicetree/bindings/powerpc/fsl/srio.txt b/Documentation/devicetree/bindings/powerpc/fsl/srio.txt index b039bcbee134..07abf0f2f440 100644 --- a/Documentation/devicetree/bindings/powerpc/fsl/srio.txt +++ b/Documentation/devicetree/bindings/powerpc/fsl/srio.txt @@ -8,9 +8,9 @@ Properties: Definition: Must include "fsl,srio" for IP blocks with IP Block Revision Register (SRIO IPBRR1) Major ID equal to 0x01c0. - Optionally, a compatiable string of "fsl,srio-vX.Y" where X is Major + Optionally, a compatible string of "fsl,srio-vX.Y" where X is Major version in IP Block Revision Register and Y is Minor version. If this - compatiable is provided it should be ordered before "fsl,srio". + compatible is provided it should be ordered before "fsl,srio". - reg Usage: required diff --git a/Documentation/devicetree/bindings/regulator/tps62360-regulator.txt b/Documentation/devicetree/bindings/regulator/tps62360-regulator.txt index c8ca6b8f6582..1b20c3dbcdb8 100644 --- a/Documentation/devicetree/bindings/regulator/tps62360-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/tps62360-regulator.txt @@ -17,9 +17,9 @@ Optional properties: - ti,vsel1-gpio: Gpio for controlling VSEL1 line. If this property is missing, then assume that there is no GPIO for vsel1 control. -- ti,vsel0-state-high: Inital state of vsel0 input is high. +- ti,vsel0-state-high: Initial state of vsel0 input is high. If this property is missing, then assume the state as low (0). -- ti,vsel1-state-high: Inital state of vsel1 input is high. +- ti,vsel1-state-high: Initial state of vsel1 input is high. If this property is missing, then assume the state as low (0). Any property defined as part of the core regulator binding, defined in diff --git a/Documentation/devicetree/bindings/rtc/s3c-rtc.txt b/Documentation/devicetree/bindings/rtc/s3c-rtc.txt index 90ec45fd33ec..7ac7259fe9ea 100644 --- a/Documentation/devicetree/bindings/rtc/s3c-rtc.txt +++ b/Documentation/devicetree/bindings/rtc/s3c-rtc.txt @@ -7,7 +7,7 @@ Required properties: - reg: physical base address of the controller and length of memory mapped region. - interrupts: Two interrupt numbers to the cpu should be specified. First - interrupt number is the rtc alarm interupt and second interrupt number + interrupt number is the rtc alarm interrupt and second interrupt number is the rtc tick interrupt. The number of cells representing a interrupt depends on the parent interrupt controller. diff --git a/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt b/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt index 79ead8263ae4..ce0d8e78ed8f 100644 --- a/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt +++ b/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt @@ -2,7 +2,7 @@ The Samsung's Watchdog controller is used for resuming system operation after a preset amount of time during which the WDT reset event has not -occured. +occurred. Required properties: - compatible : should be "samsung,s3c2410-wdt" -- cgit v1.2.3-59-g8ed1b From 5372d2d71c46e5649e5d2edd4514adcd6fe7a085 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 12 Oct 2012 23:01:58 -0700 Subject: hwmon: Driver for Maxim MAX6697 and compatibles Add support for MAX6581, MAX6602, MAX6622, MAX6636, MAX6689, MAX6693, MAX6694, MAX6697, MAX6698, and MAX6699 temperature sensors Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare --- Documentation/devicetree/bindings/i2c/max6697.txt | 64 ++ Documentation/hwmon/max6697 | 58 ++ drivers/hwmon/Kconfig | 11 + drivers/hwmon/Makefile | 1 + drivers/hwmon/max6697.c | 726 ++++++++++++++++++++++ include/linux/platform_data/max6697.h | 36 ++ 6 files changed, 896 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/max6697.txt create mode 100644 Documentation/hwmon/max6697 create mode 100644 drivers/hwmon/max6697.c create mode 100644 include/linux/platform_data/max6697.h (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/i2c/max6697.txt b/Documentation/devicetree/bindings/i2c/max6697.txt new file mode 100644 index 000000000000..5f793998e4a4 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/max6697.txt @@ -0,0 +1,64 @@ +max6697 properties + +Required properties: +- compatible: + Should be one of + maxim,max6581 + maxim,max6602 + maxim,max6622 + maxim,max6636 + maxim,max6689 + maxim,max6693 + maxim,max6694 + maxim,max6697 + maxim,max6698 + maxim,max6699 +- reg: I2C address + +Optional properties: + +- smbus-timeout-disable + Set to disable SMBus timeout. If not specified, SMBus timeout will be + enabled. +- extended-range-enable + Only valid for MAX6581. Set to enable extended temperature range. + Extended temperature will be disabled if not specified. +- beta-compensation-enable + Only valid for MAX6693 and MX6694. Set to enable beta compensation on + remote temperature channel 1. + Beta compensation will be disabled if not specified. +- alert-mask + Alert bit mask. Alert disabled for bits set. + Select bit 0 for local temperature, bit 1..7 for remote temperatures. + If not specified, alert will be enabled for all channels. +- over-temperature-mask + Over-temperature bit mask. Over-temperature reporting disabled for + bits set. + Select bit 0 for local temperature, bit 1..7 for remote temperatures. + If not specified, over-temperature reporting will be enabled for all + channels. +- resistance-cancellation + Boolean for all chips other than MAX6581. Set to enable resistance + cancellation on remote temperature channel 1. + For MAX6581, resistance cancellation enabled for all channels if + specified as boolean, otherwise as per bit mask specified. + Only supported for remote temperatures (bit 1..7). + If not specified, resistance cancellation will be disabled for all + channels. +- transistor-ideality + For MAX6581 only. Two values; first is bit mask, second is ideality + select value as per MAX6581 data sheet. Select bit 1..7 for remote + channels. + Transistor ideality will be initialized to default (1.008) if not + specified. + +Example: + +temp-sensor@1a { + compatible = "maxim,max6697"; + reg = <0x1a>; + smbus-timeout-disable; + resistance-cancellation; + alert-mask = <0x72>; + over-temperature-mask = <0x7f>; +}; diff --git a/Documentation/hwmon/max6697 b/Documentation/hwmon/max6697 new file mode 100644 index 000000000000..6594177ededa --- /dev/null +++ b/Documentation/hwmon/max6697 @@ -0,0 +1,58 @@ +Kernel driver max6697 +===================== + +Supported chips: + * Maxim MAX6581 + Prefix: 'max6581' + Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6581.pdf + * Maxim MAX6602 + Prefix: 'max6602' + Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6602.pdf + * Maxim MAX6622 + Prefix: 'max6622' + Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6622.pdf + * Maxim MAX6636 + Prefix: 'max6636' + Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6636.pdf + * Maxim MAX6689 + Prefix: 'max6689' + Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6689.pdf + * Maxim MAX6693 + Prefix: 'max6693' + Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6693.pdf + * Maxim MAX6694 + Prefix: 'max6694' + Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6694.pdf + * Maxim MAX6697 + Prefix: 'max6697' + Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6697.pdf + * Maxim MAX6698 + Prefix: 'max6698' + Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6698.pdf + * Maxim MAX6699 + Prefix: 'max6699' + Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6699.pdf + +Author: + Guenter Roeck + +Description +----------- + +This driver implements support for several MAX6697 compatible temperature sensor +chips. The chips support one local temperature sensor plus four, six, or seven +remote temperature sensors. Remote temperature sensors are diode-connected +thermal transitors, except for MAX6698 which supports three diode-connected +thermal transistors plus three thermistors in addition to the local temperature +sensor. + +The driver provides the following sysfs attributes. temp1 is the local (chip) +temperature, temp[2..n] are remote temperatures. The actually supported +per-channel attributes are chip type and channel dependent. + +tempX_input RO temperature +tempX_max RW temperature maximum threshold +tempX_max_alarm RO temperature maximum threshold alarm +tempX_crit RW temperature critical threshold +tempX_crit_alarm RO temperature critical threshold alarm +tempX_fault RO temperature diode fault (remote sensors only) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 32f238f3caea..ef5757265f78 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -854,6 +854,17 @@ config SENSORS_MAX6650 This driver can also be built as a module. If so, the module will be called max6650. +config SENSORS_MAX6697 + tristate "Maxim MAX6697 and compatibles" + depends on I2C + help + If you say yes here you get support for MAX6581, MAX6602, MAX6622, + MAX6636, MAX6689, MAX6693, MAX6694, MAX6697, MAX6698, and MAX6699 + temperature sensor chips. + + This driver can also be built as a module. If so, the module + will be called max6697. + config SENSORS_MCP3021 tristate "Microchip MCP3021 and compatibles" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 5da287443f6c..a37a82c64da2 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -99,6 +99,7 @@ obj-$(CONFIG_SENSORS_MAX197) += max197.o obj-$(CONFIG_SENSORS_MAX6639) += max6639.o obj-$(CONFIG_SENSORS_MAX6642) += max6642.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o +obj-$(CONFIG_SENSORS_MAX6697) += max6697.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c new file mode 100644 index 000000000000..bf4aa3777fc1 --- /dev/null +++ b/drivers/hwmon/max6697.c @@ -0,0 +1,726 @@ +/* + * Copyright (c) 2012 Guenter Roeck + * + * based on max1668.c + * Copyright (c) 2011 David George + * + * 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 +#include +#include +#include +#include + +#include + +enum chips { max6581, max6602, max6622, max6636, max6689, max6693, max6694, + max6697, max6698, max6699 }; + +/* Report local sensor as temp1 */ + +static const u8 MAX6697_REG_TEMP[] = { + 0x07, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08 }; +static const u8 MAX6697_REG_TEMP_EXT[] = { + 0x57, 0x09, 0x52, 0x53, 0x54, 0x55, 0x56, 0 }; +static const u8 MAX6697_REG_MAX[] = { + 0x17, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x18 }; +static const u8 MAX6697_REG_CRIT[] = { + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 }; + +/* + * Map device tree / platform data register bit map to chip bit map. + * Applies to alert register and over-temperature register. + */ +#define MAX6697_MAP_BITS(reg) ((((reg) & 0x7e) >> 1) | \ + (((reg) & 0x01) << 6) | ((reg) & 0x80)) + +#define MAX6697_REG_STAT(n) (0x44 + (n)) + +#define MAX6697_REG_CONFIG 0x41 +#define MAX6581_CONF_EXTENDED (1 << 1) +#define MAX6693_CONF_BETA (1 << 2) +#define MAX6697_CONF_RESISTANCE (1 << 3) +#define MAX6697_CONF_TIMEOUT (1 << 5) +#define MAX6697_REG_ALERT_MASK 0x42 +#define MAX6697_REG_OVERT_MASK 0x43 + +#define MAX6581_REG_RESISTANCE 0x4a +#define MAX6581_REG_IDEALITY 0x4b +#define MAX6581_REG_IDEALITY_SELECT 0x4c +#define MAX6581_REG_OFFSET 0x4d +#define MAX6581_REG_OFFSET_SELECT 0x4e + +#define MAX6697_CONV_TIME 156 /* ms per channel, worst case */ + +struct max6697_chip_data { + int channels; + u32 have_ext; + u32 have_crit; + u32 have_fault; + u8 valid_conf; + const u8 *alarm_map; +}; + +struct max6697_data { + struct device *hwmon_dev; + + enum chips type; + const struct max6697_chip_data *chip; + + int update_interval; /* in milli-seconds */ + int temp_offset; /* in degrees C */ + + struct mutex update_lock; + unsigned long last_updated; /* In jiffies */ + bool valid; /* true if following fields are valid */ + + /* 1x local and up to 7x remote */ + u8 temp[8][4]; /* [nr][0]=temp [1]=ext [2]=max [3]=crit */ +#define MAX6697_TEMP_INPUT 0 +#define MAX6697_TEMP_EXT 1 +#define MAX6697_TEMP_MAX 2 +#define MAX6697_TEMP_CRIT 3 + u32 alarms; +}; + +/* Diode fault status bits on MAX6581 are right shifted by one bit */ +static const u8 max6581_alarm_map[] = { + 0, 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23 }; + +static const struct max6697_chip_data max6697_chip_data[] = { + [max6581] = { + .channels = 8, + .have_crit = 0xff, + .have_ext = 0x7f, + .have_fault = 0xfe, + .valid_conf = MAX6581_CONF_EXTENDED | MAX6697_CONF_TIMEOUT, + .alarm_map = max6581_alarm_map, + }, + [max6602] = { + .channels = 5, + .have_crit = 0x12, + .have_ext = 0x02, + .have_fault = 0x1e, + .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT, + }, + [max6622] = { + .channels = 5, + .have_crit = 0x12, + .have_ext = 0x02, + .have_fault = 0x1e, + .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT, + }, + [max6636] = { + .channels = 7, + .have_crit = 0x72, + .have_ext = 0x02, + .have_fault = 0x7e, + .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT, + }, + [max6689] = { + .channels = 7, + .have_crit = 0x72, + .have_ext = 0x02, + .have_fault = 0x7e, + .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT, + }, + [max6693] = { + .channels = 7, + .have_crit = 0x72, + .have_ext = 0x02, + .have_fault = 0x7e, + .valid_conf = MAX6697_CONF_RESISTANCE | MAX6693_CONF_BETA | + MAX6697_CONF_TIMEOUT, + }, + [max6694] = { + .channels = 5, + .have_crit = 0x12, + .have_ext = 0x02, + .have_fault = 0x1e, + .valid_conf = MAX6697_CONF_RESISTANCE | MAX6693_CONF_BETA | + MAX6697_CONF_TIMEOUT, + }, + [max6697] = { + .channels = 7, + .have_crit = 0x72, + .have_ext = 0x02, + .have_fault = 0x7e, + .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT, + }, + [max6698] = { + .channels = 7, + .have_crit = 0x72, + .have_ext = 0x02, + .have_fault = 0x0e, + .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT, + }, + [max6699] = { + .channels = 5, + .have_crit = 0x12, + .have_ext = 0x02, + .have_fault = 0x1e, + .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT, + }, +}; + +static struct max6697_data *max6697_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct max6697_data *data = i2c_get_clientdata(client); + struct max6697_data *ret = data; + int val; + int i; + u32 alarms; + + mutex_lock(&data->update_lock); + + if (data->valid && + !time_after(jiffies, data->last_updated + + msecs_to_jiffies(data->update_interval))) + goto abort; + + for (i = 0; i < data->chip->channels; i++) { + if (data->chip->have_ext & (1 << i)) { + val = i2c_smbus_read_byte_data(client, + MAX6697_REG_TEMP_EXT[i]); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->temp[i][MAX6697_TEMP_EXT] = val; + } + + val = i2c_smbus_read_byte_data(client, MAX6697_REG_TEMP[i]); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->temp[i][MAX6697_TEMP_INPUT] = val; + + val = i2c_smbus_read_byte_data(client, MAX6697_REG_MAX[i]); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->temp[i][MAX6697_TEMP_MAX] = val; + + if (data->chip->have_crit & (1 << i)) { + val = i2c_smbus_read_byte_data(client, + MAX6697_REG_CRIT[i]); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->temp[i][MAX6697_TEMP_CRIT] = val; + } + } + + alarms = 0; + for (i = 0; i < 3; i++) { + val = i2c_smbus_read_byte_data(client, MAX6697_REG_STAT(i)); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + alarms = (alarms << 8) | val; + } + data->alarms = alarms; + data->last_updated = jiffies; + data->valid = true; +abort: + mutex_unlock(&data->update_lock); + + return ret; +} + +static ssize_t show_temp_input(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct max6697_data *data = max6697_update_device(dev); + int temp; + + if (IS_ERR(data)) + return PTR_ERR(data); + + temp = (data->temp[index][MAX6697_TEMP_INPUT] - data->temp_offset) << 3; + temp |= data->temp[index][MAX6697_TEMP_EXT] >> 5; + + return sprintf(buf, "%d\n", temp * 125); +} + +static ssize_t show_temp(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int nr = to_sensor_dev_attr_2(devattr)->nr; + int index = to_sensor_dev_attr_2(devattr)->index; + struct max6697_data *data = max6697_update_device(dev); + int temp; + + if (IS_ERR(data)) + return PTR_ERR(data); + + temp = data->temp[nr][index]; + temp -= data->temp_offset; + + return sprintf(buf, "%d\n", temp * 1000); +} + +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int index = to_sensor_dev_attr(attr)->index; + struct max6697_data *data = max6697_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + if (data->chip->alarm_map) + index = data->chip->alarm_map[index]; + + return sprintf(buf, "%u\n", (data->alarms >> index) & 0x1); +} + +static ssize_t set_temp(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + int nr = to_sensor_dev_attr_2(devattr)->nr; + int index = to_sensor_dev_attr_2(devattr)->index; + struct i2c_client *client = to_i2c_client(dev); + struct max6697_data *data = i2c_get_clientdata(client); + long temp; + int ret; + + ret = kstrtol(buf, 10, &temp); + if (ret < 0) + return ret; + + mutex_lock(&data->update_lock); + temp = DIV_ROUND_CLOSEST(temp, 1000) + data->temp_offset; + temp = clamp_val(temp, 0, data->type == max6581 ? 255 : 127); + data->temp[nr][index] = temp; + ret = i2c_smbus_write_byte_data(client, + index == 2 ? MAX6697_REG_MAX[nr] + : MAX6697_REG_CRIT[nr], + temp); + mutex_unlock(&data->update_lock); + + return ret < 0 ? ret : count; +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL, 0); +static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 0, MAX6697_TEMP_MAX); +static SENSOR_DEVICE_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR, show_temp, set_temp, + 0, MAX6697_TEMP_CRIT); + +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input, NULL, 1); +static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 1, MAX6697_TEMP_MAX); +static SENSOR_DEVICE_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR, show_temp, set_temp, + 1, MAX6697_TEMP_CRIT); + +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp_input, NULL, 2); +static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 2, MAX6697_TEMP_MAX); +static SENSOR_DEVICE_ATTR_2(temp3_crit, S_IRUGO | S_IWUSR, show_temp, set_temp, + 2, MAX6697_TEMP_CRIT); + +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp_input, NULL, 3); +static SENSOR_DEVICE_ATTR_2(temp4_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 3, MAX6697_TEMP_MAX); +static SENSOR_DEVICE_ATTR_2(temp4_crit, S_IRUGO | S_IWUSR, show_temp, set_temp, + 3, MAX6697_TEMP_CRIT); + +static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp_input, NULL, 4); +static SENSOR_DEVICE_ATTR_2(temp5_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 4, MAX6697_TEMP_MAX); +static SENSOR_DEVICE_ATTR_2(temp5_crit, S_IRUGO | S_IWUSR, show_temp, set_temp, + 4, MAX6697_TEMP_CRIT); + +static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_temp_input, NULL, 5); +static SENSOR_DEVICE_ATTR_2(temp6_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 5, MAX6697_TEMP_MAX); +static SENSOR_DEVICE_ATTR_2(temp6_crit, S_IRUGO | S_IWUSR, show_temp, set_temp, + 5, MAX6697_TEMP_CRIT); + +static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, show_temp_input, NULL, 6); +static SENSOR_DEVICE_ATTR_2(temp7_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 6, MAX6697_TEMP_MAX); +static SENSOR_DEVICE_ATTR_2(temp7_crit, S_IRUGO | S_IWUSR, show_temp, set_temp, + 6, MAX6697_TEMP_CRIT); + +static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, show_temp_input, NULL, 7); +static SENSOR_DEVICE_ATTR_2(temp8_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 7, MAX6697_TEMP_MAX); +static SENSOR_DEVICE_ATTR_2(temp8_crit, S_IRUGO | S_IWUSR, show_temp, set_temp, + 7, MAX6697_TEMP_CRIT); + +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 22); +static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 16); +static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 17); +static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, 18); +static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, show_alarm, NULL, 19); +static SENSOR_DEVICE_ATTR(temp6_max_alarm, S_IRUGO, show_alarm, NULL, 20); +static SENSOR_DEVICE_ATTR(temp7_max_alarm, S_IRUGO, show_alarm, NULL, 21); +static SENSOR_DEVICE_ATTR(temp8_max_alarm, S_IRUGO, show_alarm, NULL, 23); + +static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 14); +static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 8); +static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, 9); +static SENSOR_DEVICE_ATTR(temp4_crit_alarm, S_IRUGO, show_alarm, NULL, 10); +static SENSOR_DEVICE_ATTR(temp5_crit_alarm, S_IRUGO, show_alarm, NULL, 11); +static SENSOR_DEVICE_ATTR(temp6_crit_alarm, S_IRUGO, show_alarm, NULL, 12); +static SENSOR_DEVICE_ATTR(temp7_crit_alarm, S_IRUGO, show_alarm, NULL, 13); +static SENSOR_DEVICE_ATTR(temp8_crit_alarm, S_IRUGO, show_alarm, NULL, 15); + +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_fault, S_IRUGO, show_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(temp7_fault, S_IRUGO, show_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(temp8_fault, S_IRUGO, show_alarm, NULL, 7); + +static struct attribute *max6697_attributes[8][7] = { + { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_fault.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_crit.dev_attr.attr, + &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_fault.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_crit.dev_attr.attr, + &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_fault.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_temp6_input.dev_attr.attr, + &sensor_dev_attr_temp6_max.dev_attr.attr, + &sensor_dev_attr_temp6_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp6_crit.dev_attr.attr, + &sensor_dev_attr_temp6_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp6_fault.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_temp7_input.dev_attr.attr, + &sensor_dev_attr_temp7_max.dev_attr.attr, + &sensor_dev_attr_temp7_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp7_crit.dev_attr.attr, + &sensor_dev_attr_temp7_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp7_fault.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_temp8_input.dev_attr.attr, + &sensor_dev_attr_temp8_max.dev_attr.attr, + &sensor_dev_attr_temp8_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp8_crit.dev_attr.attr, + &sensor_dev_attr_temp8_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp8_fault.dev_attr.attr, + NULL + } +}; + +static const struct attribute_group max6697_group[8] = { + { .attrs = max6697_attributes[0] }, + { .attrs = max6697_attributes[1] }, + { .attrs = max6697_attributes[2] }, + { .attrs = max6697_attributes[3] }, + { .attrs = max6697_attributes[4] }, + { .attrs = max6697_attributes[5] }, + { .attrs = max6697_attributes[6] }, + { .attrs = max6697_attributes[7] }, +}; + +static void max6697_get_config_of(struct device_node *node, + struct max6697_platform_data *pdata) +{ + int len; + const __be32 *prop; + + prop = of_get_property(node, "smbus-timeout-disable", &len); + if (prop) + pdata->smbus_timeout_disable = true; + prop = of_get_property(node, "extended-range-enable", &len); + if (prop) + pdata->extended_range_enable = true; + prop = of_get_property(node, "beta-compensation-enable", &len); + if (prop) + pdata->beta_compensation = true; + prop = of_get_property(node, "alert-mask", &len); + if (prop && len == sizeof(u32)) + pdata->alert_mask = be32_to_cpu(prop[0]); + prop = of_get_property(node, "over-temperature-mask", &len); + if (prop && len == sizeof(u32)) + pdata->over_temperature_mask = be32_to_cpu(prop[0]); + prop = of_get_property(node, "resistance-cancellation", &len); + if (prop) { + if (len == sizeof(u32)) + pdata->resistance_cancellation = be32_to_cpu(prop[0]); + else + pdata->resistance_cancellation = 0xfe; + } + prop = of_get_property(node, "transistor-ideality", &len); + if (prop && len == 2 * sizeof(u32)) { + pdata->ideality_mask = be32_to_cpu(prop[0]); + pdata->ideality_value = be32_to_cpu(prop[1]); + } +} + +static int max6697_init_chip(struct i2c_client *client) +{ + struct max6697_data *data = i2c_get_clientdata(client); + struct max6697_platform_data *pdata = dev_get_platdata(&client->dev); + struct max6697_platform_data p; + const struct max6697_chip_data *chip = data->chip; + int factor = chip->channels; + int ret, reg; + + /* + * Don't touch configuration if neither platform data nor OF + * configuration was specified. If that is the case, use the + * current chip configuration. + */ + if (!pdata && !client->dev.of_node) { + reg = i2c_smbus_read_byte_data(client, MAX6697_REG_CONFIG); + if (reg < 0) + return reg; + if (data->type == max6581) { + if (reg & MAX6581_CONF_EXTENDED) + data->temp_offset = 64; + reg = i2c_smbus_read_byte_data(client, + MAX6581_REG_RESISTANCE); + if (reg < 0) + return reg; + factor += hweight8(reg); + } else { + if (reg & MAX6697_CONF_RESISTANCE) + factor++; + } + goto done; + } + + if (client->dev.of_node) { + memset(&p, 0, sizeof(p)); + max6697_get_config_of(client->dev.of_node, &p); + pdata = &p; + } + + reg = 0; + if (pdata->smbus_timeout_disable && + (chip->valid_conf & MAX6697_CONF_TIMEOUT)) { + reg |= MAX6697_CONF_TIMEOUT; + } + if (pdata->extended_range_enable && + (chip->valid_conf & MAX6581_CONF_EXTENDED)) { + reg |= MAX6581_CONF_EXTENDED; + data->temp_offset = 64; + } + if (pdata->resistance_cancellation && + (chip->valid_conf & MAX6697_CONF_RESISTANCE)) { + reg |= MAX6697_CONF_RESISTANCE; + factor++; + } + if (pdata->beta_compensation && + (chip->valid_conf & MAX6693_CONF_BETA)) { + reg |= MAX6693_CONF_BETA; + } + + ret = i2c_smbus_write_byte_data(client, MAX6697_REG_CONFIG, reg); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, MAX6697_REG_ALERT_MASK, + MAX6697_MAP_BITS(pdata->alert_mask)); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, MAX6697_REG_OVERT_MASK, + MAX6697_MAP_BITS(pdata->over_temperature_mask)); + if (ret < 0) + return ret; + + if (data->type == max6581) { + factor += hweight8(pdata->resistance_cancellation >> 1); + ret = i2c_smbus_write_byte_data(client, MAX6581_REG_RESISTANCE, + pdata->resistance_cancellation >> 1); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(client, MAX6581_REG_IDEALITY, + pdata->ideality_mask >> 1); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(client, + MAX6581_REG_IDEALITY_SELECT, + pdata->ideality_value); + if (ret < 0) + return ret; + } +done: + data->update_interval = factor * MAX6697_CONV_TIME; + return 0; +} + +static void max6697_remove_files(struct i2c_client *client) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(max6697_group); i++) + sysfs_remove_group(&client->dev.kobj, &max6697_group[i]); +} + +static int max6697_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct device *dev = &client->dev; + struct max6697_data *data; + int i, err; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(struct max6697_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->type = id->driver_data; + data->chip = &max6697_chip_data[data->type]; + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + err = max6697_init_chip(client); + if (err) + return err; + + for (i = 0; i < data->chip->channels; i++) { + err = sysfs_create_file(&dev->kobj, + max6697_attributes[i][0]); + if (err) + goto error; + err = sysfs_create_file(&dev->kobj, + max6697_attributes[i][1]); + if (err) + goto error; + err = sysfs_create_file(&dev->kobj, + max6697_attributes[i][2]); + if (err) + goto error; + + if (data->chip->have_crit & (1 << i)) { + err = sysfs_create_file(&dev->kobj, + max6697_attributes[i][3]); + if (err) + goto error; + err = sysfs_create_file(&dev->kobj, + max6697_attributes[i][4]); + if (err) + goto error; + } + if (data->chip->have_fault & (1 << i)) { + err = sysfs_create_file(&dev->kobj, + max6697_attributes[i][5]); + if (err) + goto error; + } + } + + data->hwmon_dev = hwmon_device_register(dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto error; + } + + return 0; + +error: + max6697_remove_files(client); + return err; +} + +static int max6697_remove(struct i2c_client *client) +{ + struct max6697_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + max6697_remove_files(client); + + return 0; +} + +static const struct i2c_device_id max6697_id[] = { + { "max6581", max6581 }, + { "max6602", max6602 }, + { "max6622", max6622 }, + { "max6636", max6636 }, + { "max6689", max6689 }, + { "max6693", max6693 }, + { "max6694", max6694 }, + { "max6697", max6697 }, + { "max6698", max6698 }, + { "max6699", max6699 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max6697_id); + +static struct i2c_driver max6697_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "max6697", + }, + .probe = max6697_probe, + .remove = max6697_remove, + .id_table = max6697_id, +}; + +module_i2c_driver(max6697_driver); + +MODULE_AUTHOR("Guenter Roeck "); +MODULE_DESCRIPTION("MAX6697 temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/max6697.h b/include/linux/platform_data/max6697.h new file mode 100644 index 000000000000..ed9d3b3daf02 --- /dev/null +++ b/include/linux/platform_data/max6697.h @@ -0,0 +1,36 @@ +/* + * max6697.h + * Copyright (c) 2012 Guenter Roeck + * + * 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 MAX6697_H +#define MAX6697_H + +#include + +/* + * For all bit masks: + * bit 0: local temperature + * bit 1..7: remote temperatures + */ +struct max6697_platform_data { + bool smbus_timeout_disable; /* set to disable SMBus timeouts */ + bool extended_range_enable; /* set to enable extended temp range */ + bool beta_compensation; /* set to enable beta compensation */ + u8 alert_mask; /* set bit to 1 to disable alert */ + u8 over_temperature_mask; /* set bit to 1 to disable */ + u8 resistance_cancellation; /* set bit to 0 to disable + * bit mask for MAX6581, + * boolean for other chips + */ + u8 ideality_mask; /* set bit to 0 to disable */ + u8 ideality_value; /* transistor ideality as per + * MAX6581 datasheet + */ +}; + +#endif /* MAX6697_H */ -- cgit v1.2.3-59-g8ed1b From b06367070de3d9f50e4f8f82b92e77ba9a8ebf8f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 7 Sep 2012 17:34:41 -0600 Subject: hwmon: (it87) Add support for IT8771E and IT8772E Assume that IT8771E and IT8772E are fully compatible with IT8728F. IT8771E support contributed by Kelly Anderson. Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare --- Documentation/hwmon/it87 | 16 ++++++++++++---- drivers/hwmon/Kconfig | 3 ++- drivers/hwmon/it87.c | 39 +++++++++++++++++++++++++++++++++++---- 3 files changed, 49 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87 index 8386aadc0a82..c263740f0cba 100644 --- a/Documentation/hwmon/it87 +++ b/Documentation/hwmon/it87 @@ -30,6 +30,14 @@ Supported chips: Prefix: 'it8728' Addresses scanned: from Super I/O config space (8 I/O ports) Datasheet: Not publicly available + * IT8771E + Prefix: 'it8771' + Addresses scanned: from Super I/O config space (8 I/O ports) + Datasheet: Not publicly available + * IT8772E + Prefix: 'it8772' + Addresses scanned: from Super I/O config space (8 I/O ports) + Datasheet: Not publicly available * IT8782F Prefix: 'it8782' Addresses scanned: from Super I/O config space (8 I/O ports) @@ -83,8 +91,8 @@ Description ----------- This driver implements support for the IT8705F, IT8712F, IT8716F, -IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8758E, IT8781F, IT8782F, -IT8783E/F, and SiS950 chips. +IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8758E, IT8771E, IT8772E, +IT8782F, IT8783E/F, and SiS950 chips. These chips are 'Super I/O chips', supporting floppy disks, infrared ports, joysticks and other miscellaneous stuff. For hardware monitoring, they @@ -118,8 +126,8 @@ The IT8726F is just bit enhanced IT8716F with additional hardware for AMD power sequencing. Therefore the chip will appear as IT8716F to userspace applications. -The IT8728F is considered compatible with the IT8721F, until a datasheet -becomes available (hopefully.) +The IT8728F, IT8771E, and IT8772E are considered compatible with the IT8721F, +until a datasheet becomes available (hopefully.) Temperatures are measured in degrees Celsius. An alarm is triggered once when the Overtemperature Shutdown limit is crossed. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index ef5757265f78..3588b2a0915c 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -506,7 +506,8 @@ config SENSORS_IT87 help If you say yes here you get support for ITE IT8705F, IT8712F, IT8716F, IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8758E, - IT8782F, and IT8783E/F sensor chips, and the SiS950 clone. + IT8771E, IT8772E, IT8782F, and IT8783E/F sensor chips, and the + SiS950 clone. This driver can also be built as a module. If so, the module will be called it87. diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 29632e849285..37fc980fde24 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -19,6 +19,8 @@ * IT8726F Super I/O chip w/LPC interface * IT8728F Super I/O chip w/LPC interface * IT8758E Super I/O chip w/LPC interface + * IT8771E Super I/O chip w/LPC interface + * IT8772E Super I/O chip w/LPC interface * IT8782F Super I/O chip w/LPC interface * IT8783E/F Super I/O chip w/LPC interface * Sis950 A clone of the IT8705F @@ -61,8 +63,8 @@ #define DRVNAME "it87" -enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8782, - it8783 }; +enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8771, + it8772, it8782, it8783 }; static unsigned short force_id; module_param(force_id, ushort, 0); @@ -140,6 +142,8 @@ static inline void superio_exit(void) #define IT8721F_DEVID 0x8721 #define IT8726F_DEVID 0x8726 #define IT8728F_DEVID 0x8728 +#define IT8771E_DEVID 0x8771 +#define IT8772E_DEVID 0x8772 #define IT8782F_DEVID 0x8782 #define IT8783E_DEVID 0x8783 #define IT87_ACT_REG 0x30 @@ -281,6 +285,24 @@ static const struct it87_devices it87_devices[] = { | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI, .peci_mask = 0x07, }, + [it8771] = { + .name = "it8771", + .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI, + /* PECI: guesswork */ + /* 12mV ADC (OHM) */ + /* 16 bit fans (OHM) */ + .peci_mask = 0x07, + }, + [it8772] = { + .name = "it8772", + .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI, + /* PECI (coreboot) */ + /* 12mV ADC (HWSensors4, OHM) */ + /* 16 bit fans (HWSensors4, OHM) */ + .peci_mask = 0x07, + }, [it8782] = { .name = "it8782", .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET @@ -1708,6 +1730,12 @@ static int __init it87_find(unsigned short *address, case IT8728F_DEVID: sio_data->type = it8728; break; + case IT8771E_DEVID: + sio_data->type = it8771; + break; + case IT8772E_DEVID: + sio_data->type = it8772; + break; case IT8782F_DEVID: sio_data->type = it8782; break; @@ -1825,10 +1853,11 @@ static int __init it87_find(unsigned short *address, reg = superio_inb(IT87_SIO_GPIO3_REG); if (sio_data->type == it8721 || sio_data->type == it8728 || + sio_data->type == it8771 || sio_data->type == it8772 || sio_data->type == it8782) { /* * IT8721F/IT8758E, and IT8782F don't have VID pins - * at all, not sure about the IT8728F. + * at all, not sure about the IT8728F and compatibles. */ sio_data->skip_vid = 1; } else { @@ -1882,7 +1911,9 @@ static int __init it87_find(unsigned short *address, if (reg & (1 << 0)) sio_data->internal |= (1 << 0); if ((reg & (1 << 1)) || sio_data->type == it8721 || - sio_data->type == it8728) + sio_data->type == it8728 || + sio_data->type == it8771 || + sio_data->type == it8772) sio_data->internal |= (1 << 1); /* -- cgit v1.2.3-59-g8ed1b From 8c14d126ae2efbcd094b24e5413b8cbe1d2c01e4 Mon Sep 17 00:00:00 2001 From: Chris Verges Date: Sat, 5 Jan 2013 01:41:19 -0800 Subject: hwmon: (lm73) Add 'update_interval' attribute The LM73 supports four A/D conversion resolutions. The default used by the existing lm73 driver is the chip's default, 11-bit (0.25 C/LSB). This patch enables changing of this resolution from userspace via the update_interval sysfs attribute. Full details on usage are included in Documentation/hwmon/lm73. Signed-off-by: Chris Verges [linux@roeck-us.net: cleanup] Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm73 | 79 +++++++++++++++++++++++++++++++++++++++ drivers/hwmon/lm73.c | 97 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 165 insertions(+), 11 deletions(-) create mode 100644 Documentation/hwmon/lm73 (limited to 'Documentation') diff --git a/Documentation/hwmon/lm73 b/Documentation/hwmon/lm73 new file mode 100644 index 000000000000..782b3e1055ca --- /dev/null +++ b/Documentation/hwmon/lm73 @@ -0,0 +1,79 @@ +Kernel driver lm73 +================== + +Supported chips: + * Texas Instruments LM73 + Prefix: 'lm73' + Addresses scanned: I2C 0x48, 0x49, 0x4a, 0x4c, 0x4d, and 0x4e + Datasheet: Publicly available at the Texas Instruments website + http://www.ti.com/product/lm73 + +Author: Guillaume Ligneul +Documentation: Chris Verges + + +Description +----------- + +The LM73 is a digital temperature sensor. All temperature values are +given in degrees Celsius. + +Measurement Resolution Support +------------------------------ + +The LM73 supports four resolutions, defined in terms of degrees C per +LSB: 0.25, 0.125, 0.0625, and 0.3125. Changing the resolution mode +affects the conversion time of the LM73's analog-to-digital converter. +From userspace, the desired resolution can be specified as a function of +conversion time via the 'update_interval' sysfs attribute for the +device. This attribute will normalize ranges of input values to the +maximum times defined for the resolution in the datasheet. + + Resolution Conv. Time Input Range + (C/LSB) (msec) (msec) + -------------------------------------- + 0.25 14 0..14 + 0.125 28 15..28 + 0.0625 56 29..56 + 0.03125 112 57..infinity + -------------------------------------- + +The following examples show how the 'update_interval' attribute can be +used to change the conversion time: + + $ echo 0 > update_interval + $ cat update_interval + 14 + $ cat temp1_input + 24250 + + $ echo 22 > update_interval + $ cat update_interval + 28 + $ cat temp1_input + 24125 + + $ echo 56 > update_interval + $ cat update_interval + 56 + $ cat temp1_input + 24062 + + $ echo 85 > update_interval + $ cat update_interval + 112 + $ cat temp1_input + 24031 + +As shown here, the lm73 driver automatically adjusts any user input for +'update_interval' via a step function. Reading back the +'update_interval' value after a write operation will confirm the +conversion time actively in use. + +Mathematically, the resolution can be derived from the conversion time +via the following function: + + g(x) = 0.250 * [log(x/14) / log(2)] + +where 'x' is the output from 'update_interval' and 'g(x)' is the +resolution in degrees C per LSB. diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c index dad83fea807d..0b19f03950e0 100644 --- a/drivers/hwmon/lm73.c +++ b/drivers/hwmon/lm73.c @@ -39,8 +39,24 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4c, #define LM73_TEMP_MIN (-256000 / 250) #define LM73_TEMP_MAX (255750 / 250) -/*-----------------------------------------------------------------------*/ +#define LM73_CTRL_RES_SHIFT 5 +#define LM73_CTRL_RES_MASK (BIT(5) | BIT(6)) +#define LM73_CTRL_TO_MASK BIT(7) + +static const unsigned short lm73_convrates[] = { + 14, /* 11-bits (0.25000 C/LSB): RES1 Bit = 0, RES0 Bit = 0 */ + 28, /* 12-bits (0.12500 C/LSB): RES1 Bit = 0, RES0 Bit = 1 */ + 56, /* 13-bits (0.06250 C/LSB): RES1 Bit = 1, RES0 Bit = 0 */ + 112, /* 14-bits (0.03125 C/LSB): RES1 Bit = 1, RES0 Bit = 1 */ +}; + +struct lm73_data { + struct device *hwmon_dev; + struct mutex lock; + u8 ctrl; /* control register value */ +}; +/*-----------------------------------------------------------------------*/ static ssize_t set_temp(struct device *dev, struct device_attribute *da, const char *buf, size_t count) @@ -78,6 +94,51 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *da, return scnprintf(buf, PAGE_SIZE, "%d\n", temp); } +static ssize_t set_convrate(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm73_data *data = i2c_get_clientdata(client); + unsigned long convrate; + s32 err; + int res = 0; + + err = kstrtoul(buf, 10, &convrate); + if (err < 0) + return err; + + /* + * Convert the desired conversion rate into register bits. + * res is already initialized, and everything past the second-to-last + * value in the array is treated as belonging to the last value + * in the array. + */ + while (res < (ARRAY_SIZE(lm73_convrates) - 1) && + convrate > lm73_convrates[res]) + res++; + + mutex_lock(&data->lock); + data->ctrl &= LM73_CTRL_TO_MASK; + data->ctrl |= res << LM73_CTRL_RES_SHIFT; + err = i2c_smbus_write_byte_data(client, LM73_REG_CTRL, data->ctrl); + mutex_unlock(&data->lock); + + if (err < 0) + return err; + + return count; +} + +static ssize_t show_convrate(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm73_data *data = i2c_get_clientdata(client); + int res; + + res = (data->ctrl & LM73_CTRL_RES_MASK) >> LM73_CTRL_RES_SHIFT; + return scnprintf(buf, PAGE_SIZE, "%hu\n", lm73_convrates[res]); +} /*-----------------------------------------------------------------------*/ @@ -89,13 +150,14 @@ static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp, set_temp, LM73_REG_MIN); static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, LM73_REG_INPUT); - +static SENSOR_DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, + show_convrate, set_convrate, 0); static struct attribute *lm73_attributes[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp1_min.dev_attr.attr, - + &sensor_dev_attr_update_interval.dev_attr.attr, NULL }; @@ -110,23 +172,36 @@ static const struct attribute_group lm73_group = { static int lm73_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct device *hwmon_dev; int status; + struct lm73_data *data; + int ctrl; + + data = devm_kzalloc(&client->dev, sizeof(struct lm73_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + mutex_init(&data->lock); + + ctrl = i2c_smbus_read_byte_data(client, LM73_REG_CTRL); + if (ctrl < 0) + return ctrl; + data->ctrl = ctrl; /* Register sysfs hooks */ status = sysfs_create_group(&client->dev.kobj, &lm73_group); if (status) return status; - hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(hwmon_dev)) { - status = PTR_ERR(hwmon_dev); + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); goto exit_remove; } - i2c_set_clientdata(client, hwmon_dev); dev_info(&client->dev, "%s: sensor '%s'\n", - dev_name(hwmon_dev), client->name); + dev_name(data->hwmon_dev), client->name); return 0; @@ -137,9 +212,9 @@ exit_remove: static int lm73_remove(struct i2c_client *client) { - struct device *hwmon_dev = i2c_get_clientdata(client); + struct lm73_data *data = i2c_get_clientdata(client); - hwmon_device_unregister(hwmon_dev); + hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &lm73_group); return 0; } -- cgit v1.2.3-59-g8ed1b From 2bf9233a108cea32edbb5a65afa8e609fa146577 Mon Sep 17 00:00:00 2001 From: Chris Verges Date: Sat, 5 Jan 2013 01:41:20 -0800 Subject: hwmon: (lm73) Add support for max/min alarms Add support for temp1_min_alarm and temp1_max_alarm Signed-off-by: Chris Verges [linux@roeck-us.net: cleanup; dropped platform data and interrupt support] Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm73 | 11 +++++++++++ drivers/hwmon/lm73.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm73 b/Documentation/hwmon/lm73 index 782b3e1055ca..8af059dcb642 100644 --- a/Documentation/hwmon/lm73 +++ b/Documentation/hwmon/lm73 @@ -77,3 +77,14 @@ via the following function: where 'x' is the output from 'update_interval' and 'g(x)' is the resolution in degrees C per LSB. + +Alarm Support +------------- + +The LM73 features a simple over-temperature alarm mechanism. This +feature is exposed via the sysfs attributes. + +The attributes 'temp1_max_alarm' and 'temp1_min_alarm' are flags +provided by the LM73 that indicate whether the measured temperature has +passed the 'temp1_max' and 'temp1_min' thresholds, respectively. These +values _must_ be read to clear the registers on the LM73. diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c index 0b19f03950e0..9bde9644b102 100644 --- a/drivers/hwmon/lm73.c +++ b/drivers/hwmon/lm73.c @@ -8,6 +8,7 @@ * Guillaume Ligneul * Adrien Demarez * Jeremy Laine + * Chris Verges * * This software program is licensed subject to the GNU General Public License * (GPL).Version 2,June 1991, available at @@ -43,6 +44,9 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4c, #define LM73_CTRL_RES_MASK (BIT(5) | BIT(6)) #define LM73_CTRL_TO_MASK BIT(7) +#define LM73_CTRL_HI_SHIFT 2 +#define LM73_CTRL_LO_SHIFT 1 + static const unsigned short lm73_convrates[] = { 14, /* 11-bits (0.25000 C/LSB): RES1 Bit = 0, RES0 Bit = 0 */ 28, /* 12-bits (0.12500 C/LSB): RES1 Bit = 0, RES0 Bit = 1 */ @@ -140,6 +144,28 @@ static ssize_t show_convrate(struct device *dev, struct device_attribute *da, return scnprintf(buf, PAGE_SIZE, "%hu\n", lm73_convrates[res]); } +static ssize_t show_maxmin_alarm(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct lm73_data *data = i2c_get_clientdata(client); + s32 ctrl; + + mutex_lock(&data->lock); + ctrl = i2c_smbus_read_byte_data(client, LM73_REG_CTRL); + if (ctrl < 0) + goto abort; + data->ctrl = ctrl; + mutex_unlock(&data->lock); + + return scnprintf(buf, PAGE_SIZE, "%d\n", (ctrl >> attr->index) & 1); + +abort: + mutex_unlock(&data->lock); + return ctrl; +} + /*-----------------------------------------------------------------------*/ /* sysfs attributes for hwmon */ @@ -152,12 +178,18 @@ static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, LM73_REG_INPUT); static SENSOR_DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_convrate, set_convrate, 0); +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, + show_maxmin_alarm, NULL, LM73_CTRL_HI_SHIFT); +static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, + show_maxmin_alarm, NULL, LM73_CTRL_LO_SHIFT); static struct attribute *lm73_attributes[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp1_min.dev_attr.attr, &sensor_dev_attr_update_interval.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, NULL }; -- cgit v1.2.3-59-g8ed1b From ceeadc5c5187b78ffbea737c7a82ecc1e31e80df Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 20 Aug 2009 14:49:25 -0700 Subject: hwmon: Driver for Texas Instruments INA209 Add support for the TI / Burr-Brown INA209 voltage / current / power monitor. Cc: Paul Hays Cc: Ira W. Snyder Tested-by: Ira W. Snyder Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/i2c/ina209.txt | 18 + Documentation/hwmon/ina209 | 93 ++++ drivers/hwmon/Kconfig | 10 + drivers/hwmon/Makefile | 1 + drivers/hwmon/ina209.c | 636 +++++++++++++++++++++++ 5 files changed, 758 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/ina209.txt create mode 100644 Documentation/hwmon/ina209 create mode 100644 drivers/hwmon/ina209.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/i2c/ina209.txt b/Documentation/devicetree/bindings/i2c/ina209.txt new file mode 100644 index 000000000000..9dd2bee80840 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/ina209.txt @@ -0,0 +1,18 @@ +ina209 properties + +Required properties: +- compatible: Must be "ti,ina209" +- reg: I2C address + +Optional properties: + +- shunt-resistor + Shunt resistor value in micro-Ohm + +Example: + +temp-sensor@4c { + compatible = "ti,ina209"; + reg = <0x4c>; + shunt-resistor = <5000>; +}; diff --git a/Documentation/hwmon/ina209 b/Documentation/hwmon/ina209 new file mode 100644 index 000000000000..672501de4509 --- /dev/null +++ b/Documentation/hwmon/ina209 @@ -0,0 +1,93 @@ +Kernel driver ina209 +===================== + +Supported chips: + * Burr-Brown / Texas Instruments INA209 + Prefix: 'ina209' + Addresses scanned: - + Datasheet: + http://www.ti.com/lit/gpn/ina209 + +Author: Paul Hays +Author: Ira W. Snyder +Author: Guenter Roeck + + +Description +----------- + +The TI / Burr-Brown INA209 monitors voltage, current, and power on the high side +of a D.C. power supply. It can perform measurements and calculations in the +background to supply readings at any time. It includes a programmable +calibration multiplier to scale the displayed current and power values. + + +Sysfs entries +------------- + +The INA209 chip is highly configurable both via hardwiring and via +the I2C bus. See the datasheet for details. + +This tries to expose most monitoring features of the hardware via +sysfs. It does not support every feature of this chip. + + +in0_input shunt voltage (mV) +in0_input_highest shunt voltage historical maximum reading (mV) +in0_input_lowest shunt voltage historical minimum reading (mV) +in0_reset_history reset shunt voltage history +in0_max shunt voltage max alarm limit (mV) +in0_min shunt voltage min alarm limit (mV) +in0_crit_max shunt voltage crit max alarm limit (mV) +in0_crit_min shunt voltage crit min alarm limit (mV) +in0_max_alarm shunt voltage max alarm limit exceeded +in0_min_alarm shunt voltage min alarm limit exceeded +in0_crit_max_alarm shunt voltage crit max alarm limit exceeded +in0_crit_min_alarm shunt voltage crit min alarm limit exceeded + +in1_input bus voltage (mV) +in1_input_highest bus voltage historical maximum reading (mV) +in1_input_lowest bus voltage historical minimum reading (mV) +in1_reset_history reset bus voltage history +in1_max bus voltage max alarm limit (mV) +in1_min bus voltage min alarm limit (mV) +in1_crit_max bus voltage crit max alarm limit (mV) +in1_crit_min bus voltage crit min alarm limit (mV) +in1_max_alarm bus voltage max alarm limit exceeded +in1_min_alarm bus voltage min alarm limit exceeded +in1_crit_max_alarm bus voltage crit max alarm limit exceeded +in1_crit_min_alarm bus voltage crit min alarm limit exceeded + +power1_input power measurement (uW) +power1_input_highest power historical maximum reading (uW) +power1_reset_history reset power history +power1_max power max alarm limit (uW) +power1_crit power crit alarm limit (uW) +power1_max_alarm power max alarm limit exceeded +power1_crit_alarm power crit alarm limit exceeded + +curr1_input current measurement (mA) + +update_interval data conversion time; affects number of samples used + to average results for shunt and bus voltages. + +General Remarks +--------------- + +The power and current registers in this chip require that the calibration +register is programmed correctly before they are used. Normally this is expected +to be done in the BIOS. In the absence of BIOS programming, the shunt resistor +voltage can be provided using platform data. The driver uses platform data from +the ina2xx driver for this purpose. If calibration register data is not provided +via platform data, the driver checks if the calibration register has been +programmed (ie has a value not equal to zero). If so, this value is retained. +Otherwise, a default value reflecting a shunt resistor value of 10 mOhm is +programmed into the calibration register. + + +Output Pins +----------- + +Output pin programming is a board feature which depends on the BIOS. It is +outside the scope of a hardware monitoring driver to enable or disable output +pins. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 3588b2a0915c..5e77cef09ca6 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1157,6 +1157,16 @@ config SENSORS_AMC6821 This driver can also be build as a module. If so, the module will be called amc6821. +config SENSORS_INA209 + tristate "TI / Burr Brown INA209" + depends on I2C + help + If you say yes here you get support for the TI / Burr Brown INA209 + voltage / current / power monitor I2C interface. + + This driver can also be built as a module. If so, the module will + be called ina209. + config SENSORS_INA2XX tristate "Texas Instruments INA219 and compatibles" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index a37a82c64da2..8d6d97ea7c1e 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o +obj-$(CONFIG_SENSORS_INA209) += ina209.o obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_JC42) += jc42.o diff --git a/drivers/hwmon/ina209.c b/drivers/hwmon/ina209.c new file mode 100644 index 000000000000..c6fdd5bd395e --- /dev/null +++ b/drivers/hwmon/ina209.c @@ -0,0 +1,636 @@ +/* + * Driver for the Texas Instruments / Burr Brown INA209 + * Bidirectional Current/Power Monitor + * + * Copyright (C) 2012 Guenter Roeck + * + * Derived from Ira W. Snyder's original driver submission + * Copyright (C) 2008 Paul Hays + * Copyright (C) 2008-2009 Ira W. Snyder + * + * Aligned with ina2xx driver + * Copyright (C) 2012 Lothar Felten + * Thanks to Jan Volkering + * + * 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. + * + * Datasheet: + * http://www.ti.com/lit/gpn/ina209 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* register definitions */ +#define INA209_CONFIGURATION 0x00 +#define INA209_STATUS 0x01 +#define INA209_STATUS_MASK 0x02 +#define INA209_SHUNT_VOLTAGE 0x03 +#define INA209_BUS_VOLTAGE 0x04 +#define INA209_POWER 0x05 +#define INA209_CURRENT 0x06 +#define INA209_SHUNT_VOLTAGE_POS_PEAK 0x07 +#define INA209_SHUNT_VOLTAGE_NEG_PEAK 0x08 +#define INA209_BUS_VOLTAGE_MAX_PEAK 0x09 +#define INA209_BUS_VOLTAGE_MIN_PEAK 0x0a +#define INA209_POWER_PEAK 0x0b +#define INA209_SHUNT_VOLTAGE_POS_WARN 0x0c +#define INA209_SHUNT_VOLTAGE_NEG_WARN 0x0d +#define INA209_POWER_WARN 0x0e +#define INA209_BUS_VOLTAGE_OVER_WARN 0x0f +#define INA209_BUS_VOLTAGE_UNDER_WARN 0x10 +#define INA209_POWER_OVER_LIMIT 0x11 +#define INA209_BUS_VOLTAGE_OVER_LIMIT 0x12 +#define INA209_BUS_VOLTAGE_UNDER_LIMIT 0x13 +#define INA209_CRITICAL_DAC_POS 0x14 +#define INA209_CRITICAL_DAC_NEG 0x15 +#define INA209_CALIBRATION 0x16 + +#define INA209_REGISTERS 0x17 + +#define INA209_CONFIG_DEFAULT 0x3c47 /* PGA=8, full range */ +#define INA209_SHUNT_DEFAULT 10000 /* uOhm */ + +struct ina209_data { + struct device *hwmon_dev; + + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + + u16 regs[INA209_REGISTERS]; /* All chip registers */ + + u16 config_orig; /* Original configuration */ + u16 calibration_orig; /* Original calibration */ + u16 update_interval; +}; + +static struct ina209_data *ina209_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ina209_data *data = i2c_get_clientdata(client); + struct ina209_data *ret = data; + s32 val; + int i; + + mutex_lock(&data->update_lock); + + if (!data->valid || + time_after(jiffies, data->last_updated + data->update_interval)) { + for (i = 0; i < ARRAY_SIZE(data->regs); i++) { + val = i2c_smbus_read_word_swapped(client, i); + if (val < 0) { + ret = ERR_PTR(val); + goto abort; + } + data->regs[i] = val; + } + data->last_updated = jiffies; + data->valid = true; + } +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +/* + * Read a value from a device register and convert it to the + * appropriate sysfs units + */ +static long ina209_from_reg(const u8 reg, const u16 val) +{ + switch (reg) { + case INA209_SHUNT_VOLTAGE: + case INA209_SHUNT_VOLTAGE_POS_PEAK: + case INA209_SHUNT_VOLTAGE_NEG_PEAK: + case INA209_SHUNT_VOLTAGE_POS_WARN: + case INA209_SHUNT_VOLTAGE_NEG_WARN: + /* LSB=10 uV. Convert to mV. */ + return DIV_ROUND_CLOSEST(val, 100); + + case INA209_BUS_VOLTAGE: + case INA209_BUS_VOLTAGE_MAX_PEAK: + case INA209_BUS_VOLTAGE_MIN_PEAK: + case INA209_BUS_VOLTAGE_OVER_WARN: + case INA209_BUS_VOLTAGE_UNDER_WARN: + case INA209_BUS_VOLTAGE_OVER_LIMIT: + case INA209_BUS_VOLTAGE_UNDER_LIMIT: + /* LSB=4 mV, last 3 bits unused */ + return (val >> 3) * 4; + + case INA209_CRITICAL_DAC_POS: + /* LSB=1 mV, in the upper 8 bits */ + return val >> 8; + + case INA209_CRITICAL_DAC_NEG: + /* LSB=1 mV, in the upper 8 bits */ + return -1 * (val >> 8); + + case INA209_POWER: + case INA209_POWER_PEAK: + case INA209_POWER_WARN: + case INA209_POWER_OVER_LIMIT: + /* LSB=20 mW. Convert to uW */ + return val * 20 * 1000L; + + case INA209_CURRENT: + /* LSB=1 mA (selected). Is in mA */ + return val; + } + + /* programmer goofed */ + WARN_ON_ONCE(1); + return 0; +} + +/* + * Take a value and convert it to register format, clamping the value + * to the appropriate range. + */ +static int ina209_to_reg(u8 reg, u16 old, long val) +{ + switch (reg) { + case INA209_SHUNT_VOLTAGE_POS_WARN: + case INA209_SHUNT_VOLTAGE_NEG_WARN: + /* Limit to +- 320 mV, 10 uV LSB */ + return clamp_val(val, -320, 320) * 100; + + case INA209_BUS_VOLTAGE_OVER_WARN: + case INA209_BUS_VOLTAGE_UNDER_WARN: + case INA209_BUS_VOLTAGE_OVER_LIMIT: + case INA209_BUS_VOLTAGE_UNDER_LIMIT: + /* + * Limit to 0-32000 mV, 4 mV LSB + * + * The last three bits aren't part of the value, but we'll + * preserve them in their original state. + */ + return (DIV_ROUND_CLOSEST(clamp_val(val, 0, 32000), 4) << 3) + | (old & 0x7); + + case INA209_CRITICAL_DAC_NEG: + /* + * Limit to -255-0 mV, 1 mV LSB + * Convert the value to a positive value for the register + * + * The value lives in the top 8 bits only, be careful + * and keep original value of other bits. + */ + return (clamp_val(-val, 0, 255) << 8) | (old & 0xff); + + case INA209_CRITICAL_DAC_POS: + /* + * Limit to 0-255 mV, 1 mV LSB + * + * The value lives in the top 8 bits only, be careful + * and keep original value of other bits. + */ + return (clamp_val(val, 0, 255) << 8) | (old & 0xff); + + case INA209_POWER_WARN: + case INA209_POWER_OVER_LIMIT: + /* 20 mW LSB */ + return DIV_ROUND_CLOSEST(val, 20 * 1000); + } + + /* Other registers are read-only, return access error */ + return -EACCES; +} + +static int ina209_interval_from_reg(u16 reg) +{ + return 68 >> (15 - ((reg >> 3) & 0x0f)); +} + +static u16 ina209_reg_from_interval(u16 config, long interval) +{ + int i, adc; + + if (interval <= 0) { + adc = 8; + } else { + adc = 15; + for (i = 34 + 34 / 2; i; i >>= 1) { + if (i < interval) + break; + adc--; + } + } + return (config & 0xf807) | (adc << 3) | (adc << 7); +} + +static ssize_t ina209_set_interval(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ina209_data *data = ina209_update_device(dev); + long val; + u16 regval; + int ret; + + if (IS_ERR(data)) + return PTR_ERR(data); + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + mutex_lock(&data->update_lock); + regval = ina209_reg_from_interval(data->regs[INA209_CONFIGURATION], + val); + i2c_smbus_write_word_swapped(client, INA209_CONFIGURATION, regval); + data->regs[INA209_CONFIGURATION] = regval; + data->update_interval = ina209_interval_from_reg(regval); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t ina209_show_interval(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ina209_data *data = i2c_get_clientdata(client); + + return snprintf(buf, PAGE_SIZE, "%d\n", data->update_interval); +} + +/* + * History is reset by writing 1 into bit 0 of the respective peak register. + * Since more than one peak register may be affected by the scope of a + * reset_history attribute write, use a bit mask in attr->index to identify + * which registers are affected. + */ +static u16 ina209_reset_history_regs[] = { + INA209_SHUNT_VOLTAGE_POS_PEAK, + INA209_SHUNT_VOLTAGE_NEG_PEAK, + INA209_BUS_VOLTAGE_MAX_PEAK, + INA209_BUS_VOLTAGE_MIN_PEAK, + INA209_POWER_PEAK +}; + +static ssize_t ina209_reset_history(struct device *dev, + struct device_attribute *da, + const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ina209_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + u32 mask = attr->index; + long val; + int i, ret; + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + mutex_lock(&data->update_lock); + for (i = 0; i < ARRAY_SIZE(ina209_reset_history_regs); i++) { + if (mask & (1 << i)) + i2c_smbus_write_word_swapped(client, + ina209_reset_history_regs[i], 1); + } + data->valid = false; + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t ina209_set_value(struct device *dev, + struct device_attribute *da, + const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ina209_data *data = ina209_update_device(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int reg = attr->index; + long val; + int ret; + + if (IS_ERR(data)) + return PTR_ERR(data); + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + mutex_lock(&data->update_lock); + ret = ina209_to_reg(reg, data->regs[reg], val); + if (ret < 0) { + count = ret; + goto abort; + } + i2c_smbus_write_word_swapped(client, reg, ret); + data->regs[reg] = ret; +abort: + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t ina209_show_value(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ina209_data *data = ina209_update_device(dev); + long val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = ina209_from_reg(attr->index, data->regs[attr->index]); + return snprintf(buf, PAGE_SIZE, "%ld\n", val); +} + +static ssize_t ina209_show_alarm(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ina209_data *data = ina209_update_device(dev); + const unsigned int mask = attr->index; + u16 status; + + if (IS_ERR(data)) + return PTR_ERR(data); + + status = data->regs[INA209_STATUS]; + + /* + * All alarms are in the INA209_STATUS register. To avoid a long + * switch statement, the mask is passed in attr->index + */ + return snprintf(buf, PAGE_SIZE, "%u\n", !!(status & mask)); +} + +/* Shunt voltage, history, limits, alarms */ +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ina209_show_value, NULL, + INA209_SHUNT_VOLTAGE); +static SENSOR_DEVICE_ATTR(in0_input_highest, S_IRUGO, ina209_show_value, NULL, + INA209_SHUNT_VOLTAGE_POS_PEAK); +static SENSOR_DEVICE_ATTR(in0_input_lowest, S_IRUGO, ina209_show_value, NULL, + INA209_SHUNT_VOLTAGE_NEG_PEAK); +static SENSOR_DEVICE_ATTR(in0_reset_history, S_IWUSR, NULL, + ina209_reset_history, (1 << 0) | (1 << 1)); +static SENSOR_DEVICE_ATTR(in0_max, S_IRUGO | S_IWUSR, ina209_show_value, + ina209_set_value, INA209_SHUNT_VOLTAGE_POS_WARN); +static SENSOR_DEVICE_ATTR(in0_min, S_IRUGO | S_IWUSR, ina209_show_value, + ina209_set_value, INA209_SHUNT_VOLTAGE_NEG_WARN); +static SENSOR_DEVICE_ATTR(in0_crit_max, S_IRUGO | S_IWUSR, ina209_show_value, + ina209_set_value, INA209_CRITICAL_DAC_POS); +static SENSOR_DEVICE_ATTR(in0_crit_min, S_IRUGO | S_IWUSR, ina209_show_value, + ina209_set_value, INA209_CRITICAL_DAC_NEG); + +static SENSOR_DEVICE_ATTR(in0_min_alarm, S_IRUGO, ina209_show_alarm, NULL, + 1 << 11); +static SENSOR_DEVICE_ATTR(in0_max_alarm, S_IRUGO, ina209_show_alarm, NULL, + 1 << 12); +static SENSOR_DEVICE_ATTR(in0_crit_min_alarm, S_IRUGO, ina209_show_alarm, NULL, + 1 << 6); +static SENSOR_DEVICE_ATTR(in0_crit_max_alarm, S_IRUGO, ina209_show_alarm, NULL, + 1 << 7); + +/* Bus voltage, history, limits, alarms */ +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ina209_show_value, NULL, + INA209_BUS_VOLTAGE); +static SENSOR_DEVICE_ATTR(in1_input_highest, S_IRUGO, ina209_show_value, NULL, + INA209_BUS_VOLTAGE_MAX_PEAK); +static SENSOR_DEVICE_ATTR(in1_input_lowest, S_IRUGO, ina209_show_value, NULL, + INA209_BUS_VOLTAGE_MIN_PEAK); +static SENSOR_DEVICE_ATTR(in1_reset_history, S_IWUSR, NULL, + ina209_reset_history, (1 << 2) | (1 << 3)); +static SENSOR_DEVICE_ATTR(in1_max, S_IRUGO | S_IWUSR, ina209_show_value, + ina209_set_value, INA209_BUS_VOLTAGE_OVER_WARN); +static SENSOR_DEVICE_ATTR(in1_min, S_IRUGO | S_IWUSR, ina209_show_value, + ina209_set_value, INA209_BUS_VOLTAGE_UNDER_WARN); +static SENSOR_DEVICE_ATTR(in1_crit_max, S_IRUGO | S_IWUSR, ina209_show_value, + ina209_set_value, INA209_BUS_VOLTAGE_OVER_LIMIT); +static SENSOR_DEVICE_ATTR(in1_crit_min, S_IRUGO | S_IWUSR, ina209_show_value, + ina209_set_value, INA209_BUS_VOLTAGE_UNDER_LIMIT); + +static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ina209_show_alarm, NULL, + 1 << 14); +static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ina209_show_alarm, NULL, + 1 << 15); +static SENSOR_DEVICE_ATTR(in1_crit_min_alarm, S_IRUGO, ina209_show_alarm, NULL, + 1 << 9); +static SENSOR_DEVICE_ATTR(in1_crit_max_alarm, S_IRUGO, ina209_show_alarm, NULL, + 1 << 10); + +/* Power */ +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ina209_show_value, NULL, + INA209_POWER); +static SENSOR_DEVICE_ATTR(power1_input_highest, S_IRUGO, ina209_show_value, + NULL, INA209_POWER_PEAK); +static SENSOR_DEVICE_ATTR(power1_reset_history, S_IWUSR, NULL, + ina209_reset_history, 1 << 4); +static SENSOR_DEVICE_ATTR(power1_max, S_IRUGO | S_IWUSR, ina209_show_value, + ina209_set_value, INA209_POWER_WARN); +static SENSOR_DEVICE_ATTR(power1_crit, S_IRUGO | S_IWUSR, ina209_show_value, + ina209_set_value, INA209_POWER_OVER_LIMIT); + +static SENSOR_DEVICE_ATTR(power1_max_alarm, S_IRUGO, ina209_show_alarm, NULL, + 1 << 13); +static SENSOR_DEVICE_ATTR(power1_crit_alarm, S_IRUGO, ina209_show_alarm, NULL, + 1 << 8); + +/* Current */ +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ina209_show_value, NULL, + INA209_CURRENT); + +static SENSOR_DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, + ina209_show_interval, ina209_set_interval, 0); + +/* + * Finally, construct an array of pointers to members of the above objects, + * as required for sysfs_create_group() + */ +static struct attribute *ina209_attributes[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_input_highest.dev_attr.attr, + &sensor_dev_attr_in0_input_lowest.dev_attr.attr, + &sensor_dev_attr_in0_reset_history.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in0_crit_max.dev_attr.attr, + &sensor_dev_attr_in0_crit_min.dev_attr.attr, + &sensor_dev_attr_in0_max_alarm.dev_attr.attr, + &sensor_dev_attr_in0_min_alarm.dev_attr.attr, + &sensor_dev_attr_in0_crit_max_alarm.dev_attr.attr, + &sensor_dev_attr_in0_crit_min_alarm.dev_attr.attr, + + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_input_highest.dev_attr.attr, + &sensor_dev_attr_in1_input_lowest.dev_attr.attr, + &sensor_dev_attr_in1_reset_history.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in1_crit_max.dev_attr.attr, + &sensor_dev_attr_in1_crit_min.dev_attr.attr, + &sensor_dev_attr_in1_max_alarm.dev_attr.attr, + &sensor_dev_attr_in1_min_alarm.dev_attr.attr, + &sensor_dev_attr_in1_crit_max_alarm.dev_attr.attr, + &sensor_dev_attr_in1_crit_min_alarm.dev_attr.attr, + + &sensor_dev_attr_power1_input.dev_attr.attr, + &sensor_dev_attr_power1_input_highest.dev_attr.attr, + &sensor_dev_attr_power1_reset_history.dev_attr.attr, + &sensor_dev_attr_power1_max.dev_attr.attr, + &sensor_dev_attr_power1_crit.dev_attr.attr, + &sensor_dev_attr_power1_max_alarm.dev_attr.attr, + &sensor_dev_attr_power1_crit_alarm.dev_attr.attr, + + &sensor_dev_attr_curr1_input.dev_attr.attr, + + &sensor_dev_attr_update_interval.dev_attr.attr, + + NULL, +}; + +static const struct attribute_group ina209_group = { + .attrs = ina209_attributes, +}; + +static void ina209_restore_conf(struct i2c_client *client, + struct ina209_data *data) +{ + /* Restore initial configuration */ + i2c_smbus_write_word_swapped(client, INA209_CONFIGURATION, + data->config_orig); + i2c_smbus_write_word_swapped(client, INA209_CALIBRATION, + data->calibration_orig); +} + +static int ina209_init_client(struct i2c_client *client, + struct ina209_data *data) +{ + struct ina2xx_platform_data *pdata = dev_get_platdata(&client->dev); + u32 shunt; + int reg; + + reg = i2c_smbus_read_word_swapped(client, INA209_CALIBRATION); + if (reg < 0) + return reg; + data->calibration_orig = reg; + + reg = i2c_smbus_read_word_swapped(client, INA209_CONFIGURATION); + if (reg < 0) + return reg; + data->config_orig = reg; + + if (pdata) { + if (pdata->shunt_uohms <= 0) + return -EINVAL; + shunt = pdata->shunt_uohms; + } else if (!of_property_read_u32(client->dev.of_node, "shunt-resistor", + &shunt)) { + if (shunt == 0) + return -EINVAL; + } else { + shunt = data->calibration_orig ? + 40960000 / data->calibration_orig : INA209_SHUNT_DEFAULT; + } + + i2c_smbus_write_word_swapped(client, INA209_CONFIGURATION, + INA209_CONFIG_DEFAULT); + data->update_interval = ina209_interval_from_reg(INA209_CONFIG_DEFAULT); + + /* + * Calibrate current LSB to 1mA. Shunt is in uOhms. + * See equation 13 in datasheet. + */ + i2c_smbus_write_word_swapped(client, INA209_CALIBRATION, + clamp_val(40960000 / shunt, 1, 65535)); + + /* Clear status register */ + i2c_smbus_read_word_swapped(client, INA209_STATUS); + + return 0; +} + +static int ina209_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct ina209_data *data; + int ret; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + ret = ina209_init_client(client, data); + if (ret) + return ret; + + /* Register sysfs hooks */ + ret = sysfs_create_group(&client->dev.kobj, &ina209_group); + if (ret) + goto out_restore_conf; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + goto out_hwmon_device_register; + } + + return 0; + +out_hwmon_device_register: + sysfs_remove_group(&client->dev.kobj, &ina209_group); +out_restore_conf: + ina209_restore_conf(client, data); + return ret; +} + +static int ina209_remove(struct i2c_client *client) +{ + struct ina209_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &ina209_group); + ina209_restore_conf(client, data); + + return 0; +} + +static const struct i2c_device_id ina209_id[] = { + { "ina209", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ina209_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver ina209_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "ina209", + }, + .probe = ina209_probe, + .remove = ina209_remove, + .id_table = ina209_id, +}; + +module_i2c_driver(ina209_driver); + +MODULE_AUTHOR("Ira W. Snyder , Paul Hays , Guenter Roeck "); +MODULE_DESCRIPTION("INA209 driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 1640eaecc4c5f62c73a87445a55c0f0cbe3b4579 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 7 Mar 2012 03:54:50 -0800 Subject: hwmon: (pmbus/zl6100) Add support for VMON/VDRV Some of the ZL6100 compatible chips support monitoring a separate voltage pin, VMON (ZL2004) or VDRV (ZL91xx). Report it as in2 / vmon. The chips support implicit warning limits for VMON/VDRV, as percentage of the respective critical voltage. Support by reading/writing the critical voltages and calculating the associated warning voltages. Signed-off-by: Guenter Roeck --- Documentation/hwmon/zl6100 | 26 +++++-- drivers/hwmon/pmbus/zl6100.c | 176 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 188 insertions(+), 14 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/zl6100 b/Documentation/hwmon/zl6100 index a995b41724fd..3d924b6b59e9 100644 --- a/Documentation/hwmon/zl6100 +++ b/Documentation/hwmon/zl6100 @@ -121,12 +121,26 @@ in1_max_alarm Input voltage high alarm. in1_lcrit_alarm Input voltage critical low alarm. in1_crit_alarm Input voltage critical high alarm. -in2_label "vout1" -in2_input Measured output voltage. -in2_lcrit Critical minimum output Voltage. -in2_crit Critical maximum output voltage. -in2_lcrit_alarm Critical output voltage critical low alarm. -in2_crit_alarm Critical output voltage critical high alarm. +in2_label "vmon" +in2_input Measured voltage on VMON (ZL2004) or VDRV (ZL9101M, + ZL9117M) pin. Reported voltage is 16x the voltage on the + pin (adjusted internally by the chip). +in2_lcrit Critical minumum VMON/VDRV Voltage. +in2_crit Critical maximum VMON/VDRV voltage. +in2_lcrit_alarm VMON/VDRV voltage critical low alarm. +in2_crit_alarm VMON/VDRV voltage critical high alarm. + + vmon attributes are supported on ZL2004, ZL9101M, + and ZL9117M only. + +inX_label "vout1" +inX_input Measured output voltage. +inX_lcrit Critical minimum output Voltage. +inX_crit Critical maximum output voltage. +inX_lcrit_alarm Critical output voltage critical low alarm. +inX_crit_alarm Critical output voltage critical high alarm. + + X is 3 for ZL2004, ZL9101M, and ZL9117M, 2 otherwise. curr1_label "iout1" curr1_input Measured output current. diff --git a/drivers/hwmon/pmbus/zl6100.c b/drivers/hwmon/pmbus/zl6100.c index fc5eed8e85bb..819644121259 100644 --- a/drivers/hwmon/pmbus/zl6100.c +++ b/drivers/hwmon/pmbus/zl6100.c @@ -2,6 +2,7 @@ * Hardware monitoring driver for ZL6100 and compatibles * * Copyright (c) 2011 Ericsson AB. + * Copyright (c) 2012 Guenter Roeck * * 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 @@ -45,12 +46,87 @@ struct zl6100_data { #define ZL6100_MFR_XTEMP_ENABLE (1 << 7) +#define MFR_VMON_OV_FAULT_LIMIT 0xf5 +#define MFR_VMON_UV_FAULT_LIMIT 0xf6 +#define MFR_READ_VMON 0xf7 + +#define VMON_UV_WARNING (1 << 5) +#define VMON_OV_WARNING (1 << 4) +#define VMON_UV_FAULT (1 << 1) +#define VMON_OV_FAULT (1 << 0) + #define ZL6100_WAIT_TIME 1000 /* uS */ static ushort delay = ZL6100_WAIT_TIME; module_param(delay, ushort, 0644); MODULE_PARM_DESC(delay, "Delay between chip accesses in uS"); +/* Convert linear sensor value to milli-units */ +static long zl6100_l2d(s16 l) +{ + s16 exponent; + s32 mantissa; + long val; + + exponent = l >> 11; + mantissa = ((s16)((l & 0x7ff) << 5)) >> 5; + + val = mantissa; + + /* scale result to milli-units */ + val = val * 1000L; + + if (exponent >= 0) + val <<= exponent; + else + val >>= -exponent; + + return val; +} + +#define MAX_MANTISSA (1023 * 1000) +#define MIN_MANTISSA (511 * 1000) + +static u16 zl6100_d2l(long val) +{ + s16 exponent = 0, mantissa; + bool negative = false; + + /* simple case */ + if (val == 0) + return 0; + + if (val < 0) { + negative = true; + val = -val; + } + + /* Reduce large mantissa until it fits into 10 bit */ + while (val >= MAX_MANTISSA && exponent < 15) { + exponent++; + val >>= 1; + } + /* Increase small mantissa to improve precision */ + while (val < MIN_MANTISSA && exponent > -15) { + exponent--; + val <<= 1; + } + + /* Convert mantissa from milli-units to units */ + mantissa = DIV_ROUND_CLOSEST(val, 1000); + + /* Ensure that resulting number is within range */ + if (mantissa > 0x3ff) + mantissa = 0x3ff; + + /* restore sign */ + if (negative) + mantissa = -mantissa; + + /* Convert to 5 bit exponent, 11 bit mantissa */ + return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800); +} + /* Some chips need a delay between accesses */ static inline void zl6100_wait(const struct zl6100_data *data) { @@ -65,9 +141,9 @@ static int zl6100_read_word_data(struct i2c_client *client, int page, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); struct zl6100_data *data = to_zl6100_data(info); - int ret; + int ret, vreg; - if (page || reg >= PMBUS_VIRT_BASE) + if (page > 0) return -ENXIO; if (data->id == zl2005) { @@ -83,9 +159,39 @@ static int zl6100_read_word_data(struct i2c_client *client, int page, int reg) } } + switch (reg) { + case PMBUS_VIRT_READ_VMON: + vreg = MFR_READ_VMON; + break; + case PMBUS_VIRT_VMON_OV_WARN_LIMIT: + case PMBUS_VIRT_VMON_OV_FAULT_LIMIT: + vreg = MFR_VMON_OV_FAULT_LIMIT; + break; + case PMBUS_VIRT_VMON_UV_WARN_LIMIT: + case PMBUS_VIRT_VMON_UV_FAULT_LIMIT: + vreg = MFR_VMON_UV_FAULT_LIMIT; + break; + default: + if (reg >= PMBUS_VIRT_BASE) + return -ENXIO; + vreg = reg; + break; + } + zl6100_wait(data); - ret = pmbus_read_word_data(client, page, reg); + ret = pmbus_read_word_data(client, page, vreg); data->access = ktime_get(); + if (ret < 0) + return ret; + + switch (reg) { + case PMBUS_VIRT_VMON_OV_WARN_LIMIT: + ret = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(ret) * 9, 10)); + break; + case PMBUS_VIRT_VMON_UV_WARN_LIMIT: + ret = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(ret) * 11, 10)); + break; + } return ret; } @@ -94,13 +200,35 @@ static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); struct zl6100_data *data = to_zl6100_data(info); - int ret; + int ret, status; if (page > 0) return -ENXIO; zl6100_wait(data); - ret = pmbus_read_byte_data(client, page, reg); + + switch (reg) { + case PMBUS_VIRT_STATUS_VMON: + ret = pmbus_read_byte_data(client, 0, + PMBUS_STATUS_MFR_SPECIFIC); + if (ret < 0) + break; + + status = 0; + if (ret & VMON_UV_WARNING) + status |= PB_VOLTAGE_UV_WARNING; + if (ret & VMON_OV_WARNING) + status |= PB_VOLTAGE_OV_WARNING; + if (ret & VMON_UV_FAULT) + status |= PB_VOLTAGE_UV_FAULT; + if (ret & VMON_OV_FAULT) + status |= PB_VOLTAGE_OV_FAULT; + ret = status; + break; + default: + ret = pmbus_read_byte_data(client, page, reg); + break; + } data->access = ktime_get(); return ret; @@ -111,13 +239,38 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg, { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); struct zl6100_data *data = to_zl6100_data(info); - int ret; + int ret, vreg; - if (page || reg >= PMBUS_VIRT_BASE) + if (page > 0) return -ENXIO; + switch (reg) { + case PMBUS_VIRT_VMON_OV_WARN_LIMIT: + word = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(word) * 10, 9)); + vreg = MFR_VMON_OV_FAULT_LIMIT; + pmbus_clear_cache(client); + break; + case PMBUS_VIRT_VMON_OV_FAULT_LIMIT: + vreg = MFR_VMON_OV_FAULT_LIMIT; + pmbus_clear_cache(client); + break; + case PMBUS_VIRT_VMON_UV_WARN_LIMIT: + word = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(word) * 10, 11)); + vreg = MFR_VMON_UV_FAULT_LIMIT; + pmbus_clear_cache(client); + break; + case PMBUS_VIRT_VMON_UV_FAULT_LIMIT: + vreg = MFR_VMON_UV_FAULT_LIMIT; + pmbus_clear_cache(client); + break; + default: + if (reg >= PMBUS_VIRT_BASE) + return -ENXIO; + vreg = reg; + } + zl6100_wait(data); - ret = pmbus_write_word_data(client, page, reg, word); + ret = pmbus_write_word_data(client, page, vreg, word); data->access = ktime_get(); return ret; @@ -225,6 +378,13 @@ static int zl6100_probe(struct i2c_client *client, | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + /* + * ZL2004, ZL9101M, and ZL9117M support monitoring an extra voltage + * (VMON for ZL2004, VDRV for ZL9101M and ZL9117M). Report it as vmon. + */ + if (data->id == zl2004 || data->id == zl9101 || data->id == zl9117) + info->func[0] |= PMBUS_HAVE_VMON | PMBUS_HAVE_STATUS_VMON; + ret = i2c_smbus_read_word_data(client, ZL6100_MFR_CONFIG); if (ret < 0) return ret; -- cgit v1.2.3-59-g8ed1b From 50115ac9b6f35ce6bbb61e5ae21fc3bbd01d490c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 27 Apr 2012 15:13:08 -0700 Subject: hwmon: (pmbus/max34440) Add support for MAX34460 and MAX34461 MAX34460 is a PMBus 12-Channel Voltage Monitor & Sequencer. MAX34461 is a PMBus 16-Channel Voltage Monitor & Sequencer. Signed-off-by: Guenter Roeck --- Documentation/hwmon/max34440 | 16 +++++++++ drivers/hwmon/pmbus/Kconfig | 2 +- drivers/hwmon/pmbus/max34440.c | 75 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 90 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/max34440 b/Documentation/hwmon/max34440 index 04482226db20..47651ff341ae 100644 --- a/Documentation/hwmon/max34440 +++ b/Documentation/hwmon/max34440 @@ -16,6 +16,16 @@ Supported chips: Prefixes: 'max34446' Addresses scanned: - Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX34446.pdf + * Maxim MAX34460 + PMBus 12-Channel Voltage Monitor & Sequencer + Prefix: 'max34460' + Addresses scanned: - + Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX34460.pdf + * Maxim MAX34461 + PMBus 16-Channel Voltage Monitor & Sequencer + Prefix: 'max34461' + Addresses scanned: - + Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX34461.pdf Author: Guenter Roeck @@ -26,6 +36,9 @@ Description This driver supports hardware montoring for Maxim MAX34440 PMBus 6-Channel Power-Supply Manager, MAX34441 PMBus 5-Channel Power-Supply Manager and Intelligent Fan Controller, and MAX34446 PMBus Power-Supply Data Logger. +It also supports the MAX34460 and MAX34461 PMBus Voltage Monitor & Sequencers. +The MAX34460 supports 12 voltage channels, and the MAX34461 supports 16 voltage +channels. The driver is a client driver to the core PMBus driver. Please see Documentation/hwmon/pmbus for details on PMBus client drivers. @@ -109,3 +122,6 @@ temp[1-8]_reset_history Write any value to reset history. temp7 and temp8 attributes only exist for MAX34440. MAX34446 only supports temp[1-3]. + +MAX34460 supports attribute groups in[1-12] and temp[1-5]. +MAX34461 supports attribute groups in[1-16] and temp[1-5]. diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 60745a535821..4f9eb0af5229 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -72,7 +72,7 @@ config SENSORS_MAX34440 default n help If you say yes here you get hardware monitoring support for Maxim - MAX34440, MAX34441, and MAX34446. + MAX34440, MAX34441, MAX34446, MAX34460, and MAX34461. This driver can also be built as a module. If so, the module will be called max34440. diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c index 2ada7b021fbe..7e930c3ce1ab 100644 --- a/drivers/hwmon/pmbus/max34440.c +++ b/drivers/hwmon/pmbus/max34440.c @@ -2,6 +2,7 @@ * Hardware monitoring driver for Maxim MAX34440/MAX34441 * * Copyright (c) 2011 Ericsson AB. + * Copyright (c) 2012 Guenter Roeck * * 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 @@ -25,7 +26,7 @@ #include #include "pmbus.h" -enum chips { max34440, max34441, max34446 }; +enum chips { max34440, max34441, max34446, max34460, max34461 }; #define MAX34440_MFR_VOUT_PEAK 0xd4 #define MAX34440_MFR_IOUT_PEAK 0xd5 @@ -87,7 +88,8 @@ static int max34440_read_word_data(struct i2c_client *client, int page, int reg) MAX34446_MFR_POUT_PEAK); break; case PMBUS_VIRT_READ_TEMP_AVG: - if (data->id != max34446) + if (data->id != max34446 && data->id != max34460 && + data->id != max34461) return -ENXIO; ret = pmbus_read_word_data(client, page, MAX34446_MFR_TEMPERATURE_AVG); @@ -322,6 +324,73 @@ static struct pmbus_driver_info max34440_info[] = { .read_word_data = max34440_read_word_data, .write_word_data = max34440_write_word_data, }, + [max34460] = { + .pages = 18, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .m[PSC_VOLTAGE_OUT] = 1, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 3, + .m[PSC_TEMPERATURE] = 1, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 2, + .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[4] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[5] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[6] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[7] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[8] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[9] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[10] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[11] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[13] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[14] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[15] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[16] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[17] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_byte_data = max34440_read_byte_data, + .read_word_data = max34440_read_word_data, + .write_word_data = max34440_write_word_data, + }, + [max34461] = { + .pages = 23, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .m[PSC_VOLTAGE_OUT] = 1, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 3, + .m[PSC_TEMPERATURE] = 1, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 2, + .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[4] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[5] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[6] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[7] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[8] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[9] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[10] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[11] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[12] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[13] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[14] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[15] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + /* page 16 is reserved */ + .func[17] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[19] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[20] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[21] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_byte_data = max34440_read_byte_data, + .read_word_data = max34440_read_word_data, + .write_word_data = max34440_write_word_data, + }, }; static int max34440_probe(struct i2c_client *client, @@ -343,6 +412,8 @@ static const struct i2c_device_id max34440_id[] = { {"max34440", max34440}, {"max34441", max34441}, {"max34446", max34446}, + {"max34460", max34460}, + {"max34461", max34461}, {} }; MODULE_DEVICE_TABLE(i2c, max34440_id); -- cgit v1.2.3-59-g8ed1b From d47682803776f147ee9662a505c63bbbf27a1274 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 28 Jan 2013 20:35:19 -0800 Subject: hwmon: (jc42) Add support for MCP98244 Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare --- Documentation/hwmon/jc42 | 3 ++- drivers/hwmon/Kconfig | 4 ++-- drivers/hwmon/jc42.c | 4 ++++ 3 files changed, 8 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/jc42 b/Documentation/hwmon/jc42 index 66ecb9fc8246..165077121238 100644 --- a/Documentation/hwmon/jc42 +++ b/Documentation/hwmon/jc42 @@ -17,12 +17,13 @@ Supported chips: * Maxim MAX6604 Datasheets: http://datasheets.maxim-ic.com/en/ds/MAX6604.pdf - * Microchip MCP9804, MCP9805, MCP98242, MCP98243, MCP9843 + * Microchip MCP9804, MCP9805, MCP98242, MCP98243, MCP98244, MCP9843 Datasheets: http://ww1.microchip.com/downloads/en/DeviceDoc/22203C.pdf http://ww1.microchip.com/downloads/en/DeviceDoc/21977b.pdf http://ww1.microchip.com/downloads/en/DeviceDoc/21996a.pdf http://ww1.microchip.com/downloads/en/DeviceDoc/22153c.pdf + http://ww1.microchip.com/downloads/en/DeviceDoc/22327A.pdf * NXP Semiconductors SE97, SE97B, SE98, SE98A Datasheets: http://www.nxp.com/documents/data_sheet/SE97.pdf diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5e77cef09ca6..52d5174792ee 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -530,8 +530,8 @@ config SENSORS_JC42 temperature sensors, which are used on many DDR3 memory modules for mobile devices and servers. Support will include, but not be limited to, ADT7408, AT30TS00, CAT34TS02, CAT6095, MAX6604, MCP9804, MCP9805, - MCP98242, MCP98243, MCP9843, SE97, SE98, STTS424(E), STTS2002, - STTS3000, TSE2002B3, TSE2002GB2, TS3000B3, and TS3000GB2. + MCP98242, MCP98243, MCP98244, MCP9843, SE97, SE98, STTS424(E), + STTS2002, STTS3000, TSE2002B3, TSE2002GB2, TS3000B3, and TS3000GB2. This driver can also be built as a module. If so, the module will be called jc42. diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 82d962488f55..4a58f130fd4e 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -103,6 +103,9 @@ static const unsigned short normal_i2c[] = { #define MCP98243_DEVID 0x2100 #define MCP98243_DEVID_MASK 0xfffc +#define MCP98244_DEVID 0x2200 +#define MCP98244_DEVID_MASK 0xfffc + #define MCP9843_DEVID 0x0000 /* Also matches mcp9805 */ #define MCP9843_DEVID_MASK 0xfffe @@ -147,6 +150,7 @@ static struct jc42_chips jc42_chips[] = { { MCP_MANID, MCP9804_DEVID, MCP9804_DEVID_MASK }, { MCP_MANID, MCP98242_DEVID, MCP98242_DEVID_MASK }, { MCP_MANID, MCP98243_DEVID, MCP98243_DEVID_MASK }, + { MCP_MANID, MCP98244_DEVID, MCP98244_DEVID_MASK }, { MCP_MANID, MCP9843_DEVID, MCP9843_DEVID_MASK }, { NXP_MANID, SE97_DEVID, SE97_DEVID_MASK }, { ONS_MANID, CAT6095_DEVID, CAT6095_DEVID_MASK }, -- cgit v1.2.3-59-g8ed1b From 12514aa0e6c6ca4e03b8a4da557b7d3ee0d91f5d Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 7 Feb 2013 12:20:34 +0000 Subject: devicetree: Move NS2 LEDs binding into LEDs directory leds-ns2.txt is a binding for LEDs, not GPIOs. Move the documentation in with the rest of the LEDs bindings. Cc: Andrew Lunn Cc: Simon Guinot Cc: Jason Cooper Signed-off-by: Grant Likely --- .../devicetree/bindings/gpio/leds-ns2.txt | 26 ---------------------- .../devicetree/bindings/leds/leds-ns2.txt | 26 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 26 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/leds-ns2.txt create mode 100644 Documentation/devicetree/bindings/leds/leds-ns2.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/gpio/leds-ns2.txt b/Documentation/devicetree/bindings/gpio/leds-ns2.txt deleted file mode 100644 index aef3aca34d2d..000000000000 --- a/Documentation/devicetree/bindings/gpio/leds-ns2.txt +++ /dev/null @@ -1,26 +0,0 @@ -Binding for dual-GPIO LED found on Network Space v2 (and parents). - -Required properties: -- compatible: "lacie,ns2-leds". - -Each LED is represented as a sub-node of the ns2-leds device. - -Required sub-node properties: -- cmd-gpio: Command LED GPIO. See OF device-tree GPIO specification. -- slow-gpio: Slow LED GPIO. See OF device-tree GPIO specification. - -Optional sub-node properties: -- label: Name for this LED. If omitted, the label is taken from the node name. -- linux,default-trigger: Trigger assigned to the LED. - -Example: - -ns2-leds { - compatible = "lacie,ns2-leds"; - - blue-sata { - label = "ns2:blue:sata"; - slow-gpio = <&gpio0 29 0>; - cmd-gpio = <&gpio0 30 0>; - }; -}; diff --git a/Documentation/devicetree/bindings/leds/leds-ns2.txt b/Documentation/devicetree/bindings/leds/leds-ns2.txt new file mode 100644 index 000000000000..aef3aca34d2d --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-ns2.txt @@ -0,0 +1,26 @@ +Binding for dual-GPIO LED found on Network Space v2 (and parents). + +Required properties: +- compatible: "lacie,ns2-leds". + +Each LED is represented as a sub-node of the ns2-leds device. + +Required sub-node properties: +- cmd-gpio: Command LED GPIO. See OF device-tree GPIO specification. +- slow-gpio: Slow LED GPIO. See OF device-tree GPIO specification. + +Optional sub-node properties: +- label: Name for this LED. If omitted, the label is taken from the node name. +- linux,default-trigger: Trigger assigned to the LED. + +Example: + +ns2-leds { + compatible = "lacie,ns2-leds"; + + blue-sata { + label = "ns2:blue:sata"; + slow-gpio = <&gpio0 29 0>; + cmd-gpio = <&gpio0 30 0>; + }; +}; -- cgit v1.2.3-59-g8ed1b From 1d0b1c74ed22d2614ae9206febf956f0b22c81a5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 10 Jan 2013 17:41:32 -0800 Subject: input: Extend matrix-keypad device tree binding Some matrix keypad drivers can support different numbers of rows and columns. Add a generic binding for these. Implementation note: In order to implement this binding in the kernel, we will need to modify matrix_keypad_() to look up the number of rows and cols in the keymap. Perhaps this could be done by passing 0 for these parameters? Many of the parameters can already be set to NULL. Ick. Signed-off-by: Simon Glass Reviewed-by: Stephen Warren Signed-off-by: Grant Likely --- Documentation/devicetree/bindings/input/lpc32xx-key.txt | 9 ++++++--- Documentation/devicetree/bindings/input/matrix-keymap.txt | 8 ++++++++ Documentation/devicetree/bindings/input/omap-keypad.txt | 13 +++++-------- Documentation/devicetree/bindings/input/tca8418_keypad.txt | 6 ++++-- 4 files changed, 23 insertions(+), 13 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/input/lpc32xx-key.txt b/Documentation/devicetree/bindings/input/lpc32xx-key.txt index 31afd5014c48..bcf62f856358 100644 --- a/Documentation/devicetree/bindings/input/lpc32xx-key.txt +++ b/Documentation/devicetree/bindings/input/lpc32xx-key.txt @@ -1,19 +1,22 @@ NXP LPC32xx Key Scan Interface +This binding is based on the matrix-keymap binding with the following +changes: + Required Properties: - compatible: Should be "nxp,lpc3220-key" - reg: Physical base address of the controller and length of memory mapped region. - interrupts: The interrupt number to the cpu. -- keypad,num-rows: Number of rows and columns, e.g. 1: 1x1, 6: 6x6 -- keypad,num-columns: Must be equal to keypad,num-rows since LPC32xx only - supports square matrices - nxp,debounce-delay-ms: Debounce delay in ms - nxp,scan-delay-ms: Repeated scan period in ms - linux,keymap: the key-code to be reported when the key is pressed and released, see also Documentation/devicetree/bindings/input/matrix-keymap.txt +Note: keypad,num-rows and keypad,num-columns are required, and must be equal +since LPC32xx only supports square matrices + Example: key@40050000 { diff --git a/Documentation/devicetree/bindings/input/matrix-keymap.txt b/Documentation/devicetree/bindings/input/matrix-keymap.txt index 3cd8b98ccd2d..c54919fad17e 100644 --- a/Documentation/devicetree/bindings/input/matrix-keymap.txt +++ b/Documentation/devicetree/bindings/input/matrix-keymap.txt @@ -9,6 +9,12 @@ Required properties: row << 24 | column << 16 | key-code Optional properties: +Properties for the number of rows and columns are optional because some +drivers will use fixed values for these. +- keypad,num-rows: Number of row lines connected to the keypad controller. +- keypad,num-columns: Number of column lines connected to the keypad + controller. + Some users of this binding might choose to specify secondary keymaps for cases where there is a modifier key such as a Fn key. Proposed names for said properties are "linux,fn-keymap" or with another descriptive @@ -17,3 +23,5 @@ word for the modifier other from "Fn". Example: linux,keymap = < 0x00030012 0x0102003a >; + keypad,num-rows = <2>; + keypad,num-columns = <8>; diff --git a/Documentation/devicetree/bindings/input/omap-keypad.txt b/Documentation/devicetree/bindings/input/omap-keypad.txt index f2fa5e10493d..34ed1c60ff95 100644 --- a/Documentation/devicetree/bindings/input/omap-keypad.txt +++ b/Documentation/devicetree/bindings/input/omap-keypad.txt @@ -6,19 +6,16 @@ A key can be placed at each intersection of a unique row and a unique column. The keypad controller can sense a key-press and key-release and report the event using a interrupt to the cpu. +This binding is based on the matrix-keymap binding with the following +changes: + +keypad,num-rows and keypad,num-columns are required. + Required SoC Specific Properties: - compatible: should be one of the following - "ti,omap4-keypad": For controllers compatible with omap4 keypad controller. -Required Board Specific Properties, in addition to those specified by -the shared matrix-keyboard bindings: -- keypad,num-rows: Number of row lines connected to the keypad - controller. - -- keypad,num-columns: Number of column lines connected to the - keypad controller. - Optional Properties specific to linux: - linux,keypad-no-autorepeat: do no enable autorepeat feature. diff --git a/Documentation/devicetree/bindings/input/tca8418_keypad.txt b/Documentation/devicetree/bindings/input/tca8418_keypad.txt index 2a1538f0053f..255185009167 100644 --- a/Documentation/devicetree/bindings/input/tca8418_keypad.txt +++ b/Documentation/devicetree/bindings/input/tca8418_keypad.txt @@ -1,8 +1,10 @@ +This binding is based on the matrix-keymap binding with the following +changes: + +keypad,num-rows and keypad,num-columns are required. Required properties: - compatible: "ti,tca8418" - reg: the I2C address - interrupts: IRQ line number, should trigger on falling edge -- keypad,num-rows: The number of rows -- keypad,num-columns: The number of columns - linux,keymap: Keys definitions, see keypad-matrix. -- cgit v1.2.3-59-g8ed1b From 22435f38337ef6a8abb33574604e77c335b75d14 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 5 Feb 2013 12:06:28 -0700 Subject: kbuild: create a rule to run the pre-processor on *.dts files Create cmd_dtc_cpp to run the C pre-processor on *.dts file before passing them to dtc for final compilation. This allows the use of #define and #include within the .dts file. Acked-by: Simon Glass Acked-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Michal Marek Acked-by: Srinivas Kandagatla Signed-off-by: Stephen Warren Signed-off-by: Grant Likely --- Documentation/kbuild/makefiles.txt | 23 +++++++++++++++++++++++ scripts/Makefile.lib | 10 ++++++++++ 2 files changed, 33 insertions(+) (limited to 'Documentation') diff --git a/Documentation/kbuild/makefiles.txt b/Documentation/kbuild/makefiles.txt index 14c3f4f1b617..5198b742fde1 100644 --- a/Documentation/kbuild/makefiles.txt +++ b/Documentation/kbuild/makefiles.txt @@ -1186,6 +1186,29 @@ When kbuild executes, the following steps are followed (roughly): clean-files += *.dtb DTC_FLAGS ?= -p 1024 + dtc_cpp + This is just like dtc as describe above, except that the C pre- + processor is invoked upon the .dtsp file before compiling the result + with dtc. + + In order for build dependencies to work, all files compiled using + dtc_cpp must use the C pre-processor's #include functionality and not + dtc's /include/ functionality. + + Using the C pre-processor allows use of #define to create named + constants. In turn, the #defines will typically appear in a header + file, which may be shared with regular C code. Since the dtc language + represents a data structure rather than code in C syntax, similar + restrictions are placed on a header file included by a device tree + file as for a header file included by an assembly language file. + In particular, the C pre-processor is passed -x assembler-with-cpp, + which sets macro __ASSEMBLY__. __DTS__ is also set. These allow header + files to restrict their content to that compatible with device tree + source. + + A central rule exists to create $(obj)/%.dtb from $(src)/%.dtsp; + architecture Makefiles do no need to explicitly write out that rule. + --- 6.8 Custom kbuild commands When kbuild is executing with KBUILD_VERBOSE=0, then only a shorthand diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index bdf42fdf64c9..7910229ec665 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -269,6 +269,16 @@ cmd_dtc = $(objtree)/scripts/dtc/dtc -O dtb -o $@ -b 0 $(DTC_FLAGS) -d $(depfile $(obj)/%.dtb: $(src)/%.dts FORCE $(call if_changed_dep,dtc) +dtc-tmp = $(subst $(comma),_,$(dot-target).dts) + +quiet_cmd_dtc_cpp = DTC+CPP $@ +cmd_dtc_cpp = $(CPP) $(cpp_flags) -x assembler-with-cpp -undef -D__DTS__ \ + -o $(dtc-tmp) $< ; \ + $(objtree)/scripts/dtc/dtc -O dtb -o $@ -b 0 $(DTC_FLAGS) $(dtc-tmp) + +$(obj)/%.dtb: $(src)/%.dtsp FORCE + $(call if_changed_dep,dtc_cpp) + # Bzip2 # --------------------------------------------------------------------------- -- cgit v1.2.3-59-g8ed1b From 686966d881bd49bd6349b47c59dfc4bfc7b5e3b6 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 8 Feb 2013 09:19:33 -0800 Subject: x86, doc: Add a bootloader ID for OVMF OVMF (an implementation of UEFI based on TianoCore used in virtual environments) now has the ability to boot Linux natively; this is used for "qemu -kernel" and similar things in a UEFI environment. Accordingly, assign it a bootloader ID. Signed-off-by: H. Peter Anvin Cc: David Woodhouse --- Documentation/x86/boot.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/x86/boot.txt b/Documentation/x86/boot.txt index e540fd67f767..b443f1de0e5a 100644 --- a/Documentation/x86/boot.txt +++ b/Documentation/x86/boot.txt @@ -390,6 +390,7 @@ Protocol: 2.00+ F Special (0xFF = undefined) 10 Reserved 11 Minimal Linux Bootloader + 12 OVMF UEFI virtualization stack Please contact if you need a bootloader ID value assigned. -- cgit v1.2.3-59-g8ed1b From 09495dda6a62c74b13412a63528093910ef80edd Mon Sep 17 00:00:00 2001 From: Ajay Kumar Date: Thu, 3 Jan 2013 08:03:34 -0500 Subject: of/exynos_g2d: Add Bindings for exynos G2D driver Add documentation for the DT bindings in exynos G2D driver. Signed-off-by: Ajay Kumar Signed-off-by: Grant Likely --- .../devicetree/bindings/drm/exynos/g2d.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Documentation/devicetree/bindings/drm/exynos/g2d.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/drm/exynos/g2d.txt b/Documentation/devicetree/bindings/drm/exynos/g2d.txt new file mode 100644 index 000000000000..1eb124d35a99 --- /dev/null +++ b/Documentation/devicetree/bindings/drm/exynos/g2d.txt @@ -0,0 +1,22 @@ +Samsung 2D Graphic Accelerator using DRM frame work + +Samsung FIMG2D is a graphics 2D accelerator which supports Bit Block Transfer. +We set the drawing-context registers for configuring rendering parameters and +then start rendering. +This driver is for SOCs which contain G2D IPs with version 4.1. + +Required properties: + -compatible: + should be "samsung,exynos-g2d-41". + -reg: + physical base address of the controller and length + of memory mapped region. + -interrupts: + interrupt combiner values. + +Example: + g2d { + compatible = "samsung,exynos-g2d-41"; + reg = <0x10850000 0x1000>; + interrupts = <0 91 0>; + }; -- cgit v1.2.3-59-g8ed1b From 1421954bf9b967d819db23c911f950a2ccd60eff Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 7 Feb 2013 17:14:35 +0530 Subject: documentation/devicetree: Fix a typo in exynos-dw-mshc.txt Fixed a typo in referenced file name. Cc: Thomas Abraham Cc: Grant Likely Signed-off-by: Sachin Kamat Signed-off-by: Rob Herring --- Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt index 792768953330..4e35e55ea14f 100644 --- a/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt +++ b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt @@ -4,7 +4,7 @@ The Synopsis designware mobile storage host controller is used to interface a SoC with storage medium such as eMMC or SD/MMC cards. This file documents differences between the core Synopsis dw mshc controller properties described -by synposis-dw-mshc.txt and the properties used by the Samsung Exynos specific +by synopsis-dw-mshc.txt and the properties used by the Samsung Exynos specific extensions to the Synopsis Designware Mobile Storage Host Controller. Required Properties: -- cgit v1.2.3-59-g8ed1b From 2a4bd9f0db24ba14c8b38777d77add2682233c79 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Tue, 5 Feb 2013 22:52:51 +0100 Subject: cpufreq: kirkwood: Add a cpufreq driver for Marvell Kirkwood SoCs The Marvell Kirkwood SoCs have simple cpufreq support in hardware. The CPU can either use the a high speed cpu clock, or the slower DDR clock. Add a driver to swap between these two clock sources. Signed-off-by: Andrew Lunn Acked-by: Jason Cooper Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- Documentation/devicetree/bindings/arm/kirkwood.txt | 27 +++ drivers/clk/mvebu/clk-gating-ctrl.c | 1 + drivers/cpufreq/Kconfig.arm | 6 + drivers/cpufreq/Makefile | 1 + drivers/cpufreq/kirkwood-cpufreq.c | 259 +++++++++++++++++++++ 5 files changed, 294 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/kirkwood.txt create mode 100644 drivers/cpufreq/kirkwood-cpufreq.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/arm/kirkwood.txt b/Documentation/devicetree/bindings/arm/kirkwood.txt new file mode 100644 index 000000000000..98cce9a653eb --- /dev/null +++ b/Documentation/devicetree/bindings/arm/kirkwood.txt @@ -0,0 +1,27 @@ +Marvell Kirkwood Platforms Device Tree Bindings +----------------------------------------------- + +Boards with a SoC of the Marvell Kirkwood +shall have the following property: + +Required root node property: + +compatible: must contain "marvell,kirkwood"; + +In order to support the kirkwood cpufreq driver, there must be a node +cpus/cpu@0 with three clocks, "cpu_clk", "ddrclk" and "powersave", +where the "powersave" clock is a gating clock used to switch the CPU +between the "cpu_clk" and the "ddrclk". + +Example: + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "marvell,sheeva-88SV131"; + clocks = <&core_clk 1>, <&core_clk 3>, <&gate_clk 11>; + clock-names = "cpu_clk", "ddrclk", "powersave"; + }; diff --git a/drivers/clk/mvebu/clk-gating-ctrl.c b/drivers/clk/mvebu/clk-gating-ctrl.c index 8fa5408b6c7d..ebf141d4374b 100644 --- a/drivers/clk/mvebu/clk-gating-ctrl.c +++ b/drivers/clk/mvebu/clk-gating-ctrl.c @@ -193,6 +193,7 @@ static const struct mvebu_soc_descr __initconst kirkwood_gating_descr[] = { { "runit", NULL, 7 }, { "xor0", NULL, 8 }, { "audio", NULL, 9 }, + { "powersave", "cpuclk", 11 }, { "sata0", NULL, 14 }, { "sata1", NULL, 15 }, { "xor1", NULL, 16 }, diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index c65226c4717d..7f333af1c059 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -77,6 +77,12 @@ config ARM_EXYNOS5250_CPUFREQ This adds the CPUFreq driver for Samsung EXYNOS5250 SoC. +config ARM_KIRKWOOD_CPUFREQ + def_bool ARCH_KIRKWOOD && OF + help + This adds the CPUFreq driver for Marvell Kirkwood + SoCs. + config ARM_IMX6Q_CPUFREQ tristate "Freescale i.MX6Q cpufreq support" depends on SOC_IMX6Q diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index cd3d4ffe9626..5399c45ac311 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_ARM_EXYNOS_CPUFREQ) += exynos-cpufreq.o obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o +obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o diff --git a/drivers/cpufreq/kirkwood-cpufreq.c b/drivers/cpufreq/kirkwood-cpufreq.c new file mode 100644 index 000000000000..0e83e3c24f5b --- /dev/null +++ b/drivers/cpufreq/kirkwood-cpufreq.c @@ -0,0 +1,259 @@ +/* + * kirkwood_freq.c: cpufreq driver for the Marvell kirkwood + * + * Copyright (C) 2013 Andrew Lunn + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define CPU_SW_INT_BLK BIT(28) + +static struct priv +{ + struct clk *cpu_clk; + struct clk *ddr_clk; + struct clk *powersave_clk; + struct device *dev; + void __iomem *base; +} priv; + +#define STATE_CPU_FREQ 0x01 +#define STATE_DDR_FREQ 0x02 + +/* + * Kirkwood can swap the clock to the CPU between two clocks: + * + * - cpu clk + * - ddr clk + * + * The frequencies are set at runtime before registering this * + * table. + */ +static struct cpufreq_frequency_table kirkwood_freq_table[] = { + {STATE_CPU_FREQ, 0}, /* CPU uses cpuclk */ + {STATE_DDR_FREQ, 0}, /* CPU uses ddrclk */ + {0, CPUFREQ_TABLE_END}, +}; + +static unsigned int kirkwood_cpufreq_get_cpu_frequency(unsigned int cpu) +{ + if (__clk_is_enabled(priv.powersave_clk)) + return kirkwood_freq_table[1].frequency; + return kirkwood_freq_table[0].frequency; +} + +static void kirkwood_cpufreq_set_cpu_state(unsigned int index) +{ + struct cpufreq_freqs freqs; + unsigned int state = kirkwood_freq_table[index].index; + unsigned long reg; + + freqs.old = kirkwood_cpufreq_get_cpu_frequency(0); + freqs.new = kirkwood_freq_table[index].frequency; + freqs.cpu = 0; /* Kirkwood is UP */ + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + dev_dbg(priv.dev, "Attempting to set frequency to %i KHz\n", + kirkwood_freq_table[index].frequency); + dev_dbg(priv.dev, "old frequency was %i KHz\n", + kirkwood_cpufreq_get_cpu_frequency(0)); + + if (freqs.old != freqs.new) { + local_irq_disable(); + + /* Disable interrupts to the CPU */ + reg = readl_relaxed(priv.base); + reg |= CPU_SW_INT_BLK; + writel_relaxed(reg, priv.base); + + switch (state) { + case STATE_CPU_FREQ: + clk_disable(priv.powersave_clk); + break; + case STATE_DDR_FREQ: + clk_enable(priv.powersave_clk); + break; + } + + /* Wait-for-Interrupt, while the hardware changes frequency */ + cpu_do_idle(); + + /* Enable interrupts to the CPU */ + reg = readl_relaxed(priv.base); + reg &= ~CPU_SW_INT_BLK; + writel_relaxed(reg, priv.base); + + local_irq_enable(); + } + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); +}; + +static int kirkwood_cpufreq_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, kirkwood_freq_table); +} + +static int kirkwood_cpufreq_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned int index = 0; + + if (cpufreq_frequency_table_target(policy, kirkwood_freq_table, + target_freq, relation, &index)) + return -EINVAL; + + kirkwood_cpufreq_set_cpu_state(index); + + return 0; +} + +/* Module init and exit code */ +static int kirkwood_cpufreq_cpu_init(struct cpufreq_policy *policy) +{ + int result; + + /* cpuinfo and default policy values */ + policy->cpuinfo.transition_latency = 5000; /* 5uS */ + policy->cur = kirkwood_cpufreq_get_cpu_frequency(0); + + result = cpufreq_frequency_table_cpuinfo(policy, kirkwood_freq_table); + if (result) + return result; + + cpufreq_frequency_table_get_attr(kirkwood_freq_table, policy->cpu); + + return 0; +} + +static int kirkwood_cpufreq_cpu_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; +} + +static struct freq_attr *kirkwood_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver kirkwood_cpufreq_driver = { + .get = kirkwood_cpufreq_get_cpu_frequency, + .verify = kirkwood_cpufreq_verify, + .target = kirkwood_cpufreq_target, + .init = kirkwood_cpufreq_cpu_init, + .exit = kirkwood_cpufreq_cpu_exit, + .name = "kirkwood-cpufreq", + .owner = THIS_MODULE, + .attr = kirkwood_cpufreq_attr, +}; + +static int kirkwood_cpufreq_probe(struct platform_device *pdev) +{ + struct device_node *np; + struct resource *res; + int err; + + priv.dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Cannot get memory resource\n"); + return -ENODEV; + } + priv.base = devm_request_and_ioremap(&pdev->dev, res); + if (!priv.base) { + dev_err(&pdev->dev, "Cannot ioremap\n"); + return -EADDRNOTAVAIL; + } + + np = of_find_node_by_path("/cpus/cpu@0"); + if (!np) + return -ENODEV; + + priv.cpu_clk = of_clk_get_by_name(np, "cpu_clk"); + if (IS_ERR(priv.cpu_clk)) { + dev_err(priv.dev, "Unable to get cpuclk"); + return PTR_ERR(priv.cpu_clk); + } + + clk_prepare_enable(priv.cpu_clk); + kirkwood_freq_table[0].frequency = clk_get_rate(priv.cpu_clk) / 1000; + + priv.ddr_clk = of_clk_get_by_name(np, "ddrclk"); + if (IS_ERR(priv.ddr_clk)) { + dev_err(priv.dev, "Unable to get ddrclk"); + err = PTR_ERR(priv.ddr_clk); + goto out_cpu; + } + + clk_prepare_enable(priv.ddr_clk); + kirkwood_freq_table[1].frequency = clk_get_rate(priv.ddr_clk) / 1000; + + priv.powersave_clk = of_clk_get_by_name(np, "powersave"); + if (IS_ERR(priv.powersave_clk)) { + dev_err(priv.dev, "Unable to get powersave"); + err = PTR_ERR(priv.powersave_clk); + goto out_ddr; + } + clk_prepare(priv.powersave_clk); + + of_node_put(np); + np = NULL; + + err = cpufreq_register_driver(&kirkwood_cpufreq_driver); + if (!err) + return 0; + + dev_err(priv.dev, "Failed to register cpufreq driver"); + + clk_disable_unprepare(priv.powersave_clk); +out_ddr: + clk_disable_unprepare(priv.ddr_clk); +out_cpu: + clk_disable_unprepare(priv.cpu_clk); + of_node_put(np); + + return err; +} + +static int kirkwood_cpufreq_remove(struct platform_device *pdev) +{ + cpufreq_unregister_driver(&kirkwood_cpufreq_driver); + + clk_disable_unprepare(priv.powersave_clk); + clk_disable_unprepare(priv.ddr_clk); + clk_disable_unprepare(priv.cpu_clk); + + return 0; +} + +static struct platform_driver kirkwood_cpufreq_platform_driver = { + .probe = kirkwood_cpufreq_probe, + .remove = kirkwood_cpufreq_remove, + .driver = { + .name = "kirkwood-cpufreq", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(kirkwood_cpufreq_platform_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Andrew Lunn Date: Fri, 1 Feb 2013 08:56:03 +0000 Subject: suspend: enable freeze timeout configuration through sys At present, the value of timeout for freezing is 20s, which is meaningless in case that one thread is frozen with mutex locked and another thread is trying to lock the mutex, as this time of freezing will fail unavoidably. And if there is no new wakeup event registered, the system will waste at most 20s for such meaningless trying of freezing. With this patch, the value of timeout can be configured to smaller value, so such meaningless trying of freezing will be aborted in earlier time, and later freezing can be also triggered in earlier time. And more power will be saved. In normal case on mobile phone, it costs real little time to freeze processes. On some platform, it only costs about 20ms to freeze user space processes and 10ms to freeze kernel freezable threads. Signed-off-by: Liu Chuansheng Signed-off-by: Li Fei Signed-off-by: Rafael J. Wysocki --- Documentation/power/freezing-of-tasks.txt | 5 +++++ include/linux/freezer.h | 5 +++++ kernel/power/main.c | 27 +++++++++++++++++++++++++++ kernel/power/process.c | 4 ++-- 4 files changed, 39 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/power/freezing-of-tasks.txt b/Documentation/power/freezing-of-tasks.txt index 6ec291ea1c78..85894d83b352 100644 --- a/Documentation/power/freezing-of-tasks.txt +++ b/Documentation/power/freezing-of-tasks.txt @@ -223,3 +223,8 @@ since they ask the freezer to skip freezing this task, since it is anyway only after the entire suspend/hibernation sequence is complete. So, to summarize, use [un]lock_system_sleep() instead of directly using mutex_[un]lock(&pm_mutex). That would prevent freezing failures. + +V. Miscellaneous +/sys/power/pm_freeze_timeout controls how long it will cost at most to freeze +all user space processes or all freezable kernel threads, in unit of millisecond. +The default value is 20000, with range of unsigned integer. diff --git a/include/linux/freezer.h b/include/linux/freezer.h index e4238ceaa4d6..e70df40d84f6 100644 --- a/include/linux/freezer.h +++ b/include/linux/freezer.h @@ -12,6 +12,11 @@ extern atomic_t system_freezing_cnt; /* nr of freezing conds in effect */ extern bool pm_freezing; /* PM freezing in effect */ extern bool pm_nosig_freezing; /* PM nosig freezing in effect */ +/* + * Timeout for stopping processes + */ +extern unsigned int freeze_timeout_msecs; + /* * Check if a process has been frozen */ diff --git a/kernel/power/main.c b/kernel/power/main.c index b1c26a92ca9f..d77663bfedeb 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -553,6 +553,30 @@ power_attr(pm_trace_dev_match); #endif /* CONFIG_PM_TRACE */ +#ifdef CONFIG_FREEZER +static ssize_t pm_freeze_timeout_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", freeze_timeout_msecs); +} + +static ssize_t pm_freeze_timeout_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + unsigned long val; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + freeze_timeout_msecs = val; + return n; +} + +power_attr(pm_freeze_timeout); + +#endif /* CONFIG_FREEZER*/ + static struct attribute * g[] = { &state_attr.attr, #ifdef CONFIG_PM_TRACE @@ -575,6 +599,9 @@ static struct attribute * g[] = { #ifdef CONFIG_PM_SLEEP_DEBUG &pm_print_times_attr.attr, #endif +#endif +#ifdef CONFIG_FREEZER + &pm_freeze_timeout_attr.attr, #endif NULL, }; diff --git a/kernel/power/process.c b/kernel/power/process.c index d5a258b60c6f..98088e0e71e8 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -21,7 +21,7 @@ /* * Timeout for stopping processes */ -#define TIMEOUT (20 * HZ) +unsigned int __read_mostly freeze_timeout_msecs = 20 * MSEC_PER_SEC; static int try_to_freeze_tasks(bool user_only) { @@ -36,7 +36,7 @@ static int try_to_freeze_tasks(bool user_only) do_gettimeofday(&start); - end_time = jiffies + TIMEOUT; + end_time = jiffies + msecs_to_jiffies(freeze_timeout_msecs); if (!user_only) freeze_workqueues_begin(); -- cgit v1.2.3-59-g8ed1b From 69fb3676df3329a7142803bb3502fa59dc0db2e3 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Sun, 10 Feb 2013 01:38:39 -0500 Subject: x86 idle: remove mwait_idle() and "idle=mwait" cmdline param mwait_idle() is a C1-only idle loop intended to be more efficient than HLT, starting on Pentium-4 HT-enabled processors. But mwait_idle() has been replaced by the more general mwait_idle_with_hints(), which handles both C1 and deeper C-states. ACPI processor_idle and intel_idle use only mwait_idle_with_hints(), and no longer use mwait_idle(). Here we simplify the x86 native idle code by removing mwait_idle(), and the "idle=mwait" bootparam used to invoke it. Since Linux 3.0 there has been a boot-time warning when "idle=mwait" was invoked saying it would be removed in 2012. This removal was also noted in the (now removed:-) feature-removal-schedule.txt. After this change, kernels configured with (CONFIG_ACPI=n && CONFIG_INTEL_IDLE=n) when run on hardware that supports MWAIT will simply use HLT. If MWAIT is desired on those systems, cpuidle and the cpuidle drivers above can be enabled. Signed-off-by: Len Brown Cc: x86@kernel.org --- Documentation/kernel-parameters.txt | 7 +--- arch/x86/include/asm/processor.h | 2 +- arch/x86/kernel/process.c | 79 +------------------------------------ arch/x86/kernel/smpboot.c | 2 +- drivers/acpi/processor_idle.c | 1 - 5 files changed, 4 insertions(+), 87 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 363e348bff9b..3b0cd1e612da 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1039,16 +1039,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted. Claim all unknown PCI IDE storage controllers. idle= [X86] - Format: idle=poll, idle=mwait, idle=halt, idle=nomwait + Format: idle=poll, idle=halt, idle=nomwait Poll forces a polling idle loop that can slightly improve the performance of waking up a idle CPU, but will use a lot of power and make the system run hot. Not recommended. - idle=mwait: On systems which support MONITOR/MWAIT but - the kernel chose to not use it because it doesn't save - as much power as a normal idle loop, use the - MONITOR/MWAIT idle loop anyways. Performance should be - the same as idle=poll. idle=halt: Halt is forced to be used for CPU idle. In such case C2/C3 won't be used again. idle=nomwait: Disable mwait for CPU C-states diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index c2f7f472275e..8a28feae1c92 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -725,7 +725,7 @@ extern unsigned long boot_option_idle_override; extern bool amd_e400_c1e_detected; enum idle_boot_override {IDLE_NO_OVERRIDE=0, IDLE_HALT, IDLE_NOMWAIT, - IDLE_POLL, IDLE_FORCE_MWAIT}; + IDLE_POLL}; extern void enable_sep_cpu(void); extern int sysenter_setup(void); diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 7ed9f6b08ba0..cd5a4c9ef835 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -421,27 +421,6 @@ void stop_this_cpu(void *dummy) } } -/* Default MONITOR/MWAIT with no hints, used for default C1 state */ -static void mwait_idle(void) -{ - if (!need_resched()) { - trace_power_start_rcuidle(POWER_CSTATE, 1, smp_processor_id()); - trace_cpu_idle_rcuidle(1, smp_processor_id()); - if (this_cpu_has(X86_FEATURE_CLFLUSH_MONITOR)) - clflush((void *)¤t_thread_info()->flags); - - __monitor((void *)¤t_thread_info()->flags, 0, 0); - smp_mb(); - if (!need_resched()) - __sti_mwait(0, 0); - else - local_irq_enable(); - trace_power_end_rcuidle(smp_processor_id()); - trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, smp_processor_id()); - } else - local_irq_enable(); -} - /* * On SMP it's slightly faster (but much more power-consuming!) * to poll the ->work.need_resched flag instead of waiting for the @@ -458,53 +437,6 @@ static void poll_idle(void) trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, smp_processor_id()); } -/* - * mwait selection logic: - * - * It depends on the CPU. For AMD CPUs that support MWAIT this is - * wrong. Family 0x10 and 0x11 CPUs will enter C1 on HLT. Powersavings - * then depend on a clock divisor and current Pstate of the core. If - * all cores of a processor are in halt state (C1) the processor can - * enter the C1E (C1 enhanced) state. If mwait is used this will never - * happen. - * - * idle=mwait overrides this decision and forces the usage of mwait. - */ - -#define MWAIT_INFO 0x05 -#define MWAIT_ECX_EXTENDED_INFO 0x01 -#define MWAIT_EDX_C1 0xf0 - -int mwait_usable(const struct cpuinfo_x86 *c) -{ - u32 eax, ebx, ecx, edx; - - /* Use mwait if idle=mwait boot option is given */ - if (boot_option_idle_override == IDLE_FORCE_MWAIT) - return 1; - - /* - * Any idle= boot option other than idle=mwait means that we must not - * use mwait. Eg: idle=halt or idle=poll or idle=nomwait - */ - if (boot_option_idle_override != IDLE_NO_OVERRIDE) - return 0; - - if (c->cpuid_level < MWAIT_INFO) - return 0; - - cpuid(MWAIT_INFO, &eax, &ebx, &ecx, &edx); - /* Check, whether EDX has extended info about MWAIT */ - if (!(ecx & MWAIT_ECX_EXTENDED_INFO)) - return 1; - - /* - * edx enumeratios MONITOR/MWAIT extensions. Check, whether - * C1 supports MWAIT - */ - return (edx & MWAIT_EDX_C1); -} - bool amd_e400_c1e_detected; EXPORT_SYMBOL(amd_e400_c1e_detected); @@ -576,13 +508,7 @@ void __cpuinit select_idle_routine(const struct cpuinfo_x86 *c) if (pm_idle) return; - if (cpu_has(c, X86_FEATURE_MWAIT) && mwait_usable(c)) { - /* - * One CPU supports mwait => All CPUs supports mwait - */ - pr_info("using mwait in idle threads\n"); - pm_idle = mwait_idle; - } else if (cpu_has_amd_erratum(amd_erratum_400)) { + if (cpu_has_amd_erratum(amd_erratum_400)) { /* E400: APIC timer interrupt does not wake up CPU from C1e */ pr_info("using AMD E400 aware idle routine\n"); pm_idle = amd_e400_idle; @@ -606,9 +532,6 @@ static int __init idle_setup(char *str) pr_info("using polling idle threads\n"); pm_idle = poll_idle; boot_option_idle_override = IDLE_POLL; - } else if (!strcmp(str, "mwait")) { - boot_option_idle_override = IDLE_FORCE_MWAIT; - WARN_ONCE(1, "\"idle=mwait\" will be removed in 2012\n"); } else if (!strcmp(str, "halt")) { /* * When the boot option of idle=halt is added, halt is diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index ed0fe385289d..a6ceaedc396a 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -1369,7 +1369,7 @@ static inline void mwait_play_dead(void) void *mwait_ptr; struct cpuinfo_x86 *c = __this_cpu_ptr(&cpu_info); - if (!(this_cpu_has(X86_FEATURE_MWAIT) && mwait_usable(c))) + if (!this_cpu_has(X86_FEATURE_MWAIT)) return; if (!this_cpu_has(X86_FEATURE_CLFLSH)) return; diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index ed9a1cc690be..52a5e3a4cdc3 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -84,7 +84,6 @@ static DEFINE_PER_CPU(struct cpuidle_device *, acpi_cpuidle_device); static int disabled_by_idle_boot_param(void) { return boot_option_idle_override == IDLE_POLL || - boot_option_idle_override == IDLE_FORCE_MWAIT || boot_option_idle_override == IDLE_HALT; } -- cgit v1.2.3-59-g8ed1b From 27be457000211a6903968dfce06d5f73f051a217 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Sun, 10 Feb 2013 02:28:46 -0500 Subject: x86 idle: remove 32-bit-only "no-hlt" parameter, hlt_works_ok flag Remove 32-bit x86 a cmdline param "no-hlt", and the cpuinfo_x86.hlt_works_ok that it sets. If a user wants to avoid HLT, then "idle=poll" is much more useful, as it avoids invocation of HLT in idle, while "no-hlt" failed to do so. Indeed, hlt_works_ok was consulted in only 3 places. First, in /proc/cpuinfo where "hlt_bug yes" would be printed if and only if the user booted the system with "no-hlt" -- as there was no other code to set that flag. Second, check_hlt() would not invoke halt() if "no-hlt" were on the cmdline. Third, it was consulted in stop_this_cpu(), which is invoked by native_machine_halt()/reboot_interrupt()/smp_stop_nmi_callback() -- all cases where the machine is being shutdown/reset. The flag was not consulted in the more frequently invoked play_dead()/hlt_play_dead() used in processor offline and suspend. Since Linux-3.0 there has been a run-time notice upon "no-hlt" invocations indicating that it would be removed in 2012. Signed-off-by: Len Brown Cc: x86@kernel.org --- Documentation/kernel-parameters.txt | 4 ---- arch/x86/include/asm/processor.h | 10 ---------- arch/x86/kernel/cpu/bugs.c | 27 --------------------------- arch/x86/kernel/cpu/proc.c | 2 -- arch/x86/kernel/process.c | 6 ++---- arch/x86/xen/setup.c | 3 --- 6 files changed, 2 insertions(+), 50 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 3b0cd1e612da..109ee45cf20d 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1881,10 +1881,6 @@ bytes respectively. Such letter suffixes can also be entirely omitted. wfi(ARM) instruction doesn't work correctly and not to use it. This is also useful when using JTAG debugger. - no-hlt [BUGS=X86-32] Tells the kernel that the hlt - instruction doesn't work correctly and not to - use it. - no_file_caps Tells the kernel not to honor file capabilities. The only way then for a file to be executed with privilege is to be setuid root or executed by root. diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 8a28feae1c92..b9e7d279f8ef 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -89,7 +89,6 @@ struct cpuinfo_x86 { char wp_works_ok; /* It doesn't on 386's */ /* Problems on some 486Dx4's and old 386's: */ - char hlt_works_ok; char hard_math; char rfu; char fdiv_bug; @@ -165,15 +164,6 @@ DECLARE_PER_CPU_SHARED_ALIGNED(struct cpuinfo_x86, cpu_info); extern const struct seq_operations cpuinfo_op; -static inline int hlt_works(int cpu) -{ -#ifdef CONFIG_X86_32 - return cpu_data(cpu).hlt_works_ok; -#else - return 1; -#endif -} - #define cache_line_size() (boot_cpu_data.x86_cache_alignment) extern void cpu_detect(struct cpuinfo_x86 *c); diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c index 92dfec986a48..af6455e3fcc9 100644 --- a/arch/x86/kernel/cpu/bugs.c +++ b/arch/x86/kernel/cpu/bugs.c @@ -17,15 +17,6 @@ #include #include -static int __init no_halt(char *s) -{ - WARN_ONCE(1, "\"no-hlt\" is deprecated, please use \"idle=poll\"\n"); - boot_cpu_data.hlt_works_ok = 0; - return 1; -} - -__setup("no-hlt", no_halt); - static int __init no_387(char *s) { boot_cpu_data.hard_math = 0; @@ -89,23 +80,6 @@ static void __init check_fpu(void) pr_warn("Hmm, FPU with FDIV bug\n"); } -static void __init check_hlt(void) -{ - if (boot_cpu_data.x86 >= 5 || paravirt_enabled()) - return; - - pr_info("Checking 'hlt' instruction... "); - if (!boot_cpu_data.hlt_works_ok) { - pr_cont("disabled\n"); - return; - } - halt(); - halt(); - halt(); - halt(); - pr_cont("OK\n"); -} - /* * Check whether we are able to run this kernel safely on SMP. * @@ -129,7 +103,6 @@ void __init check_bugs(void) print_cpu_info(&boot_cpu_data); #endif check_config(); - check_hlt(); init_utsname()->machine[1] = '0' + (boot_cpu_data.x86 > 6 ? 6 : boot_cpu_data.x86); alternative_instructions(); diff --git a/arch/x86/kernel/cpu/proc.c b/arch/x86/kernel/cpu/proc.c index 3286a92e662a..e280253f6f94 100644 --- a/arch/x86/kernel/cpu/proc.c +++ b/arch/x86/kernel/cpu/proc.c @@ -28,7 +28,6 @@ static void show_cpuinfo_misc(struct seq_file *m, struct cpuinfo_x86 *c) { seq_printf(m, "fdiv_bug\t: %s\n" - "hlt_bug\t\t: %s\n" "f00f_bug\t: %s\n" "coma_bug\t: %s\n" "fpu\t\t: %s\n" @@ -36,7 +35,6 @@ static void show_cpuinfo_misc(struct seq_file *m, struct cpuinfo_x86 *c) "cpuid level\t: %d\n" "wp\t\t: %s\n", c->fdiv_bug ? "yes" : "no", - c->hlt_works_ok ? "no" : "yes", c->f00f_bug ? "yes" : "no", c->coma_bug ? "yes" : "no", c->hard_math ? "yes" : "no", diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index cd5a4c9ef835..aef852eac292 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -415,10 +415,8 @@ void stop_this_cpu(void *dummy) set_cpu_online(smp_processor_id(), false); disable_local_APIC(); - for (;;) { - if (hlt_works(smp_processor_id())) - halt(); - } + for (;;) + halt(); } /* diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c index 2b73b5c8555f..94eac5c85cdc 100644 --- a/arch/x86/xen/setup.c +++ b/arch/x86/xen/setup.c @@ -556,9 +556,6 @@ void __init xen_arch_setup(void) COMMAND_LINE_SIZE : MAX_GUEST_CMDLINE); /* Set up idle, making sure it calls safe_halt() pvop */ -#ifdef CONFIG_X86_32 - boot_cpu_data.hlt_works_ok = 1; -#endif disable_cpuidle(); disable_cpufreq(); WARN_ON(xen_set_default_idle()); -- cgit v1.2.3-59-g8ed1b From d9ba8f9e6298af71ec1c1fd3d88c3ef68abd0ec3 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Mon, 11 Feb 2013 09:52:20 +0000 Subject: driver: net: ethernet: cpsw: dual emac interface implementation The CPSW switch can act as Dual EMAC by segregating the switch ports using VLAN and port VLAN as per the TRM description in 14.3.2.10.2 Dual Mac Mode Following CPSW components will be common for both the interfaces. * Interrupt source is common for both eth interfaces * Interrupt pacing is common for both interfaces * Hardware statistics is common for all the ports * CPDMA is common for both eth interface * CPTS is common for both the interface and it should not be enabled on both the interface as timestamping information doesn't contain port information. Constrains * Reserved VID of One port should not be used in other interface which will enable switching functionality * Same VID must not be used in both the interface which will enable switching functionality Signed-off-by: Mugunthan V N Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/cpsw.txt | 2 + drivers/net/ethernet/ti/cpsw.c | 335 +++++++++++++++++++++---- include/linux/platform_data/cpsw.h | 3 + 3 files changed, 288 insertions(+), 52 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt index 6ddd0286a9b7..ecfdf756d10f 100644 --- a/Documentation/devicetree/bindings/net/cpsw.txt +++ b/Documentation/devicetree/bindings/net/cpsw.txt @@ -24,6 +24,8 @@ Required properties: Optional properties: - ti,hwmods : Must be "cpgmac0" - no_bd_ram : Must be 0 or 1 +- dual_emac : Specifies Switch to act as Dual EMAC +- 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 resources from TI, omap hwmod data base during device registration. diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 4b964bb02d4c..4ceed6e0f1be 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -122,6 +122,10 @@ do { \ #define CPSW_VLAN_AWARE BIT(1) #define CPSW_ALE_VLAN_AWARE 1 +#define CPSW_FIFO_NORMAL_MODE (0 << 15) +#define CPSW_FIFO_DUAL_MAC_MODE (1 << 15) +#define CPSW_FIFO_RATE_LIMIT_MODE (2 << 15) + #define cpsw_enable_irq(priv) \ do { \ u32 i; \ @@ -254,7 +258,7 @@ struct cpsw_ss_regs { struct cpsw_host_regs { u32 max_blks; u32 blk_cnt; - u32 flow_thresh; + u32 tx_in_ctl; u32 port_vlan; u32 tx_pri_map; u32 cpdma_tx_pri_map; @@ -281,6 +285,9 @@ struct cpsw_slave { u32 mac_control; struct cpsw_slave_data *data; struct phy_device *phy; + struct net_device *ndev; + u32 port_vlan; + u32 open_stat; }; static inline u32 slave_read(struct cpsw_slave *slave, u32 offset) @@ -320,15 +327,63 @@ struct cpsw_priv { u32 irqs_table[4]; u32 num_irqs; struct cpts *cpts; + u32 emac_port; }; #define napi_to_priv(napi) container_of(napi, struct cpsw_priv, napi) -#define for_each_slave(priv, func, arg...) \ - do { \ - int idx; \ - for (idx = 0; idx < (priv)->data.slaves; idx++) \ - (func)((priv)->slaves + idx, ##arg); \ +#define for_each_slave(priv, func, arg...) \ + do { \ + int idx; \ + if (priv->data.dual_emac) \ + (func)((priv)->slaves + priv->emac_port, ##arg);\ + else \ + for (idx = 0; idx < (priv)->data.slaves; idx++) \ + (func)((priv)->slaves + idx, ##arg); \ + } while (0) +#define cpsw_get_slave_ndev(priv, __slave_no__) \ + (priv->slaves[__slave_no__].ndev) +#define cpsw_get_slave_priv(priv, __slave_no__) \ + ((priv->slaves[__slave_no__].ndev) ? \ + netdev_priv(priv->slaves[__slave_no__].ndev) : NULL) \ + +#define cpsw_dual_emac_src_port_detect(status, priv, ndev, skb) \ + do { \ + if (!priv->data.dual_emac) \ + break; \ + if (CPDMA_RX_SOURCE_PORT(status) == 1) { \ + ndev = cpsw_get_slave_ndev(priv, 0); \ + priv = netdev_priv(ndev); \ + skb->dev = ndev; \ + } else if (CPDMA_RX_SOURCE_PORT(status) == 2) { \ + ndev = cpsw_get_slave_ndev(priv, 1); \ + priv = netdev_priv(ndev); \ + skb->dev = ndev; \ + } \ } while (0) +#define cpsw_add_mcast(priv, addr) \ + do { \ + if (priv->data.dual_emac) { \ + struct cpsw_slave *slave = priv->slaves + \ + priv->emac_port; \ + int slave_port = cpsw_get_slave_port(priv, \ + slave->slave_num); \ + cpsw_ale_add_mcast(priv->ale, addr, \ + 1 << slave_port | 1 << priv->host_port, \ + ALE_VLAN, slave->port_vlan, 0); \ + } else { \ + cpsw_ale_add_mcast(priv->ale, addr, \ + ALE_ALL_PORTS << priv->host_port, \ + 0, 0, 0); \ + } \ + } while (0) + +static inline int cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num) +{ + if (priv->host_port == 0) + return slave_num + 1; + else + return slave_num; +} static void cpsw_ndo_set_rx_mode(struct net_device *ndev) { @@ -348,8 +403,7 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev) /* program multicast address list into ALE register */ netdev_for_each_mc_addr(ha, ndev) { - cpsw_ale_add_mcast(priv->ale, (u8 *)ha->addr, - ALE_ALL_PORTS << priv->host_port, 0, 0, 0); + cpsw_add_mcast(priv, (u8 *)ha->addr); } } } @@ -396,6 +450,8 @@ void cpsw_rx_handler(void *token, int len, int status) struct cpsw_priv *priv = netdev_priv(ndev); int ret = 0; + cpsw_dual_emac_src_port_detect(status, priv, ndev, skb); + /* free and bail if we are shutting down */ if (unlikely(!netif_running(ndev)) || unlikely(!netif_carrier_ok(ndev))) { @@ -437,18 +493,17 @@ static irqreturn_t cpsw_interrupt(int irq, void *dev_id) cpsw_intr_disable(priv); cpsw_disable_irq(priv); napi_schedule(&priv->napi); + } else { + priv = cpsw_get_slave_priv(priv, 1); + if (likely(priv) && likely(netif_running(priv->ndev))) { + cpsw_intr_disable(priv); + cpsw_disable_irq(priv); + napi_schedule(&priv->napi); + } } return IRQ_HANDLED; } -static inline int cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num) -{ - if (priv->host_port == 0) - return slave_num + 1; - else - return slave_num; -} - static int cpsw_poll(struct napi_struct *napi, int budget) { struct cpsw_priv *priv = napi_to_priv(napi); @@ -566,6 +621,54 @@ static inline int __show_stat(char *buf, int maxlen, const char *name, u32 val) leader + strlen(name), val); } +static int cpsw_common_res_usage_state(struct cpsw_priv *priv) +{ + u32 i; + u32 usage_count = 0; + + if (!priv->data.dual_emac) + return 0; + + for (i = 0; i < priv->data.slaves; i++) + if (priv->slaves[i].open_stat) + usage_count++; + + return usage_count; +} + +static inline int cpsw_tx_packet_submit(struct net_device *ndev, + struct cpsw_priv *priv, struct sk_buff *skb) +{ + if (!priv->data.dual_emac) + return cpdma_chan_submit(priv->txch, skb, skb->data, + skb->len, 0, GFP_KERNEL); + + if (ndev == cpsw_get_slave_ndev(priv, 0)) + return cpdma_chan_submit(priv->txch, skb, skb->data, + skb->len, 1, GFP_KERNEL); + else + return cpdma_chan_submit(priv->txch, skb, skb->data, + skb->len, 2, GFP_KERNEL); +} + +static inline void cpsw_add_dual_emac_def_ale_entries( + struct cpsw_priv *priv, struct cpsw_slave *slave, + u32 slave_port) +{ + u32 port_mask = 1 << slave_port | 1 << priv->host_port; + + if (priv->version == CPSW_VERSION_1) + slave_write(slave, slave->port_vlan, CPSW1_PORT_VLAN); + else + slave_write(slave, slave->port_vlan, CPSW2_PORT_VLAN); + cpsw_ale_add_vlan(priv->ale, slave->port_vlan, port_mask, + port_mask, port_mask, 0); + cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast, + port_mask, ALE_VLAN, slave->port_vlan, 0); + cpsw_ale_add_ucast(priv->ale, priv->mac_addr, + priv->host_port, ALE_VLAN, slave->port_vlan); +} + static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) { char name[32]; @@ -595,8 +698,11 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) slave_port = cpsw_get_slave_port(priv, slave->slave_num); - cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast, - 1 << slave_port, 0, 0, ALE_MCAST_FWD_2); + if (priv->data.dual_emac) + cpsw_add_dual_emac_def_ale_entries(priv, slave, slave_port); + else + cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast, + 1 << slave_port, 0, 0, ALE_MCAST_FWD_2); slave->phy = phy_connect(priv->ndev, slave->data->phy_id, &cpsw_adjust_link, slave->data->phy_if); @@ -634,6 +740,7 @@ static inline void cpsw_add_default_vlan(struct cpsw_priv *priv) static void cpsw_init_host_port(struct cpsw_priv *priv) { u32 control_reg; + u32 fifo_mode; /* soft reset the controller and initialize ale */ soft_reset("cpsw", &priv->regs->soft_reset); @@ -645,6 +752,9 @@ static void cpsw_init_host_port(struct cpsw_priv *priv) control_reg = readl(&priv->regs->control); control_reg |= CPSW_VLAN_AWARE; writel(control_reg, &priv->regs->control); + fifo_mode = (priv->data.dual_emac) ? CPSW_FIFO_DUAL_MAC_MODE : + CPSW_FIFO_NORMAL_MODE; + writel(fifo_mode, &priv->host_port_regs->tx_in_ctl); /* setup host port priority mapping */ __raw_writel(CPDMA_TX_PRIORITY_MAP, @@ -654,9 +764,12 @@ static void cpsw_init_host_port(struct cpsw_priv *priv) cpsw_ale_control_set(priv->ale, priv->host_port, ALE_PORT_STATE, ALE_PORT_STATE_FORWARD); - cpsw_ale_add_ucast(priv->ale, priv->mac_addr, priv->host_port, 0, 0); - cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast, - 1 << priv->host_port, 0, 0, ALE_MCAST_FWD_2); + if (!priv->data.dual_emac) { + cpsw_ale_add_ucast(priv->ale, priv->mac_addr, priv->host_port, + 0, 0); + cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast, + 1 << priv->host_port, 0, 0, ALE_MCAST_FWD_2); + } } static int cpsw_ndo_open(struct net_device *ndev) @@ -665,7 +778,8 @@ static int cpsw_ndo_open(struct net_device *ndev) int i, ret; u32 reg; - cpsw_intr_disable(priv); + if (!cpsw_common_res_usage_state(priv)) + cpsw_intr_disable(priv); netif_carrier_off(ndev); pm_runtime_get_sync(&priv->pdev->dev); @@ -677,46 +791,54 @@ static int cpsw_ndo_open(struct net_device *ndev) CPSW_RTL_VERSION(reg)); /* initialize host and slave ports */ - cpsw_init_host_port(priv); + if (!cpsw_common_res_usage_state(priv)) + cpsw_init_host_port(priv); for_each_slave(priv, cpsw_slave_open, priv); /* Add default VLAN */ - cpsw_add_default_vlan(priv); + if (!priv->data.dual_emac) + cpsw_add_default_vlan(priv); - /* setup tx dma to fixed prio and zero offset */ - cpdma_control_set(priv->dma, CPDMA_TX_PRIO_FIXED, 1); - cpdma_control_set(priv->dma, CPDMA_RX_BUFFER_OFFSET, 0); + if (!cpsw_common_res_usage_state(priv)) { + /* setup tx dma to fixed prio and zero offset */ + cpdma_control_set(priv->dma, CPDMA_TX_PRIO_FIXED, 1); + cpdma_control_set(priv->dma, CPDMA_RX_BUFFER_OFFSET, 0); - /* disable priority elevation and enable statistics on all ports */ - __raw_writel(0, &priv->regs->ptype); + /* disable priority elevation */ + __raw_writel(0, &priv->regs->ptype); - /* enable statistics collection only on the host port */ - __raw_writel(0x7, &priv->regs->stat_port_en); + /* enable statistics collection only on all ports */ + __raw_writel(0x7, &priv->regs->stat_port_en); - if (WARN_ON(!priv->data.rx_descs)) - priv->data.rx_descs = 128; + if (WARN_ON(!priv->data.rx_descs)) + priv->data.rx_descs = 128; - for (i = 0; i < priv->data.rx_descs; i++) { - struct sk_buff *skb; + for (i = 0; i < priv->data.rx_descs; i++) { + struct sk_buff *skb; - ret = -ENOMEM; - skb = netdev_alloc_skb_ip_align(priv->ndev, - priv->rx_packet_max); - if (!skb) - break; - ret = cpdma_chan_submit(priv->rxch, skb, skb->data, + ret = -ENOMEM; + skb = netdev_alloc_skb_ip_align(priv->ndev, + priv->rx_packet_max); + if (!skb) + break; + ret = cpdma_chan_submit(priv->rxch, skb, skb->data, skb_tailroom(skb), 0, GFP_KERNEL); - if (WARN_ON(ret < 0)) - break; + if (WARN_ON(ret < 0)) + break; + } + /* continue even if we didn't manage to submit all + * receive descs + */ + cpsw_info(priv, ifup, "submitted %d rx descriptors\n", i); } - /* continue even if we didn't manage to submit all receive descs */ - cpsw_info(priv, ifup, "submitted %d rx descriptors\n", i); cpdma_ctlr_start(priv->dma); cpsw_intr_enable(priv); napi_enable(&priv->napi); cpdma_ctlr_eoi(priv->dma); + if (priv->data.dual_emac) + priv->slaves[priv->emac_port].open_stat = true; return 0; } @@ -737,12 +859,17 @@ static int cpsw_ndo_stop(struct net_device *ndev) netif_stop_queue(priv->ndev); napi_disable(&priv->napi); netif_carrier_off(priv->ndev); - cpsw_intr_disable(priv); - cpdma_ctlr_int_ctrl(priv->dma, false); - cpdma_ctlr_stop(priv->dma); - cpsw_ale_stop(priv->ale); + + if (cpsw_common_res_usage_state(priv) <= 1) { + cpsw_intr_disable(priv); + cpdma_ctlr_int_ctrl(priv->dma, false); + cpdma_ctlr_stop(priv->dma); + cpsw_ale_stop(priv->ale); + } for_each_slave(priv, cpsw_slave_stop, priv); pm_runtime_put_sync(&priv->pdev->dev); + if (priv->data.dual_emac) + priv->slaves[priv->emac_port].open_stat = false; return 0; } @@ -766,8 +893,7 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb, skb_tx_timestamp(skb); - ret = cpdma_chan_submit(priv->txch, skb, skb->data, - skb->len, 0, GFP_KERNEL); + ret = cpsw_tx_packet_submit(ndev, priv, skb); if (unlikely(ret != 0)) { cpsw_err(priv, tx_err, "desc submit failed\n"); goto fail; @@ -836,9 +962,14 @@ static void cpsw_hwtstamp_v1(struct cpsw_priv *priv) static void cpsw_hwtstamp_v2(struct cpsw_priv *priv) { - struct cpsw_slave *slave = &priv->slaves[priv->data.cpts_active_slave]; + struct cpsw_slave *slave; u32 ctrl, mtype; + if (priv->data.dual_emac) + slave = &priv->slaves[priv->emac_port]; + else + slave = &priv->slaves[priv->data.cpts_active_slave]; + ctrl = slave_read(slave, CPSW2_CONTROL); ctrl &= ~CTRL_ALL_TS_MASK; @@ -1124,6 +1255,7 @@ static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv, slave->data = data; slave->regs = regs + slave_reg_ofs; slave->sliver = regs + sliver_reg_ofs; + slave->port_vlan = data->dual_emac_res_vlan; } static int cpsw_probe_dt(struct cpsw_platform_data *data, @@ -1204,6 +1336,9 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->mac_control = prop; + if (!of_property_read_u32(node, "dual_emac", &prop)) + data->dual_emac = prop; + /* * Populate all the child nodes here... */ @@ -1237,6 +1372,18 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, if (mac_addr) memcpy(slave_data->mac_addr, mac_addr, ETH_ALEN); + if (data->dual_emac) { + if (of_property_read_u32(node, "dual_emac_res_vlan", + &prop)) { + pr_err("Missing dual_emac_res_vlan in DT.\n"); + slave_data->dual_emac_res_vlan = i+1; + pr_err("Using %d as Reserved VLAN for %d slave\n", + slave_data->dual_emac_res_vlan, i); + } else { + slave_data->dual_emac_res_vlan = prop; + } + } + i++; } @@ -1247,6 +1394,79 @@ error_ret: return ret; } +static int cpsw_probe_dual_emac(struct platform_device *pdev, + struct cpsw_priv *priv) +{ + struct cpsw_platform_data *data = &priv->data; + struct net_device *ndev; + struct cpsw_priv *priv_sl2; + int ret = 0, i; + + ndev = alloc_etherdev(sizeof(struct cpsw_priv)); + if (!ndev) { + pr_err("cpsw: error allocating net_device\n"); + return -ENOMEM; + } + + priv_sl2 = netdev_priv(ndev); + spin_lock_init(&priv_sl2->lock); + priv_sl2->data = *data; + priv_sl2->pdev = pdev; + priv_sl2->ndev = ndev; + priv_sl2->dev = &ndev->dev; + priv_sl2->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG); + priv_sl2->rx_packet_max = max(rx_packet_max, 128); + + if (is_valid_ether_addr(data->slave_data[1].mac_addr)) { + memcpy(priv_sl2->mac_addr, data->slave_data[1].mac_addr, + ETH_ALEN); + pr_info("cpsw: Detected MACID = %pM\n", priv_sl2->mac_addr); + } else { + random_ether_addr(priv_sl2->mac_addr); + pr_info("cpsw: Random MACID = %pM\n", priv_sl2->mac_addr); + } + memcpy(ndev->dev_addr, priv_sl2->mac_addr, ETH_ALEN); + + priv_sl2->slaves = priv->slaves; + priv_sl2->clk = priv->clk; + + priv_sl2->cpsw_res = priv->cpsw_res; + priv_sl2->regs = priv->regs; + priv_sl2->host_port = priv->host_port; + priv_sl2->host_port_regs = priv->host_port_regs; + priv_sl2->wr_regs = priv->wr_regs; + priv_sl2->dma = priv->dma; + priv_sl2->txch = priv->txch; + priv_sl2->rxch = priv->rxch; + priv_sl2->ale = priv->ale; + priv_sl2->emac_port = 1; + priv->slaves[1].ndev = ndev; + priv_sl2->cpts = priv->cpts; + priv_sl2->version = priv->version; + + for (i = 0; i < priv->num_irqs; i++) { + priv_sl2->irqs_table[i] = priv->irqs_table[i]; + priv_sl2->num_irqs = priv->num_irqs; + } + + ndev->features |= NETIF_F_HW_VLAN_FILTER; + + ndev->netdev_ops = &cpsw_netdev_ops; + SET_ETHTOOL_OPS(ndev, &cpsw_ethtool_ops); + netif_napi_add(ndev, &priv_sl2->napi, cpsw_poll, CPSW_POLL_WEIGHT); + + /* register the network device */ + SET_NETDEV_DEV(ndev, &pdev->dev); + ret = register_netdev(ndev); + if (ret) { + pr_err("cpsw: error registering net device\n"); + free_netdev(ndev); + ret = -ENODEV; + } + + return ret; +} + static int cpsw_probe(struct platform_device *pdev) { struct cpsw_platform_data *data = pdev->dev.platform_data; @@ -1310,6 +1530,9 @@ static int cpsw_probe(struct platform_device *pdev) for (i = 0; i < data->slaves; i++) priv->slaves[i].slave_num = i; + priv->slaves[0].ndev = ndev; + priv->emac_port = 0; + priv->clk = clk_get(&pdev->dev, "fck"); if (IS_ERR(priv->clk)) { dev_err(&pdev->dev, "fck is not found\n"); @@ -1484,6 +1707,14 @@ static int cpsw_probe(struct platform_device *pdev) cpsw_notice(priv, probe, "initialized device (regs %x, irq %d)\n", priv->cpsw_res->start, ndev->irq); + if (priv->data.dual_emac) { + ret = cpsw_probe_dual_emac(pdev, priv); + if (ret) { + cpsw_err(priv, probe, "error probe slave 2 emac interface\n"); + goto clean_irq_ret; + } + } + return 0; clean_irq_ret: diff --git a/include/linux/platform_data/cpsw.h b/include/linux/platform_data/cpsw.h index e962cfd552e3..798fb80b024b 100644 --- a/include/linux/platform_data/cpsw.h +++ b/include/linux/platform_data/cpsw.h @@ -21,6 +21,8 @@ struct cpsw_slave_data { char phy_id[MII_BUS_ID_SIZE]; int phy_if; u8 mac_addr[ETH_ALEN]; + u16 dual_emac_res_vlan; /* Reserved VLAN for DualEMAC */ + }; struct cpsw_platform_data { @@ -36,6 +38,7 @@ struct cpsw_platform_data { u32 rx_descs; /* Number of Rx Descriptios */ u32 mac_control; /* Mac control register */ u16 default_vlan; /* Def VLAN for ALE lookup in VLAN aware mode*/ + bool dual_emac; /* Enable Dual EMAC mode */ }; #endif /* __CPSW_H__ */ -- cgit v1.2.3-59-g8ed1b From e253673ec5e3e0b82e6261d5c14505fed77d8b18 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Sat, 9 Feb 2013 00:00:13 +0100 Subject: ACPI / Documentation: refer to correct file for acpi_platform_device_ids[] table When the ACPI platform device code was converted to the new ACPI scan handler facility, the the acpi_platform_device_ids[] was moved to drivers/acpi/acpi_platform.c. Update the documentation accordingly. Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki --- Documentation/acpi/enumeration.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/acpi/enumeration.txt b/Documentation/acpi/enumeration.txt index 54469bc81b1c..94a656131885 100644 --- a/Documentation/acpi/enumeration.txt +++ b/Documentation/acpi/enumeration.txt @@ -63,8 +63,8 @@ from ACPI tables. Currently the kernel is not able to automatically determine from which ACPI device it should make the corresponding platform device so we need to add the ACPI device explicitly to acpi_platform_device_ids list defined in -drivers/acpi/scan.c. This limitation is only for the platform devices, SPI -and I2C devices are created automatically as described below. +drivers/acpi/acpi_platform.c. This limitation is only for the platform +devices, SPI and I2C devices are created automatically as described below. SPI serial bus support ~~~~~~~~~~~~~~~~~~~~~~ -- cgit v1.2.3-59-g8ed1b From 6be264986152c498562f26b15ed73c70aa37ce48 Mon Sep 17 00:00:00 2001 From: Dirk Brandewie Date: Fri, 15 Feb 2013 22:55:10 +0100 Subject: cpufreq / intel_pstate: Add kernel command line option disable intel_pstate. When intel_pstate is configured into the kernel it will become the preferred scaling driver for processors that it supports. Allow the user to override this by adding: intel_pstate=disable on the kernel command line. Signed-off-by: Dirk Brandewie Signed-off-by: Rafael J. Wysocki --- Documentation/kernel-parameters.txt | 5 +++++ drivers/cpufreq/intel_pstate.c | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 363e348bff9b..b335c456feb9 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1131,6 +1131,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted. 0 disables intel_idle and fall back on acpi_idle. 1 to 6 specify maximum depth of C-state. + intel_pstate= [X86] + disable + Do not enable intel_pstate as the default + scaling driver for the supported processors + intremap= [X86-64, Intel-IOMMU] on enable Interrupt Remapping (default) off disable Interrupt Remapping diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index e87996355da0..096fde0ebcb5 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -773,11 +773,16 @@ static void intel_pstate_exit(void) } module_exit(intel_pstate_exit); +static int __initdata no_load; + static int __init intel_pstate_init(void) { int rc = 0; const struct x86_cpu_id *id; + if (no_load) + return -ENODEV; + id = x86_match_cpu(intel_pstate_cpu_ids); if (!id) return -ENODEV; @@ -802,6 +807,17 @@ out: } device_initcall(intel_pstate_init); +static int __init intel_pstate_setup(char *str) +{ + if (!str) + return -EINVAL; + + if (!strcmp(str, "disable")) + no_load = 1; + return 0; +} +early_param("intel_pstate", intel_pstate_setup); + MODULE_AUTHOR("Dirk Brandewie "); MODULE_DESCRIPTION("'intel_pstate' - P state driver Intel Core processors"); MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 6295ca184b6216ecde68f16f487a524734761adf Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 15 Feb 2013 10:11:24 +0100 Subject: cfg80211: add correct docbook entries Update the 802.11 book to list the correct data structures. Signed-off-by: Johannes Berg --- Documentation/DocBook/80211.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl index 42e7f030cb16..284ced7a228f 100644 --- a/Documentation/DocBook/80211.tmpl +++ b/Documentation/DocBook/80211.tmpl @@ -107,8 +107,8 @@ !Finclude/net/cfg80211.h key_params !Finclude/net/cfg80211.h survey_info_flags !Finclude/net/cfg80211.h survey_info -!Finclude/net/cfg80211.h beacon_parameters -!Finclude/net/cfg80211.h plink_actions +!Finclude/net/cfg80211.h cfg80211_beacon_data +!Finclude/net/cfg80211.h cfg80211_ap_settings !Finclude/net/cfg80211.h station_parameters !Finclude/net/cfg80211.h station_info_flags !Finclude/net/cfg80211.h rate_info_flags -- cgit v1.2.3-59-g8ed1b