* [PATCHv3 08/10] Bluetooth: hci_serdev: allow modular drivers
From: Sebastian Reichel @ 2017-03-28 15:59 UTC (permalink / raw)
To: Sebastian Reichel, Marcel Holtmann, Gustavo Padovan,
Johan Hedberg, Rob Herring
Cc: Samuel Thibault, Pavel Machek, Tony Lindgren, Greg Kroah-Hartman,
Jiri Slaby, Mark Rutland, linux-bluetooth-u79uwXL29TY76Z2rM5mHXA,
linux-serial-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170328155939.31566-1-sre-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
For bluetooth protocol driver only supporting serdev it makes
sense to follow common practice and built them into their own
module.
Such modules need access to hci_uart_register_device and
hci_uart_tx_wakeup for using the common protocol helpers.
Signed-off-by: Sebastian Reichel <sre-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
New patch since patchset v2.
---
drivers/bluetooth/hci_ldisc.c | 1 +
drivers/bluetooth/hci_serdev.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 0ec8a94bd712..17bcbc13623f 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -134,6 +134,7 @@ int hci_uart_tx_wakeup(struct hci_uart *hu)
return 0;
}
+EXPORT_SYMBOL_GPL(hci_uart_tx_wakeup);
static void hci_uart_write_work(struct work_struct *work)
{
diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c
index 3b8ac0ece3fb..7de0edc0ff8c 100644
--- a/drivers/bluetooth/hci_serdev.c
+++ b/drivers/bluetooth/hci_serdev.c
@@ -353,3 +353,4 @@ int hci_uart_register_device(struct hci_uart *hu,
p->close(hu);
return err;
}
+EXPORT_SYMBOL_GPL(hci_uart_register_device);
--
2.11.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCHv3 09/10] dt-bindings: net: bluetooth: Add nokia-bluetooth
From: Sebastian Reichel @ 2017-03-28 15:59 UTC (permalink / raw)
To: Sebastian Reichel, Marcel Holtmann, Gustavo Padovan,
Johan Hedberg, Rob Herring
Cc: Samuel Thibault, Pavel Machek, Tony Lindgren, Greg Kroah-Hartman,
Jiri Slaby, Mark Rutland, linux-bluetooth-u79uwXL29TY76Z2rM5mHXA,
linux-serial-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170328155939.31566-1-sre-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Add binding document for serial bluetooth chips using
Nokia H4+ protocol.
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Signed-off-by: Sebastian Reichel <sre-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Changes since PATCHv1:
* change compatible strings
* mention active high/low state for GPIOs
---
.../devicetree/bindings/net/nokia-bluetooth.txt | 51 ++++++++++++++++++++++
1 file changed, 51 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/nokia-bluetooth.txt
diff --git a/Documentation/devicetree/bindings/net/nokia-bluetooth.txt b/Documentation/devicetree/bindings/net/nokia-bluetooth.txt
new file mode 100644
index 000000000000..42be7dc9a70b
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/nokia-bluetooth.txt
@@ -0,0 +1,51 @@
+Nokia Bluetooth Chips
+---------------------
+
+Nokia phones often come with UART connected bluetooth chips from different
+vendors and modified device API. Those devices speak a protocol named H4+
+(also known as h4p) by Nokia, which is similar to the H4 protocol from the
+Bluetooth standard. In addition to the H4 protocol it specifies two more
+UART status lines for wakeup of UART transceivers to improve power management
+and a few new packet types used to negotiate uart speed.
+
+Required properties:
+
+ - compatible: should contain "nokia,h4p-bluetooth" as well as one of the following:
+ * "brcm,bcm2048-nokia"
+ * "ti,wl1271-bluetooth-nokia"
+ - reset-gpios: GPIO specifier, used to reset the BT module (active low)
+ - bluetooth-wakeup-gpios: GPIO specifier, used to wakeup the BT module (active high)
+ - host-wakeup-gpios: GPIO specifier, used to wakeup the host processor (active high)
+ - clock-names: should be "sysclk"
+ - clocks: should contain a clock specifier for every name in clock-names
+
+Optional properties:
+
+ - None
+
+Example:
+
+/ {
+ /* controlled (enabled/disabled) directly by BT module */
+ bluetooth_clk: vctcxo {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <38400000>;
+ };
+};
+
+&uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart2_pins>;
+
+ bluetooth {
+ compatible = "ti,wl1271-bluetooth-nokia", "nokia,h4p-bluetooth";
+
+ reset-gpios = <&gpio1 26 GPIO_ACTIVE_LOW>; /* gpio26 */
+ host-wakeup-gpios = <&gpio4 5 GPIO_ACTIVE_HIGH>; /* gpio101 */
+ bluetooth-wakeup-gpios = <&gpio2 5 GPIO_ACTIVE_HIGH>; /* gpio37 */
+
+ clocks = <&bluetooth_clk>;
+ clock-names = "sysclk";
+ };
+};
--
2.11.0
^ permalink raw reply related
* [PATCHv3 10/10] Bluetooth: add nokia driver
From: Sebastian Reichel @ 2017-03-28 15:59 UTC (permalink / raw)
To: Sebastian Reichel, Marcel Holtmann, Gustavo Padovan,
Johan Hedberg, Rob Herring
Cc: Samuel Thibault, Pavel Machek, Tony Lindgren, Greg Kroah-Hartman,
Jiri Slaby, Mark Rutland, linux-bluetooth-u79uwXL29TY76Z2rM5mHXA,
linux-serial-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20170328155939.31566-1-sre-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
This adds a driver for the Nokia H4+ protocol, which is used
at least on the Nokia N9, N900 & N950.
Signed-off-by: Sebastian Reichel <sre-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
Changes since PATCHv1:
* replace __u8 and uint8_t with u8
* replace __u16 and uint16_t with u16
* drop BT_BAUDRATE_DIVIDER and use btdev->sysclk_speed * 10 instead
* fix wording of a sentence
* fix error path of negotation & alive package receive functions
* replaced nokia_wait_for_cts with newly introduced serdev function
* use "nokia,h4p-bluetooth" as compatible string
---
drivers/bluetooth/Kconfig | 12 +
drivers/bluetooth/Makefile | 2 +
drivers/bluetooth/hci_nokia.c | 819 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 833 insertions(+)
create mode 100644 drivers/bluetooth/hci_nokia.c
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index c2c14a12713b..2e3e4d3547ad 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -86,6 +86,18 @@ config BT_HCIUART_H4
Say Y here to compile support for HCI UART (H4) protocol.
+config BT_HCIUART_NOKIA
+ tristate "UART Nokia H4+ protocol support"
+ depends on BT_HCIUART
+ depends on SERIAL_DEV_BUS
+ depends on PM
+ help
+ Nokia H4+ is serial protocol for communication between Bluetooth
+ device and host. This protocol is required for Bluetooth devices
+ with UART interface in Nokia devices.
+
+ Say Y here to compile support for Nokia's H4+ protocol.
+
config BT_HCIUART_BCSP
bool "BCSP protocol support"
depends on BT_HCIUART
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index fd571689eed6..a7f237320f4b 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -25,6 +25,8 @@ obj-$(CONFIG_BT_BCM) += btbcm.o
obj-$(CONFIG_BT_RTL) += btrtl.o
obj-$(CONFIG_BT_QCA) += btqca.o
+obj-$(CONFIG_BT_HCIUART_NOKIA) += hci_nokia.o
+
btmrvl-y := btmrvl_main.o
btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
diff --git a/drivers/bluetooth/hci_nokia.c b/drivers/bluetooth/hci_nokia.c
new file mode 100644
index 000000000000..c77f04af01bf
--- /dev/null
+++ b/drivers/bluetooth/hci_nokia.c
@@ -0,0 +1,819 @@
+/*
+ * Bluetooth HCI UART H4 driver with Nokia Extensions AKA Nokia H4+
+ *
+ * Copyright (C) 2015 Marcel Holtmann <marcel-kz+m5ild9QBg9hUCZPvPmw@public.gmane.org>
+ * Copyright (C) 2015-2017 Sebastian Reichel <sre-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
+ *
+ * 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 <linux/module.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+#include <linux/gpio/consumer.h>
+#include <linux/unaligned/le_struct.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <linux/serdev.h>
+
+#include "hci_uart.h"
+#include "btbcm.h"
+
+#define NOKIA_ID_BCM2048 0x04
+#define NOKIA_ID_TI1271 0x31
+
+#define FIRMWARE_BCM2048 "nokia/bcmfw.bin"
+#define FIRMWARE_TI1271 "nokia/ti1273.bin"
+
+#define HCI_NOKIA_NEG_PKT 0x06
+#define HCI_NOKIA_ALIVE_PKT 0x07
+#define HCI_NOKIA_RADIO_PKT 0x08
+
+#define HCI_NOKIA_NEG_HDR_SIZE 1
+#define HCI_NOKIA_MAX_NEG_SIZE 255
+#define HCI_NOKIA_ALIVE_HDR_SIZE 1
+#define HCI_NOKIA_MAX_ALIVE_SIZE 255
+#define HCI_NOKIA_RADIO_HDR_SIZE 2
+#define HCI_NOKIA_MAX_RADIO_SIZE 255
+
+#define NOKIA_PROTO_PKT 0x44
+#define NOKIA_PROTO_BYTE 0x4c
+
+#define NOKIA_NEG_REQ 0x00
+#define NOKIA_NEG_ACK 0x20
+#define NOKIA_NEG_NAK 0x40
+
+#define H4_TYPE_SIZE 1
+
+#define NOKIA_RECV_ALIVE \
+ .type = HCI_NOKIA_ALIVE_PKT, \
+ .hlen = HCI_NOKIA_ALIVE_HDR_SIZE, \
+ .loff = 0, \
+ .lsize = 1, \
+ .maxlen = HCI_NOKIA_MAX_ALIVE_SIZE \
+
+#define NOKIA_RECV_NEG \
+ .type = HCI_NOKIA_NEG_PKT, \
+ .hlen = HCI_NOKIA_NEG_HDR_SIZE, \
+ .loff = 0, \
+ .lsize = 1, \
+ .maxlen = HCI_NOKIA_MAX_NEG_SIZE \
+
+#define NOKIA_RECV_RADIO \
+ .type = HCI_NOKIA_RADIO_PKT, \
+ .hlen = HCI_NOKIA_RADIO_HDR_SIZE, \
+ .loff = 1, \
+ .lsize = 1, \
+ .maxlen = HCI_NOKIA_MAX_RADIO_SIZE \
+
+struct hci_nokia_neg_hdr {
+ u8 dlen;
+} __packed;
+
+struct hci_nokia_neg_cmd {
+ u8 ack;
+ u16 baud;
+ u16 unused1;
+ u8 proto;
+ u16 sys_clk;
+ u16 unused2;
+} __packed;
+
+#define NOKIA_ALIVE_REQ 0x55
+#define NOKIA_ALIVE_RESP 0xcc
+
+struct hci_nokia_alive_hdr {
+ u8 dlen;
+} __packed;
+
+struct hci_nokia_alive_pkt {
+ u8 mid;
+ u8 unused;
+} __packed;
+
+struct hci_nokia_neg_evt {
+ u8 ack;
+ u16 baud;
+ u16 unused1;
+ u8 proto;
+ u16 sys_clk;
+ u16 unused2;
+ u8 man_id;
+ u8 ver_id;
+} __packed;
+
+#define MAX_BAUD_RATE 3692300
+#define SETUP_BAUD_RATE 921600
+#define INIT_BAUD_RATE 120000
+
+struct hci_nokia_radio_hdr {
+ u8 evt;
+ u8 dlen;
+} __packed;
+
+struct nokia_bt_dev {
+ struct hci_uart hu;
+ struct serdev_device *serdev;
+
+ struct gpio_desc *reset;
+ struct gpio_desc *wakeup_host;
+ struct gpio_desc *wakeup_bt;
+ unsigned long sysclk_speed;
+
+ int wake_irq;
+ struct sk_buff *rx_skb;
+ struct sk_buff_head txq;
+ bdaddr_t bdaddr;
+
+ int init_error;
+ struct completion init_completion;
+
+ u8 man_id;
+ u8 ver_id;
+
+ bool initialized;
+ bool tx_enabled;
+ bool rx_enabled;
+};
+
+static int nokia_enqueue(struct hci_uart *hu, struct sk_buff *skb);
+
+static void nokia_flow_control(struct serdev_device *serdev, bool enable)
+{
+ if (enable) {
+ serdev_device_set_rts(serdev, true);
+ serdev_device_set_flow_control(serdev, true);
+ } else {
+ serdev_device_set_flow_control(serdev, false);
+ serdev_device_set_rts(serdev, false);
+ }
+}
+
+static irqreturn_t wakeup_handler(int irq, void *data)
+{
+ struct nokia_bt_dev *btdev = data;
+ struct device *dev = &btdev->serdev->dev;
+ int wake_state = gpiod_get_value(btdev->wakeup_host);
+
+ if (btdev->rx_enabled == wake_state)
+ return IRQ_HANDLED;
+
+ if (wake_state)
+ pm_runtime_get(dev);
+ else
+ pm_runtime_put(dev);
+
+ btdev->rx_enabled = wake_state;
+
+ return IRQ_HANDLED;
+}
+
+static int nokia_reset(struct hci_uart *hu)
+{
+ struct nokia_bt_dev *btdev = hu->priv;
+ struct device *dev = &btdev->serdev->dev;
+ int err;
+
+ /* reset routine */
+ gpiod_set_value_cansleep(btdev->reset, 1);
+ gpiod_set_value_cansleep(btdev->wakeup_bt, 1);
+
+ msleep(100);
+
+ /* safety check */
+ err = gpiod_get_value_cansleep(btdev->wakeup_host);
+ if (err == 1) {
+ dev_err(dev, "reset: host wakeup not low!");
+ return -EPROTO;
+ }
+
+ /* flush queue */
+ serdev_device_write_flush(btdev->serdev);
+
+ /* init uart */
+ nokia_flow_control(btdev->serdev, false);
+ serdev_device_set_baudrate(btdev->serdev, INIT_BAUD_RATE);
+
+ gpiod_set_value_cansleep(btdev->reset, 0);
+
+ /* wait for cts */
+ err = serdev_device_wait_for_cts(btdev->serdev, true, 200);
+ if (err < 0) {
+ dev_err(dev, "CTS not received: %d", err);
+ return err;
+ }
+
+ nokia_flow_control(btdev->serdev, true);
+
+ return 0;
+}
+
+static int nokia_send_alive_packet(struct hci_uart *hu)
+{
+ struct nokia_bt_dev *btdev = hu->priv;
+ struct device *dev = &btdev->serdev->dev;
+ struct hci_nokia_alive_hdr *hdr;
+ struct hci_nokia_alive_pkt *pkt;
+ struct sk_buff *skb;
+ int len;
+
+ init_completion(&btdev->init_completion);
+
+ len = H4_TYPE_SIZE + sizeof(*hdr) + sizeof(*pkt);
+ skb = bt_skb_alloc(len, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ hci_skb_pkt_type(skb) = HCI_NOKIA_ALIVE_PKT;
+ memset(skb->data, 0x00, len);
+
+ hdr = (struct hci_nokia_alive_hdr *)skb_put(skb, sizeof(*hdr));
+ hdr->dlen = sizeof(*pkt);
+ pkt = (struct hci_nokia_alive_pkt *)skb_put(skb, sizeof(*pkt));
+ pkt->mid = NOKIA_ALIVE_REQ;
+
+ nokia_enqueue(hu, skb);
+ hci_uart_tx_wakeup(hu);
+
+ dev_dbg(dev, "Alive sent");
+
+ if (!wait_for_completion_interruptible_timeout(&btdev->init_completion,
+ msecs_to_jiffies(1000))) {
+ return -ETIMEDOUT;
+ }
+
+ if (btdev->init_error < 0)
+ return btdev->init_error;
+
+ return 0;
+}
+
+static int nokia_send_negotiation(struct hci_uart *hu)
+{
+ struct nokia_bt_dev *btdev = hu->priv;
+ struct device *dev = &btdev->serdev->dev;
+ struct hci_nokia_neg_cmd *neg_cmd;
+ struct hci_nokia_neg_hdr *neg_hdr;
+ struct sk_buff *skb;
+ int len, err;
+ u16 baud = DIV_ROUND_CLOSEST(btdev->sysclk_speed * 10, SETUP_BAUD_RATE);
+ int sysclk = btdev->sysclk_speed / 1000;
+
+ len = H4_TYPE_SIZE + sizeof(*neg_hdr) + sizeof(*neg_cmd);
+ skb = bt_skb_alloc(len, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ hci_skb_pkt_type(skb) = HCI_NOKIA_NEG_PKT;
+
+ neg_hdr = (struct hci_nokia_neg_hdr *)skb_put(skb, sizeof(*neg_hdr));
+ neg_hdr->dlen = sizeof(*neg_cmd);
+
+ neg_cmd = (struct hci_nokia_neg_cmd *)skb_put(skb, sizeof(*neg_cmd));
+ neg_cmd->ack = NOKIA_NEG_REQ;
+ neg_cmd->baud = cpu_to_le16(baud);
+ neg_cmd->unused1 = 0x0000;
+ neg_cmd->proto = NOKIA_PROTO_BYTE;
+ neg_cmd->sys_clk = cpu_to_le16(sysclk);
+ neg_cmd->unused2 = 0x0000;
+
+ btdev->init_error = 0;
+ init_completion(&btdev->init_completion);
+
+ nokia_enqueue(hu, skb);
+ hci_uart_tx_wakeup(hu);
+
+ dev_dbg(dev, "Negotiation sent");
+
+ if (!wait_for_completion_interruptible_timeout(&btdev->init_completion,
+ msecs_to_jiffies(10000))) {
+ return -ETIMEDOUT;
+ }
+
+ if (btdev->init_error < 0)
+ return btdev->init_error;
+
+ /* Change to previously negotiated speed. Flow Control
+ * is disabled until bluetooth adapter is ready to avoid
+ * broken bytes being received.
+ */
+ nokia_flow_control(btdev->serdev, false);
+ serdev_device_set_baudrate(btdev->serdev, SETUP_BAUD_RATE);
+ err = serdev_device_wait_for_cts(btdev->serdev, true, 200);
+ if (err < 0) {
+ dev_err(dev, "CTS not received: %d", err);
+ return err;
+ }
+ nokia_flow_control(btdev->serdev, true);
+
+ dev_dbg(dev, "Negotiation successful");
+
+ return 0;
+}
+
+static int nokia_setup_fw(struct hci_uart *hu)
+{
+ struct nokia_bt_dev *btdev = hu->priv;
+ struct device *dev = &btdev->serdev->dev;
+ const char *fwname;
+ const struct firmware *fw;
+ const u8 *fw_ptr;
+ size_t fw_size;
+ int err;
+
+ dev_dbg(dev, "setup firmware");
+
+ if (btdev->man_id == NOKIA_ID_BCM2048) {
+ fwname = FIRMWARE_BCM2048;
+ } else if (btdev->man_id == NOKIA_ID_TI1271) {
+ fwname = FIRMWARE_TI1271;
+ } else {
+ dev_err(dev, "Unsupported bluetooth device!");
+ return -ENODEV;
+ }
+
+ err = request_firmware(&fw, fwname, dev);
+ if (err < 0) {
+ dev_err(dev, "%s: Failed to load Nokia firmware file (%d)",
+ hu->hdev->name, err);
+ return err;
+ }
+
+ fw_ptr = fw->data;
+ fw_size = fw->size;
+
+ while (fw_size >= 4) {
+ u16 pkt_size = get_unaligned_le16(fw_ptr);
+ u8 pkt_type = fw_ptr[2];
+ const struct hci_command_hdr *cmd;
+ u16 opcode;
+ struct sk_buff *skb;
+
+ switch (pkt_type) {
+ case HCI_COMMAND_PKT:
+ cmd = (struct hci_command_hdr *)(fw_ptr + 3);
+ opcode = le16_to_cpu(cmd->opcode);
+
+ skb = __hci_cmd_sync(hu->hdev, opcode, cmd->plen,
+ fw_ptr + 3 + HCI_COMMAND_HDR_SIZE,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ dev_err(dev, "%s: FW command %04x failed (%d)",
+ hu->hdev->name, opcode, err);
+ goto done;
+ }
+ kfree_skb(skb);
+ break;
+ case HCI_NOKIA_RADIO_PKT:
+ case HCI_NOKIA_NEG_PKT:
+ case HCI_NOKIA_ALIVE_PKT:
+ break;
+ }
+
+ fw_ptr += pkt_size + 2;
+ fw_size -= pkt_size + 2;
+ }
+
+done:
+ release_firmware(fw);
+ return err;
+}
+
+static int nokia_setup(struct hci_uart *hu)
+{
+ struct nokia_bt_dev *btdev = hu->priv;
+ struct device *dev = &btdev->serdev->dev;
+ int err;
+
+ btdev->initialized = false;
+
+ nokia_flow_control(btdev->serdev, false);
+
+ pm_runtime_get_sync(dev);
+
+ if (btdev->tx_enabled) {
+ gpiod_set_value_cansleep(btdev->wakeup_bt, 0);
+ pm_runtime_put(&btdev->serdev->dev);
+ btdev->tx_enabled = false;
+ }
+
+ dev_dbg(dev, "protocol setup");
+
+ /* 0. reset connection */
+ err = nokia_reset(hu);
+ if (err < 0) {
+ dev_err(dev, "Reset failed: %d", err);
+ goto out;
+ }
+
+ /* 1. negotiate speed etc */
+ err = nokia_send_negotiation(hu);
+ if (err < 0) {
+ dev_err(dev, "Negotiation failed: %d", err);
+ goto out;
+ }
+
+ /* 2. verify correct setup using alive packet */
+ err = nokia_send_alive_packet(hu);
+ if (err < 0) {
+ dev_err(dev, "Alive check failed: %d", err);
+ goto out;
+ }
+
+ /* 3. send firmware */
+ err = nokia_setup_fw(hu);
+ if (err < 0) {
+ dev_err(dev, "Could not setup FW: %d", err);
+ goto out;
+ }
+
+ nokia_flow_control(btdev->serdev, false);
+ serdev_device_set_baudrate(btdev->serdev, MAX_BAUD_RATE);
+ nokia_flow_control(btdev->serdev, true);
+
+ if (btdev->man_id == NOKIA_ID_BCM2048) {
+ hu->hdev->set_bdaddr = btbcm_set_bdaddr;
+ set_bit(HCI_QUIRK_INVALID_BDADDR, &hu->hdev->quirks);
+ dev_dbg(dev, "bcm2048 has invalid bluetooth address!");
+ }
+
+ dev_dbg(dev, "protocol setup done!");
+
+ gpiod_set_value_cansleep(btdev->wakeup_bt, 0);
+ pm_runtime_put(dev);
+ btdev->tx_enabled = false;
+ btdev->initialized = true;
+
+ return 0;
+out:
+ pm_runtime_put(dev);
+
+ return err;
+}
+
+static int nokia_open(struct hci_uart *hu)
+{
+ struct device *dev = &hu->serdev->dev;
+
+ dev_dbg(dev, "protocol open");
+
+ serdev_device_open(hu->serdev);
+
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
+static int nokia_flush(struct hci_uart *hu)
+{
+ struct nokia_bt_dev *btdev = hu->priv;
+
+ dev_dbg(&btdev->serdev->dev, "flush device");
+
+ skb_queue_purge(&btdev->txq);
+
+ return 0;
+}
+
+static int nokia_close(struct hci_uart *hu)
+{
+ struct nokia_bt_dev *btdev = hu->priv;
+ struct device *dev = &btdev->serdev->dev;
+
+ dev_dbg(dev, "close device");
+
+ btdev->initialized = false;
+
+ skb_queue_purge(&btdev->txq);
+
+ kfree_skb(btdev->rx_skb);
+
+ /* disable module */
+ gpiod_set_value(btdev->reset, 1);
+ gpiod_set_value(btdev->wakeup_bt, 0);
+
+ pm_runtime_disable(&btdev->serdev->dev);
+ serdev_device_close(btdev->serdev);
+
+ return 0;
+}
+
+/* Enqueue frame for transmittion (padding, crc, etc) */
+static int nokia_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+ struct nokia_bt_dev *btdev = hu->priv;
+ int err;
+
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+
+ /* Packets must be word aligned */
+ if (skb->len % 2) {
+ err = skb_pad(skb, 1);
+ if (err)
+ return err;
+ *skb_put(skb, 1) = 0x00;
+ }
+
+ skb_queue_tail(&btdev->txq, skb);
+
+ return 0;
+}
+
+static int nokia_recv_negotiation_packet(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_uart *hu = hci_get_drvdata(hdev);
+ struct nokia_bt_dev *btdev = hu->priv;
+ struct device *dev = &btdev->serdev->dev;
+ struct hci_nokia_neg_hdr *hdr;
+ struct hci_nokia_neg_evt *evt;
+ int ret = 0;
+
+ hdr = (struct hci_nokia_neg_hdr *)skb->data;
+ if (hdr->dlen != sizeof(*evt)) {
+ btdev->init_error = -EIO;
+ ret = -EIO;
+ goto finish_neg;
+ }
+
+ evt = (struct hci_nokia_neg_evt *)skb_pull(skb, sizeof(*hdr));
+
+ if (evt->ack != NOKIA_NEG_ACK) {
+ dev_err(dev, "Negotiation received: wrong reply");
+ btdev->init_error = -EINVAL;
+ ret = -EINVAL;
+ goto finish_neg;
+ }
+
+ btdev->man_id = evt->man_id;
+ btdev->ver_id = evt->ver_id;
+
+ dev_dbg(dev, "Negotiation received: baud=%u:clk=%u:manu=%u:vers=%u",
+ evt->baud, evt->sys_clk, evt->man_id, evt->ver_id);
+
+finish_neg:
+ complete(&btdev->init_completion);
+ kfree_skb(skb);
+ return ret;
+}
+
+static int nokia_recv_alive_packet(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_uart *hu = hci_get_drvdata(hdev);
+ struct nokia_bt_dev *btdev = hu->priv;
+ struct device *dev = &btdev->serdev->dev;
+ struct hci_nokia_alive_hdr *hdr;
+ struct hci_nokia_alive_pkt *pkt;
+ int ret = 0;
+
+ hdr = (struct hci_nokia_alive_hdr *)skb->data;
+ if (hdr->dlen != sizeof(*pkt)) {
+ dev_err(dev, "Corrupted alive message");
+ btdev->init_error = -EIO;
+ ret = -EIO;
+ goto finish_alive;
+ }
+
+ pkt = (struct hci_nokia_alive_pkt *)skb_pull(skb, sizeof(*hdr));
+
+ if (pkt->mid != NOKIA_ALIVE_RESP) {
+ dev_err(dev, "Alive received: invalid response: 0x%02x!",
+ pkt->mid);
+ btdev->init_error = -EINVAL;
+ ret = -EINVAL;
+ goto finish_alive;
+ }
+
+ dev_dbg(dev, "Alive received");
+
+finish_alive:
+ complete(&btdev->init_completion);
+ kfree_skb(skb);
+ return ret;
+}
+
+static int nokia_recv_radio(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ /* Packets received on the dedicated radio channel are
+ * HCI events and so feed them back into the core.
+ */
+ hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
+ return hci_recv_frame(hdev, skb);
+}
+
+/* Recv data */
+static const struct h4_recv_pkt nokia_recv_pkts[] = {
+ { H4_RECV_ACL, .recv = hci_recv_frame },
+ { H4_RECV_SCO, .recv = hci_recv_frame },
+ { H4_RECV_EVENT, .recv = hci_recv_frame },
+ { NOKIA_RECV_ALIVE, .recv = nokia_recv_alive_packet },
+ { NOKIA_RECV_NEG, .recv = nokia_recv_negotiation_packet },
+ { NOKIA_RECV_RADIO, .recv = nokia_recv_radio },
+};
+
+static int nokia_recv(struct hci_uart *hu, const void *data, int count)
+{
+ struct nokia_bt_dev *btdev = hu->priv;
+ struct device *dev = &btdev->serdev->dev;
+ int err;
+
+ if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
+ return -EUNATCH;
+
+ btdev->rx_skb = h4_recv_buf(hu->hdev, btdev->rx_skb, data, count,
+ nokia_recv_pkts, ARRAY_SIZE(nokia_recv_pkts));
+ if (IS_ERR(btdev->rx_skb)) {
+ err = PTR_ERR(btdev->rx_skb);
+ dev_err(dev, "Frame reassembly failed (%d)", err);
+ btdev->rx_skb = NULL;
+ return err;
+ }
+
+ return count;
+}
+
+static struct sk_buff *nokia_dequeue(struct hci_uart *hu)
+{
+ struct nokia_bt_dev *btdev = hu->priv;
+ struct device *dev = &btdev->serdev->dev;
+ struct sk_buff *result = skb_dequeue(&btdev->txq);
+
+ if (!btdev->initialized)
+ return result;
+
+ if (btdev->tx_enabled == !!result)
+ return result;
+
+ if (result) {
+ pm_runtime_get_sync(dev);
+ gpiod_set_value_cansleep(btdev->wakeup_bt, 1);
+ } else {
+ serdev_device_wait_until_sent(btdev->serdev, 0);
+ gpiod_set_value_cansleep(btdev->wakeup_bt, 0);
+ pm_runtime_put(dev);
+ }
+
+ btdev->tx_enabled = !!result;
+
+ return result;
+}
+
+static const struct hci_uart_proto nokia_proto = {
+ .id = HCI_UART_NOKIA,
+ .name = "Nokia",
+ .open = nokia_open,
+ .close = nokia_close,
+ .recv = nokia_recv,
+ .enqueue = nokia_enqueue,
+ .dequeue = nokia_dequeue,
+ .flush = nokia_flush,
+ .setup = nokia_setup,
+ .manufacturer = 1,
+};
+
+static int nokia_bluetooth_serdev_probe(struct serdev_device *serdev)
+{
+ struct device *dev = &serdev->dev;
+ struct nokia_bt_dev *btdev;
+ struct clk *sysclk;
+ int err = 0;
+
+ btdev = devm_kzalloc(dev, sizeof(*btdev), GFP_KERNEL);
+ if (!btdev)
+ return -ENOMEM;
+
+ btdev->hu.serdev = btdev->serdev = serdev;
+ serdev_device_set_drvdata(serdev, btdev);
+
+ btdev->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(btdev->reset)) {
+ err = PTR_ERR(btdev->reset);
+ dev_err(dev, "could not get reset gpio: %d", err);
+ return err;
+ }
+
+ btdev->wakeup_host = devm_gpiod_get(dev, "host-wakeup", GPIOD_IN);
+ if (IS_ERR(btdev->wakeup_host)) {
+ err = PTR_ERR(btdev->wakeup_host);
+ dev_err(dev, "could not get host wakeup gpio: %d", err);
+ return err;
+ }
+
+ btdev->wake_irq = gpiod_to_irq(btdev->wakeup_host);
+
+ err = devm_request_threaded_irq(dev, btdev->wake_irq, NULL,
+ wakeup_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "wakeup", btdev);
+ if (err) {
+ dev_err(dev, "could request wakeup irq: %d", err);
+ return err;
+ }
+
+ btdev->wakeup_bt = devm_gpiod_get(dev, "bluetooth-wakeup",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(btdev->wakeup_bt)) {
+ err = PTR_ERR(btdev->wakeup_bt);
+ dev_err(dev, "could not get BT wakeup gpio: %d", err);
+ return err;
+ }
+
+ sysclk = devm_clk_get(dev, "sysclk");
+ if (IS_ERR(sysclk)) {
+ err = PTR_ERR(sysclk);
+ dev_err(dev, "could not get sysclk: %d", err);
+ return err;
+ }
+
+ clk_prepare_enable(sysclk);
+ btdev->sysclk_speed = clk_get_rate(sysclk);
+ clk_disable_unprepare(sysclk);
+
+ skb_queue_head_init(&btdev->txq);
+
+ btdev->hu.priv = btdev;
+ btdev->hu.alignment = 2; /* Nokia H4+ is word aligned */
+
+ err = hci_uart_register_device(&btdev->hu, &nokia_proto);
+ if (err) {
+ dev_err(dev, "could not register bluetooth uart: %d", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static void nokia_bluetooth_serdev_remove(struct serdev_device *serdev)
+{
+ struct nokia_bt_dev *btdev = serdev_device_get_drvdata(serdev);
+ struct hci_uart *hu = &btdev->hu;
+ struct hci_dev *hdev = hu->hdev;
+
+ cancel_work_sync(&hu->write_work);
+
+ hci_unregister_dev(hdev);
+ hci_free_dev(hdev);
+ hu->proto->close(hu);
+
+ pm_runtime_disable(&btdev->serdev->dev);
+}
+
+static int nokia_bluetooth_runtime_suspend(struct device *dev)
+{
+ struct serdev_device *serdev = to_serdev_device(dev);
+
+ nokia_flow_control(serdev, false);
+ return 0;
+}
+
+static int nokia_bluetooth_runtime_resume(struct device *dev)
+{
+ struct serdev_device *serdev = to_serdev_device(dev);
+
+ nokia_flow_control(serdev, true);
+ return 0;
+}
+
+static const struct dev_pm_ops nokia_bluetooth_pm_ops = {
+ SET_RUNTIME_PM_OPS(nokia_bluetooth_runtime_suspend,
+ nokia_bluetooth_runtime_resume,
+ NULL)
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id nokia_bluetooth_of_match[] = {
+ { .compatible = "nokia,h4p-bluetooth", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, nokia_bluetooth_of_match);
+#endif
+
+static struct serdev_device_driver nokia_bluetooth_serdev_driver = {
+ .probe = nokia_bluetooth_serdev_probe,
+ .remove = nokia_bluetooth_serdev_remove,
+ .driver = {
+ .name = "nokia-bluetooth",
+ .pm = &nokia_bluetooth_pm_ops,
+ .of_match_table = of_match_ptr(nokia_bluetooth_of_match),
+ },
+};
+
+module_serdev_device_driver(nokia_bluetooth_serdev_driver);
--
2.11.0
^ permalink raw reply related
* Re: [PATCH v2] ARM: dts: am335x-baltos: add LED support
From: Tony Lindgren @ 2017-03-28 16:07 UTC (permalink / raw)
To: yegorslists-gM/Ye1E23mwN+BqQ9rBEUg
Cc: linux-omap-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A
In-Reply-To: <1490706556-30046-1-git-send-email-yegorslists-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
* yegorslists-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org <yegorslists-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org> [170328 06:11]:
> From: Yegor Yefremov <yegorslists-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
>
> All three devices provide GPIO based LEDs named power,
> wlan and app.
>
> Place LEDs definition into a separate dtsi file as not all
> devices including am335x-baltos.dtsi have the same LED layout.
>
> Signed-off-by: Yegor Yefremov <yegorslists-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
> ---
> Changes v1 -> v2:
> - move LEDs definition to a separate dtsi file (Tony Lindgren)
Thanks for updating it, applying into omap-for-v4.12/dt-v2.
Tony
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v2 0/5] firmware: google memconsole
From: Thierry Escande @ 2017-03-28 16:11 UTC (permalink / raw)
To: Rob Herring, Greg Kroah-Hartman
Cc: Olof Johansson, Stephen Warren, linux-kernel, devicetree,
Julius Werner, Brian Norris
Hi,
This series extends the Google memory console driver to work on x86 and
ARM platforms running coreboot.
The Google memory console driver provides access to an in-memory log of
the firmware's output.
The legacy x86 memconsole driver is now named memconsole-x86-legacy and
a new driver named memconsole-coreboot is added.
On coreboot devices and depending on the device architecture, the driver
coreboot_table-acpi on x86 platforms or coreboot_table-of on ARM/ARM64
platforms is responsible to get the coreboot table header memory address
and then obtain the memory console address.
Thus, this series adds device tree binding for coreboot entries under
the '/firmware' node. The place where this should be/have been declared
has been discussed years ago when first submitted. Please see previous
discussions [1] and [2]. In the meantime a lot of chromebook devices
have been shipped with this binding in their firmware making it hard to
change/update.
[1] https://patchwork.ozlabs.org/patch/359672/
[2] https://patchwork.kernel.org/patch/4362661/
Regards,
Thierry
This v2 adds a dependency on X86 for the GOOGLE_SMI symbol and few more
comments in this cover letter.
Julius Werner (1):
firmware: Add coreboot device tree binding documentation
Thierry Escande (4):
firmware: google memconsole: Remove useless submenu in Kconfig
firmware: google memconsole: Move specific EBDA parts
firmware: google memconsole: Add coreboot support
firmware: google memconsole: Add ARM/ARM64 support
.../devicetree/bindings/firmware/coreboot.txt | 33 +++++
drivers/firmware/google/Kconfig | 50 ++++++-
drivers/firmware/google/Makefile | 7 +-
drivers/firmware/google/coreboot_table-acpi.c | 88 ++++++++++++
drivers/firmware/google/coreboot_table-of.c | 82 +++++++++++
drivers/firmware/google/coreboot_table.c | 94 +++++++++++++
drivers/firmware/google/coreboot_table.h | 50 +++++++
drivers/firmware/google/memconsole-coreboot.c | 109 +++++++++++++++
drivers/firmware/google/memconsole-x86-legacy.c | 154 ++++++++++++++++++++
drivers/firmware/google/memconsole.c | 155 +++------------------
drivers/firmware/google/memconsole.h | 43 ++++++
11 files changed, 725 insertions(+), 140 deletions(-)
create mode 100644 Documentation/devicetree/bindings/firmware/coreboot.txt
create mode 100644 drivers/firmware/google/coreboot_table-acpi.c
create mode 100644 drivers/firmware/google/coreboot_table-of.c
create mode 100644 drivers/firmware/google/coreboot_table.c
create mode 100644 drivers/firmware/google/coreboot_table.h
create mode 100644 drivers/firmware/google/memconsole-coreboot.c
create mode 100644 drivers/firmware/google/memconsole-x86-legacy.c
create mode 100644 drivers/firmware/google/memconsole.h
--
2.7.4
^ permalink raw reply
* [PATCH v2 1/5] firmware: google memconsole: Remove useless submenu in Kconfig
From: Thierry Escande @ 2017-03-28 16:11 UTC (permalink / raw)
To: Rob Herring, Greg Kroah-Hartman
Cc: Olof Johansson, Stephen Warren, linux-kernel, devicetree,
Julius Werner, Brian Norris
In-Reply-To: <1490717489-19218-1-git-send-email-thierry.escande@collabora.com>
This patch removes the "Google Firmware Drivers" menu containing a
menuconfig entry with the exact same name. The menuconfig is now
directly under the "Firmware Drivers" entry.
Signed-off-by: Thierry Escande <thierry.escande@collabora.com>
---
drivers/firmware/google/Kconfig | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig
index 29c8cdd..475bd01 100644
--- a/drivers/firmware/google/Kconfig
+++ b/drivers/firmware/google/Kconfig
@@ -1,4 +1,4 @@
-config GOOGLE_FIRMWARE
+menuconfig GOOGLE_FIRMWARE
bool "Google Firmware Drivers"
depends on X86
default n
@@ -7,8 +7,7 @@ config GOOGLE_FIRMWARE
only useful if you are working directly on one of their
proprietary servers. If in doubt, say "N".
-menu "Google Firmware Drivers"
- depends on GOOGLE_FIRMWARE
+if GOOGLE_FIRMWARE
config GOOGLE_SMI
tristate "SMI interface for Google platforms"
@@ -28,4 +27,4 @@ config GOOGLE_MEMCONSOLE
the EBDA on Google servers. If found, this log is exported to
userland in the file /sys/firmware/log.
-endmenu
+endif # GOOGLE_FIRMWARE
--
2.7.4
^ permalink raw reply related
* [PATCH v2 2/5] firmware: google memconsole: Move specific EBDA parts
From: Thierry Escande @ 2017-03-28 16:11 UTC (permalink / raw)
To: Rob Herring, Greg Kroah-Hartman
Cc: Olof Johansson, Stephen Warren, linux-kernel, devicetree,
Julius Werner, Brian Norris
In-Reply-To: <1490717489-19218-1-git-send-email-thierry.escande@collabora.com>
This patch splits memconsole.c in 2 parts. One containing the
architecture-independent part and the other one containing the EBDA
specific part. This prepares the integration of coreboot support for the
memconsole.
The memconsole driver is now named as memconsole-x86-legacy.
Signed-off-by: Thierry Escande <thierry.escande@collabora.com>
---
drivers/firmware/google/Kconfig | 9 +-
drivers/firmware/google/Makefile | 3 +-
drivers/firmware/google/memconsole-x86-legacy.c | 154 +++++++++++++++++++++++
drivers/firmware/google/memconsole.c | 155 ++++--------------------
drivers/firmware/google/memconsole.h | 43 +++++++
5 files changed, 229 insertions(+), 135 deletions(-)
create mode 100644 drivers/firmware/google/memconsole-x86-legacy.c
create mode 100644 drivers/firmware/google/memconsole.h
diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig
index 475bd01..27a0658 100644
--- a/drivers/firmware/google/Kconfig
+++ b/drivers/firmware/google/Kconfig
@@ -20,8 +20,13 @@ config GOOGLE_SMI
variables.
config GOOGLE_MEMCONSOLE
- tristate "Firmware Memory Console"
- depends on DMI
+ tristate
+ depends on GOOGLE_MEMCONSOLE_X86_LEGACY
+
+config GOOGLE_MEMCONSOLE_X86_LEGACY
+ tristate "Firmware Memory Console - X86 Legacy support"
+ depends on X86 && ACPI && DMI
+ select GOOGLE_MEMCONSOLE
help
This option enables the kernel to search for a firmware log in
the EBDA on Google servers. If found, this log is exported to
diff --git a/drivers/firmware/google/Makefile b/drivers/firmware/google/Makefile
index 54a294e..10683ef 100644
--- a/drivers/firmware/google/Makefile
+++ b/drivers/firmware/google/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_GOOGLE_SMI) += gsmi.o
-obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.o
+obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.o
+obj-$(CONFIG_GOOGLE_MEMCONSOLE_X86_LEGACY) += memconsole-x86-legacy.o
diff --git a/drivers/firmware/google/memconsole-x86-legacy.c b/drivers/firmware/google/memconsole-x86-legacy.c
new file mode 100644
index 0000000..529078c
--- /dev/null
+++ b/drivers/firmware/google/memconsole-x86-legacy.c
@@ -0,0 +1,154 @@
+/*
+ * memconsole-x86-legacy.c
+ *
+ * EBDA specific parts of the memory based BIOS console.
+ *
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/mm.h>
+#include <asm/bios_ebda.h>
+#include <asm/e820.h>
+#include <linux/acpi.h>
+
+#include "memconsole.h"
+
+#define BIOS_MEMCONSOLE_V1_MAGIC 0xDEADBABE
+#define BIOS_MEMCONSOLE_V2_MAGIC (('M')|('C'<<8)|('O'<<16)|('N'<<24))
+
+struct biosmemcon_ebda {
+ u32 signature;
+ union {
+ struct {
+ u8 enabled;
+ u32 buffer_addr;
+ u16 start;
+ u16 end;
+ u16 num_chars;
+ u8 wrapped;
+ } __packed v1;
+ struct {
+ u32 buffer_addr;
+ /* Misdocumented as number of pages! */
+ u16 num_bytes;
+ u16 start;
+ u16 end;
+ } __packed v2;
+ };
+} __packed;
+
+static void found_v1_header(struct biosmemcon_ebda *hdr)
+{
+ pr_info("memconsole: BIOS console v1 EBDA structure found at %p\n",
+ hdr);
+ pr_info("memconsole: BIOS console buffer at 0x%.8x, start = %d, end = %d, num = %d\n",
+ hdr->v1.buffer_addr, hdr->v1.start,
+ hdr->v1.end, hdr->v1.num_chars);
+
+ memconsole_setup(phys_to_virt(hdr->v1.buffer_addr), hdr->v1.num_chars);
+}
+
+static void found_v2_header(struct biosmemcon_ebda *hdr)
+{
+ pr_info("memconsole: BIOS console v2 EBDA structure found at %p\n",
+ hdr);
+ pr_info("memconsole: BIOS console buffer at 0x%.8x, start = %d, end = %d, num_bytes = %d\n",
+ hdr->v2.buffer_addr, hdr->v2.start,
+ hdr->v2.end, hdr->v2.num_bytes);
+
+ memconsole_setup(phys_to_virt(hdr->v2.buffer_addr + hdr->v2.start),
+ hdr->v2.end - hdr->v2.start);
+}
+
+/*
+ * Search through the EBDA for the BIOS Memory Console, and
+ * set the global variables to point to it. Return true if found.
+ */
+static bool memconsole_ebda_init(void)
+{
+ unsigned int address;
+ size_t length, cur;
+
+ address = get_bios_ebda();
+ if (!address) {
+ pr_info("memconsole: BIOS EBDA non-existent.\n");
+ return false;
+ }
+
+ /* EBDA length is byte 0 of EBDA (in KB) */
+ length = *(u8 *)phys_to_virt(address);
+ length <<= 10; /* convert to bytes */
+
+ /*
+ * Search through EBDA for BIOS memory console structure
+ * note: signature is not necessarily dword-aligned
+ */
+ for (cur = 0; cur < length; cur++) {
+ struct biosmemcon_ebda *hdr = phys_to_virt(address + cur);
+
+ /* memconsole v1 */
+ if (hdr->signature == BIOS_MEMCONSOLE_V1_MAGIC) {
+ found_v1_header(hdr);
+ return true;
+ }
+
+ /* memconsole v2 */
+ if (hdr->signature == BIOS_MEMCONSOLE_V2_MAGIC) {
+ found_v2_header(hdr);
+ return true;
+ }
+ }
+
+ pr_info("memconsole: BIOS console EBDA structure not found!\n");
+ return false;
+}
+
+static struct dmi_system_id memconsole_dmi_table[] __initdata = {
+ {
+ .ident = "Google Board",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Google, Inc."),
+ },
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(dmi, memconsole_dmi_table);
+
+static bool __init memconsole_find(void)
+{
+ if (!dmi_check_system(memconsole_dmi_table))
+ return false;
+
+ return memconsole_ebda_init();
+}
+
+static int __init memconsole_x86_init(void)
+{
+ if (!memconsole_find())
+ return -ENODEV;
+
+ return memconsole_sysfs_init();
+}
+
+static void __exit memconsole_x86_exit(void)
+{
+ memconsole_exit();
+}
+
+module_init(memconsole_x86_init);
+module_exit(memconsole_x86_exit);
+
+MODULE_AUTHOR("Google, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/firmware/google/memconsole.c b/drivers/firmware/google/memconsole.c
index 2f569aa..94e200d 100644
--- a/drivers/firmware/google/memconsole.c
+++ b/drivers/firmware/google/memconsole.c
@@ -1,66 +1,36 @@
/*
* memconsole.c
*
- * Infrastructure for importing the BIOS memory based console
- * into the kernel log ringbuffer.
+ * Architecture-independent parts of the memory based BIOS console.
*
- * Copyright 2010 Google Inc. All rights reserved.
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*/
-#include <linux/ctype.h>
#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>
#include <linux/module.h>
-#include <linux/dmi.h>
-#include <linux/io.h>
-#include <asm/bios_ebda.h>
-#define BIOS_MEMCONSOLE_V1_MAGIC 0xDEADBABE
-#define BIOS_MEMCONSOLE_V2_MAGIC (('M')|('C'<<8)|('O'<<16)|('N'<<24))
+#include "memconsole.h"
-struct biosmemcon_ebda {
- u32 signature;
- union {
- struct {
- u8 enabled;
- u32 buffer_addr;
- u16 start;
- u16 end;
- u16 num_chars;
- u8 wrapped;
- } __packed v1;
- struct {
- u32 buffer_addr;
- /* Misdocumented as number of pages! */
- u16 num_bytes;
- u16 start;
- u16 end;
- } __packed v2;
- };
-} __packed;
-
-static u32 memconsole_baseaddr;
+static char *memconsole_baseaddr;
static size_t memconsole_length;
static ssize_t memconsole_read(struct file *filp, struct kobject *kobp,
struct bin_attribute *bin_attr, char *buf,
loff_t pos, size_t count)
{
- char *memconsole;
- ssize_t ret;
-
- memconsole = ioremap_cache(memconsole_baseaddr, memconsole_length);
- if (!memconsole) {
- pr_err("memconsole: ioremap_cache failed\n");
- return -ENOMEM;
- }
- ret = memory_read_from_buffer(buf, count, &pos, memconsole,
- memconsole_length);
- iounmap(memconsole);
- return ret;
+ return memory_read_from_buffer(buf, count, &pos, memconsole_baseaddr,
+ memconsole_length);
}
static struct bin_attribute memconsole_bin_attr = {
@@ -68,104 +38,25 @@ static struct bin_attribute memconsole_bin_attr = {
.read = memconsole_read,
};
-
-static void __init found_v1_header(struct biosmemcon_ebda *hdr)
-{
- pr_info("BIOS console v1 EBDA structure found at %p\n", hdr);
- pr_info("BIOS console buffer at 0x%.8x, "
- "start = %d, end = %d, num = %d\n",
- hdr->v1.buffer_addr, hdr->v1.start,
- hdr->v1.end, hdr->v1.num_chars);
-
- memconsole_length = hdr->v1.num_chars;
- memconsole_baseaddr = hdr->v1.buffer_addr;
-}
-
-static void __init found_v2_header(struct biosmemcon_ebda *hdr)
-{
- pr_info("BIOS console v2 EBDA structure found at %p\n", hdr);
- pr_info("BIOS console buffer at 0x%.8x, "
- "start = %d, end = %d, num_bytes = %d\n",
- hdr->v2.buffer_addr, hdr->v2.start,
- hdr->v2.end, hdr->v2.num_bytes);
-
- memconsole_length = hdr->v2.end - hdr->v2.start;
- memconsole_baseaddr = hdr->v2.buffer_addr + hdr->v2.start;
-}
-
-/*
- * Search through the EBDA for the BIOS Memory Console, and
- * set the global variables to point to it. Return true if found.
- */
-static bool __init found_memconsole(void)
+void memconsole_setup(void *baseaddr, size_t length)
{
- unsigned int address;
- size_t length, cur;
-
- address = get_bios_ebda();
- if (!address) {
- pr_info("BIOS EBDA non-existent.\n");
- return false;
- }
-
- /* EBDA length is byte 0 of EBDA (in KB) */
- length = *(u8 *)phys_to_virt(address);
- length <<= 10; /* convert to bytes */
-
- /*
- * Search through EBDA for BIOS memory console structure
- * note: signature is not necessarily dword-aligned
- */
- for (cur = 0; cur < length; cur++) {
- struct biosmemcon_ebda *hdr = phys_to_virt(address + cur);
-
- /* memconsole v1 */
- if (hdr->signature == BIOS_MEMCONSOLE_V1_MAGIC) {
- found_v1_header(hdr);
- return true;
- }
-
- /* memconsole v2 */
- if (hdr->signature == BIOS_MEMCONSOLE_V2_MAGIC) {
- found_v2_header(hdr);
- return true;
- }
- }
-
- pr_info("BIOS console EBDA structure not found!\n");
- return false;
+ memconsole_baseaddr = baseaddr;
+ memconsole_length = length;
}
+EXPORT_SYMBOL(memconsole_setup);
-static struct dmi_system_id memconsole_dmi_table[] __initdata = {
- {
- .ident = "Google Board",
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "Google, Inc."),
- },
- },
- {}
-};
-MODULE_DEVICE_TABLE(dmi, memconsole_dmi_table);
-
-static int __init memconsole_init(void)
+int memconsole_sysfs_init(void)
{
- if (!dmi_check_system(memconsole_dmi_table))
- return -ENODEV;
-
- if (!found_memconsole())
- return -ENODEV;
-
memconsole_bin_attr.size = memconsole_length;
return sysfs_create_bin_file(firmware_kobj, &memconsole_bin_attr);
}
+EXPORT_SYMBOL(memconsole_sysfs_init);
-static void __exit memconsole_exit(void)
+void memconsole_exit(void)
{
sysfs_remove_bin_file(firmware_kobj, &memconsole_bin_attr);
}
-
-module_init(memconsole_init);
-module_exit(memconsole_exit);
+EXPORT_SYMBOL(memconsole_exit);
MODULE_AUTHOR("Google, Inc.");
MODULE_LICENSE("GPL");
diff --git a/drivers/firmware/google/memconsole.h b/drivers/firmware/google/memconsole.h
new file mode 100644
index 0000000..190fc03
--- /dev/null
+++ b/drivers/firmware/google/memconsole.h
@@ -0,0 +1,43 @@
+/*
+ * memconsole.h
+ *
+ * Internal headers of the memory based BIOS console.
+ *
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 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.
+ */
+
+#ifndef __FIRMWARE_GOOGLE_MEMCONSOLE_H
+#define __FIRMWARE_GOOGLE_MEMCONSOLE_H
+
+/*
+ * memconsole_setup
+ *
+ * Initialize the memory console from raw (virtual) base
+ * address and length.
+ */
+void memconsole_setup(void *baseaddr, size_t length);
+
+/*
+ * memconsole_sysfs_init
+ *
+ * Update memory console length and create binary file
+ * for firmware object.
+ */
+int memconsole_sysfs_init(void);
+
+/* memconsole_exit
+ *
+ * Unmap the console buffer.
+ */
+void memconsole_exit(void);
+
+#endif /* __FIRMWARE_GOOGLE_MEMCONSOLE_H */
--
2.7.4
^ permalink raw reply related
* [PATCH v2 3/5] firmware: google memconsole: Add coreboot support
From: Thierry Escande @ 2017-03-28 16:11 UTC (permalink / raw)
To: Rob Herring, Greg Kroah-Hartman
Cc: Olof Johansson, Stephen Warren,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Julius Werner, Brian Norris
In-Reply-To: <1490717489-19218-1-git-send-email-thierry.escande-ZGY8ohtN/8qB+jHODAdFcQ@public.gmane.org>
Coreboot (http://www.coreboot.org) allows to save the firmware console
output in a memory buffer. With this patch, the address of this memory
buffer is obtained from coreboot tables on x86 chromebook devices
declaring an ACPI device with name matching GOOGCB00 or BOOT0000.
If the memconsole-coreboot driver is able to find the coreboot table,
the memconsole driver sets the cbmem_console address and initializes the
memconsole sysfs entries.
The coreboot_table-acpi driver is responsible for setting the address of
the coreboot table header when probed. If this address is not yet set
when memconsole-coreboot is probed, then the probe is deferred by
returning -EPROBE_DEFER.
This patch is a rework/split/merge of patches from the chromeos v4.4
kernel tree originally authored by:
Vadim Bendebury <vbendeb-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
Wei-Ning Huang <wnhuang-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Yuji Sasaki <sasakiy-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
Duncan Laurie <dlaurie-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
Julius Werner <jwerner-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
Brian Norris <briannorris-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
Signed-off-by: Thierry Escande <thierry.escande-ZGY8ohtN/8qB+jHODAdFcQ@public.gmane.org>
---
drivers/firmware/google/Kconfig | 25 +++++-
drivers/firmware/google/Makefile | 3 +
drivers/firmware/google/coreboot_table-acpi.c | 88 +++++++++++++++++++++
drivers/firmware/google/coreboot_table.c | 94 ++++++++++++++++++++++
drivers/firmware/google/coreboot_table.h | 50 ++++++++++++
drivers/firmware/google/memconsole-coreboot.c | 109 ++++++++++++++++++++++++++
6 files changed, 368 insertions(+), 1 deletion(-)
create mode 100644 drivers/firmware/google/coreboot_table-acpi.c
create mode 100644 drivers/firmware/google/coreboot_table.c
create mode 100644 drivers/firmware/google/coreboot_table.h
create mode 100644 drivers/firmware/google/memconsole-coreboot.c
diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig
index 27a0658..840bd62 100644
--- a/drivers/firmware/google/Kconfig
+++ b/drivers/firmware/google/Kconfig
@@ -19,9 +19,23 @@ config GOOGLE_SMI
clearing the EFI event log and reading and writing NVRAM
variables.
+config GOOGLE_COREBOOT_TABLE
+ tristate
+ depends on GOOGLE_COREBOOT_TABLE_ACPI
+
+config GOOGLE_COREBOOT_TABLE_ACPI
+ tristate "Coreboot Table Access - ACPI"
+ depends on ACPI
+ select GOOGLE_COREBOOT_TABLE
+ help
+ This option enables the coreboot_table module, which provides other
+ firmware modules to access to the coreboot table. The coreboot table
+ pointer is accessed through the ACPI "GOOGCB00" object.
+ If unsure say N.
+
config GOOGLE_MEMCONSOLE
tristate
- depends on GOOGLE_MEMCONSOLE_X86_LEGACY
+ depends on GOOGLE_MEMCONSOLE_X86_LEGACY || GOOGLE_MEMCONSOLE_COREBOOT
config GOOGLE_MEMCONSOLE_X86_LEGACY
tristate "Firmware Memory Console - X86 Legacy support"
@@ -32,4 +46,13 @@ config GOOGLE_MEMCONSOLE_X86_LEGACY
the EBDA on Google servers. If found, this log is exported to
userland in the file /sys/firmware/log.
+config GOOGLE_MEMCONSOLE_COREBOOT
+ tristate "Firmware Memory Console"
+ depends on GOOGLE_COREBOOT_TABLE
+ select GOOGLE_MEMCONSOLE
+ help
+ This option enables the kernel to search for a firmware log in
+ the coreboot table. If found, this log is exported to userland
+ in the file /sys/firmware/log.
+
endif # GOOGLE_FIRMWARE
diff --git a/drivers/firmware/google/Makefile b/drivers/firmware/google/Makefile
index 10683ef..662a83e 100644
--- a/drivers/firmware/google/Makefile
+++ b/drivers/firmware/google/Makefile
@@ -1,4 +1,7 @@
obj-$(CONFIG_GOOGLE_SMI) += gsmi.o
+obj-$(CONFIG_GOOGLE_COREBOOT_TABLE) += coreboot_table.o
+obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_ACPI) += coreboot_table-acpi.o
obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.o
+obj-$(CONFIG_GOOGLE_MEMCONSOLE_COREBOOT) += memconsole-coreboot.o
obj-$(CONFIG_GOOGLE_MEMCONSOLE_X86_LEGACY) += memconsole-x86-legacy.o
diff --git a/drivers/firmware/google/coreboot_table-acpi.c b/drivers/firmware/google/coreboot_table-acpi.c
new file mode 100644
index 0000000..fb98db2
--- /dev/null
+++ b/drivers/firmware/google/coreboot_table-acpi.c
@@ -0,0 +1,88 @@
+/*
+ * coreboot_table-acpi.c
+ *
+ * Using ACPI to locate Coreboot table and provide coreboot table access.
+ *
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "coreboot_table.h"
+
+static int coreboot_table_acpi_probe(struct platform_device *pdev)
+{
+ phys_addr_t phyaddr;
+ resource_size_t len;
+ struct coreboot_table_header __iomem *header = NULL;
+ struct resource *res;
+ void __iomem *ptr = NULL;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ len = resource_size(res);
+ if (!res->start || !len)
+ return -EINVAL;
+
+ phyaddr = res->start;
+ header = ioremap_cache(phyaddr, sizeof(*header));
+ if (header == NULL)
+ return -ENOMEM;
+
+ ptr = ioremap_cache(phyaddr,
+ header->header_bytes + header->table_bytes);
+ iounmap(header);
+ if (!ptr)
+ return -ENOMEM;
+
+ return coreboot_table_init(ptr);
+}
+
+static int coreboot_table_acpi_remove(struct platform_device *pdev)
+{
+ return coreboot_table_exit();
+}
+
+static const struct acpi_device_id cros_coreboot_acpi_match[] = {
+ { "GOOGCB00", 0 },
+ { "BOOT0000", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, cros_coreboot_acpi_match);
+
+static struct platform_driver coreboot_table_acpi_driver = {
+ .probe = coreboot_table_acpi_probe,
+ .remove = coreboot_table_acpi_remove,
+ .driver = {
+ .name = "coreboot_table_acpi",
+ .acpi_match_table = ACPI_PTR(cros_coreboot_acpi_match),
+ },
+};
+
+static int __init coreboot_table_acpi_init(void)
+{
+ return platform_driver_register(&coreboot_table_acpi_driver);
+}
+
+module_init(coreboot_table_acpi_init);
+
+MODULE_AUTHOR("Google, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/firmware/google/coreboot_table.c b/drivers/firmware/google/coreboot_table.c
new file mode 100644
index 0000000..0019d3e
--- /dev/null
+++ b/drivers/firmware/google/coreboot_table.c
@@ -0,0 +1,94 @@
+/*
+ * coreboot_table.c
+ *
+ * Module providing coreboot table access.
+ *
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "coreboot_table.h"
+
+struct coreboot_table_entry {
+ u32 tag;
+ u32 size;
+};
+
+static struct coreboot_table_header __iomem *ptr_header;
+
+/*
+ * This function parses the coreboot table for an entry that contains the base
+ * address of the given entry tag. The coreboot table consists of a header
+ * directly followed by a number of small, variable-sized entries, which each
+ * contain an identifying tag and their length as the first two fields.
+ */
+int coreboot_table_find(int tag, void *data, size_t data_size)
+{
+ struct coreboot_table_header header;
+ struct coreboot_table_entry entry;
+ void *ptr_entry;
+ int i;
+
+ if (!ptr_header)
+ return -EPROBE_DEFER;
+
+ memcpy_fromio(&header, ptr_header, sizeof(header));
+
+ if (strncmp(header.signature, "LBIO", sizeof(header.signature))) {
+ pr_warn("coreboot_table: coreboot table missing or corrupt!\n");
+ return -ENODEV;
+ }
+
+ ptr_entry = (void *)ptr_header + header.header_bytes;
+
+ for (i = 0; i < header.table_entries; i++) {
+ memcpy_fromio(&entry, ptr_entry, sizeof(entry));
+ if (entry.tag == tag) {
+ if (data_size < entry.size)
+ return -EINVAL;
+
+ memcpy_fromio(data, ptr_entry, entry.size);
+
+ return 0;
+ }
+
+ ptr_entry += entry.size;
+ }
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL(coreboot_table_find);
+
+int coreboot_table_init(void __iomem *ptr)
+{
+ ptr_header = ptr;
+
+ return 0;
+}
+EXPORT_SYMBOL(coreboot_table_init);
+
+int coreboot_table_exit(void)
+{
+ if (ptr_header)
+ iounmap(ptr_header);
+
+ return 0;
+}
+EXPORT_SYMBOL(coreboot_table_exit);
+
+MODULE_AUTHOR("Google, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/firmware/google/coreboot_table.h b/drivers/firmware/google/coreboot_table.h
new file mode 100644
index 0000000..6eff1ae
--- /dev/null
+++ b/drivers/firmware/google/coreboot_table.h
@@ -0,0 +1,50 @@
+/*
+ * coreboot_table.h
+ *
+ * Internal header for coreboot table access.
+ *
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 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.
+ */
+
+#ifndef __COREBOOT_TABLE_H
+#define __COREBOOT_TABLE_H
+
+#include <linux/io.h>
+
+/* List of coreboot entry structures that is used */
+struct lb_cbmem_ref {
+ uint32_t tag;
+ uint32_t size;
+
+ uint64_t cbmem_addr;
+};
+
+/* Coreboot table header structure */
+struct coreboot_table_header {
+ char signature[4];
+ u32 header_bytes;
+ u32 header_checksum;
+ u32 table_bytes;
+ u32 table_checksum;
+ u32 table_entries;
+};
+
+/* Retrieve coreboot table entry with tag *tag* and copy it to data */
+int coreboot_table_find(int tag, void *data, size_t data_size);
+
+/* Initialize coreboot table module given a pointer to iomem */
+int coreboot_table_init(void __iomem *ptr);
+
+/* Cleanup coreboot table module */
+int coreboot_table_exit(void);
+
+#endif /* __COREBOOT_TABLE_H */
diff --git a/drivers/firmware/google/memconsole-coreboot.c b/drivers/firmware/google/memconsole-coreboot.c
new file mode 100644
index 0000000..2121014
--- /dev/null
+++ b/drivers/firmware/google/memconsole-coreboot.c
@@ -0,0 +1,109 @@
+/*
+ * memconsole-coreboot.c
+ *
+ * Memory based BIOS console accessed through coreboot table.
+ *
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "memconsole.h"
+#include "coreboot_table.h"
+
+#define CB_TAG_CBMEM_CONSOLE 0x17
+
+/* CBMEM firmware console log descriptor. */
+struct cbmem_cons {
+ u32 buffer_size;
+ u32 buffer_cursor;
+ u8 buffer_body[0];
+} __packed;
+
+static struct cbmem_cons __iomem *cbmem_console;
+
+static int memconsole_coreboot_init(phys_addr_t physaddr)
+{
+ struct cbmem_cons __iomem *tmp_cbmc;
+
+ tmp_cbmc = memremap(physaddr, sizeof(*tmp_cbmc), MEMREMAP_WB);
+
+ if (!tmp_cbmc)
+ return -ENOMEM;
+
+ cbmem_console = memremap(physaddr,
+ tmp_cbmc->buffer_size + sizeof(*cbmem_console),
+ MEMREMAP_WB);
+ memunmap(tmp_cbmc);
+
+ if (!cbmem_console)
+ return -ENOMEM;
+
+ memconsole_setup(cbmem_console->buffer_body,
+ min(cbmem_console->buffer_cursor, cbmem_console->buffer_size));
+
+ return 0;
+}
+
+static int memconsole_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct lb_cbmem_ref entry;
+
+ ret = coreboot_table_find(CB_TAG_CBMEM_CONSOLE, &entry, sizeof(entry));
+ if (ret)
+ return ret;
+
+ ret = memconsole_coreboot_init(entry.cbmem_addr);
+ if (ret)
+ return ret;
+
+ return memconsole_sysfs_init();
+}
+
+static int memconsole_remove(struct platform_device *pdev)
+{
+ memconsole_exit();
+
+ if (cbmem_console)
+ memunmap(cbmem_console);
+
+ return 0;
+}
+
+static struct platform_driver memconsole_driver = {
+ .probe = memconsole_probe,
+ .remove = memconsole_remove,
+ .driver = {
+ .name = "memconsole",
+ },
+};
+
+static int __init platform_memconsole_init(void)
+{
+ struct platform_device *pdev;
+
+ pdev = platform_device_register_simple("memconsole", -1, NULL, 0);
+ if (pdev == NULL)
+ return -ENODEV;
+
+ platform_driver_register(&memconsole_driver);
+
+ return 0;
+}
+
+module_init(platform_memconsole_init);
+
+MODULE_AUTHOR("Google, Inc.");
+MODULE_LICENSE("GPL");
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 4/5] firmware: Add coreboot device tree binding documentation
From: Thierry Escande @ 2017-03-28 16:11 UTC (permalink / raw)
To: Rob Herring, Greg Kroah-Hartman
Cc: Olof Johansson, Stephen Warren,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Julius Werner, Brian Norris
In-Reply-To: <1490717489-19218-1-git-send-email-thierry.escande-ZGY8ohtN/8qB+jHODAdFcQ@public.gmane.org>
From: Julius Werner <jwerner-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
This patch adds documentation describing a device tree binding for the
coreboot firmware. It is meant to be dynamically added during boot and
contains address definitions for the coreboot table (a list of
variable-sized descriptors providing information about various compile-
and run-time generated firmware parameters) and the CBMEM area (the
structure containing most run-time resident memory regions set up by
coreboot).
These definitions allow kernel drivers to easily access data contained
in and pointed to by these regions (such as coreboot's in-memory log).
(An example implementation can be seen in the following patch)
Signed-off-by: Julius Werner <jwerner-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
Signed-off-by: Thierry Escande <thierry.escande-ZGY8ohtN/8qB+jHODAdFcQ@public.gmane.org>
---
.../devicetree/bindings/firmware/coreboot.txt | 33 ++++++++++++++++++++++
1 file changed, 33 insertions(+)
create mode 100644 Documentation/devicetree/bindings/firmware/coreboot.txt
diff --git a/Documentation/devicetree/bindings/firmware/coreboot.txt b/Documentation/devicetree/bindings/firmware/coreboot.txt
new file mode 100644
index 0000000..4c95570
--- /dev/null
+++ b/Documentation/devicetree/bindings/firmware/coreboot.txt
@@ -0,0 +1,33 @@
+COREBOOT firmware information
+
+The device tree node to communicate the location of coreboot's memory-resident
+bookkeeping structures to the kernel. Since coreboot itself cannot boot a
+device-tree-based kernel (yet), this node needs to be inserted by a
+second-stage bootloader (a coreboot "payload").
+
+Required properties:
+ - compatible: Should be "coreboot"
+ - reg: Address and length of the following two memory regions, in order:
+ 1.) The coreboot table. This is a list of variable-sized descriptors
+ that contain various compile- and run-time generated firmware
+ parameters. It is identified by the magic string "LBIO" in its first
+ four bytes.
+ See coreboot's src/commonlib/include/commonlib/coreboot_tables.h for
+ details.
+ 2.) The CBMEM area. This is a downward-growing memory region used by
+ coreboot to dynamically allocate data structures that remain resident.
+ It may or may not include the coreboot table as one of its members. It
+ is identified by a root node descriptor with the magic number
+ 0xc0389481 that resides in the topmost 8 bytes of the area.
+ See coreboot's src/include/imd.h for details.
+
+Example:
+ firmware {
+ ranges;
+
+ coreboot {
+ compatible = "coreboot";
+ reg = <0xfdfea000 0x264>,
+ <0xfdfea000 0x16000>;
+ }
+ };
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 5/5] firmware: google memconsole: Add ARM/ARM64 support
From: Thierry Escande @ 2017-03-28 16:11 UTC (permalink / raw)
To: Rob Herring, Greg Kroah-Hartman
Cc: Olof Johansson, Stephen Warren, linux-kernel, devicetree,
Julius Werner, Brian Norris
In-Reply-To: <1490717489-19218-1-git-send-email-thierry.escande@collabora.com>
This patch expands the Google firmware memory console driver to also
work on certain tree based platforms running coreboot, such as ARM/ARM64
Chromebooks. This patch now adds another path to find the coreboot table
through the device tree. In order to find that, a second level
bootloader must have installed the 'coreboot' compatible device tree
node that describes its base address and size.
This patch is a rework/split/merge of patches from the chromeos v4.4
kernel tree originally authored by:
Wei-Ning Huang <wnhuang@chromium.org>
Julius Werner <jwerner@chromium.org>
Brian Norris <briannorris@chromium.org>
Signed-off-by: Thierry Escande <thierry.escande@collabora.com>
---
In this v2:
Adds a missing dependency on X86 for the GOOGLE_SMI symbol reported by
the kbuild test robot as the X86 dependency is removed from the
GOOGLE_FIRMWARE symbol for ARM support.
drivers/firmware/google/Kconfig | 15 ++++--
drivers/firmware/google/Makefile | 1 +
drivers/firmware/google/coreboot_table-of.c | 82 +++++++++++++++++++++++++++++
3 files changed, 95 insertions(+), 3 deletions(-)
create mode 100644 drivers/firmware/google/coreboot_table-of.c
diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig
index 840bd62..00000e0 100644
--- a/drivers/firmware/google/Kconfig
+++ b/drivers/firmware/google/Kconfig
@@ -1,6 +1,5 @@
menuconfig GOOGLE_FIRMWARE
bool "Google Firmware Drivers"
- depends on X86
default n
help
These firmware drivers are used by Google's servers. They are
@@ -11,7 +10,7 @@ if GOOGLE_FIRMWARE
config GOOGLE_SMI
tristate "SMI interface for Google platforms"
- depends on ACPI && DMI && EFI
+ depends on X86 && ACPI && DMI && EFI
select EFI_VARS
help
Say Y here if you want to enable SMI callbacks for Google
@@ -21,7 +20,7 @@ config GOOGLE_SMI
config GOOGLE_COREBOOT_TABLE
tristate
- depends on GOOGLE_COREBOOT_TABLE_ACPI
+ depends on GOOGLE_COREBOOT_TABLE_ACPI || GOOGLE_COREBOOT_TABLE_OF
config GOOGLE_COREBOOT_TABLE_ACPI
tristate "Coreboot Table Access - ACPI"
@@ -33,6 +32,16 @@ config GOOGLE_COREBOOT_TABLE_ACPI
pointer is accessed through the ACPI "GOOGCB00" object.
If unsure say N.
+config GOOGLE_COREBOOT_TABLE_OF
+ tristate "Coreboot Table Access - Device Tree"
+ depends on OF
+ select GOOGLE_COREBOOT_TABLE
+ help
+ This option enable the coreboot_table module, which provide other
+ firmware modules to access coreboot table. The coreboot table pointer
+ is accessed through the device tree node /firmware/coreboot.
+ If unsure say N.
+
config GOOGLE_MEMCONSOLE
tristate
depends on GOOGLE_MEMCONSOLE_X86_LEGACY || GOOGLE_MEMCONSOLE_COREBOOT
diff --git a/drivers/firmware/google/Makefile b/drivers/firmware/google/Makefile
index 662a83e..bb952c6 100644
--- a/drivers/firmware/google/Makefile
+++ b/drivers/firmware/google/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_GOOGLE_SMI) += gsmi.o
obj-$(CONFIG_GOOGLE_COREBOOT_TABLE) += coreboot_table.o
obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_ACPI) += coreboot_table-acpi.o
+obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_OF) += coreboot_table-of.o
obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.o
obj-$(CONFIG_GOOGLE_MEMCONSOLE_COREBOOT) += memconsole-coreboot.o
obj-$(CONFIG_GOOGLE_MEMCONSOLE_X86_LEGACY) += memconsole-x86-legacy.o
diff --git a/drivers/firmware/google/coreboot_table-of.c b/drivers/firmware/google/coreboot_table-of.c
new file mode 100644
index 0000000..727acdc
--- /dev/null
+++ b/drivers/firmware/google/coreboot_table-of.c
@@ -0,0 +1,82 @@
+/*
+ * coreboot_table-of.c
+ *
+ * Coreboot table access through open firmware.
+ *
+ * Copyright 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2.0 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include "coreboot_table.h"
+
+static int coreboot_table_of_probe(struct platform_device *pdev)
+{
+ struct device_node *fw_dn = pdev->dev.of_node;
+ void __iomem *ptr;
+
+ ptr = of_iomap(fw_dn, 0);
+ of_node_put(fw_dn);
+ if (!ptr)
+ return -ENOMEM;
+
+ return coreboot_table_init(ptr);
+}
+
+static int coreboot_table_of_remove(struct platform_device *pdev)
+{
+ return coreboot_table_exit();
+}
+
+static const struct of_device_id coreboot_of_match[] = {
+ { .compatible = "coreboot" },
+ {},
+};
+
+static struct platform_driver coreboot_table_of_driver = {
+ .probe = coreboot_table_of_probe,
+ .remove = coreboot_table_of_remove,
+ .driver = {
+ .name = "coreboot_table_of",
+ .of_match_table = coreboot_of_match,
+ },
+};
+
+static int __init platform_coreboot_table_of_init(void)
+{
+ struct platform_device *pdev;
+ struct device_node *of_node;
+
+ /* Limit device creation to the presence of /firmware/coreboot node */
+ of_node = of_find_node_by_path("/firmware/coreboot");
+ if (!of_node)
+ return -ENODEV;
+
+ if (!of_match_node(coreboot_of_match, of_node))
+ return -ENODEV;
+
+ pdev = of_platform_device_create(of_node, "coreboot_table_of", NULL);
+ if (!pdev)
+ return -ENODEV;
+
+ return platform_driver_register(&coreboot_table_of_driver);
+}
+
+module_init(platform_coreboot_table_of_init);
+
+MODULE_AUTHOR("Google, Inc.");
+MODULE_LICENSE("GPL");
--
2.7.4
^ permalink raw reply related
* Re: [PATCH RFC v4 07/10] dt-bindings: net: add binding for QCA7000 UART
From: Stefan Wahren @ 2017-03-28 16:18 UTC (permalink / raw)
To: Rob Herring
Cc: Mark Rutland, David S. Miller, Greg Kroah-Hartman, Jiri Slaby,
Marcel Holtmann, Sebastian Reichel, netdev,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-serial-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <CAL_Jsq+gxS4u+b+C_eWeguFR0AFKuq8Hhe9Xo_5B_5w1hxvzEg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Am 27.03.2017 um 22:30 schrieb Rob Herring:
> On Mon, Mar 27, 2017 at 8:37 AM, Stefan Wahren <stefan.wahren-eS4NqCHxEME@public.gmane.org> wrote:
>> This is the serdev binding for the QCA7000 UART driver (Ethernet over UART).
>>
>> Signed-off-by: Stefan Wahren <stefan.wahren-eS4NqCHxEME@public.gmane.org>
>> ---
>>
>> According to this binding are still some questions:
>>
>> Where should be the optional hardware flow control defined (at master or slave side)?
> Probably should be in the slave side. We already have uart-has-rtscts
> and rts/cts-gpios for the UART. Those mean we have RTS/CTS, but not
> necessarily that we want to enable them.
>
> In many cases, the driver may know what it needs.
Like all the other UART settings the hardware flow control can be
configured in the QCA7000 firmware and the driver can't detect it.
Property suggestion for the slave side:
use-rtscts
>
>> Is it okay to have two bindings (qca-qca7000-spi and qca-qca7000-uart) or should they be merged?
> Are they mutually-exclusive or both are used at the same time?
They are mutually-exclusive because they use the same pins.
> What
> are the dependencies between the interfaces?
Except they uses the same pins of the QCA7000, i can't see any dependency.
>
>>
>> .../devicetree/bindings/net/qca-qca7000-uart.txt | 31 ++++++++++++++++++++++
>> 1 file changed, 31 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/net/qca-qca7000-uart.txt
>>
>> diff --git a/Documentation/devicetree/bindings/net/qca-qca7000-uart.txt b/Documentation/devicetree/bindings/net/qca-qca7000-uart.txt
>> new file mode 100644
>> index 0000000..f2e0450
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/net/qca-qca7000-uart.txt
>> @@ -0,0 +1,31 @@
>> +* Qualcomm QCA7000 (Ethernet over UART protocol)
>> +
>> +Note: This binding applies in case the QCA7000 is configured as a
>> +UART slave device. It is possible to preconfigure the UART settings
>> +of the QCA7000 firmware, which can't be changed during runtime.
>> +
>> +Required properties:
>> +- compatible : Should be "qca,qca7000-uart"
>> +
>> +Optional properties:
>> +- local-mac-address : 6 bytes, Specifies MAC address
> The description can be "see ./ethernet.txt"
>
>> +- current-speed : Specifies the serial device speed in
>> + bits per second (default = 115200), which is
>> + predefined by the QCA7000 firmware configuration
> Add this to the slave binding doc with some caveats as to when this
> should or should not be used as we discussed.
>
> Rob
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 1/7] Documentation: dt: net: Update the ath9k binding for SoC devices
From: Christian Lamparter @ 2017-03-28 16:21 UTC (permalink / raw)
To: Andrew Lunn
Cc: Alban, QCA ath9k Development, John Crispin, Kalle Valo,
Rob Herring, Mark Rutland, linux-wireless, netdev, devicetree,
linux-kernel, m
In-Reply-To: <20170328151837.GC3152@lunn.ch>
On Tuesday, March 28, 2017 5:18:37 PM CEST Andrew Lunn wrote:
> > But LEDE/OpenWRT rely on the firmware loading API more than ever and
> > currently there is not a replacement for it.
>
> ....
>
>
> > I looked into 10-ath9k-eeprom [0] of LEDE's AR71XX target and I noticed
> > that quite a few devices patch the MACs of the wifi.
> > If you look at the code for the Airtight C-55 and C-60, Meraki MR18,
> > Meraki Z1, you'll notice that each one has to add a fixed value (+1,
> > +2, ...) to the extraced MAC-Address. So how would you replicate this,
> > with "nvmem-cell-names = address" without some sort of
> > nvmem-provider-processor?
>
> ...
>
> > https://github.com/lede-project/source/blob/master/target/linux/ar71xx/files/arch/mips/ath79/dev-eth.c#L1204
> >
> > and grep lists the following devices:
> > mach-dgl-5500-a1.c, mach-dhp-1565-a1.c, mach-dir-505-a1.c, mach-dir-615-c1.c
> > mach-dir-615-i1.c, mach-dir-825-b1.c, mach-dir-825-c1.c, mach-tew-673gru.c
> > mach-tew-712br.c, mach-tew-732br.c, mach-tew-823dru.c
>
> I would say a big part of the problem is that all of these use cases
> are outside of mainline. Why should mainline support something which
> is not actually used in mainline.
>
> So i would suggest your first step is to bring some of these devices
> into mainline. Once in mainline, it becomes a mainline issue, and
> people will help get it solved.
>
Oh, in that case you should probably go "all out" and ask on the
LKML to remove all of the ath9k and ath10k ahb work. From what I
know all the "users" are running some sort of OpenWRT/LEDE or a
derivative. This is because Atheros/QCA provided a SDK based on
OpenWRT.
Alban has been trying to convert the platform to device-tree
and add them to the mainline for a while now:
https://patchwork.kernel.org/patch/6514551/
So, you are questioning this work as well.
Thanks,
Christian
^ permalink raw reply
* Re: [PATCH] arm64: tegra: Add CPU and PSCI nodes for NVIDIA Tegra210 platforms
From: Stephen Warren @ 2017-03-28 16:23 UTC (permalink / raw)
To: Jon Hunter
Cc: devicetree, Stephen Warren, Vagrant Cascadian, Thierry Reding,
linux-tegra, Martin Michlmayr, linux-arm-kernel
In-Reply-To: <1490701718-16571-1-git-send-email-jonathanh@nvidia.com>
On 03/28/2017 05:48 AM, Jon Hunter wrote:
> Add the CPU and PSCI nodes for the NVIDIA Tegra210 platforms so that
> all CPUs can be enabled on boot. This assumes that the PSCI firmware
> has been loaded during the initial bootstrap on the device before the
> kernel starts (which is typically the case for these platforms). The
> PSCI firmware version is set to v0.2 which aligns with the current
> shipping version for Tegra.
This seems fine, although I'd expect PSCI to be used everywhere on
Tegra, so putting this in tegra210.dtsi would make more sense.
^ permalink raw reply
* Re: [PATCH RFC v4 06/10] net: qualcomm: make qca_common a separate kernel module
From: Stefan Wahren @ 2017-03-28 16:24 UTC (permalink / raw)
To: Dan Williams, Rob Herring, Mark Rutland, David S. Miller
Cc: Greg Kroah-Hartman, Jiri Slaby, Marcel Holtmann,
Sebastian Reichel, netdev, devicetree, linux-serial, linux-kernel
In-Reply-To: <1490629444.2719.3.camel@redhat.com>
Am 27.03.2017 um 17:44 schrieb Dan Williams:
> On Mon, 2017-03-27 at 15:37 +0200, Stefan Wahren wrote:
>> In order to share common functions between QCA7000 SPI and UART
>> protocol
>> driver the qca_common needs to be a separate kernel module.
> Maybe "qca_eth_common"? There are many things Qualcomm, slightly fewer
> things Qualcomm Atheros, and "qca_common" isn't very descriptive.
Since it is a Homeplug powerline chip which doesn't have any physical
connection to Ethernet only on protocol level, i wouldn't use eth in the
module name.
The code is very specific to the QCA7000, so how about "qca_7k_common"?
>
> Dan
>
>> Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
>> ---
>> drivers/net/ethernet/qualcomm/Kconfig | 8 +++++++-
>> drivers/net/ethernet/qualcomm/Makefile | 5 +++--
>> drivers/net/ethernet/qualcomm/qca_common.c | 10 ++++++++++
>> 3 files changed, 20 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/qualcomm/Kconfig
>> b/drivers/net/ethernet/qualcomm/Kconfig
>> index d7720bf..b4c369d 100644
>> --- a/drivers/net/ethernet/qualcomm/Kconfig
>> +++ b/drivers/net/ethernet/qualcomm/Kconfig
>> @@ -16,7 +16,13 @@ config NET_VENDOR_QUALCOMM
>> if NET_VENDOR_QUALCOMM
>>
>> config QCA7000
>> - tristate "Qualcomm Atheros QCA7000 support"
>> + tristate
>> + help
>> + This enables support for the Qualcomm Atheros QCA7000.
>> +
>> +config QCA7000_SPI
>> + tristate "Qualcomm Atheros QCA7000 SPI support"
>> + select QCA7000
>> depends on SPI_MASTER && OF
>> ---help---
>> This SPI protocol driver supports the Qualcomm Atheros
>> QCA7000.
>> diff --git a/drivers/net/ethernet/qualcomm/Makefile
>> b/drivers/net/ethernet/qualcomm/Makefile
>> index 8080570..00d8729 100644
>> --- a/drivers/net/ethernet/qualcomm/Makefile
>> +++ b/drivers/net/ethernet/qualcomm/Makefile
>> @@ -2,7 +2,8 @@
>> # Makefile for the Qualcomm network device drivers.
>> #
>>
>> -obj-$(CONFIG_QCA7000) += qcaspi.o
>> -qcaspi-objs := qca_spi.o qca_common.o qca_7k.o qca_debug.o
>> +obj-$(CONFIG_QCA7000) += qca_common.o
>> +obj-$(CONFIG_QCA7000_SPI) += qcaspi.o
>> +qcaspi-objs := qca_7k.o qca_debug.o qca_spi.o
>>
>> obj-y += emac/
>> diff --git a/drivers/net/ethernet/qualcomm/qca_common.c
>> b/drivers/net/ethernet/qualcomm/qca_common.c
>> index d930524..f2c9e76 100644
>> --- a/drivers/net/ethernet/qualcomm/qca_common.c
>> +++ b/drivers/net/ethernet/qualcomm/qca_common.c
>> @@ -21,7 +21,9 @@
>> * by an atheros frame while transmitted over a serial channel;
>> */
>>
>> +#include <linux/init.h>
>> #include <linux/kernel.h>
>> +#include <linux/module.h>
>>
>> #include "qca_common.h"
>>
>> @@ -46,6 +48,7 @@ qcafrm_create_header(u8 *buf, u16 length)
>>
>> return QCAFRM_HEADER_LEN;
>> }
>> +EXPORT_SYMBOL_GPL(qcafrm_create_header);
>>
>> u16
>> qcafrm_create_footer(u8 *buf)
>> @@ -57,6 +60,7 @@ qcafrm_create_footer(u8 *buf)
>> buf[1] = 0x55;
>> return QCAFRM_FOOTER_LEN;
>> }
>> +EXPORT_SYMBOL_GPL(qcafrm_create_footer);
>>
>> /* Gather received bytes and try to extract a full ethernet frame
>> by
>> * following a simple state machine.
>> @@ -154,3 +158,9 @@ qcafrm_fsm_decode(struct qcafrm_handle *handle,
>> u8 *buf, u16 buf_len, u8 recv_by
>>
>> return ret;
>> }
>> +EXPORT_SYMBOL_GPL(qcafrm_fsm_decode);
>> +
>> +MODULE_DESCRIPTION("Qualcomm Atheros Common");
>> +MODULE_AUTHOR("Qualcomm Atheros Communications");
>> +MODULE_AUTHOR("Stefan Wahren <stefan.wahren@i2se.com>");
>> +MODULE_LICENSE("Dual BSD/GPL");
*** Diese E-Mail ist allein für den bezeichneten Adressaten bestimmt. Sie kann rechtlich vertrauliche Informationen enthalten. Wenn Sie diese E-Mail irrtümlich erhalten haben, informieren Sie bitte unverzüglich den Absender per E-Mail und löschen Sie diese E-Mail von Ihrem Computer, ohne Kopien anzufertigen.
Vielen Dank. ***
*** This email is for the exclusive use of the addressee. It may contain legally privileged information. If you have received this message in error, please notify the sender by email immediately and delete the message from your computer without making any copies.
Thank you. ***
^ permalink raw reply
* Re: [PATCH 1/2] dt-bindings: Document the STM32 MDMA bindings
From: Rob Herring @ 2017-03-28 16:26 UTC (permalink / raw)
To: M'boumba Cedric Madianga
Cc: Vinod Koul, Mark Rutland, Maxime Coquelin, Alexandre Torgue,
Dan Williams, dmaengine-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <CAOAejn10dPjYTj8vBTDj5bc_RPTmkYC7uuBMPnP-R0BsQiRwhw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Mon, Mar 27, 2017 at 5:06 AM, M'boumba Cedric Madianga
<cedric.madianga-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> Hi Rob,
>
> 2017-03-20 22:52 GMT+01:00 Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>:
>> On Mon, Mar 13, 2017 at 04:06:38PM +0100, M'boumba Cedric Madianga wrote:
>>> This patch adds documentation of device tree bindings for the STM32 MDMA
>>> controller.
>>>
>>> Signed-off-by: M'boumba Cedric Madianga <cedric.madianga-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>>> Reviewed-by: Ludovic BARRE <ludovic.barre-qxv4g6HH51o@public.gmane.org>
>>> ---
>>> .../devicetree/bindings/dma/stm32-mdma.txt | 94 ++++++++++++++++++++++
>>> 1 file changed, 94 insertions(+)
>>> create mode 100644 Documentation/devicetree/bindings/dma/stm32-mdma.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/dma/stm32-mdma.txt b/Documentation/devicetree/bindings/dma/stm32-mdma.txt
>>> new file mode 100644
>>> index 0000000..26a930c
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/dma/stm32-mdma.txt
>>> @@ -0,0 +1,94 @@
>>> +* STMicroelectronics STM32 MDMA controller
>>> +
>>> +The STM32 MDMA is a general-purpose direct memory access controller capable of
>>> +supporting 64 independent DMA channels with 256 HW requests.
>>> +
>>> +Required properties:
>>> +- compatible: Should be "st,stm32-mdma"
>>
>> Should be more specific.
>
> The compatible string is not specific enough ? I don't see how to specific more.
stm32 is not a specific SoC. Compatible strings should be specific to
an SoC (with fallback strings to whatever they are compatible with) so
you can handle SoC specific differences or errata.
>>> +- reg: Should contain MDMA registers location and length. This should include
>>> + all of the per-channel registers.
>>> +- interrupts: Should contain the MDMA interrupt.
>>> +- clocks: Should contain the input clock of the DMA instance.
>>> +- resets: Reference to a reset controller asserting the DMA controller.
>>> +- #dma-cells : Must be <5>. See DMA client paragraph for more details.
>>> +
>>> +Optional properties:
>>> +- dma-channels: Number of DMA channels supported by the controller.
>>> +- dma-requests: Number of DMA request signals supported by the controller.
>>> +- st,ahb-addr-masks: Array of u32 mask to list memory devices addressed via
>>> + AHB bus.
>>
>> I don't understand what this is.
>
> The MDMA is able to address memories and devices from AHB bus or AXI bus.
> The MDMA has a little different way of working to address AHB
> device/memory and AXI device/memory.
> In STM32H7 platform, almost all devices are addressed by AXI bus.
> But we have some memories adress by AHB bus.
> So, we add these parameter to be able to handle this.
I still don't understand. Is the difference in DMA addresses vs. CPU
addresses? If so, you should dma-ranges to translate these.
Rob
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH RFC v4 10/10] tty: serdev: add functions to retrieve common UART settings
From: Stefan Wahren @ 2017-03-28 16:33 UTC (permalink / raw)
To: Rob Herring
Cc: Mark Rutland, David S. Miller, Greg Kroah-Hartman, Jiri Slaby,
Marcel Holtmann, Sebastian Reichel, netdev,
devicetree@vger.kernel.org, linux-serial@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <CAL_JsqJ3b2zTGNHNVtt6Y+k23iw+2G2z-Jm2786a8CEzZBS7=g@mail.gmail.com>
Am 27.03.2017 um 22:00 schrieb Rob Herring:
> On Mon, Mar 27, 2017 at 8:37 AM, Stefan Wahren <stefan.wahren@i2se.com> wrote:
>> Currently serdev core doesn't provide functions to retrieve common
>> UART settings like data bits, stop bits or parity. This patch adds
>> the interface to the core and the necessary implementation for
>> serdev-ttyport.
> It doesn't provide them because why do you need to know? The attached
> device should request the settings it needs and be done with it. Maybe
> some devices can support a number of settings and you could want
> negotiate the settings with the UART, though surely 8N1 is in that
> list. It's rare to see something that's not 8N1 from what I've seen.
During development it's very helpful to check the current UART settings
and error counter. Currently i can't see a replacement for
/sys/class/tty/ttyXYZ .
Are there any plans about it?
>
> Rob
^ permalink raw reply
* [PATCH v2] iio: adc: add max1117/max1118/max1119 ADC driver
From: Akinobu Mita @ 2017-03-28 16:34 UTC (permalink / raw)
To: linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Akinobu Mita, Jonathan Cameron, Hartmut Knaack,
Lars-Peter Clausen, Peter Meerwald-Stadler, Rob Herring,
Mark Rutland, Mark Brown
This adds max1117/max1118/max1119 8-bit, dual-channel ADC driver.
This new driver uses the zero length spi_transfers with the cs_change
flag set and/or the non-zero delay_usecs.
1. The zero length transfer with the spi_transfer.cs_change set is
required in order to select CH1. The chip select line must be brought
high and low again without transfer.
2. The zero length transfer with the spi_transfer.delay_usecs > 0 is
required for waiting the conversion to be complete. The conversion
begins with the falling edge of the chip select. During the conversion
process, SCLK is ignored.
These two usages are unusual. But the spi controller drivers that use
a default implementation of transfer_one_message() are likely to work.
(I've tested this adc driver with spi-omap2-mcspi and spi-xilinx)
On the other hand, some spi controller drivers that have their own
transfer_one_message() may not work. But at least for the zero length
transfer with delay_usecs > 0, I'm proposing a new testcase for the
spi-loopback-test that can test whether the delay_usecs setting has
taken effect.
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Signed-off-by: Akinobu Mita <akinobu.mita-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Cc: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Cc: Hartmut Knaack <knaack.h-Mmb7MZpHnFY@public.gmane.org>
Cc: Lars-Peter Clausen <lars-Qo5EllUWu/uELgA04lAiVw@public.gmane.org>
Cc: Peter Meerwald-Stadler <pmeerw-jW+XmwGofnusTnJN9+BGXg@public.gmane.org>
Cc: Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Cc: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
Cc: Mark Brown <broonie-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
* v2
- move max1118_remove() after max1118_probe() for usual code ordering
- make the probe() fail for max1118 if vref-supply isn't acquired
- add acked-by line
.../devicetree/bindings/iio/adc/max1118.txt | 21 ++
drivers/iio/adc/Kconfig | 12 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/max1118.c | 307 +++++++++++++++++++++
4 files changed, 341 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/max1118.txt
create mode 100644 drivers/iio/adc/max1118.c
diff --git a/Documentation/devicetree/bindings/iio/adc/max1118.txt b/Documentation/devicetree/bindings/iio/adc/max1118.txt
new file mode 100644
index 0000000..cf33d0b
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/max1118.txt
@@ -0,0 +1,21 @@
+* MAX1117/MAX1118/MAX1119 8-bit, dual-channel ADCs
+
+Required properties:
+ - compatible: Should be one of
+ * "maxim,max1117"
+ * "maxim,max1118"
+ * "maxim,max1119"
+ - reg: spi chip select number for the device
+ - (max1118 only) vref-supply: The regulator supply for ADC reference voltage
+
+Recommended properties:
+ - spi-max-frequency: Definition as per
+ Documentation/devicetree/bindings/spi/spi-bus.txt
+
+Example:
+adc@0 {
+ compatible = "maxim,max1118";
+ reg = <0>;
+ vref-supply = <&vdd_supply>;
+ spi-max-frequency = <1000000>;
+};
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index dedae7a..66262d1 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -335,6 +335,18 @@ config MAX11100
To compile this driver as a module, choose M here: the module will be
called max11100.
+config MAX1118
+ tristate "Maxim max1117/max1118/max1119 ADCs driver"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Maxim max1117/max1118/max1119
+ 8-bit, dual-channel ADCs.
+
+ To compile this driver as a module, choose M here: the module will be
+ called max1118.
+
config MAX1363
tristate "Maxim max1363 ADC driver"
depends on I2C
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index d001262..5ef3470 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
obj-$(CONFIG_LTC2485) += ltc2485.o
obj-$(CONFIG_MAX1027) += max1027.o
obj-$(CONFIG_MAX11100) += max11100.o
+obj-$(CONFIG_MAX1118) += max1118.o
obj-$(CONFIG_MAX1363) += max1363.o
obj-$(CONFIG_MCP320X) += mcp320x.o
obj-$(CONFIG_MCP3422) += mcp3422.o
diff --git a/drivers/iio/adc/max1118.c b/drivers/iio/adc/max1118.c
new file mode 100644
index 0000000..2e9648a
--- /dev/null
+++ b/drivers/iio/adc/max1118.c
@@ -0,0 +1,307 @@
+/*
+ * MAX1117/MAX1118/MAX1119 8-bit, dual-channel ADCs driver
+ *
+ * Copyright (c) 2017 Akinobu Mita <akinobu.mita-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX1117-MAX1119.pdf
+ *
+ * SPI interface connections
+ *
+ * SPI MAXIM
+ * Master Direction MAX1117/8/9
+ * ------ --------- -----------
+ * nCS --> CNVST
+ * SCK --> SCLK
+ * MISO <-- DOUT
+ * ------ --------- -----------
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/regulator/consumer.h>
+
+enum max1118_id {
+ max1117,
+ max1118,
+ max1119,
+};
+
+struct max1118 {
+ struct spi_device *spi;
+ struct mutex lock;
+ struct regulator *reg;
+
+ u8 data ____cacheline_aligned;
+};
+
+#define MAX1118_CHANNEL(ch) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (ch), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = ch, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 8, \
+ .storagebits = 8, \
+ }, \
+ }
+
+static const struct iio_chan_spec max1118_channels[] = {
+ MAX1118_CHANNEL(0),
+ MAX1118_CHANNEL(1),
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static int max1118_read(struct spi_device *spi, int channel)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct max1118 *adc = iio_priv(indio_dev);
+ struct spi_transfer xfers[] = {
+ /*
+ * To select CH1 for conversion, CNVST pin must be brought high
+ * and low for a second time.
+ */
+ {
+ .len = 0,
+ .delay_usecs = 1, /* > CNVST Low Time 100 ns */
+ .cs_change = 1,
+ },
+ /*
+ * The acquisition interval begins with the falling edge of
+ * CNVST. The total acquisition and conversion process takes
+ * <7.5us.
+ */
+ {
+ .len = 0,
+ .delay_usecs = 8,
+ },
+ {
+ .rx_buf = &adc->data,
+ .len = 1,
+ },
+ };
+ int ret;
+
+ if (channel == 0)
+ ret = spi_sync_transfer(spi, xfers + 1, 2);
+ else
+ ret = spi_sync_transfer(spi, xfers, 3);
+
+ if (ret)
+ return ret;
+
+ return adc->data;
+}
+
+static int max1118_get_vref_mV(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct max1118 *adc = iio_priv(indio_dev);
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ int vref_uV;
+
+ switch (id->driver_data) {
+ case max1117:
+ return 2048;
+ case max1119:
+ return 4096;
+ case max1118:
+ vref_uV = regulator_get_voltage(adc->reg);
+ if (vref_uV < 0)
+ return vref_uV;
+ return vref_uV / 1000;
+ }
+
+ return -ENODEV;
+}
+
+static int max1118_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct max1118 *adc = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&adc->lock);
+ *val = max1118_read(adc->spi, chan->channel);
+ mutex_unlock(&adc->lock);
+ if (*val < 0)
+ return *val;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = max1118_get_vref_mV(adc->spi);
+ if (*val < 0)
+ return *val;
+ *val2 = 8;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info max1118_info = {
+ .read_raw = max1118_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static irqreturn_t max1118_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct max1118 *adc = iio_priv(indio_dev);
+ u8 data[16] = { }; /* 2x 8-bit ADC data + padding + 8 bytes timestamp */
+ int scan_index;
+ int i = 0;
+
+ mutex_lock(&adc->lock);
+
+ for_each_set_bit(scan_index, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ const struct iio_chan_spec *scan_chan =
+ &indio_dev->channels[scan_index];
+ int ret = max1118_read(adc->spi, scan_chan->channel);
+
+ if (ret < 0) {
+ dev_warn(&adc->spi->dev,
+ "failed to get conversion data\n");
+ goto out;
+ }
+
+ data[i] = ret;
+ i++;
+ }
+ iio_push_to_buffers_with_timestamp(indio_dev, data,
+ iio_get_time_ns(indio_dev));
+out:
+ mutex_unlock(&adc->lock);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int max1118_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct max1118 *adc;
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->spi = spi;
+ mutex_init(&adc->lock);
+
+ if (id->driver_data == max1118) {
+ adc->reg = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(adc->reg)) {
+ dev_err(&spi->dev, "failed to get vref regulator\n");
+ return PTR_ERR(adc->reg);
+ }
+ ret = regulator_enable(adc->reg);
+ if (ret)
+ return ret;
+ }
+
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &max1118_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = max1118_channels;
+ indio_dev->num_channels = ARRAY_SIZE(max1118_channels);
+
+ /*
+ * To reinitiate a conversion on CH0, it is necessary to allow for a
+ * conversion to be complete and all of the data to be read out. Once
+ * a conversion has been completed, the MAX1117/MAX1118/MAX1119 will go
+ * into AutoShutdown mode until the next conversion is initiated.
+ */
+ max1118_read(spi, 0);
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ max1118_trigger_handler, NULL);
+ if (ret)
+ goto err_reg_disable;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto err_buffer_cleanup;
+
+ return 0;
+
+err_buffer_cleanup:
+ iio_triggered_buffer_cleanup(indio_dev);
+err_reg_disable:
+ if (id->driver_data == max1118)
+ regulator_disable(adc->reg);
+
+ return ret;
+}
+
+static int max1118_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct max1118 *adc = iio_priv(indio_dev);
+ const struct spi_device_id *id = spi_get_device_id(spi);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ if (id->driver_data == max1118)
+ return regulator_disable(adc->reg);
+
+ return 0;
+}
+
+static const struct spi_device_id max1118_id[] = {
+ { "max1117", max1117 },
+ { "max1118", max1118 },
+ { "max1119", max1119 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, max1118_id);
+
+#ifdef CONFIG_OF
+
+static const struct of_device_id max1118_dt_ids[] = {
+ { .compatible = "maxim,max1117" },
+ { .compatible = "maxim,max1118" },
+ { .compatible = "maxim,max1119" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, max1118_dt_ids);
+
+#endif
+
+static struct spi_driver max1118_spi_driver = {
+ .driver = {
+ .name = "max1118",
+ .of_match_table = of_match_ptr(max1118_dt_ids),
+ },
+ .probe = max1118_probe,
+ .remove = max1118_remove,
+ .id_table = max1118_id,
+};
+module_spi_driver(max1118_spi_driver);
+
+MODULE_AUTHOR("Akinobu Mita <akinobu.mita-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>");
+MODULE_DESCRIPTION("MAXIM MAX1117/MAX1118/MAX1119 ADCs driver");
+MODULE_LICENSE("GPL v2");
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [PATCH v3] ASoC: Add support for Maxim Integrated MAX98927 Amplifier
From: Mark Brown @ 2017-03-28 16:39 UTC (permalink / raw)
To: Ryan Lee
Cc: mark.rutland, alsa-devel, kuninori.morimoto.gx, lgirdwood, tiwai,
srinivas.kandagatla, romain.perier, bardliao, lars, axel.lin,
Paul.Handrigan, devicetree, arnd, nh6z, robh+dt, ckeepax, dgreid,
oder_chiou, ryan.lee.maxim, KCHSU0, linux-kernel
In-Reply-To: <1490715148-14827-1-git-send-email-ryans.lee@maximintegrated.com>
[-- Attachment #1.1: Type: text/plain, Size: 310 bytes --]
On Wed, Mar 29, 2017 at 12:32:28AM +0900, Ryan Lee wrote:
> Signed-off-by: Ryan Lee <ryans.lee@maximintegrated.com>
Please stop sending patches in the middle of existing threads, it makes
it hard to see new versions and for multi-patch serieses makes it hard
to see what the current version of the series is.
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply
* Re: [PATCH 1/7] Documentation: dt: net: Update the ath9k binding for SoC devices
From: Andrew Lunn @ 2017-03-28 16:41 UTC (permalink / raw)
To: Christian Lamparter
Cc: Alban, QCA ath9k Development, John Crispin, Kalle Valo,
Rob Herring, Mark Rutland, linux-wireless, netdev, devicetree,
linux-kernel, m
In-Reply-To: <8622591.q4sXytZqXW@debian64>
> Oh, in that case you should probably go "all out" and ask on the
> LKML to remove all of the ath9k and ath10k ahb work. From what I
> know all the "users" are running some sort of OpenWRT/LEDE or a
> derivative. This is because Atheros/QCA provided a SDK based on
> OpenWRT.
>
> Alban has been trying to convert the platform to device-tree
> and add them to the mainline for a while now:
>
> https://patchwork.kernel.org/patch/6514551/
>
> So, you are questioning this work as well.
Not at all. Ralph Sennhauser has been doing a great job of getting all
the Marvell devices into Mainline, and i help as much as i can, being
one of the Marvell SoC Maintainers.
I'm just saying, get a few boards which require these facilities into
the mainline, and then you have a much stranger base to argue from.
Andrew
^ permalink raw reply
* Re: [RFC 4/8] regulator: core: Check enabling bypass respects constraints
From: Mark Brown @ 2017-03-28 16:47 UTC (permalink / raw)
To: Leonard Crestez
Cc: Sascha Hauer, Liam Girdwood, Viresh Kumar, Rafael J. Wysocki,
Shawn Guo, Robin Gong, Anson Huang, Irina Tirdea, Rob Herring,
Mark Rutland, Fabio Estevam, Octavian Purdila,
linux-pm-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1490704781.3546.57.camel-3arQi8VN3Tc@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 1852 bytes --]
On Tue, Mar 28, 2017 at 03:39:41PM +0300, Leonard Crestez wrote:
> On Fri, 2017-03-24 at 12:52 +0000, Mark Brown wrote:
Please fix your mail client to word wrap within paragraphs at something
substantially less than 80 columns. Doing this makes your messages much
easier to read and reply to.
> > to the supply. Usually bypass is used for low power retention modes
> > with different settings to those used in normal operation that wouldn't
> > be desired in normal operation, if we were going to have constraints for
> > this I'd expect a separate set used during bypass.
> In this particular case it's not possible to set constraints on the parent
> regulator so that both ldo-enable and ldo-bypass modes work. The maximum allowed
> voltage for ldo-bypass is lower than the minimum required to support the chip at
> max frequency wit ldo-enable.
If things are really so sensitive that you can't bypass without lowering
the voltage then it's hard to see how you can safely transition into and
out of bypass mode.
> I'm not sure I understand why you are against applying constraints to the parent
> when in bypass mode, it seems like the obvious thing to do if you want to
> support flexible configuration. The check I introduced is probably not enough to
> cover all cases, for example it would still be possible to explicitly change
> parent voltage afterwards.
To repeat what I said previously the whole point of bypassing is to not
do regulation and generally the constraints in the unregulated idle case
are substantially more relaxed. This would break use cases relying on
the existing behaviour which wouldn't expect to affect the parent
voltage at all, either stopping things working or making them less
efficient by needlessly regulating the voltage down which defeats the
main point of bypassing.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH] arm64: tegra: Add CPU and PSCI nodes for NVIDIA Tegra210 platforms
From: Jon Hunter @ 2017-03-28 16:49 UTC (permalink / raw)
To: Stephen Warren
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Stephen Warren,
Vagrant Cascadian, Thierry Reding,
linux-tegra-u79uwXL29TY76Z2rM5mHXA, Martin Michlmayr,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <9069e2fd-bf7b-b1df-3d1b-071ee21642ef-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
On 28/03/17 17:23, Stephen Warren wrote:
> On 03/28/2017 05:48 AM, Jon Hunter wrote:
>> Add the CPU and PSCI nodes for the NVIDIA Tegra210 platforms so that
>> all CPUs can be enabled on boot. This assumes that the PSCI firmware
>> has been loaded during the initial bootstrap on the device before the
>> kernel starts (which is typically the case for these platforms). The
>> PSCI firmware version is set to v0.2 which aligns with the current
>> shipping version for Tegra.
>
> This seems fine, although I'd expect PSCI to be used everywhere on
> Tegra, so putting this in tegra210.dtsi would make more sense.
It's fine with me. Although technically, tegra210-smaug ships with
psci-v1.0 and tegra210-jetson-tx1 currently have psci-v0.2. However, I
did test both with the psci version set to v1.0 and the jetson-tx1, did
detect the version automatically and reverted to v0.2 (which is a
feature of psci >= v0.2). For Tegra186 we also have the psci node in the
tegra186-p3310.dtsi.
Jon
--
nvpublic
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v5 6/9] coresight: add support for CPU debug module
From: Mathieu Poirier @ 2017-03-28 16:50 UTC (permalink / raw)
To: Leo Yan
Cc: Jonathan Corbet, Rob Herring, Mark Rutland, Wei Xu,
Catalin Marinas, Will Deacon, Andy Gross, David Brown,
Michael Turquette, Stephen Boyd, Guodong Xu, John Stultz,
linux-doc, linux-kernel, devicetree, linux-arm-kernel,
linux-arm-msm, linux-soc, linux-clk, mike.leach, Suzuki.Poulose,
sudeep.holla
In-Reply-To: <1490466197-29163-7-git-send-email-leo.yan@linaro.org>
Hi Leo,
On Sun, Mar 26, 2017 at 02:23:14AM +0800, Leo Yan wrote:
> Coresight includes debug module and usually the module connects with CPU
> debug logic. ARMv8 architecture reference manual (ARM DDI 0487A.k) has
> description for related info in "Part H: External Debug".
>
> Chapter H7 "The Sample-based Profiling Extension" introduces several
> sampling registers, e.g. we can check program counter value with
> combined CPU exception level, secure state, etc. So this is helpful for
> analysis CPU lockup scenarios, e.g. if one CPU has run into infinite
> loop with IRQ disabled. In this case the CPU cannot switch context and
> handle any interrupt (including IPIs), as the result it cannot handle
> SMP call for stack dump.
>
> This patch is to enable coresight debug module, so firstly this driver
> is to bind apb clock for debug module and this is to ensure the debug
> module can be accessed from program or external debugger. And the driver
> uses sample-based registers for debug purpose, e.g. when system triggers
> panic, the driver will dump program counter and combined context
> registers (EDCIDSR, EDVIDSR); by parsing context registers so can
> quickly get to know CPU secure state, exception level, etc.
>
> Some of the debug module registers are located in CPU power domain, so
> this requires the CPU power domain stays on when access related debug
> registers, but the power management for CPU power domain is quite
> dependent on SoC integration for power management. For the platforms
> which with sane power controller implementations, this driver follows
> the method to set EDPRCR to try to pull the CPU out of low power state
> and then set 'no power down request' bit so the CPU has no chance to
> lose power.
>
> If the SoC has not followed up this design well for power management
> controller, the driver introduces module parameter "idle_constraint".
> Setting this parameter for latency requirement in microseconds, finally
> we can constrain all or partial idle states to ensure the CPU power
> domain is enabled, this is a backup method to access coresight CPU
> debug component safely.
>
> Signed-off-by: Leo Yan <leo.yan@linaro.org>
> ---
> drivers/hwtracing/coresight/Kconfig | 11 +
> drivers/hwtracing/coresight/Makefile | 1 +
> drivers/hwtracing/coresight/coresight-cpu-debug.c | 704 ++++++++++++++++++++++
> 3 files changed, 716 insertions(+)
> create mode 100644 drivers/hwtracing/coresight/coresight-cpu-debug.c
>
> diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
> index 130cb21..18d7931 100644
> --- a/drivers/hwtracing/coresight/Kconfig
> +++ b/drivers/hwtracing/coresight/Kconfig
> @@ -89,4 +89,15 @@ config CORESIGHT_STM
> logging useful software events or data coming from various entities
> in the system, possibly running different OSs
>
> +config CORESIGHT_CPU_DEBUG
> + tristate "CoreSight CPU Debug driver"
> + depends on ARM || ARM64
> + depends on DEBUG_FS
> + help
> + This driver provides support for coresight debugging module. This
> + is primarily used to dump sample-based profiling registers when
> + system triggers panic, the driver will parse context registers so
> + can quickly get to know program counter (PC), secure state,
> + exception level, etc.
> +
> endif
> diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
> index af480d9..433d590 100644
> --- a/drivers/hwtracing/coresight/Makefile
> +++ b/drivers/hwtracing/coresight/Makefile
> @@ -16,3 +16,4 @@ obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \
> coresight-etm4x-sysfs.o
> obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o
> obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o
> +obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o
> diff --git a/drivers/hwtracing/coresight/coresight-cpu-debug.c b/drivers/hwtracing/coresight/coresight-cpu-debug.c
> new file mode 100644
> index 0000000..fbec1d1
> --- /dev/null
> +++ b/drivers/hwtracing/coresight/coresight-cpu-debug.c
> @@ -0,0 +1,704 @@
> +/*
> + * Copyright (c) 2017 Linaro Limited. All rights reserved.
> + *
> + * Author: Leo Yan <leo.yan@linaro.org>
> + *
> + * 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 <http://www.gnu.org/licenses/>.
> + *
> + */
> +#include <linux/amba/bus.h>
> +#include <linux/coresight.h>
> +#include <linux/cpu.h>
> +#include <linux/debugfs.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/pm_qos.h>
> +#include <linux/slab.h>
> +#include <linux/smp.h>
> +#include <linux/types.h>
> +#include <linux/uaccess.h>
> +
> +#include "coresight-priv.h"
> +
> +#define EDPCSR 0x0A0
> +#define EDCIDSR 0x0A4
> +#define EDVIDSR 0x0A8
> +#define EDPCSR_HI 0x0AC
> +#define EDOSLAR 0x300
> +#define EDPRCR 0x310
> +#define EDPRSR 0x314
> +#define EDDEVID1 0xFC4
> +#define EDDEVID 0xFC8
> +
> +#define EDPCSR_PROHIBITED 0xFFFFFFFF
> +
> +/* bits definition for EDPCSR */
> +#ifndef CONFIG_64BIT
> +#define EDPCSR_THUMB BIT(0)
> +#define EDPCSR_ARM_INST_MASK GENMASK(31, 2)
> +#define EDPCSR_THUMB_INST_MASK GENMASK(31, 1)
> +#endif
> +
> +/* bits definition for EDPRCR */
> +#define EDPRCR_COREPURQ BIT(3)
> +#define EDPRCR_CORENPDRQ BIT(0)
> +
> +/* bits definition for EDPRSR */
> +#define EDPRSR_DLK BIT(6)
> +#define EDPRSR_PU BIT(0)
> +
> +/* bits definition for EDVIDSR */
> +#define EDVIDSR_NS BIT(31)
> +#define EDVIDSR_E2 BIT(30)
> +#define EDVIDSR_E3 BIT(29)
> +#define EDVIDSR_HV BIT(28)
> +#define EDVIDSR_VMID GENMASK(7, 0)
> +
> +/*
> + * bits definition for EDDEVID1:PSCROffset
> + *
> + * NOTE: armv8 and armv7 have different definition for the register,
> + * so consolidate the bits definition as below:
> + *
> + * 0b0000 - Sample offset applies based on the instruction state, we
> + * rely on EDDEVID to check if EDPCSR is implemented or not
> + * 0b0001 - No offset applies.
> + * 0b0010 - No offset applies, but do not use in AArch32 mode
> + *
> + */
> +#define EDDEVID1_PCSR_OFFSET_MASK GENMASK(3, 0)
> +#define EDDEVID1_PCSR_OFFSET_INS_SET (0x0)
> +#define EDDEVID1_PCSR_NO_OFFSET_DIS_AARCH32 (0x2)
> +
> +/* bits definition for EDDEVID */
> +#define EDDEVID_PCSAMPLE_MODE GENMASK(3, 0)
> +#define EDDEVID_IMPL_NONE (0x0)
> +#define EDDEVID_IMPL_EDPCSR (0x1)
> +#define EDDEVID_IMPL_EDPCSR_EDCIDSR (0x2)
> +#define EDDEVID_IMPL_FULL (0x3)
> +
> +#define DEBUG_WAIT_TIMEOUT 32
> +
> +struct debug_drvdata {
> + void __iomem *base;
> + struct device *dev;
> + int cpu;
> +
> + bool edpcsr_present;
> + bool edcidsr_present;
> + bool edvidsr_present;
> + bool pc_has_offset;
> +
> + u32 eddevid;
> + u32 eddevid1;
> +
> + u32 edpcsr;
> + u32 edpcsr_hi;
> + u32 edprcr;
> + u32 edprsr;
> + u32 edvidsr;
> + u32 edcidsr;
> +};
> +
> +static DEFINE_MUTEX(debug_lock);
> +static DEFINE_PER_CPU(struct debug_drvdata *, debug_drvdata);
> +static int debug_count;
> +static struct dentry *debug_debugfs_dir;
> +
> +static struct pm_qos_request debug_qos_req;
> +static int idle_constraint = PM_QOS_DEFAULT_VALUE;
> +module_param(idle_constraint, int, 0600);
> +MODULE_PARM_DESC(idle_constraint, "Latency requirement in microseconds for CPU "
> + "idle states (default is -1, which means have no limiation "
> + "to CPU idle states; 0 means disabling all idle states; user "
> + "can choose other platform dependent values so can disable "
> + "specific idle states for the platform)");
> +
> +static bool debug_enable;
> +module_param_named(enable, debug_enable, bool, 0600);
> +MODULE_PARM_DESC(enable, "Knob to enable debug functionality "
> + "(default is 0, which means is disabled by default)");
> +
> +static void debug_os_unlock(struct debug_drvdata *drvdata)
> +{
> + /* Unlocks the debug registers */
> + writel_relaxed(0x0, drvdata->base + EDOSLAR);
> + wmb();
> +}
> +
> +/*
> + * According to ARM DDI 0487A.k, before access external debug
> + * registers should firstly check the access permission; if any
> + * below condition has been met then cannot access debug
> + * registers to avoid lockup issue:
> + *
> + * - CPU power domain is powered off;
> + * - The OS Double Lock is locked;
> + *
> + * By checking EDPRSR can get to know if meet these conditions.
> + */
> +static bool debug_access_permitted(struct debug_drvdata *drvdata)
> +{
> + /* CPU is powered off */
> + if (!(drvdata->edprsr & EDPRSR_PU))
> + return false;
> +
> + /* The OS Double Lock is locked */
> + if (drvdata->edprsr & EDPRSR_DLK)
> + return false;
> +
> + return true;
> +}
> +
> +static void debug_force_cpu_powered_up(struct debug_drvdata *drvdata)
> +{
> + int timeout = DEBUG_WAIT_TIMEOUT;
> +
> + drvdata->edprsr = readl_relaxed(drvdata->base + EDPRSR);
> +
> + CS_UNLOCK(drvdata->base);
> +
> + /* Bail out if CPU is powered up yet */
> + if (drvdata->edprsr & EDPRSR_PU)
> + goto out_powered_up;
> +
> + /*
> + * Send request to power management controller and assert
> + * DBGPWRUPREQ signal; if power management controller has
> + * sane implementation, it should enable CPU power domain
> + * in case CPU is in low power state.
> + */
> + drvdata->edprsr = readl(drvdata->base + EDPRCR);
> + drvdata->edprsr |= EDPRCR_COREPURQ;
> + writel(drvdata->edprsr, drvdata->base + EDPRCR);
Here ->edprsr is used but EDPRCR is accessed. Is this intentional or a
copy/paste error? The same is true for accesses in the out_powered_up section.
> +
> + /* Wait for CPU to be powered up (timeout~=32ms) */
> + while (timeout--) {
> + drvdata->edprsr = readl_relaxed(drvdata->base + EDPRSR);
> + if (drvdata->edprsr & EDPRSR_PU)
> + break;
> +
> + usleep_range(1000, 2000);
> + }
See if function readx_poll_timeout() can be used.
> +
> + /*
> + * Unfortunately the CPU cannot be powered up, so return
> + * back and later has no permission to access other
> + * registers. For this case, should set 'idle_constraint'
> + * to ensure CPU power domain is enabled!
> + */
> + if (!(drvdata->edprsr & EDPRSR_PU)) {
> + pr_err("%s: power up request for CPU%d failed\n",
> + __func__, drvdata->cpu);
> + goto out;
> + }
> +
> +out_powered_up:
> + debug_os_unlock(drvdata);
> +
> + /*
> + * At this point the CPU is powered up, so set the no powerdown
> + * request bit so we don't lose power and emulate power down.
> + */
> + drvdata->edprsr = readl(drvdata->base + EDPRCR);
> + drvdata->edprsr |= EDPRCR_COREPURQ | EDPRCR_CORENPDRQ;
If we are here the core is already up. Shouldn't we need to set
EDPRCR_CORENPDRQ only?
> + writel(drvdata->edprsr, drvdata->base + EDPRCR);
This section is a little racy - between the time the PU bit has been
checked and the time COREPDRQ has been flipped, the state of PU may have
changed. You can probably get around this by checking edprsr.PU rigth here. If
it is not set you go through the process again. Note that doing this will
probably force a refactoring of the whole function.
> +
> +out:
> + CS_LOCK(drvdata->base);
> +}
> +
> +static void debug_read_regs(struct debug_drvdata *drvdata)
> +{
> + /*
> + * Ensure CPU power domain is enabled to let registers
> + * are accessiable.
> + */
> + debug_force_cpu_powered_up(drvdata);
> +
> + if (!debug_access_permitted(drvdata))
> + return;
> +
> + CS_UNLOCK(drvdata->base);
> +
> + debug_os_unlock(drvdata);
> +
> + drvdata->edpcsr = readl_relaxed(drvdata->base + EDPCSR);
> +
> + /*
> + * As described in ARM DDI 0487A.k, if the processing
> + * element (PE) is in debug state, or sample-based
> + * profiling is prohibited, EDPCSR reads as 0xFFFFFFFF;
> + * EDCIDSR, EDVIDSR and EDPCSR_HI registers also become
> + * UNKNOWN state. So directly bail out for this case.
> + */
> + if (drvdata->edpcsr == EDPCSR_PROHIBITED)
> + goto out;
> +
> + /*
> + * A read of the EDPCSR normally has the side-effect of
> + * indirectly writing to EDCIDSR, EDVIDSR and EDPCSR_HI;
> + * at this point it's safe to read value from them.
> + */
> + if (IS_ENABLED(CONFIG_64BIT))
> + drvdata->edpcsr_hi = readl_relaxed(drvdata->base + EDPCSR_HI);
> +
> + if (drvdata->edcidsr_present)
> + drvdata->edcidsr = readl_relaxed(drvdata->base + EDCIDSR);
> +
> + if (drvdata->edvidsr_present)
> + drvdata->edvidsr = readl_relaxed(drvdata->base + EDVIDSR);
> +
> +out:
> + CS_LOCK(drvdata->base);
> +}
> +
> +#ifndef CONFIG_64BIT
> +static unsigned long debug_adjust_pc(struct debug_drvdata *drvdata,
> + unsigned long pc)
> +{
> + unsigned long arm_inst_offset = 0, thumb_inst_offset = 0;
> +
> + if (drvdata->pc_has_offset) {
> + arm_inst_offset = 8;
> + thumb_inst_offset = 4;
> + }
> +
> + /* Handle thumb instruction */
> + if (pc & EDPCSR_THUMB) {
> + pc = (pc & EDPCSR_THUMB_INST_MASK) - thumb_inst_offset;
> + return pc;
> + }
> +
> + /*
> + * Handle arm instruction offset, if the arm instruction
> + * is not 4 byte alignment then it's possible the case
> + * for implementation defined; keep original value for this
> + * case and print info for notice.
> + */
> + if (pc & BIT(1))
> + pr_emerg("Instruction offset is implementation defined\n");
> + else
> + pc = (pc & EDPCSR_ARM_INST_MASK) - arm_inst_offset;
> +
> + return pc;
> +}
> +#endif
> +
> +static void debug_dump_regs(struct debug_drvdata *drvdata)
> +{
> + unsigned long pc;
> +
> + pr_emerg("\tEDPRSR: %08x (Power:%s DLK:%s)\n", drvdata->edprsr,
> + drvdata->edprsr & EDPRSR_PU ? "On" : "Off",
> + drvdata->edprsr & EDPRSR_DLK ? "Lock" : "Unlock");
> +
> + if (!debug_access_permitted(drvdata)) {
> + pr_emerg("No permission to access debug registers!\n");
> + return;
> + }
> +
> + if (drvdata->edpcsr == EDPCSR_PROHIBITED) {
> + pr_emerg("CPU is in Debug state or profiling is prohibited!\n");
> + return;
> + }
> +
> +#ifdef CONFIG_64BIT
> + pc = (unsigned long)drvdata->edpcsr_hi << 32 |
> + (unsigned long)drvdata->edpcsr;
> +#else
> + pc = debug_adjust_pc(drvdata, (unsigned long)drvdata->edpcsr);
> +#endif
> +
> + pr_emerg("\tEDPCSR: [<%p>] %pS\n", (void *)pc, (void *)pc);
> +
> + if (drvdata->edcidsr_present)
> + pr_emerg("\tEDCIDSR: %08x\n", drvdata->edcidsr);
> +
> + if (drvdata->edvidsr_present)
> + pr_emerg("\tEDVIDSR: %08x (State:%s Mode:%s Width:%dbits VMID:%x)\n",
> + drvdata->edvidsr,
> + drvdata->edvidsr & EDVIDSR_NS ? "Non-secure" : "Secure",
> + drvdata->edvidsr & EDVIDSR_E3 ? "EL3" :
> + (drvdata->edvidsr & EDVIDSR_E2 ? "EL2" : "EL1/0"),
> + drvdata->edvidsr & EDVIDSR_HV ? 64 : 32,
> + drvdata->edvidsr & (u32)EDVIDSR_VMID);
> +}
> +
> +static void debug_init_arch_data(void *info)
> +{
> + struct debug_drvdata *drvdata = info;
> + u32 mode, pcsr_offset;
> +
> + CS_UNLOCK(drvdata->base);
> +
> + debug_os_unlock(drvdata);
> +
> + /* Read device info */
> + drvdata->eddevid = readl_relaxed(drvdata->base + EDDEVID);
> + drvdata->eddevid1 = readl_relaxed(drvdata->base + EDDEVID1);
> +
> + CS_LOCK(drvdata->base);
> +
> + /* Parse implementation feature */
> + mode = drvdata->eddevid & EDDEVID_PCSAMPLE_MODE;
> + pcsr_offset = drvdata->eddevid1 & EDDEVID1_PCSR_OFFSET_MASK;
> +
> + if (mode == EDDEVID_IMPL_NONE) {
> + drvdata->edpcsr_present = false;
> + drvdata->edcidsr_present = false;
> + drvdata->edvidsr_present = false;
> + } else if (mode == EDDEVID_IMPL_EDPCSR) {
> + drvdata->edpcsr_present = true;
> + drvdata->edcidsr_present = false;
> + drvdata->edvidsr_present = false;
> + } else if (mode == EDDEVID_IMPL_EDPCSR_EDCIDSR) {
> + /*
> + * In ARM DDI 0487A.k, the EDDEVID1.PCSROffset is used to
> + * define if has the offset for PC sampling value; if read
> + * back EDDEVID1.PCSROffset == 0x2, then this means the debug
> + * module does not sample the instruction set state when
> + * armv8 CPU in AArch32 state.
> + */
> + if (!IS_ENABLED(CONFIG_64BIT) &&
> + (pcsr_offset == EDDEVID1_PCSR_NO_OFFSET_DIS_AARCH32))
> + drvdata->edpcsr_present = false;
> + else
> + drvdata->edpcsr_present = true;
> +
> + drvdata->edcidsr_present = true;
> + drvdata->edvidsr_present = false;
> + } else if (mode == EDDEVID_IMPL_FULL) {
> + drvdata->edpcsr_present = true;
> + drvdata->edcidsr_present = true;
> + drvdata->edvidsr_present = true;
> + }
> +
> + if (IS_ENABLED(CONFIG_64BIT))
> + drvdata->pc_has_offset = false;
> + else
> + drvdata->pc_has_offset =
> + (pcsr_offset == EDDEVID1_PCSR_OFFSET_INS_SET);
> +
> + return;
> +}
> +
> +/*
> + * Dump out information on panic.
> + */
> +static int debug_notifier_call(struct notifier_block *self,
> + unsigned long v, void *p)
> +{
> + int cpu;
> + struct debug_drvdata *drvdata;
> +
> + pr_emerg("ARM external debug module:\n");
> +
> + for_each_possible_cpu(cpu) {
> + drvdata = per_cpu(debug_drvdata, cpu);
> + if (!drvdata)
> + continue;
> +
> + pr_emerg("CPU[%d]:\n", drvdata->cpu);
> +
> + debug_read_regs(drvdata);
> + debug_dump_regs(drvdata);
> + }
> +
> + return 0;
> +}
> +
> +static struct notifier_block debug_notifier = {
> + .notifier_call = debug_notifier_call,
> +};
> +
> +static int debug_enable_func(void)
> +{
> + int ret;
> +
> + pm_qos_add_request(&debug_qos_req,
> + PM_QOS_CPU_DMA_LATENCY, idle_constraint);
> +
> + ret = atomic_notifier_chain_register(&panic_notifier_list,
> + &debug_notifier);
> + if (ret)
> + goto err;
> +
> + return 0;
> +
> +err:
> + pm_qos_remove_request(&debug_qos_req);
> + return ret;
> +}
> +
> +static void debug_disable_func(void)
> +{
> + atomic_notifier_chain_unregister(&panic_notifier_list,
> + &debug_notifier);
> + pm_qos_remove_request(&debug_qos_req);
> +}
> +
> +static ssize_t debug_func_knob_write(struct file *f,
> + const char __user *buf, size_t count, loff_t *ppos)
> +{
> e u8 on;
> + int ret;
> +
> + ret = kstrtou8_from_user(buf, count, 2, &on);
> + if (ret)
> + return ret;
> +
> + mutex_lock(&debug_lock);
> +
> + if (!on ^ debug_enable)
> + goto out;
I had to read this condition too many times - please refactor.
> +
> + if (on) {
> + ret = debug_enable_func();
> + if (ret) {
> + pr_err("%s: unable to disable debug function: %d\n",
> + __func__, ret);
Based on the semantic this is the wrong error message.
> + goto err;
> + }
> + } else
> + debug_disable_func();
Did checkpatch.pl complain about extra curly braces? If not please add them.
> +
> + debug_enable = on;
Here we can't set debug_enable if we just called debug_disable_func(). Maybe
I'm missing something. If that's the case a comment in the code would be worth
it.
> +
> +out:
> + ret = count;
> +err:
> + mutex_unlock(&debug_lock);
> + return ret;
> +}
> +
> +static ssize_t debug_func_knob_read(struct file *f,
> + char __user *ubuf, size_t count, loff_t *ppos)
> +{
> + char val[] = { '0' + debug_enable, '\n' };
> +
> + return simple_read_from_buffer(ubuf, count, ppos, val, sizeof(val));
Use the debug_lock to avoid race conditions.
> +}
> +
> +static ssize_t debug_idle_constraint_write(struct file *f,
> + const char __user *buf, size_t count, loff_t *ppos)
> +{
> + int val;
> + ssize_t ret;
> +
> + ret = kstrtoint_from_user(buf, count, 10, &val);
> + if (ret)
> + return ret;
> +
> + mutex_lock(&debug_lock);
> + idle_constraint = val;
> +
> + if (debug_enable)
> + pm_qos_update_request(&debug_qos_req, idle_constraint);
> +
> + mutex_unlock(&debug_lock);
> + return count;
> +}
> +
> +static ssize_t debug_idle_constraint_read(struct file *f,
> + char __user *ubuf, size_t count, loff_t *ppos)
> +{
> + char buf[32];
> + int len;
> +
> + if (*ppos)
> + return 0;
> +
> + len = sprintf(buf, "%d\n", idle_constraint);
> + return simple_read_from_buffer(ubuf, count, ppos, buf, len);
Use the debug_lock to avoid race conditions.
> +}
> +
> +static const struct file_operations debug_func_knob_fops = {
> + .open = simple_open,
> + .read = debug_func_knob_read,
> + .write = debug_func_knob_write,
> +};
> +
> +static const struct file_operations debug_idle_constraint_fops = {
> + .open = simple_open,
> + .read = debug_idle_constraint_read,
> + .write = debug_idle_constraint_write,
> +};
> +
> +static int debug_func_init(void)
> +{
> + struct dentry *file;
> + int ret;
> +
> + /* Create debugfs node */
> + debug_debugfs_dir = debugfs_create_dir("coresight_cpu_debug", NULL);
> + if (!debug_debugfs_dir) {
> + pr_err("%s: unable to create debugfs directory\n", __func__);
> + return -ENOMEM;
return PTR_ERR(debug_debugfs_dir);
> + }
> +
> + file = debugfs_create_file("enable", S_IRUGO | S_IWUSR,
> + debug_debugfs_dir, NULL, &debug_func_knob_fops);
> + if (!file) {
> + pr_err("%s: unable to create enable knob file\n", __func__);
> + ret = -ENOMEM;
Same as above.
> + goto err;
> + }
> +
> + file = debugfs_create_file("idle_constraint", S_IRUGO | S_IWUSR,
> + debug_debugfs_dir, NULL, &debug_idle_constraint_fops);
> + if (!file) {
> + pr_err("%s: unable to create idle constraint file\n", __func__);
> + ret = -ENOMEM;
Same as above.
> + goto err;
> + }
> +
> + /* Use sysfs node to enable functionality */
> + if (!debug_enable)
> + return 0;
> +
> + /* Enable debug module at boot time */
> + ret = debug_enable_func();
> + if (ret) {
> + pr_err("%s: unable to disable debug function: %d\n",
> + __func__, ret);
> + goto err;
> + }
Use the debug_lock to avoid race conditions.
> +
> + return 0;
> +
> +err:
> + debugfs_remove_recursive(debug_debugfs_dir);
> + return ret;
> +}
> +
> +static void debug_func_exit(void)
> +{
> + debugfs_remove_recursive(debug_debugfs_dir);
> +
> + /* Disable functionality if has enabled */
> + if (debug_enable)
> + debug_disable_func();
> +}
> +
> +static int debug_probe(struct amba_device *adev, const struct amba_id *id)
> +{
> + void __iomem *base;
> + struct device *dev = &adev->dev;
> + struct debug_drvdata *drvdata;
> + struct resource *res = &adev->res;
> + struct device_node *np = adev->dev.of_node;
> + int ret;
> +
> + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
> + if (!drvdata)
> + return -ENOMEM;
> +
> + drvdata->cpu = np ? of_coresight_get_cpu(np) : 0;
> + if (per_cpu(debug_drvdata, drvdata->cpu)) {
> + dev_err(dev, "CPU's drvdata has been initialized\n");
Might be worth adding the CPU number in the error message.
> + return -EBUSY;
> + }
> +
> + drvdata->dev = &adev->dev;
> + amba_set_drvdata(adev, drvdata);
> +
> + /* Validity for the resource is already checked by the AMBA core */
> + base = devm_ioremap_resource(dev, res);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + drvdata->base = base;
> +
> + get_online_cpus();
> + per_cpu(debug_drvdata, drvdata->cpu) = drvdata;
> + ret = smp_call_function_single(drvdata->cpu,
> + debug_init_arch_data, drvdata, 1);
> + put_online_cpus();
> +
> + if (ret) {
> + dev_err(dev, "Debug arch init failed\n");
Once again add the CPU number in the error message.
> + goto err;
> + }
> +
> + if (!drvdata->edpcsr_present) {
> + ret = -ENXIO;
> + dev_err(dev, "Sample-based profiling is not implemented\n");
Same as above.
> + goto err;
> + }
> +
> + if (!debug_count++) {
> + ret = debug_func_init();
> + if (ret)
> + goto err_func_init;
> + }
> +
> + dev_info(dev, "Coresight debug-CPU%d initialized\n", drvdata->cpu);
> + return 0;
> +
> +err_func_init:
> + debug_count--;
> +err:
> + per_cpu(debug_drvdata, drvdata->cpu) = NULL;
> + return ret;
> +}
This driver doesn't call the pm_runtime_put/get() operations needed to handle the
debug power domain. See the other CoreSight drivers for details.
Also, from the conversation that followed the previous post we agreed that we wouldn't
deal with CPUidle issues in this driver. We deal with the CPU power domain
using the EDPRCR register (like you did) and that's it. System that don't honor that register
can use other (external) means to solve this. As such please remove the
pm_qos_xyz() functions.
Thanks,
Mathieu
> +
> +static int debug_remove(struct amba_device *adev)
> +{
> + struct debug_drvdata *drvdata = amba_get_drvdata(adev);
> +
> + per_cpu(debug_drvdata, drvdata->cpu) = NULL;
> +
> + if (!--debug_count)
> + debug_func_exit();
> +
> + return 0;
> +}
> +
> +static struct amba_id debug_ids[] = {
> + { /* Debug for Cortex-A53 */
> + .id = 0x000bbd03,
> + .mask = 0x000fffff,
> + },
> + { /* Debug for Cortex-A57 */
> + .id = 0x000bbd07,
> + .mask = 0x000fffff,
> + },
> + { /* Debug for Cortex-A72 */
> + .id = 0x000bbd08,
> + .mask = 0x000fffff,
> + },
> + { 0, 0 },
> +};
> +
> +static struct amba_driver debug_driver = {
> + .drv = {
> + .name = "coresight-cpu-debug",
> + .suppress_bind_attrs = true,
> + },
> + .probe = debug_probe,
> + .remove = debug_remove,
> + .id_table = debug_ids,
> +};
> +
> +module_amba_driver(debug_driver);
> +
> +MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>");
> +MODULE_DESCRIPTION("ARM Coresight CPU Debug Driver");
> +MODULE_LICENSE("GPL");
> --
> 2.7.4
>
^ permalink raw reply
* Re: [PATCH 1/7] Documentation: dt: net: Update the ath9k binding for SoC devices
From: Christian Lamparter @ 2017-03-28 17:09 UTC (permalink / raw)
To: Andrew Lunn
Cc: Alban, QCA ath9k Development, John Crispin, Kalle Valo,
Rob Herring, Mark Rutland, linux-wireless-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, m
In-Reply-To: <20170328164159.GB29742-g2DYL2Zd6BY@public.gmane.org>
On Tuesday, March 28, 2017 6:41:59 PM CEST Andrew Lunn wrote:
> > Oh, in that case you should probably go "all out" and ask on the
> > LKML to remove all of the ath9k and ath10k ahb work. From what I
> > know all the "users" are running some sort of OpenWRT/LEDE or a
> > derivative. This is because Atheros/QCA provided a SDK based on
> > OpenWRT.
> >
> > Alban has been trying to convert the platform to device-tree
> > and add them to the mainline for a while now:
> >
> > https://patchwork.kernel.org/patch/6514551/
> >
> > So, you are questioning this work as well.
>
> Not at all. Ralph Sennhauser has been doing a great job of getting all
> the Marvell devices into Mainline, and i help as much as i can, being
> one of the Marvell SoC Maintainers.
>
> I'm just saying, get a few boards which require these facilities into
> the mainline, and then you have a much stronger base to argue from.
I was arguing not to deprecate "qca,no-eeprom" property.
based on this quote from Linus' <https://lkml.org/lkml/2016/6/16/995>:
|if a new interface is truly more flexible, then it should be able
|to implement the old interface with no changes, so that drivers
|shouldn't need to be changed/upgraded.
what stronger point to do you want?
Thanks,
Christian
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH RFC v4 06/10] net: qualcomm: make qca_common a separate kernel module
From: Dan Williams @ 2017-03-28 17:18 UTC (permalink / raw)
To: Stefan Wahren, Rob Herring, Mark Rutland, David S. Miller
Cc: Greg Kroah-Hartman, Jiri Slaby, Marcel Holtmann,
Sebastian Reichel, netdev, devicetree, linux-serial, linux-kernel
In-Reply-To: <d90f8e33-8b86-2cf7-145b-b15997e152a7@i2se.com>
On Tue, 2017-03-28 at 18:24 +0200, Stefan Wahren wrote:
> Am 27.03.2017 um 17:44 schrieb Dan Williams:
> > On Mon, 2017-03-27 at 15:37 +0200, Stefan Wahren wrote:
> > > In order to share common functions between QCA7000 SPI and UART
> > > protocol
> > > driver the qca_common needs to be a separate kernel module.
> >
> > Maybe "qca_eth_common"? There are many things Qualcomm, slightly
> > fewer
> > things Qualcomm Atheros, and "qca_common" isn't very descriptive.
>
> Since it is a Homeplug powerline chip which doesn't have any physical
> connection to Ethernet only on protocol level, i wouldn't use eth in
> the
> module name.
>
> The code is very specific to the QCA7000, so how about
> "qca_7k_common"?
Sure; I just saw it was under drivers/net/ethernet, which kinda means
it's ethernet-related... But 7k common is fine.
Dan
> >
> > Dan
> >
> > > Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
> > > ---
> > > drivers/net/ethernet/qualcomm/Kconfig | 8 +++++++-
> > > drivers/net/ethernet/qualcomm/Makefile | 5 +++--
> > > drivers/net/ethernet/qualcomm/qca_common.c | 10 ++++++++++
> > > 3 files changed, 20 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/drivers/net/ethernet/qualcomm/Kconfig
> > > b/drivers/net/ethernet/qualcomm/Kconfig
> > > index d7720bf..b4c369d 100644
> > > --- a/drivers/net/ethernet/qualcomm/Kconfig
> > > +++ b/drivers/net/ethernet/qualcomm/Kconfig
> > > @@ -16,7 +16,13 @@ config NET_VENDOR_QUALCOMM
> > > if NET_VENDOR_QUALCOMM
> > >
> > > config QCA7000
> > > - tristate "Qualcomm Atheros QCA7000 support"
> > > + tristate
> > > + help
> > > + This enables support for the Qualcomm Atheros QCA7000.
> > > +
> > > +config QCA7000_SPI
> > > + tristate "Qualcomm Atheros QCA7000 SPI support"
> > > + select QCA7000
> > > depends on SPI_MASTER && OF
> > > ---help---
> > > This SPI protocol driver supports the Qualcomm Atheros
> > > QCA7000.
> > > diff --git a/drivers/net/ethernet/qualcomm/Makefile
> > > b/drivers/net/ethernet/qualcomm/Makefile
> > > index 8080570..00d8729 100644
> > > --- a/drivers/net/ethernet/qualcomm/Makefile
> > > +++ b/drivers/net/ethernet/qualcomm/Makefile
> > > @@ -2,7 +2,8 @@
> > > # Makefile for the Qualcomm network device drivers.
> > > #
> > >
> > > -obj-$(CONFIG_QCA7000) += qcaspi.o
> > > -qcaspi-objs := qca_spi.o qca_common.o qca_7k.o qca_debug.o
> > > +obj-$(CONFIG_QCA7000) += qca_common.o
> > > +obj-$(CONFIG_QCA7000_SPI) += qcaspi.o
> > > +qcaspi-objs := qca_7k.o qca_debug.o qca_spi.o
> > >
> > > obj-y += emac/
> > > diff --git a/drivers/net/ethernet/qualcomm/qca_common.c
> > > b/drivers/net/ethernet/qualcomm/qca_common.c
> > > index d930524..f2c9e76 100644
> > > --- a/drivers/net/ethernet/qualcomm/qca_common.c
> > > +++ b/drivers/net/ethernet/qualcomm/qca_common.c
> > > @@ -21,7 +21,9 @@
> > > * by an atheros frame while transmitted over a serial
> > > channel;
> > > */
> > >
> > > +#include <linux/init.h>
> > > #include <linux/kernel.h>
> > > +#include <linux/module.h>
> > >
> > > #include "qca_common.h"
> > >
> > > @@ -46,6 +48,7 @@ qcafrm_create_header(u8 *buf, u16 length)
> > >
> > > return QCAFRM_HEADER_LEN;
> > > }
> > > +EXPORT_SYMBOL_GPL(qcafrm_create_header);
> > >
> > > u16
> > > qcafrm_create_footer(u8 *buf)
> > > @@ -57,6 +60,7 @@ qcafrm_create_footer(u8 *buf)
> > > buf[1] = 0x55;
> > > return QCAFRM_FOOTER_LEN;
> > > }
> > > +EXPORT_SYMBOL_GPL(qcafrm_create_footer);
> > >
> > > /* Gather received bytes and try to extract a full ethernet
> > > frame
> > > by
> > > * following a simple state machine.
> > > @@ -154,3 +158,9 @@ qcafrm_fsm_decode(struct qcafrm_handle
> > > *handle,
> > > u8 *buf, u16 buf_len, u8 recv_by
> > >
> > > return ret;
> > > }
> > > +EXPORT_SYMBOL_GPL(qcafrm_fsm_decode);
> > > +
> > > +MODULE_DESCRIPTION("Qualcomm Atheros Common");
> > > +MODULE_AUTHOR("Qualcomm Atheros Communications");
> > > +MODULE_AUTHOR("Stefan Wahren <stefan.wahren@i2se.com>");
> > > +MODULE_LICENSE("Dual BSD/GPL");
>
>
> *** Diese E-Mail ist allein für den bezeichneten Adressaten bestimmt.
> Sie kann rechtlich vertrauliche Informationen enthalten. Wenn Sie
> diese E-Mail irrtümlich erhalten haben, informieren Sie bitte
> unverzüglich den Absender per E-Mail und löschen Sie diese E-Mail von
> Ihrem Computer, ohne Kopien anzufertigen.
> Vielen Dank. ***
>
> *** This email is for the exclusive use of the addressee. It may
> contain legally privileged information. If you have received this
> message in error, please notify the sender by email immediately and
> delete the message from your computer without making any copies.
> Thank you. ***
>
^ permalink raw reply
* Re: [RESEND PATCH 1/3] dt: pwm: lpc32xx: add description of clocks and #pwm-cells properties
From: Sylvain Lemieux @ 2017-03-28 17:24 UTC (permalink / raw)
To: thierry.reding; +Cc: linux-pwm, devicetree, robh+dt, linux-arm-kernel, vz
In-Reply-To: <20170216202749.20653-2-slemieux.tyco@gmail.com>
ping
On Thu, 2017-02-16 at 15:27 -0500, slemieux.tyco@gmail.com wrote:
> From: Vladimir Zapolskiy <vz@mleia.com>
>
> NXP LPC32xx SoCs have two simple independent PWM controllers with a single
> output each, in this case there is no need to specify PWM channel argument
> on client side, one cell for setting PWM output frequency is sufficient.
>
> Another added to the description property 'clocks' has a standard meaning
> of a controller supply clock, in the LPC32xx User's Manual the clock is
> denoted as PWM1_CLK or PWM2_CLK clock.
>
> Signed-off-by: Vladimir Zapolskiy <vz@mleia.com>
> Reviewed-by: Sylvain Lemieux <slemieux.tyco@gmail.com>
> Acked-by: Rob Herring <robh@kernel.org>
> ---
> Documentation/devicetree/bindings/pwm/lpc32xx-pwm.txt | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/pwm/lpc32xx-pwm.txt b/Documentation/devicetree/bindings/pwm/lpc32xx-pwm.txt
> index 74b5bc5dd19a..523d79662861 100644
> --- a/Documentation/devicetree/bindings/pwm/lpc32xx-pwm.txt
> +++ b/Documentation/devicetree/bindings/pwm/lpc32xx-pwm.txt
> @@ -3,15 +3,22 @@ LPC32XX PWM controller
> Required properties:
> - compatible: should be "nxp,lpc3220-pwm"
> - reg: physical base address and length of the controller's registers
> +- clocks: clock phandle and clock specifier pair
> +- #pwm-cells: should be 1, the cell is used to specify the period in
> + nanoseconds.
>
> Examples:
>
> pwm@4005c000 {
> compatible = "nxp,lpc3220-pwm";
> reg = <0x4005c000 0x4>;
> + clocks = <&clk LPC32XX_CLK_PWM1>;
> + #pwm-cells = <1>;
> };
>
> pwm@4005c004 {
> compatible = "nxp,lpc3220-pwm";
> reg = <0x4005c004 0x4>;
> + clocks = <&clk LPC32XX_CLK_PWM2>;
> + #pwm-cells = <1>;
> };
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox