Skip to contentSkip to conversation

serial: 8250_dw: Add DMA support for non-ACPI platforms

PATCH In review Ray Jui 30 participants 360 messages Oct 8, 2014 – Mar 6, 2015 8 messages not in the archive Page 3 of 3

Subsystems (18)
lore
Share & tools
lore
author maintainer
#289 lore raw

PATCH v3 2/4 pinctrl: Broadcom Cygnus pinctrl device tree binding

In reply to Ray Jui

Device tree binding documentation for Broadcom Cygnus IOMUX driver

---
 .../bindings/pinctrl/brcm,cygnus-pinmux.txt        |  159 ++++++++++++++++++++
 1 file changed, 159 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinmux.txt
Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinmux.txt new file +159 -0
new file mode 100644
index 0000000..1082b8b
@@ -0,0 +1,159 @@
+Broadcom Cygnus IOMUX Controller
+
+The Cygnus IOMUX controller supports group based mux configuration. In
+addition, certain pins can be muxed to GPIO function individually.
+
+Required properties:
+
+- compatible:
+    Must be "brcm,cygnus-pinmux"
+
+- reg:
+    Define the base and range of the I/O address space that contains the Cygnus
+IOMUX registers
+
+Required properties in child nodes:
+
+- function:
+    The mux function to select
+
+- groups:
+    The list of groups to select with a given function
+
+Each child node represents a configuration. Client devices reference the child
+node to enable a mux configuration
+
+For more details, refer to
+Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
+
+For example:
+
+	pinmux: pinmux at 0x0301d0c8 {
+		compatible = "brcm,cygnus-pinmux";
+		reg = <0x0301d0c8 0x1b0>;
+
+		i2s_0: i2s_0 {
+			function = "i2s0";
+			groups = "i2s0_0_grp", "i2s0_1_grp";
+		};
+
+		i2s_1: i2s_1 {
+			function = "i2s1";
+			groups = "i2s1_0_grp", "i2s1_1_grp";
+		};
+
+		i2s_2: i2s_2 {
+			function = "i2s2";
+			groups = "i2s2_0_grp", "i2s2_1_grp", "i2s2_2_grp",
+				 "i2s2_3_grp", "i2s2_4_grp";
+		};
+
+		spi_0: spi_0 {
+			function = "spi0";
+			groups = "spi0_grp";
+		};
+	}
+
+	spi0 at 18028000 {
+			compatible = "arm,pl022", "arm,primecell";
+			reg = <0x18028000 0x1000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-0 = <&spi_0>;
+			clocks = <&axi81_clk>;
+			clock-names = "apb_pclk";
+	};
+
+List of supported functions and groups in Cygnus:
+
+"gpio": N/A (to be removed for A0)
+
+"i2s0": "i2s0_0_grp", "i2s0_1_grp"
+
+"i2s1": "i2s1_0_grp", "i2s1_1_grp"
+
+"i2s2": "i2s2_0_grp", "i2s2_1_grp", "i2s2_2_grp", "i2s2_3_grp", "i2s2_4_grp"
+
+"spdif": "spdif_grp"
+
+"pwm0": "pwm0_grp"
+
+"pwm1": "pwm1_grp"
+
+"pwm2": "pwm2_grp"
+
+"pwm3": "pwm3_grp"
+
+"pwm4": "pwm4_grp"
+
+"pwm5": "pwm5_grp"
+
+"key": "key0_grp", "key1_grp", "key2_grp", "key3_grp", "key4_grp", "key5_grp",
+"key6_grp", "key7_grp", "key8_grp", "key9_grp", "key10_grp", "key11_grp",
+"key12_grp", "key13_grp", "key14_grp", "key15_grp"
+
+"audio_dte": "audio_dte0_grp", "audio_dte1_grp", "audio_dte2_grp", "audio_dte3_grp"
+
+"smart_card0": "smart_card0_grp", "smart_card0_fcb_grp"
+
+"smart_card1": "smart_card1_grp", "smart_card1_fcb_grp"
+
+"spi0": "spi0_grp"
+
+"spi1": "spi1_grp"
+
+"spi2": "spi2_grp"
+
+"spi3": "spi3_grp"
+
+"spi4": "spi4_0_grp", "spi4_1_grp"
+
+"spi5": "spi5_grp"
+
+"sw_led0": "sw_led0_0_grp", "sw_led0_1_grp"
+
+"sw_led1": "sw_led1_grp"
+
+"sw_led2": "sw_led2_0_grp", "sw_led2_1_grp"
+
+"d1w": "d1w_grp"
+
+"lcd": "lcd_grp"
+
+"sram": "sram_0_grp", "sram_1_grp"
+
+"uart0": "uart0_grp"
+
+"uart1": "uart1_grp", "uart1_dte_grp"
+
+"uart2": "uart2_grp"
+
+"uart3": "uart3_grp"
+
+"uart4": "uart4_grp"
+
+"qspi": "qspi_0_grp", "qspi_1_grp"
+
+"nand": "nand_grp"
+
+"sdio0": "sdio0_grp", "sdio0_cd_grp", "sdio0_mmc_grp"
+
+"sdio1": "sdio1_data_0_grp", "sdio1_data_1_grp", "sdio1_cd_grp",
+"sdio1_led_grp", "sdio1_mmc_grp"
+
+"can0": "can0_grp"
+
+"can1": "can1_grp"
+
+"cam": "cam_led_grp", "cam_0_grp", "cam_1_grp"
+
+"bsc1": "bsc1_grp"
+
+"pcie_clkreq": "pcie_clkreq_grp"
+
+"usb0_oc": "usb0_oc_grp"
+
+"usb1_oc": "usb1_oc_grp"
+
+"usb2_oc": "usb2_oc_grp"
Signature · 1 line
1.7.9.5
author maintainer
#290 lore raw

PATCH v3 3/4 pinctrl: cygnus: add initial IOMUX driver support

In reply to Ray Jui

This adds the initial driver support for the Broadcom Cygnus IOMUX controller. The Cygnus IOMUX controller supports group based mux configuration but allows certain pins to be muxed to GPIO individually

---
 drivers/pinctrl/bcm/Kconfig              |   13 +
 drivers/pinctrl/bcm/Makefile             |    5 +-
 drivers/pinctrl/bcm/pinctrl-cygnus-mux.c | 1087 ++++++++++++++++++++++++++++++
 3 files changed, 1103 insertions(+), 2 deletions(-)
 create mode 100644 drivers/pinctrl/bcm/pinctrl-cygnus-mux.c
drivers/pinctrl/bcm/Kconfig +13 -0
index bc6d048..eb13201 100644
@@ -19,3 +19,16 @@ config PINCTRL_BCM2835
 	bool
 	select PINMUX
 	select PINCONF
+
+config PINCTRL_CYGNUS_MUX
+	bool "Broadcom Cygnus IOMUX driver"
+	depends on (ARCH_BCM_CYGNUS || COMPILE_TEST)
+	select PINMUX
+	select GENERIC_PINCONF
+	default ARCH_BCM_CYGNUS
+	help
+	  Say yes here to enable the Broadcom Cygnus IOMUX driver.
+
+	  The Broadcom Cygnus IOMUX driver supports group based IOMUX
+	  configuration, with the exception that certain individual pins
+	  can be overrided to GPIO function
drivers/pinctrl/bcm/Makefile +3 -2
index 7ba80a3..bb6beb6 100644
@@ -1,4 +1,5 @@
 # Broadcom pinctrl support
 
-obj-$(CONFIG_PINCTRL_BCM281XX)	+= pinctrl-bcm281xx.o
-obj-$(CONFIG_PINCTRL_BCM2835)	+= pinctrl-bcm2835.o
+obj-$(CONFIG_PINCTRL_BCM281XX)		+= pinctrl-bcm281xx.o
+obj-$(CONFIG_PINCTRL_BCM2835)		+= pinctrl-bcm2835.o
+obj-$(CONFIG_PINCTRL_CYGNUS_MUX)	+= pinctrl-cygnus-mux.o
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +1087 -0

1,087 lines not on this page · raw message

Signature · 1 line
1.7.9.5
#291 lore raw

Re: [PATCH v3 3/4] pinctrl: cygnus: add initial IOMUX driver support

Hi Ray,

Quoted 6 lines · Ray Jui

This adds the initial driver support for the Broadcom Cygnus IOMUX controller. The Cygnus IOMUX controller supports group based mux configuration but allows certain pins to be muxed to GPIO individually

Just a few random nits/comments...

Quoted 109 lines
---
 drivers/pinctrl/bcm/Kconfig              |   13 +
 drivers/pinctrl/bcm/Makefile             |    5 +-
 drivers/pinctrl/bcm/pinctrl-cygnus-mux.c | 1087 ++++++++++++++++++++++++++++++
 3 files changed, 1103 insertions(+), 2 deletions(-)
 create mode 100644 drivers/pinctrl/bcm/pinctrl-cygnus-mux.c
drivers/pinctrl/bcm/Kconfig +13 -0
index bc6d048..eb13201 100644
@@ -19,3 +19,16 @@ config PINCTRL_BCM2835
 	bool
 	select PINMUX
 	select PINCONF
+
+config PINCTRL_CYGNUS_MUX
+	bool "Broadcom Cygnus IOMUX driver"
+	depends on (ARCH_BCM_CYGNUS || COMPILE_TEST)
+	select PINMUX
+	select GENERIC_PINCONF
+	default ARCH_BCM_CYGNUS
+	help
+	  Say yes here to enable the Broadcom Cygnus IOMUX driver.
+
+	  The Broadcom Cygnus IOMUX driver supports group based IOMUX
+	  configuration, with the exception that certain individual pins
+	  can be overrided to GPIO function
drivers/pinctrl/bcm/Makefile +3 -2
index 7ba80a3..bb6beb6 100644
@@ -1,4 +1,5 @@
 # Broadcom pinctrl support
 
-obj-$(CONFIG_PINCTRL_BCM281XX)	+= pinctrl-bcm281xx.o
-obj-$(CONFIG_PINCTRL_BCM2835)	+= pinctrl-bcm2835.o
+obj-$(CONFIG_PINCTRL_BCM281XX)		+= pinctrl-bcm281xx.o
+obj-$(CONFIG_PINCTRL_BCM2835)		+= pinctrl-bcm2835.o
+obj-$(CONFIG_PINCTRL_CYGNUS_MUX)	+= pinctrl-cygnus-mux.o
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +63 -0
new file mode 100644
index 0000000..33565b4
@@ -0,0 +1,1087 @@
+/* Copyright (C) 2014-2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * This file contains the Cygnus IOMUX driver that supports group based PINMUX
+ * configuration. Although PINMUX configuration is mainly group based, the
+ * Cygnus IOMUX controller allows certain pins to be individually muxed to GPIO
+ * function, and therefore be controlled by the Cygnus ASIU GPIO controller
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include "../core.h"
+#include "../pinctrl-utils.h"
+
+#define CYGNUS_NUM_IOMUX_REGS     8
+#define CYGNUS_NUM_MUX_PER_REG    8
+#define CYGNUS_NUM_IOMUX          (CYGNUS_NUM_IOMUX_REGS * \
+				   CYGNUS_NUM_MUX_PER_REG)
+
+/*
+ * Cygnus IOMUX register description
+ *
+ * @offset: register offset for mux configuration of a group
+ * @shift: bit shift for mux configuration of a group
+ * @alt: alternate function to set to
+ */
+struct cygnus_mux {
+	unsigned int offset;
+	unsigned int shift;
+	unsigned int alt;
+};
+
+/*
+ * Keep track of Cygnus IOMUX configuration and prevent double configuration
+ *
+ * @cygnus_mux: Cygnus IOMUX register description
+ * @is_configured: flag to indicate whether a mux setting has already been
+ * configured
+ */
+struct cygnus_mux_log {
+	struct cygnus_mux mux;
+	bool is_configured;
+};
+
+/*
+ * Group based IOMUX configuration
+ *

drivers/pinctrl/bcm/pinctrl-cygnus-mux.c line 73

Quoted 10 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +10 -0
+ * @name: name of the group
+ * @pins: array of pins used by this group
+ * @num_pins: total number of pins used by this group
+ * @mux: Cygnus group based IOMUX configuration
+ */
+struct cygnus_pin_group {
+	const char *name;
+	const unsigned *pins;
+	const unsigned num_pins;
+	const struct cygnus_mux mux;

Not: the last 2 consts are quite weird - if you want to make an instance of cygnus_pin_group immutable you declare it as a const (and I see you are already doing that below). With the structure as it laid out currently you can only do static initializers.

drivers/pinctrl/bcm/pinctrl-cygnus-mux.c line 86

Quoted 13 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +13 -0
+};
+
+/*
+ * Cygnus mux function and supported pin groups
+ *
+ * @name: name of the function
+ * @groups: array of groups that can be supported by this function
+ * @num_groups: total number of groups that can be supported by this function
+ */
+struct cygnus_pin_function {
+	const char *name;
+	const char * const *groups;
+	const unsigned num_groups;

Here as well.

...

drivers/pinctrl/bcm/pinctrl-cygnus-mux.c line 160

Quoted 5 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +5 -0
+
+/*
+ * List of pins in Cygnus
+ */
+static struct cygnus_pin cygnus_pins[] = {

const?

Quoted 2 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +2 -0
+	CYGNUS_PIN_DESC(0, "ext_device_reset_n", 0, 0, 0),
+	CYGNUS_PIN_DESC(1, "chip_mode0", 0, 0, 0),

...

drivers/pinctrl/bcm/pinctrl-cygnus-mux.c line 482

Quoted 3 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +3 -0
+#define CYGNUS_PIN_GROUP(group_name, off, sh, al)	\
+{							\
+	.name = #group_name"""_grp",			\

Why do we need extra pair of quotes? BTW we can also do

	.name = __stringify(group_name) "_grp",
Quoted 8 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +8 -0
+	.pins = group_name ## _pins,			\
+	.num_pins = ARRAY_SIZE(group_name ## _pins),	\
+	.mux = {					\
+		.offset = off,				\
+		.shift = sh,				\
+		.alt = al,				\
+	}						\
+}

...

drivers/pinctrl/bcm/pinctrl-cygnus-mux.c line 800

Quoted 2 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +2 -0
+
+static struct pinctrl_ops cygnus_pinctrl_ops = {

const?

Quoted 104 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +104 -0
+	.get_groups_count = cygnus_get_groups_count,
+	.get_group_name = cygnus_get_group_name,
+	.get_group_pins = cygnus_get_group_pins,
+	.pin_dbg_show = cygnus_pin_dbg_show,
+	.dt_node_to_map = cygnus_dt_node_to_map,
+	.dt_free_map = pinctrl_utils_dt_free_map,
+};
+
+static int cygnus_get_functions_count(struct pinctrl_dev *pctrl_dev)
+{
+	struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+	return pinctrl->num_functions;
+}
+
+static const char *cygnus_get_function_name(struct pinctrl_dev *pctrl_dev,
+					    unsigned selector)
+{
+	struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+	return pinctrl->functions[selector].name;
+}
+
+static int cygnus_get_function_groups(struct pinctrl_dev *pctrl_dev,
+				      unsigned selector,
+				      const char * const **groups,
+				      unsigned * const num_groups)
+{
+	struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+	*groups = pinctrl->functions[selector].groups;
+	*num_groups = pinctrl->functions[selector].num_groups;
+
+	return 0;
+}
+
+static int cygnus_pinmux_set(struct cygnus_pinctrl *pinctrl,
+			     const struct cygnus_pin_function *func,
+			     const struct cygnus_pin_group *grp,
+			     struct cygnus_mux_log *mux_log)
+{
+	const struct cygnus_mux *mux = &grp->mux;
+	int i;
+	u32 val, mask = 0x7;
+	unsigned long flags;
+
+	for (i = 0; i < CYGNUS_NUM_IOMUX; i++) {
+		if (mux->offset != mux_log[i].mux.offset ||
+		    mux->shift != mux_log[i].mux.shift)
+			continue;
+
+		/* match found if we reach here */
+
+		/* if this is a new configuration, just do it! */
+		if (!mux_log[i].is_configured)
+			break;
+
+		/*
+		 * IOMUX has been configured previously and one is trying to
+		 * configure it to a different function
+		 */
+		if (mux_log[i].mux.alt != mux->alt) {
+			dev_err(pinctrl->dev,
+				"double configuration error detected!\n");
+			dev_err(pinctrl->dev, "func:%s grp:%s\n",
+				func->name, grp->name);
+			return -EINVAL;
+		} else {
+			/*
+			 * One tries to configure it to the same function.
+			 * Just quit and don't bother
+			 */
+			return 0;
+		}
+	}
+
+	mux_log[i].mux.alt = mux->alt;
+	mux_log[i].is_configured = true;
+
+	spin_lock_irqsave(&pinctrl->lock, flags);
+
+	val = readl(pinctrl->base0 + grp->mux.offset);
+	val &= ~(mask << grp->mux.shift);
+	val |= grp->mux.alt << grp->mux.shift;
+	writel(val, pinctrl->base0 + grp->mux.offset);
+
+	spin_unlock_irqrestore(&pinctrl->lock, flags);
+
+	return 0;
+}
+
+static int cygnus_pinmux_set_mux(struct pinctrl_dev *pctrl_dev,
+				 unsigned func_select, unsigned grp_select)
+{
+	struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+	const struct cygnus_pin_function *func =
+		&pinctrl->functions[func_select];
+	const struct cygnus_pin_group *grp = &pinctrl->groups[grp_select];
+
+	dev_dbg(pctrl_dev->dev, "func:%u name:%s grp:%u name:%s\n",
+		func_select, func->name, grp_select, grp->name);
+
+	dev_dbg(pctrl_dev->dev, "offset:0x%08x shift:%u alt:%u\n",
+		grp->mux.offset, grp->mux.shift, grp->mux.alt);

drivers/pinctrl/bcm/pinctrl-cygnus-mux.c line 914

Quoted 10 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +10 -0
+
+	return cygnus_pinmux_set(pinctrl, func, grp, pinctrl->mux_log);
+}
+
+static int cygnus_gpio_request_enable(struct pinctrl_dev *pctrl_dev,
+				      struct pinctrl_gpio_range *range,
+				      unsigned pin)
+{
+	struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+	struct cygnus_gpio_mux *mux = pctrl_dev->desc->pins[pin].drv_data;

const?

Quoted 38 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +38 -0
+	u32 val;
+	unsigned long flags;
+
+	/* not all pins support GPIO pinmux override */
+	if (!mux->is_supported)
+		return -ENOTSUPP;
+
+	spin_lock_irqsave(&pinctrl->lock, flags);
+
+	val = readl(pinctrl->base1 + mux->offset);
+	val |= 0x3 << mux->shift;
+	writel(val, pinctrl->base1 + mux->offset);
+
+	spin_unlock_irqrestore(&pinctrl->lock, flags);
+
+	dev_dbg(pctrl_dev->dev,
+		"gpio request enable pin=%u offset=0x%x shift=%u\n",
+		pin, mux->offset, mux->shift);
+
+	return 0;
+}
+
+static void cygnus_gpio_disable_free(struct pinctrl_dev *pctrl_dev,
+				     struct pinctrl_gpio_range *range,
+				     unsigned pin)
+{
+	struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+	struct cygnus_gpio_mux *mux = pctrl_dev->desc->pins[pin].drv_data;
+	u32 val;
+	unsigned long flags;
+
+	if (!mux->is_supported)
+		return;
+
+	spin_lock_irqsave(&pinctrl->lock, flags);
+
+	val = readl(pinctrl->base1 + mux->offset);
+	val &= ~(0x3 << mux->shift);

drivers/pinctrl/bcm/pinctrl-cygnus-mux.c line 962

Quoted 10 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +10 -0
+	writel(val, pinctrl->base1 + mux->offset);
+
+	spin_unlock_irqrestore(&pinctrl->lock, flags);
+
+	dev_err(pctrl_dev->dev,
+		"gpio disable free pin=%u offset=0x%x shift=%u\n",
+		pin, mux->offset, mux->shift);
+}
+
+static struct pinmux_ops cygnus_pinmux_ops = {

const?

Quoted 104 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +104 -0
+	.get_functions_count = cygnus_get_functions_count,
+	.get_function_name = cygnus_get_function_name,
+	.get_function_groups = cygnus_get_function_groups,
+	.set_mux = cygnus_pinmux_set_mux,
+	.gpio_request_enable = cygnus_gpio_request_enable,
+	.gpio_disable_free = cygnus_gpio_disable_free,
+};
+
+static struct pinctrl_desc cygnus_pinctrl_desc = {
+	.name = "cygnus-pinmux",
+	.pctlops = &cygnus_pinctrl_ops,
+	.pmxops = &cygnus_pinmux_ops,
+};
+
+static int cygnus_mux_log_init(struct cygnus_pinctrl *pinctrl)
+{
+	struct cygnus_mux_log *log;
+	unsigned int i, j;
+
+	pinctrl->mux_log = devm_kcalloc(pinctrl->dev, CYGNUS_NUM_IOMUX,
+					sizeof(struct cygnus_mux_log),
+					GFP_KERNEL);
+	if (!pinctrl->mux_log)
+		return -ENOMEM;
+
+	log = pinctrl->mux_log;
+	for (i = 0; i < CYGNUS_NUM_IOMUX_REGS; i++) {
+		for (j = 0; j < CYGNUS_NUM_MUX_PER_REG; j++) {
+			log = &pinctrl->mux_log[i * CYGNUS_NUM_MUX_PER_REG
+				+ j];
+			log->mux.offset = i * 4;
+			log->mux.shift = j * 4;
+			log->mux.alt = 0;
+			log->is_configured = false;
+		}
+	}
+
+	return 0;
+}
+
+static int cygnus_pinmux_probe(struct platform_device *pdev)
+{
+	struct cygnus_pinctrl *pinctrl;
+	struct resource *res;
+	int i, ret;
+	struct pinctrl_pin_desc *pins;
+	unsigned num_pins = ARRAY_SIZE(cygnus_pins);
+
+	pinctrl = devm_kzalloc(&pdev->dev, sizeof(*pinctrl), GFP_KERNEL);
+	if (!pinctrl)
+		return -ENOMEM;
+
+	pinctrl->dev = &pdev->dev;
+	platform_set_drvdata(pdev, pinctrl);
+	spin_lock_init(&pinctrl->lock);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	pinctrl->base0 = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(pinctrl->base0)) {
+		dev_err(&pdev->dev, "unable to map I/O space\n");
+		return PTR_ERR(pinctrl->base0);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	pinctrl->base1 = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(pinctrl->base1)) {
+		dev_err(&pdev->dev, "unable to map I/O space\n");
+		return PTR_ERR(pinctrl->base1);
+	}
+
+	ret = cygnus_mux_log_init(pinctrl);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to initialize IOMUX log\n");
+		return ret;
+	}
+
+	pins = devm_kcalloc(&pdev->dev, num_pins, sizeof(*pins), GFP_KERNEL);
+	if (!pins)
+		return -ENOMEM;
+
+	for (i = 0; i < num_pins; i++) {
+		pins[i].number = cygnus_pins[i].pin;
+		pins[i].name = cygnus_pins[i].name;
+		pins[i].drv_data = &cygnus_pins[i].gpio_mux;
+	}
+
+	pinctrl->groups = cygnus_pin_groups;
+	pinctrl->num_groups = ARRAY_SIZE(cygnus_pin_groups);
+	pinctrl->functions = cygnus_pin_functions;
+	pinctrl->num_functions = ARRAY_SIZE(cygnus_pin_functions);
+	cygnus_pinctrl_desc.pins = pins;
+	cygnus_pinctrl_desc.npins = num_pins;
+
+	pinctrl->pctl = pinctrl_register(&cygnus_pinctrl_desc, &pdev->dev,
+			pinctrl);
+	if (!pinctrl->pctl) {
+		dev_err(&pdev->dev, "unable to register Cygnus IOMUX pinctrl\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct of_device_id cygnus_pinmux_of_match[] = {

drivers/pinctrl/bcm/pinctrl-cygnus-mux.c line 1076

Quoted 10 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +10 -0
+	{ .compatible = "brcm,cygnus-pinmux" },
+	{ }
+};
+
+static struct platform_driver cygnus_pinmux_driver = {
+	.driver = {
+		.name = "cygnus-pinmux",
+		.of_match_table = cygnus_pinmux_of_match,
+	},
+	.probe = cygnus_pinmux_probe,

You also need to either provide remove() method or disallow unbinding via sysfs by setting suppress_bind_attrs in platform driver.

Quoted 14 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +11 -0
+};
+
+static int __init cygnus_pinmux_init(void)
+{
+	return platform_driver_register(&cygnus_pinmux_driver);
+}
+arch_initcall(cygnus_pinmux_init);
+
+MODULE_AUTHOR("Ray Jui <[email protected]>");
+MODULE_DESCRIPTION("Broadcom Cygnus IOMUX driver");
+MODULE_LICENSE("GPL v2");
Signature · 1 line
1.7.9.5

Thanks.

Signature · 1 line
Dmitry
author maintainer
#292 lore raw

Re: [PATCH v3 3/4] pinctrl: cygnus: add initial IOMUX driver support

Quoted 128 lines · Dmitry Torokhov

Hi Ray,

Quoted 6 lines · Ray Jui

This adds the initial driver support for the Broadcom Cygnus IOMUX controller. The Cygnus IOMUX controller supports group based mux configuration but allows certain pins to be muxed to GPIO individually

Just a few random nits/comments...

Quoted 116 lines
---
 drivers/pinctrl/bcm/Kconfig              |   13 +
 drivers/pinctrl/bcm/Makefile             |    5 +-
 drivers/pinctrl/bcm/pinctrl-cygnus-mux.c | 1087 ++++++++++++++++++++++++++++++
 3 files changed, 1103 insertions(+), 2 deletions(-)
 create mode 100644 drivers/pinctrl/bcm/pinctrl-cygnus-mux.c
drivers/pinctrl/bcm/Kconfig +13 -0
index bc6d048..eb13201 100644
@@ -19,3 +19,16 @@ config PINCTRL_BCM2835
 	bool
 	select PINMUX
 	select PINCONF
+
+config PINCTRL_CYGNUS_MUX
+	bool "Broadcom Cygnus IOMUX driver"
+	depends on (ARCH_BCM_CYGNUS || COMPILE_TEST)
+	select PINMUX
+	select GENERIC_PINCONF
+	default ARCH_BCM_CYGNUS
+	help
+	  Say yes here to enable the Broadcom Cygnus IOMUX driver.
+
+	  The Broadcom Cygnus IOMUX driver supports group based IOMUX
+	  configuration, with the exception that certain individual pins
+	  can be overrided to GPIO function
drivers/pinctrl/bcm/Makefile +3 -2
index 7ba80a3..bb6beb6 100644
@@ -1,4 +1,5 @@
 # Broadcom pinctrl support
 
-obj-$(CONFIG_PINCTRL_BCM281XX)	+= pinctrl-bcm281xx.o
-obj-$(CONFIG_PINCTRL_BCM2835)	+= pinctrl-bcm2835.o
+obj-$(CONFIG_PINCTRL_BCM281XX)		+= pinctrl-bcm281xx.o
+obj-$(CONFIG_PINCTRL_BCM2835)		+= pinctrl-bcm2835.o
+obj-$(CONFIG_PINCTRL_CYGNUS_MUX)	+= pinctrl-cygnus-mux.o
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +70 -0
new file mode 100644
index 0000000..33565b4
@@ -0,0 +1,1087 @@
+/* Copyright (C) 2014-2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * This file contains the Cygnus IOMUX driver that supports group based PINMUX
+ * configuration. Although PINMUX configuration is mainly group based, the
+ * Cygnus IOMUX controller allows certain pins to be individually muxed to GPIO
+ * function, and therefore be controlled by the Cygnus ASIU GPIO controller
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include "../core.h"
+#include "../pinctrl-utils.h"
+
+#define CYGNUS_NUM_IOMUX_REGS     8
+#define CYGNUS_NUM_MUX_PER_REG    8
+#define CYGNUS_NUM_IOMUX          (CYGNUS_NUM_IOMUX_REGS * \
+				   CYGNUS_NUM_MUX_PER_REG)
+
+/*
+ * Cygnus IOMUX register description
+ *
+ * @offset: register offset for mux configuration of a group
+ * @shift: bit shift for mux configuration of a group
+ * @alt: alternate function to set to
+ */
+struct cygnus_mux {
+	unsigned int offset;
+	unsigned int shift;
+	unsigned int alt;
+};
+
+/*
+ * Keep track of Cygnus IOMUX configuration and prevent double configuration
+ *
+ * @cygnus_mux: Cygnus IOMUX register description
+ * @is_configured: flag to indicate whether a mux setting has already been
+ * configured
+ */
+struct cygnus_mux_log {
+	struct cygnus_mux mux;
+	bool is_configured;
+};
+
+/*
+ * Group based IOMUX configuration
+ *
+ * @name: name of the group
+ * @pins: array of pins used by this group
+ * @num_pins: total number of pins used by this group
+ * @mux: Cygnus group based IOMUX configuration
+ */
+struct cygnus_pin_group {
+	const char *name;
Quoted 9 lines

drivers/pinctrl/bcm/pinctrl-cygnus-mux.c line 73

Quoted 3 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +3 -0
+	const unsigned *pins;
+	const unsigned num_pins;
+	const struct cygnus_mux mux;

Not: the last 2 consts are quite weird - if you want to make an instance of cygnus_pin_group immutable you declare it as a const (and I see you are already doing that below). With the structure as it laid out currently you can only do static initializers.

Right. I'll remove the last two const.

Quoted 16 lines

drivers/pinctrl/bcm/pinctrl-cygnus-mux.c line 86

Quoted 13 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +13 -0
+};
+
+/*
+ * Cygnus mux function and supported pin groups
+ *
+ * @name: name of the function
+ * @groups: array of groups that can be supported by this function
+ * @num_groups: total number of groups that can be supported by this function
+ */
+struct cygnus_pin_function {
+	const char *name;
+	const char * const *groups;
+	const unsigned num_groups;

Here as well.

Quoted 2 lines

...

Yes. Will remove the last const.

Quoted 8 lines

drivers/pinctrl/bcm/pinctrl-cygnus-mux.c line 160

Quoted 5 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +5 -0
+
+/*
+ * List of pins in Cygnus
+ */
+static struct cygnus_pin cygnus_pins[] = {

const?

I cannot make it const here, since the address of "gpio_mux" is later passed to pinctrl_pin_desc's private data:

pins[i].drv_data = &cygnus_pins[i].gpio_mux;

Quoted 5 lines
Quoted 2 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +2 -0
+	CYGNUS_PIN_DESC(0, "ext_device_reset_n", 0, 0, 0),
+	CYGNUS_PIN_DESC(1, "chip_mode0", 0, 0, 0),

...

Quoted 8 lines

drivers/pinctrl/bcm/pinctrl-cygnus-mux.c line 482

Quoted 3 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +3 -0
+#define CYGNUS_PIN_GROUP(group_name, off, sh, al)	\
+{							\
+	.name = #group_name"""_grp",			\

Why do we need extra pair of quotes? BTW we can also do

	.name = __stringify(group_name) "_grp",

Okay. I will change to use __stringify. Thanks.

Quoted 11 lines
Quoted 8 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +8 -0
+	.pins = group_name ## _pins,			\
+	.num_pins = ARRAY_SIZE(group_name ## _pins),	\
+	.mux = {					\
+		.offset = off,				\
+		.shift = sh,				\
+		.alt = al,				\
+	}						\
+}

...

Quoted 5 lines

drivers/pinctrl/bcm/pinctrl-cygnus-mux.c line 800

Quoted 2 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +2 -0
+
+static struct pinctrl_ops cygnus_pinctrl_ops = {

const?

Yes.

Quoted 108 lines
Quoted 108 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +108 -0
+	.get_groups_count = cygnus_get_groups_count,
+	.get_group_name = cygnus_get_group_name,
+	.get_group_pins = cygnus_get_group_pins,
+	.pin_dbg_show = cygnus_pin_dbg_show,
+	.dt_node_to_map = cygnus_dt_node_to_map,
+	.dt_free_map = pinctrl_utils_dt_free_map,
+};
+
+static int cygnus_get_functions_count(struct pinctrl_dev *pctrl_dev)
+{
+	struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+	return pinctrl->num_functions;
+}
+
+static const char *cygnus_get_function_name(struct pinctrl_dev *pctrl_dev,
+					    unsigned selector)
+{
+	struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+	return pinctrl->functions[selector].name;
+}
+
+static int cygnus_get_function_groups(struct pinctrl_dev *pctrl_dev,
+				      unsigned selector,
+				      const char * const **groups,
+				      unsigned * const num_groups)
+{
+	struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+
+	*groups = pinctrl->functions[selector].groups;
+	*num_groups = pinctrl->functions[selector].num_groups;
+
+	return 0;
+}
+
+static int cygnus_pinmux_set(struct cygnus_pinctrl *pinctrl,
+			     const struct cygnus_pin_function *func,
+			     const struct cygnus_pin_group *grp,
+			     struct cygnus_mux_log *mux_log)
+{
+	const struct cygnus_mux *mux = &grp->mux;
+	int i;
+	u32 val, mask = 0x7;
+	unsigned long flags;
+
+	for (i = 0; i < CYGNUS_NUM_IOMUX; i++) {
+		if (mux->offset != mux_log[i].mux.offset ||
+		    mux->shift != mux_log[i].mux.shift)
+			continue;
+
+		/* match found if we reach here */
+
+		/* if this is a new configuration, just do it! */
+		if (!mux_log[i].is_configured)
+			break;
+
+		/*
+		 * IOMUX has been configured previously and one is trying to
+		 * configure it to a different function
+		 */
+		if (mux_log[i].mux.alt != mux->alt) {
+			dev_err(pinctrl->dev,
+				"double configuration error detected!\n");
+			dev_err(pinctrl->dev, "func:%s grp:%s\n",
+				func->name, grp->name);
+			return -EINVAL;
+		} else {
+			/*
+			 * One tries to configure it to the same function.
+			 * Just quit and don't bother
+			 */
+			return 0;
+		}
+	}
+
+	mux_log[i].mux.alt = mux->alt;
+	mux_log[i].is_configured = true;
+
+	spin_lock_irqsave(&pinctrl->lock, flags);
+
+	val = readl(pinctrl->base0 + grp->mux.offset);
+	val &= ~(mask << grp->mux.shift);
+	val |= grp->mux.alt << grp->mux.shift;
+	writel(val, pinctrl->base0 + grp->mux.offset);
+
+	spin_unlock_irqrestore(&pinctrl->lock, flags);
+
+	return 0;
+}
+
+static int cygnus_pinmux_set_mux(struct pinctrl_dev *pctrl_dev,
+				 unsigned func_select, unsigned grp_select)
+{
+	struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+	const struct cygnus_pin_function *func =
+		&pinctrl->functions[func_select];
+	const struct cygnus_pin_group *grp = &pinctrl->groups[grp_select];
+
+	dev_dbg(pctrl_dev->dev, "func:%u name:%s grp:%u name:%s\n",
+		func_select, func->name, grp_select, grp->name);
+
+	dev_dbg(pctrl_dev->dev, "offset:0x%08x shift:%u alt:%u\n",
+		grp->mux.offset, grp->mux.shift, grp->mux.alt);
+
+	return cygnus_pinmux_set(pinctrl, func, grp, pinctrl->mux_log);
+}
+
Quoted 9 lines

drivers/pinctrl/bcm/pinctrl-cygnus-mux.c line 914

Quoted 6 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +6 -0
+static int cygnus_gpio_request_enable(struct pinctrl_dev *pctrl_dev,
+				      struct pinctrl_gpio_range *range,
+				      unsigned pin)
+{
+	struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+	struct cygnus_gpio_mux *mux = pctrl_dev->desc->pins[pin].drv_data;

const?

Yes.

Quoted 42 lines
Quoted 42 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +42 -0
+	u32 val;
+	unsigned long flags;
+
+	/* not all pins support GPIO pinmux override */
+	if (!mux->is_supported)
+		return -ENOTSUPP;
+
+	spin_lock_irqsave(&pinctrl->lock, flags);
+
+	val = readl(pinctrl->base1 + mux->offset);
+	val |= 0x3 << mux->shift;
+	writel(val, pinctrl->base1 + mux->offset);
+
+	spin_unlock_irqrestore(&pinctrl->lock, flags);
+
+	dev_dbg(pctrl_dev->dev,
+		"gpio request enable pin=%u offset=0x%x shift=%u\n",
+		pin, mux->offset, mux->shift);
+
+	return 0;
+}
+
+static void cygnus_gpio_disable_free(struct pinctrl_dev *pctrl_dev,
+				     struct pinctrl_gpio_range *range,
+				     unsigned pin)
+{
+	struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+	struct cygnus_gpio_mux *mux = pctrl_dev->desc->pins[pin].drv_data;
+	u32 val;
+	unsigned long flags;
+
+	if (!mux->is_supported)
+		return;
+
+	spin_lock_irqsave(&pinctrl->lock, flags);
+
+	val = readl(pinctrl->base1 + mux->offset);
+	val &= ~(0x3 << mux->shift);
+	writel(val, pinctrl->base1 + mux->offset);
+
+	spin_unlock_irqrestore(&pinctrl->lock, flags);
+
Quoted 9 lines

drivers/pinctrl/bcm/pinctrl-cygnus-mux.c line 962

Quoted 6 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +6 -0
+	dev_err(pctrl_dev->dev,
+		"gpio disable free pin=%u offset=0x%x shift=%u\n",
+		pin, mux->offset, mux->shift);
+}
+
+static struct pinmux_ops cygnus_pinmux_ops = {

const?

Yes.

Quoted 109 lines
Quoted 109 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +109 -0
+	.get_functions_count = cygnus_get_functions_count,
+	.get_function_name = cygnus_get_function_name,
+	.get_function_groups = cygnus_get_function_groups,
+	.set_mux = cygnus_pinmux_set_mux,
+	.gpio_request_enable = cygnus_gpio_request_enable,
+	.gpio_disable_free = cygnus_gpio_disable_free,
+};
+
+static struct pinctrl_desc cygnus_pinctrl_desc = {
+	.name = "cygnus-pinmux",
+	.pctlops = &cygnus_pinctrl_ops,
+	.pmxops = &cygnus_pinmux_ops,
+};
+
+static int cygnus_mux_log_init(struct cygnus_pinctrl *pinctrl)
+{
+	struct cygnus_mux_log *log;
+	unsigned int i, j;
+
+	pinctrl->mux_log = devm_kcalloc(pinctrl->dev, CYGNUS_NUM_IOMUX,
+					sizeof(struct cygnus_mux_log),
+					GFP_KERNEL);
+	if (!pinctrl->mux_log)
+		return -ENOMEM;
+
+	log = pinctrl->mux_log;
+	for (i = 0; i < CYGNUS_NUM_IOMUX_REGS; i++) {
+		for (j = 0; j < CYGNUS_NUM_MUX_PER_REG; j++) {
+			log = &pinctrl->mux_log[i * CYGNUS_NUM_MUX_PER_REG
+				+ j];
+			log->mux.offset = i * 4;
+			log->mux.shift = j * 4;
+			log->mux.alt = 0;
+			log->is_configured = false;
+		}
+	}
+
+	return 0;
+}
+
+static int cygnus_pinmux_probe(struct platform_device *pdev)
+{
+	struct cygnus_pinctrl *pinctrl;
+	struct resource *res;
+	int i, ret;
+	struct pinctrl_pin_desc *pins;
+	unsigned num_pins = ARRAY_SIZE(cygnus_pins);
+
+	pinctrl = devm_kzalloc(&pdev->dev, sizeof(*pinctrl), GFP_KERNEL);
+	if (!pinctrl)
+		return -ENOMEM;
+
+	pinctrl->dev = &pdev->dev;
+	platform_set_drvdata(pdev, pinctrl);
+	spin_lock_init(&pinctrl->lock);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	pinctrl->base0 = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(pinctrl->base0)) {
+		dev_err(&pdev->dev, "unable to map I/O space\n");
+		return PTR_ERR(pinctrl->base0);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	pinctrl->base1 = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(pinctrl->base1)) {
+		dev_err(&pdev->dev, "unable to map I/O space\n");
+		return PTR_ERR(pinctrl->base1);
+	}
+
+	ret = cygnus_mux_log_init(pinctrl);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to initialize IOMUX log\n");
+		return ret;
+	}
+
+	pins = devm_kcalloc(&pdev->dev, num_pins, sizeof(*pins), GFP_KERNEL);
+	if (!pins)
+		return -ENOMEM;
+
+	for (i = 0; i < num_pins; i++) {
+		pins[i].number = cygnus_pins[i].pin;
+		pins[i].name = cygnus_pins[i].name;
+		pins[i].drv_data = &cygnus_pins[i].gpio_mux;
+	}
+
+	pinctrl->groups = cygnus_pin_groups;
+	pinctrl->num_groups = ARRAY_SIZE(cygnus_pin_groups);
+	pinctrl->functions = cygnus_pin_functions;
+	pinctrl->num_functions = ARRAY_SIZE(cygnus_pin_functions);
+	cygnus_pinctrl_desc.pins = pins;
+	cygnus_pinctrl_desc.npins = num_pins;
+
+	pinctrl->pctl = pinctrl_register(&cygnus_pinctrl_desc, &pdev->dev,
+			pinctrl);
+	if (!pinctrl->pctl) {
+		dev_err(&pdev->dev, "unable to register Cygnus IOMUX pinctrl\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct of_device_id cygnus_pinmux_of_match[] = {
+	{ .compatible = "brcm,cygnus-pinmux" },
+	{ }
+};
+
+static struct platform_driver cygnus_pinmux_driver = {
Quoted 9 lines

drivers/pinctrl/bcm/pinctrl-cygnus-mux.c line 1076

Quoted 5 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +5 -0
+	.driver = {
+		.name = "cygnus-pinmux",
+		.of_match_table = cygnus_pinmux_of_match,
+	},
+	.probe = cygnus_pinmux_probe,

You also need to either provide remove() method or disallow unbinding via sysfs by setting suppress_bind_attrs in platform driver.

I do not expect this driver to ever be compiled as module and uninstalled at runtime. I'll add .suppress_bind_attrs = true, thanks!

Quoted 8 lines
Quoted 8 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +8 -0
+};
+
+static int __init cygnus_pinmux_init(void)
+{
+	return platform_driver_register(&cygnus_pinmux_driver);
+}
+arch_initcall(cygnus_pinmux_init);
+
Quoted 9 lines
Quoted 6 lines
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +3 -0
+MODULE_AUTHOR("Ray Jui <[email protected]>");
+MODULE_DESCRIPTION("Broadcom Cygnus IOMUX driver");
+MODULE_LICENSE("GPL v2");
Signature · 1 line
1.7.9.5

Thanks.

Thanks for the review!

Ray

#293 lore raw

Re: [PATCH v3 3/4] pinctrl: cygnus: add initial IOMUX driver support

Quoted 10 lines · Ray Jui
Quoted 9 lines · Dmitry Torokhov

drivers/pinctrl/bcm/pinctrl-cygnus-mux.c line 160

Quoted 5 lines · Ray Jui
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +5 -0
+
+/*
+ * List of pins in Cygnus
+ */
+static struct cygnus_pin cygnus_pins[] = {

const?

Quoted 4 lines

I cannot make it const here, since the address of "gpio_mux" is later passed to pinctrl_pin_desc's private data:

pins[i].drv_data = &cygnus_pins[i].gpio_mux;

The pinctrl code says:

"@drv_data: driver-defined per-pin data. pinctrl core does not touch this"

so we could theoretically cast away the constness and restore it when we access drv_data in pin control methods, but I won't insist. I am not sure which way looks nicer.

Thanks.

Signature · 1 line
Dmitry
author maintainer
#294 lore raw

Re: [PATCH v3 3/4] pinctrl: cygnus: add initial IOMUX driver support

Quoted 18 lines · Dmitry Torokhov
Quoted 10 lines · Ray Jui
Quoted 9 lines · Dmitry Torokhov

drivers/pinctrl/bcm/pinctrl-cygnus-mux.c line 160

Quoted 5 lines · Ray Jui
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +5 -0
+
+/*
+ * List of pins in Cygnus
+ */
+static struct cygnus_pin cygnus_pins[] = {

const?

Quoted 4 lines

I cannot make it const here, since the address of "gpio_mux" is later passed to pinctrl_pin_desc's private data:

pins[i].drv_data = &cygnus_pins[i].gpio_mux;

The pinctrl code says:

Quoted 9 lines

"@drv_data: driver-defined per-pin data. pinctrl core does not touch this"

so we could theoretically cast away the constness and restore it when we access drv_data in pin control methods, but I won't insist. I am not sure which way looks nicer.

Thanks.

Yeah, I agreed that by declaring the cygnus_pins array const, it makes it more obvious that "we do not expect any of its parameters to change." But later if we cast &gpio_mux to void * before assigned to drv_data of pinctrl_pin_desc, we sort of break it up...I'll keep this part of code as it is for now.

Thanks,

Ray

author maintainer
#295 lore raw

PATCH v3 4/4 ARM: dts: enable IOMUX for Broadcom Cygnus

In reply to Ray Jui

This enables the IOMUX support for Broadcom Cygnus SoC

---
 arch/arm/boot/dts/bcm-cygnus.dtsi |    6 ++++++
 1 file changed, 6 insertions(+)
arch/arm/boot/dts/bcm-cygnus.dtsi +6 -0
index 5126f9e..b014ce5 100644
@@ -54,6 +54,12 @@
 
 	/include/ "bcm-cygnus-clock.dtsi"
 
+	pinctrl: pinctrl@0x0301d0c8 {
+		compatible = "brcm,cygnus-pinmux";
+		reg = <0x0301d0c8 0x30>,
+		      <0x0301d24c 0x2c>;
+	};
+
 	amba {
 		#address-cells = <1>;
 		#size-cells = <1>;
Signature · 1 line
1.7.9.5
author maintainer
#296 lore raw

v4 cover Add common clock support for Broadcom iProc architecture

In reply to a message not in the archive

This patchset contains the initial common clock support for Broadcom's iProc family of SoCs. The iProc clock architecture comprises of various PLLs, e.g., ARMPLL, GENPLL, LCPLL0, MIPIPLL, and etc. An onboard crystal serves as the basic reference clock for these PLLs. Each PLL may have several leaf clocks. One special group of clocks is the ASIU clocks, which are dervied directly from the crystal reference clock.

This patchset also contains the basic clock support for the Broadcom Cygnus SoC, which implements the iProc clock architecture

Changes from v3:
- Fix incorrect use of passing in of_clk_src_onecell_get when adding ARM PLL and other iProc PLLs as clock provider. These PLLs have zero cells in DT and thefore of_clk_src_simple_get should be used instead
- Rename Cygnus MIPI PLL Channel 2 clock from BCM_CYGNUS_MIPIPLL_CH2_UNUSED to BCM_CYGNUS_MIPIPLL_CH2_V3D, since a 3D graphic rendering engine has been integrated into Cygnus revision B0 and has its core clock running off MIPI PLL Channel 2
- Changed default MIPI PLL VCO frequency from 1.75 GHz to 2.1 GHz. This allows us to derive 300 MHz V3D clock from channel 2 through the post divisor

Changes from v2:
- Re-arrange Cygnus clock/pll init functions so each init function is right next to its clock table
- Removed #defines for number of clocks in Cygnus. Have the number of clocks automatically determined based on array size of the clock table

Changes from v1:
- Separate drivers/clk/Makefile change for drivers/clk/bcm out to a standalone patch

Ray Jui (5):
clk: iproc: define Broadcom iProc clock binding
clk: iproc: add initial common clock support
clk: Change bcm clocks build dependency
clk: cygnus: add clock support for Broadcom Cygnus
ARM: dts: enable clock support for Broadcom Cygnus

 arch/arm/boot/dts/bcm-cygnus-clock.dtsi |  110 +++++--
 arch/arm/boot/dts/bcm-cygnus.dtsi       |    2 +-
 brcm,iproc-clocks.txt                   |  178 ++++++++++++
 drivers/clk/Makefile                    |    2 +-
 drivers/clk/bcm/Kconfig                 |    9 +
 drivers/clk/bcm/Makefile                |    2 +
 drivers/clk/bcm/clk-cygnus.c            |  277 ++++++++++++++++++
 drivers/clk/bcm/clk-iproc-armpll.c      |  282 ++++++++++++++++++
 drivers/clk/bcm/clk-iproc-asiu.c        |  275 ++++++++++++++++++
 drivers/clk/bcm/clk-iproc-clk.c         |  238 +++++++++++++++
 drivers/clk/bcm/clk-iproc-pll.c         |  478 +++++++++++++++++++++++++++++++
 drivers/clk/bcm/clk-iproc.h             |  155 ++++++++++
 include/dt-bindings/clock/bcm-cygnus.h  |   65 +++++
 13 files changed, 2046 insertions(+), 27 deletions(-)
 create mode 100644 brcm,iproc-clocks.txt
 create mode 100644 drivers/clk/bcm/clk-cygnus.c
 create mode 100644 drivers/clk/bcm/clk-iproc-armpll.c
 create mode 100644 drivers/clk/bcm/clk-iproc-asiu.c
 create mode 100644 drivers/clk/bcm/clk-iproc-clk.c
 create mode 100644 drivers/clk/bcm/clk-iproc-pll.c
 create mode 100644 drivers/clk/bcm/clk-iproc.h
 create mode 100644 include/dt-bindings/clock/bcm-cygnus.h
Signature · 1 line
1.7.9.5
author maintainer
#297 lore raw

PATCH v4 1/5 clk: iproc: define Broadcom iProc clock binding

Document the device tree binding for Broadcom iProc architecture based clock controller

---
 brcm,iproc-clocks.txt |  178 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 178 insertions(+)
 create mode 100644 brcm,iproc-clocks.txt
brcm,iproc-clocks.txt new file +178 -0
new file mode 100644
index 0000000..cc64fd2
@@ -0,0 +1,178 @@
+Broadcom iProc Family Clocks
+
+This binding uses the common clock binding:
+    Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+The iProc clock controller manages clocks that are common to the iProc family.
+An SoC from the iProc family may have several PPLs, e.g., ARMPLL, GENPLL,
+LCPLL0, MIPIPLL, and etc., all derived from an onboard crystal. Each PLL
+comprises of several leaf clocks
+
+Required properties for PLLs:
+- compatible:
+    Should have a value of the form "brcm,<soc>-<pll>". For example, GENPLL on
+Cygnus has a compatible string of "brcm,cygnus-genpll"
+
+- #clock-cells:
+    Must be <0>
+
+- reg:
+    Define the base and range of the I/O address space that contain the iProc
+clock control registers required for the PLL
+
+- clocks:
+    The input parent clock phandle for the PLL. For all iProc PLLs, this is an
+onboard crystal with a fixed rate
+
+Optional Properties for PLLs:
+- clock-frequency:
+    PLL frequency in Hz. If specified, PLL will be configured to run at
+<clock-frequency> instead of the default frequency after chip reset, provided
+that <clock-frequency> and its parameters are defined in the SoC specific
+frequency parameter table
+
+Example:
+
+	osc: oscillator {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <25000000>;
+	};
+
+	genpll: genpll {
+		#clock-cells = <0>;
+		compatible = "brcm,cygnus-genpll";
+		reg = <0x0301d000 0x2c>,
+			<0x0301c020 0x4>;
+		clocks = <&osc>;
+	};
+
+Required properties for leaf clocks of a PLL:
+
+- compatible:
+    Should have a value of the form "brcm,<soc>-<pll>-clk". For example, leaf
+clocks derived from the GENPLL on Cygnus SoC have a compatible string of
+"brcm,cygnus-genpll-clk"
+
+- #clock-cells:
+    Have a value of <1> since there are more than 1 leaf clock of a
+given PLL
+
+- reg:
+    Define the base and range of the I/O address space that contain the iProc
+clock control registers required for the PLL leaf clocks
+
+- clocks:
+    The input parent PLL phandle for the leaf clock
+
+- clock-output-names:
+    An ordered list of strings defining the names of the leaf clocks
+
+Example:
+
+	genpll: genpll {
+		#clock-cells = <0>;
+		compatible = "brcm,cygnus-genpll";
+		reg = <0x0301d000 0x2c>,
+			<0x0301c020 0x4>;
+		clocks = <&osc>;
+	};
+
+	genpll_clks: genpll_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-genpll-clk";
+		reg = <0x0301d000 0x2c>;
+		clocks = <&genpll>;
+		clock-output-names = "axi21", "250mhz", "ihost_sys",
+			"enet_sw", "audio_125", "can";
+	};
+
+Required properties for ASIU clocks:
+
+ASIU clocks are a special case. These clocks are derived directly from the
+reference clock of the onboard crystal
+
+- compatible:
+    Should have a value of the form "brcm,<soc>-asiu-clk". For example, ASIU
+clocks for Cygnus have a compatible string of "brcm,cygnus-asiu-clk"
+
+- #clock-cells:
+    Have a value of <1> since there are more than 1 ASIU clocks
+
+- reg:
+    Define the base and range of the I/O address space that contain the iProc
+clock control registers required for ASIU clocks
+
+- clocks:
+    The input parent clock phandle for the ASIU clock, i.e., the onboard
+crystal
+
+- clock-output-names:
+    An ordered list of strings defining the names of the ASIU clocks
+
+Example:
+
+	osc: oscillator {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <25000000>;
+	};
+
+	asiu_clks: asiu_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-asiu-clk";
+		reg = <0x0301d048 0xc>,
+			<0x180aa024 0x4>;
+		clocks = <&osc>;
+		clock-output-names = "keypad", "adc/touch", "pwm";
+	};
+
+Cygnus
+------
+PLL and leaf clock compatible strings for Cygnus are:
+    "brcm,cygnus-armpll"
+    "brcm,cygnus-genpll"
+    "brcm,cygnus-lcpll0"
+    "brcm,cygnus-mipipll"
+    "brcm,cygnus-genpll-clk"
+    "brcm,cygnus-lcpll0-clk"
+    "brcm,cygnus-mipipll-clk"
+    "brcm,cygnus-asiu-clk"
+
+The following table defines the set of PLL/clock index and ID for Cygnus.
+These clock IDs are defined in:
+    "include/dt-bindings/clock/bcm-cygnus.h"
+
+    Clock      Source           Index   ID
+    ---        -----            -----   ---------
+    crystal    N/A              N/A     N/A
+
+    armpll     crystal          N/A     N/A
+    genpll     crystal          N/A     N/A
+    lcpll0     crystal          N/A     N/A
+    mipipll    crystal          N/A     N/A
+
+    keypad     crystal (ASIU)   0       BCM_CYGNUS_ASIU_KEYPAD_CLK
+    adc/tsc    crystal (ASIU)   1       BCM_CYGNUS_ASIU_ADC_CLK
+    pwm        crystal (ASIU)   2       BCM_CYGNUS_ASIU_PWM_CLK
+
+    axi21      genpll           0       BCM_CYGNUS_GENPLL_AXI21_CLK
+    250mhz     genpll           1       BCM_CYGNUS_GENPLL_250MHZ_CLK
+    ihost_sys  genpll           2       BCM_CYGNUS_GENPLL_IHOST_SYS_CLK
+    enet_sw    genpll           3       BCM_CYGNUS_GENPLL_ENET_SW_CLK
+    audio_125  genpll           4       BCM_CYGNUS_GENPLL_AUDIO_125_CLK
+    can        genpll           5       BCM_CYGNUS_GENPLL_CAN_CLK
+
+    pcie_phy   lcpll0           0       BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK
+    ddr_phy    lcpll0           1       BCM_CYGNUS_LCPLL0_DDR_PHY_CLK
+    sdio       lcpll0           2       BCM_CYGNUS_LCPLL0_SDIO_CLK
+    usb_phy    lcpll0           3       BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK
+    smart_card lcpll0           4       BCM_CYGNUS_LCPLL0_SMART_CARD_CLK
+    ch5        lcpll0           5       BCM_CYGNUS_LCPLL0_CH5_UNUSED
+
+    ch0_unused mipipll          0       BCM_CYGNUS_MIPIPLL_CH0_UNUSED
+    ch1_lcd    mipipll          1       BCM_CYGNUS_MIPIPLL_CH1_LCD
+    ch2_unused mipipll          2       BCM_CYGNUS_MIPIPLL_CH2_UNUSED
+    ch3_unused mipipll          3       BCM_CYGNUS_MIPIPLL_CH3_UNUSED
+    ch4_unused mipipll          4       BCM_CYGNUS_MIPIPLL_CH4_UNUSED
+    ch5_unused mipipll          5       BCM_CYGNUS_MIPIPLL_CH5_UNUSED
Signature · 1 line
1.7.9.5
author maintainer
#298 lore raw

PATCH v4 2/5 clk: iproc: add initial common clock support

In reply to Ray Jui

This adds basic and generic support for various iProc PLLs and clocks including the ARMPLL, GENPLL, LCPLL, MIPIPLL, and ASIU clocks.

SoCs under the iProc architecture can define their specific register offsets and clock parameters for their PLL and clock controllers. These parameters can be passed as arugments into the generic iProc PLL and clock setup functions

Derived from code originally provided by Jonathan Richardson <[email protected]>

---
 drivers/clk/bcm/Kconfig            |    9 +
 drivers/clk/bcm/Makefile           |    1 +
 drivers/clk/bcm/clk-iproc-armpll.c |  282 +++++++++++++++++++++
 drivers/clk/bcm/clk-iproc-asiu.c   |  275 +++++++++++++++++++++
 drivers/clk/bcm/clk-iproc-clk.c    |  238 ++++++++++++++++++
 drivers/clk/bcm/clk-iproc-pll.c    |  478 ++++++++++++++++++++++++++++++++++++
 drivers/clk/bcm/clk-iproc.h        |  155 ++++++++++++
 7 files changed, 1438 insertions(+)
 create mode 100644 drivers/clk/bcm/clk-iproc-armpll.c
 create mode 100644 drivers/clk/bcm/clk-iproc-asiu.c
 create mode 100644 drivers/clk/bcm/clk-iproc-clk.c
 create mode 100644 drivers/clk/bcm/clk-iproc-pll.c
 create mode 100644 drivers/clk/bcm/clk-iproc.h
drivers/clk/bcm/Kconfig +9 -0
index 75506e5..66b5b7f 100644
@@ -7,3 +7,12 @@ config CLK_BCM_KONA
 	  Enable common clock framework support for Broadcom SoCs
 	  using "Kona" style clock control units, including those
 	  in the BCM281xx and BCM21664 families.
+
+config COMMON_CLK_IPROC
+	bool "Broadcom iProc clock support"
+	depends on ARCH_BCM_IPROC
+	depends on COMMON_CLK
+	default y
+	help
+	  Enable common clock framework support for Broadcom SoCs
+	  based on the "iProc" architecture
drivers/clk/bcm/Makefile +1 -0
index 6297d05..6926636 100644
@@ -2,3 +2,4 @@ obj-$(CONFIG_CLK_BCM_KONA)	+= clk-kona.o
 obj-$(CONFIG_CLK_BCM_KONA)	+= clk-kona-setup.o
 obj-$(CONFIG_CLK_BCM_KONA)	+= clk-bcm281xx.o
 obj-$(CONFIG_CLK_BCM_KONA)	+= clk-bcm21664.o
+obj-$(CONFIG_COMMON_CLK_IPROC)	+= clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-clk.o clk-iproc-asiu.o
drivers/clk/bcm/clk-iproc-armpll.c new file +282 -0

282 lines not on this page · raw message

drivers/clk/bcm/clk-iproc-asiu.c new file +275 -0

275 lines not on this page · raw message

drivers/clk/bcm/clk-iproc-clk.c new file +238 -0

238 lines not on this page · raw message

drivers/clk/bcm/clk-iproc-pll.c new file +478 -0

478 lines not on this page · raw message

drivers/clk/bcm/clk-iproc.h new file +155 -0

155 lines not on this page · raw message

Signature · 1 line
1.7.9.5
#299 lore raw

Re: [PATCH v4 2/5] clk: iproc: add initial common clock support

Quoted 7 lines · Ray Jui
drivers/clk/bcm/clk-iproc-pll.c new file +7 -0
+/*
+ * Get the clock rate based on name
+ */
+static unsigned long __get_rate(const char *clk_name)
+{
+	struct clk *clk;
+

drivers/clk/bcm/clk-iproc-pll.c line 96

Quoted 10 lines
drivers/clk/bcm/clk-iproc-pll.c new file +10 -0
+	clk = __clk_lookup(clk_name);
+	if (!clk) {
+		pr_err("%s: unable to find clock by name: %s\n", __func__,
+				clk_name);
+		return 0;
+	}
+
+	return clk_get_rate(clk);
+}
+

This looks like something we should be providing in the core framework. Care to make it into an of_clk_get_parent_rate() API?

drivers/clk/bcm/clk-iproc.h line 153

Quoted 10 lines
drivers/clk/bcm/clk-iproc.h new file +10 -0
+extern void __init iproc_armpll_setup(struct device_node *node);
+extern void __init iproc_pll_setup(struct device_node *node,
+		const struct iproc_pll_ctrl *ctrl,
+		const struct iproc_pll_vco_freq_param *vco_param,
+		unsigned int num_freqs);
+extern void __init iproc_clk_setup(struct device_node *node,
+		const struct iproc_clk_ctrl *ctrl, unsigned int num_clks);
+extern void __init iproc_asiu_setup(struct device_node *node,
+		const struct iproc_asiu_div *div,
+		const struct iproc_asiu_gate *gate, unsigned int num_clks);

__init is not necessary in header files.

Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
author maintainer
#300 lore raw

Re: [PATCH v4 2/5] clk: iproc: add initial common clock support

Quoted 13 lines · Stephen Boyd
Quoted 12 lines · Ray Jui
drivers/clk/bcm/clk-iproc-pll.c new file +12 -0
+/*
+ * Get the clock rate based on name
+ */
+static unsigned long __get_rate(const char *clk_name)
+{
+	struct clk *clk;
+
+	clk = __clk_lookup(clk_name);
+	if (!clk) {
+		pr_err("%s: unable to find clock by name: %s\n", __func__,
+				clk_name);
+		return 0;
Quoted 9 lines

drivers/clk/bcm/clk-iproc-pll.c line 96

Quoted 5 lines
drivers/clk/bcm/clk-iproc-pll.c new file +5 -0
+	}
+
+	return clk_get_rate(clk);
+}
+

This looks like something we should be providing in the core framework. Care to make it into an of_clk_get_parent_rate() API?

I would love to! Note __get_rate here is really getting the clock rate with a name provided. Can you be more specific on what you want? If I'm not mistaken, what you really want is this?

unsigned long of_clk_get_parent_rate(struct device_node *np, int index);

Quoted 4 lines
Quoted 4 lines
drivers/clk/bcm/clk-iproc.h new file +4 -0
+extern void __init iproc_armpll_setup(struct device_node *node);
+extern void __init iproc_pll_setup(struct device_node *node,
+		const struct iproc_pll_ctrl *ctrl,
+		const struct iproc_pll_vco_freq_param *vco_param,
Quoted 9 lines

drivers/clk/bcm/clk-iproc.h line 153

Quoted 6 lines
drivers/clk/bcm/clk-iproc.h new file +6 -0
+		unsigned int num_freqs);
+extern void __init iproc_clk_setup(struct device_node *node,
+		const struct iproc_clk_ctrl *ctrl, unsigned int num_clks);
+extern void __init iproc_asiu_setup(struct device_node *node,
+		const struct iproc_asiu_div *div,
+		const struct iproc_asiu_gate *gate, unsigned int num_clks);

__init is not necessary in header files.

Okay I'll remove __init. Thanks.

Ray

#301 lore raw

Re: [PATCH v4 2/5] clk: iproc: add initial common clock support

Quoted 23 lines · Ray Jui
Quoted 12 lines · Stephen Boyd
Quoted 11 lines · Ray Jui
drivers/clk/bcm/clk-iproc-pll.c new file +11 -0
+/*
+ * Get the clock rate based on name
+ */
+static unsigned long __get_rate(const char *clk_name)
+{
+	struct clk *clk;
+
+	clk = __clk_lookup(clk_name);
+	if (!clk) {
+		pr_err("%s: unable to find clock by name: %s\n", __func__,
+				clk_name);
Quoted 9 lines

drivers/clk/bcm/clk-iproc-pll.c line 96

Quoted 6 lines
drivers/clk/bcm/clk-iproc-pll.c new file +6 -0
+		return 0;
+	}
+
+	return clk_get_rate(clk);
+}
+

This looks like something we should be providing in the core framework. Care to make it into an of_clk_get_parent_rate() API?

Quoted 5 lines

I would love to! Note __get_rate here is really getting the clock rate with a name provided. Can you be more specific on what you want? If I'm not mistaken, what you really want is this?

unsigned long of_clk_get_parent_rate(struct device_node *np, int index);

Yes that looks good.

Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
author maintainer
#302 lore raw

PATCH v4 3/5 clk: Change bcm clocks build dependency

In reply to Ray Jui

The clock code under drivers/clk/bcm now contains code for both the Broadcom mobile SoCs and the iProc SoCs. Change the the makefile dependency to be under config flag CONFIG_ARCH_BCM that's enabled for both families of SoCs

---
 drivers/clk/Makefile |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/clk/Makefile +1 -1
index d5fba5b..eff0213 100644
@@ -41,7 +41,7 @@ obj-$(CONFIG_ARCH_VT8500)		+= clk-vt8500.o
 obj-$(CONFIG_COMMON_CLK_WM831X)		+= clk-wm831x.o
 obj-$(CONFIG_COMMON_CLK_XGENE)		+= clk-xgene.o
 obj-$(CONFIG_COMMON_CLK_AT91)		+= at91/
-obj-$(CONFIG_ARCH_BCM_MOBILE)		+= bcm/
+obj-$(CONFIG_ARCH_BCM)			+= bcm/
 obj-$(CONFIG_ARCH_BERLIN)		+= berlin/
 obj-$(CONFIG_ARCH_HI3xxx)		+= hisilicon/
 obj-$(CONFIG_ARCH_HIP04)		+= hisilicon/
Signature · 1 line
1.7.9.5
author maintainer
#303 lore raw

PATCH v4 4/5 clk: cygnus: add clock support for Broadcom Cygnus

In reply to Ray Jui

The Broadcom Cygnus SoC is architected under the iProc architecture. It has the following PLLs: ARMPLL, GENPLL, LCPLL0, MIPIPLL, all dervied from an onboard crystal. Cygnus also has various ASIU clocks that are derived directly from the onboard crystal.

---
 drivers/clk/bcm/Makefile               |    1 +
 drivers/clk/bcm/clk-cygnus.c           |  277 ++++++++++++++++++++++++++++++++
 include/dt-bindings/clock/bcm-cygnus.h |   65 ++++++++
 3 files changed, 343 insertions(+)
 create mode 100644 drivers/clk/bcm/clk-cygnus.c
 create mode 100644 include/dt-bindings/clock/bcm-cygnus.h
drivers/clk/bcm/Makefile +1 -0
index 6926636..afcbe55 100644
@@ -3,3 +3,4 @@ obj-$(CONFIG_CLK_BCM_KONA)	+= clk-kona-setup.o
 obj-$(CONFIG_CLK_BCM_KONA)	+= clk-bcm281xx.o
 obj-$(CONFIG_CLK_BCM_KONA)	+= clk-bcm21664.o
 obj-$(CONFIG_COMMON_CLK_IPROC)	+= clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-clk.o clk-iproc-asiu.o
+obj-$(CONFIG_ARCH_BCM_CYGNUS)	+= clk-cygnus.o
drivers/clk/bcm/clk-cygnus.c new file +277 -0

277 lines not on this page · raw message

include/dt-bindings/clock/bcm-cygnus.h new file +65 -0

65 lines not on this page · raw message

Signature · 1 line
1.7.9.5
author maintainer
#304 lore raw

PATCH v4 5/5 ARM: dts: enable clock support for Broadcom Cygnus

In reply to Ray Jui

Replace current device tree dummy clocks with real clock support for Broadcom Cygnus SoC

---
 arch/arm/boot/dts/bcm-cygnus-clock.dtsi |  110 ++++++++++++++++++++++++-------
 arch/arm/boot/dts/bcm-cygnus.dtsi       |    2 +-
 2 files changed, 86 insertions(+), 26 deletions(-)
arch/arm/boot/dts/bcm-cygnus-clock.dtsi +85 -25
index 60d8389..abb8a3f 100644
@@ -36,56 +36,116 @@ clocks {
 	ranges;
 
 	osc: oscillator {
+		#clock-cells = <0>;
 		compatible = "fixed-clock";
-		#clock-cells = <1>;
 		clock-frequency = <25000000>;
 	};
 
-	apb_clk: apb_clk {
-		compatible = "fixed-clock";
+	/* Cygnus ARM PLL */
+	armpll: armpll {
 		#clock-cells = <0>;
-		clock-frequency = <1000000000>;
+		compatible = "brcm,cygnus-armpll";
+		clocks = <&osc>;
+		reg = <0x19000000 0x1000>;
 	};
 
-	periph_clk: periph_clk {
-		compatible = "fixed-clock";
+	/* peripheral clock for system timer */
+	arm_periph_clk: arm_periph_clk {
 		#clock-cells = <0>;
-		clock-frequency = <500000000>;
+		compatible = "fixed-factor-clock";
+		clocks = <&armpll>;
+		clock-div = <2>;
+		clock-mult = <1>;
 	};
 
-	sdio_clk: lcpll_ch2 {
-		compatible = "fixed-clock";
+	/* APB bus clock */
+	apb_clk: apb_clk {
 		#clock-cells = <0>;
-		clock-frequency = <200000000>;
+		compatible = "fixed-factor-clock";
+		clocks = <&armpll>;
+		clock-div = <4>;
+		clock-mult = <1>;
 	};
 
-	axi81_clk: axi81_clk {
-		compatible = "fixed-clock";
+	genpll: genpll {
 		#clock-cells = <0>;
-		clock-frequency = <100000000>;
+		compatible = "brcm,cygnus-genpll";
+		reg = <0x0301d000 0x2c>,
+			<0x0301c020 0x4>;
+		clocks = <&osc>;
 	};
 
-	keypad_clk: keypad_clk {
-		compatible = "fixed-clock";
+	/* various clocks running off the GENPLL */
+	genpll_clks: genpll_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-genpll-clk";
+		reg = <0x0301d000 0x2c>;
+		clocks = <&genpll>;
+		clock-output-names = "axi21", "250mhz", "ihost_sys",
+			"enet_sw", "audio_125", "can";
+	};
+
+	/* always 1/2 of the axi21 clock */
+	axi41_clk: axi41_clk {
 		#clock-cells = <0>;
-		clock-frequency = <31806>;
+		compatible = "fixed-factor-clock";
+		clocks = <&genpll_clks 0>;
+		clock-div = <2>;
+		clock-mult = <1>;
 	};
 
-	adc_clk: adc_clk {
-		compatible = "fixed-clock";
+	/* always 1/4 of the axi21 clock */
+	axi81_clk: axi81_clk {
 		#clock-cells = <0>;
-		clock-frequency = <1562500>;
+		compatible = "fixed-factor-clock";
+		clocks = <&genpll_clks 0>;
+		clock-div = <4>;
+		clock-mult = <1>;
 	};
 
-	pwm_clk: pwm_clk {
-		compatible = "fixed-clock";
+	lcpll0: lcpll0 {
 		#clock-cells = <0>;
-		clock-frequency = <1000000>;
+		compatible = "brcm,cygnus-lcpll0";
+		reg = <0x0301d02c 0x1c>,
+			<0x0301c020 0x4>;
+		clocks = <&osc>;
 	};
 
-	lcd_clk: mipipll_ch1 {
-		compatible = "fixed-clock";
+	/* various clocks running off the LCPLL0 */
+	lcpll0_clks: lcpll0_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-lcpll0-clk";
+		reg = <0x0301d02c 0x1c>;
+		clocks = <&lcpll0>;
+		clock-output-names = "pcie_phy", "ddr_phy", "sdio",
+			"usb_phy", "smart_card", "ch5";
+	};
+
+	mipipll: mipipll {
 		#clock-cells = <0>;
-		clock-frequency = <100000000>;
+		compatible = "brcm,cygnus-mipipll";
+		reg = <0x180a9800 0x2c>,
+			<0x0301c020 0x4>,
+			<0x180aa024 0x4>;
+		clock-frequency = <2100000000>;
+		clocks = <&osc>;
+	};
+
+	mipipll_clks: mipipll_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-mipipll-clk";
+		reg = <0x180a9800 0x2c>;
+		clocks = <&mipipll>;
+		clock-output-names = "ch0_unused", "ch1_lcd", "ch2_unused",
+			"ch3_unused", "ch4_unused", "ch5_unused";
+	};
+
+	asiu_clks: asiu_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-asiu-clk";
+		reg = <0x0301d048 0xc>,
+			<0x180aa024 0x4>;
+		clocks = <&osc>;
+		clock-output-names = "keypad", "adc/touch", "pwm";
 	};
 };
arch/arm/boot/dts/bcm-cygnus.dtsi +1 -1
index 5126f9e..2b99e9c 100644
@@ -134,7 +134,7 @@
 		compatible = "arm,cortex-a9-global-timer";
 		reg = <0x19020200 0x100>;
 		interrupts = <GIC_PPI 11 IRQ_TYPE_LEVEL_HIGH>;
-		clocks = <&periph_clk>;
+		clocks = <&arm_periph_clk>;
 	};
 
 };
Signature · 1 line
1.7.9.5
author maintainer
#305 lore raw

v7 cover Add gpio/pinconf support to Broadcom Cygnus SoC

In reply to a message not in the archive

This patchset contains the initial GPIO/PINCONF support for the Broadcom Cygnus SoC.

Cygnus has 3 GPIO controllers: 1) the ASIU GPIO; 2) the chipCommonG GPIO; and 3) the ALWAYS-ON GPIO. All 3 types of GPIO controllers are supported by the this driver.

All 3 Cygnus GPIO controllers support basic PINCONF functions such as bias pull up, pull down, and drive strength configurations, when these pins are muxed to GPIO.

Pins from the ASIU GPIO can be individually muxed to GPIO function, through interaction with the Cygnus IOMUX controller.

Note this patchset has a dependency on the other patchset "Add pinctrl support to Broadcom Cygnus SoC" that is also under review

Changes from v6:
- Move the driver from drivers/gpio/* to drivers/pinctrl/* since this driver supports both GPIO and some basic PINCONF features
- Support PINCONF features through standard DT subnodes properties including "bias-disable", "bias-pull-up", "bias-pull-down", and "drive-strength", by creating local PINCONF controller
- Add support to allow individual ASIU GPIO pins to be muxed as GPIO, through interactions with the Cygnus IOMUX driver
- Convert the driver to use standard GPIOCHIP_IRQ APIs. This helps to reduce customized code in the driver
- Other miscellaneous imrpovements in the driver
- Enable GPIO based phone hook detection support for BCM911360 phone factor board

Changes from v5:
- Get rid of DT property "linux,gpio-base". Use dynamic allocation for GPIO base number

Changes from v4:
- Use DT property "linux,gpio-base" to define GPIO base number
- factorize common code to improve code readability and reduce code size
- remove "bcm_" prefix on function and struct names
- improve debugging prints
- default GPIO_BCM_CYGNUS to y in Kconfig (it still depends on ARCH_BCM_CYGNUS). This way we do not need to select it from the arch/arm/mach-bcm/Kconfig
- Get rid of redundant MAINTAINER entry for this driver. It will be maintained by Broadcom iProc/Cygnus maintainers
- Update device tree document based on driver changes

Changes from v3:
- Fix dt property tpyo
- Fix incorrect GPIO compatible ID in device tree binding document example

Changes from v2:
- Consolidate different compatible IDs into "brcm,cygnus-gpio"
- Get rid of redundant "no-interrupt" property

Changes from v1:
- Get rid of inline qualifier
- Get rid of redundant check in the ISR
- Other minor fixes to imrove code readability

Ray Jui (4):
pinctrl: Cygnus: define Broadcom Cygnus GPIO/PINCONF binding
pinctrl: cygnus: add gpio/pinconf driver
ARM: dts: enable GPIO for Broadcom Cygnus
ARM: dts: cygnus: enable GPIO based hook detection

 .../bindings/pinctrl/brcm,cygnus-gpio.txt          |  102 +++
 arch/arm/boot/dts/bcm-cygnus.dtsi                  |   33 +
 arch/arm/boot/dts/bcm911360_entphn.dts             |   13 +
 drivers/pinctrl/bcm/Kconfig                        |   22 +
 drivers/pinctrl/bcm/Makefile                       |    1 +
 drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c          |  920 ++++++++++++++++++++
 6 files changed, 1091 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-gpio.txt
 create mode 100644 drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c
Signature · 1 line
1.7.9.5
author maintainer
#306 lore raw

PATCH v7 1/4 pinctrl: Cygnus: define Broadcom Cygnus GPIO/PINCONF binding

Document the GPIO/PINCONF device tree binding for Broadcom Cygnus SoC

---
 .../bindings/pinctrl/brcm,cygnus-gpio.txt          |  102 ++++++++++++++++++++
 1 file changed, 102 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-gpio.txt
Documentation/devicetree/bindings/pinctrl/brcm,cygnus-gpio.txt new file +102 -0
new file mode 100644
index 0000000..9b9196c
@@ -0,0 +1,102 @@
+Broadcom Cygnus GPIO/PINCONF Controller
+
+Required properties:
+
+- compatible:
+    Must be "brcm,cygnus-gpio"
+
+- reg:
+    Define the base and range of the I/O address space that contains the Cygnus
+GPIO/PINCONF controller registers
+
+- ngpios:
+    Total number of GPIOs the controller provides
+
+- #gpio-cells:
+    Must be two. The first cell is the GPIO pin number (within the
+controller's pin space) and the second cell is used for the following:
+    bit[0]: polarity (0 for active high and 1 for active low)
+
+- gpio-controller:
+    Specifies that the node is a GPIO controller
+
+Optional properties:
+
+- interrupts:
+    Interrupt ID
+
+- interrupt-controller:
+    Specifies that the node is an interrupt controller
+
+- pinmux:
+    Specifies the phandle to the IOMUX device, where pins can be individually
+muxed to GPIO
+
+Supported generic PINCONF properties in child nodes:
+
+- pins:
+    The list of pins (within the controller's own pin space) that properties
+in the node apply to. Pin names are "gpio-<pin>"
+
+- bias-disable:
+    Disable pin bias
+
+- bias-pull-up:
+    Enable internal pull up resistor
+
+- bias-pull-down:
+    Enable internal pull down resistor
+
+- drive-strength:
+    Valid drive strength values include 2, 4, 6, 8, 10, 12, 14, 16 (mA)
+
+Example:
+	gpio_ccm: gpio@1800a000 {
+		compatible = "brcm,cygnus-gpio";
+		reg = <0x1800a000 0x50>,
+		      <0x0301d164 0x20>;
+		ngpios = <24>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-controller;
+
+		touch_pins: touch_pins {
+			pwr: pwr {
+				pins = "gpio-0";
+				drive-strength = <16>;
+			};
+
+			event: event {
+				pins = "gpio-1";
+				bias-pull-up;
+			};
+		};
+	};
+
+	gpio_asiu: gpio@180a5000 {
+		compatible = "brcm,cygnus-gpio";
+		reg = <0x180a5000 0x668>;
+		ngpios = <146>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-controller;
+	};
+
+	/*
+	 * Touchscreen that uses the CCM GPIO 0 and 1
+	 */
+	tsc {
+		...
+		...
+		gpio-pwr = <&gpio_ccm 0 0>;
+		gpio-event = <&gpio_ccm 1 0>;
+	};
+
+	/* Bluetooth that uses the ASIU GPIO 5, with polarity inverted */
+	bluetooth {
+		...
+		...
+		bcm,rfkill-bank-sel = <&gpio_asiu 5 1>
+	}
Signature · 1 line
1.7.9.5
author maintainer
#307 lore raw

PATCH v7 2/4 pinctrl: cygnus: add gpio/pinconf driver

In reply to Ray Jui

This adds the initial support of the Broadcom Cygnus GPIO/PINCONF driver that supports all 3 GPIO controllers on Cygnus including the ASIU GPIO controller, the chipCommonG GPIO controller, and the always-on GPIO controller. Basic PINCONF configurations such as bias pull up/down, and drive strength are also supported in this driver.

Pins from the ASIU GPIO controller can be individually muxed to GPIO function, through interaction with the Cygnus IOMUX controller

---
 drivers/pinctrl/bcm/Kconfig               |   22 +
 drivers/pinctrl/bcm/Makefile              |    1 +
 drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c |  920 +++++++++++++++++++++++++++++
 3 files changed, 943 insertions(+)
 create mode 100644 drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c
drivers/pinctrl/bcm/Kconfig +22 -0
index eb13201..cd11d4d 100644
@@ -20,6 +20,28 @@ config PINCTRL_BCM2835
 	select PINMUX
 	select PINCONF
 
+config PINCTRL_CYGNUS_GPIO
+	bool "Broadcom Cygnus GPIO (with PINCONF) driver"
+	depends on OF_GPIO && ARCH_BCM_CYGNUS
+	select GPIOLIB_IRQCHIP
+	select PINCONF
+	select GENERIC_PINCONF
+	default ARCH_BCM_CYGNUS
+	help
+	  Say yes here to enable the Broadcom Cygnus GPIO driver.
+
+	  The Broadcom Cygnus SoC has 3 GPIO controllers including the ASIU
+	  GPIO controller (ASIU), the chipCommonG GPIO controller (CCM), and
+	  the always-ON GPIO controller (CRMU/AON). All 3 GPIO controllers are
+	  supported by this driver.
+
+	  All 3 Cygnus GPIO controllers support basic PINCONF functions such
+	  as bias pull up, pull down, and drive strength configurations, when
+	  these pins are muxed to GPIO.
+
+	  Pins from the ASIU GPIO can be individually muxed to GPIO function,
+	  through interaction with the Cygnus IOMUX controller.
+
 config PINCTRL_CYGNUS_MUX
 	bool "Broadcom Cygnus IOMUX driver"
 	depends on (ARCH_BCM_CYGNUS || COMPILE_TEST)
drivers/pinctrl/bcm/Makefile +1 -0
index bb6beb6..2b2f70e 100644
@@ -2,4 +2,5 @@
 
 obj-$(CONFIG_PINCTRL_BCM281XX)		+= pinctrl-bcm281xx.o
 obj-$(CONFIG_PINCTRL_BCM2835)		+= pinctrl-bcm2835.o
+obj-$(CONFIG_PINCTRL_CYGNUS_GPIO)	+= pinctrl-cygnus-gpio.o
 obj-$(CONFIG_PINCTRL_CYGNUS_MUX)	+= pinctrl-cygnus-mux.o
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +920 -0

920 lines not on this page · raw message

Signature · 1 line
1.7.9.5
#308 lore raw

Re: [PATCH v7 2/4] pinctrl: cygnus: add gpio/pinconf driver

Quoted 153 lines · Ray Jui

This adds the initial support of the Broadcom Cygnus GPIO/PINCONF driver that supports all 3 GPIO controllers on Cygnus including the ASIU GPIO controller, the chipCommonG GPIO controller, and the always-on GPIO controller. Basic PINCONF configurations such as bias pull up/down, and drive strength are also supported in this driver.

Pins from the ASIU GPIO controller can be individually muxed to GPIO function, through interaction with the Cygnus IOMUX controller

---
 drivers/pinctrl/bcm/Kconfig               |   22 +
 drivers/pinctrl/bcm/Makefile              |    1 +
 drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c |  920 +++++++++++++++++++++++++++++
 3 files changed, 943 insertions(+)
 create mode 100644 drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c
drivers/pinctrl/bcm/Kconfig +22 -0
index eb13201..cd11d4d 100644
@@ -20,6 +20,28 @@ config PINCTRL_BCM2835
 	select PINMUX
 	select PINCONF
 
+config PINCTRL_CYGNUS_GPIO
+	bool "Broadcom Cygnus GPIO (with PINCONF) driver"
+	depends on OF_GPIO && ARCH_BCM_CYGNUS
+	select GPIOLIB_IRQCHIP
+	select PINCONF
+	select GENERIC_PINCONF
+	default ARCH_BCM_CYGNUS
+	help
+	  Say yes here to enable the Broadcom Cygnus GPIO driver.
+
+	  The Broadcom Cygnus SoC has 3 GPIO controllers including the ASIU
+	  GPIO controller (ASIU), the chipCommonG GPIO controller (CCM), and
+	  the always-ON GPIO controller (CRMU/AON). All 3 GPIO controllers are
+	  supported by this driver.
+
+	  All 3 Cygnus GPIO controllers support basic PINCONF functions such
+	  as bias pull up, pull down, and drive strength configurations, when
+	  these pins are muxed to GPIO.
+
+	  Pins from the ASIU GPIO can be individually muxed to GPIO function,
+	  through interaction with the Cygnus IOMUX controller.
+
 config PINCTRL_CYGNUS_MUX
 	bool "Broadcom Cygnus IOMUX driver"
 	depends on (ARCH_BCM_CYGNUS || COMPILE_TEST)
drivers/pinctrl/bcm/Makefile +1 -0
index bb6beb6..2b2f70e 100644
@@ -2,4 +2,5 @@
 
 obj-$(CONFIG_PINCTRL_BCM281XX)		+= pinctrl-bcm281xx.o
 obj-$(CONFIG_PINCTRL_BCM2835)		+= pinctrl-bcm2835.o
+obj-$(CONFIG_PINCTRL_CYGNUS_GPIO)	+= pinctrl-cygnus-gpio.o
 obj-$(CONFIG_PINCTRL_CYGNUS_MUX)	+= pinctrl-cygnus-mux.o
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +86 -0
new file mode 100644
index 0000000..cfe4478
@@ -0,0 +1,920 @@
+/*
+ * Copyright (C) 2014-2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * This file contains the Broadcom Cygnus GPIO driver that supports 3
+ * GPIO controllers on Cygnus including the ASIU GPIO controller, the
+ * chipCommonG GPIO controller, and the always-on GPIO controller. Basic
+ * PINCONF such as bias pull up/down, and drive strength are also supported
+ * in this driver.
+ *
+ * Pins from the ASIU GPIO can be individually muxed to GPIO function,
+ * through the interaction with the Cygnus IOMUX controller
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/ioport.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+
+#include "../pinctrl-utils.h"
+
+#define CYGNUS_GPIO_DATA_IN_OFFSET   0x00
+#define CYGNUS_GPIO_DATA_OUT_OFFSET  0x04
+#define CYGNUS_GPIO_OUT_EN_OFFSET    0x08
+#define CYGNUS_GPIO_IN_TYPE_OFFSET   0x0c
+#define CYGNUS_GPIO_INT_DE_OFFSET    0x10
+#define CYGNUS_GPIO_INT_EDGE_OFFSET  0x14
+#define CYGNUS_GPIO_INT_MSK_OFFSET   0x18
+#define CYGNUS_GPIO_INT_STAT_OFFSET  0x1c
+#define CYGNUS_GPIO_INT_MSTAT_OFFSET 0x20
+#define CYGNUS_GPIO_INT_CLR_OFFSET   0x24
+#define CYGNUS_GPIO_PAD_RES_OFFSET   0x34
+#define CYGNUS_GPIO_RES_EN_OFFSET    0x38
+
+/* drive strength control for ASIU GPIO */
+#define CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58
+
+/* drive strength control for CCM/CRMU (AON) GPIO */
+#define CYGNUS_GPIO_DRV0_CTRL_OFFSET  0x00
+
+#define GPIO_BANK_SIZE 0x200
+#define NGPIOS_PER_BANK 32
+#define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK)
+
+#define CYGNUS_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + (reg))
+#define CYGNUS_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK)
+
+#define GPIO_DRV_STRENGTH_BIT_SHIFT  20
+#define GPIO_DRV_STRENGTH_BITS       3
+#define GPIO_DRV_STRENGTH_BIT_MASK   ((1 << GPIO_DRV_STRENGTH_BITS) - 1)
+
+/*
+ * Cygnus GPIO core
+ *
+ * @dev: pointer to device
+ * @base: I/O register base for Cygnus GPIO controller
+ * @io_ctrl: I/O register base for certain type of Cygnus GPIO controller that
+ * has the PINCONF support implemented outside of the GPIO block
+ * @lock: lock to protect access to I/O registers
+ * @gc: GPIO chip
+ * @num_banks: number of GPIO banks, each bank supports up to 32 GPIOs
+ * @pinmux_is_supported: flag to indicate this GPIO controller contains pins
+ * that can be individually muxed to GPIO
+ * @pctl: pointer to pinctrl_dev
+ * @pctldesc: pinctrl descriptor
+ * @pins: pointer to array of pins
+ */
+struct cygnus_gpio {
+	struct device *dev;

drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c line 96

Quoted 10 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +10 -0
+
+	void __iomem *base;
+	void __iomem *io_ctrl;
+
+	spinlock_t lock;
+
+	struct gpio_chip gc;
+	unsigned num_banks;
+
+	int pinmux_is_supported;

bool?

Quoted 290 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +290 -0
+
+	struct pinctrl_dev *pctl;
+	struct pinctrl_desc pctldesc;
+	struct pinctrl_pin_desc *pins;
+};
+
+static struct cygnus_gpio *to_cygnus_gpio(struct gpio_chip *gc)
+{
+	return container_of(gc, struct cygnus_gpio, gc);
+}
+
+/*
+ * Mapping from PINCONF pins to GPIO pins is 1-to-1
+ */
+static unsigned cygnus_pin_to_gpio(unsigned pin)
+{
+	return pin;
+}
+
+static u32 cygnus_readl(struct cygnus_gpio *chip, unsigned int offset)
+{
+	return readl(chip->base + offset);
+}
+
+static void cygnus_writel(struct cygnus_gpio *chip, unsigned int offset,
+			  u32 val)
+{
+	writel(val, chip->base + offset);
+}
+
+/**
+ *  cygnus_set_bit - set or clear one bit (corresponding to the GPIO pin) in a
+ *  Cygnus GPIO register
+ *
+ *  @cygnus_gpio: Cygnus GPIO device
+ *  @reg: register offset
+ *  @gpio: GPIO pin
+ *  @set: set or clear. 1 - set; 0 -clear
+ */
+static void cygnus_set_bit(struct cygnus_gpio *chip, unsigned int reg,
+			   unsigned gpio, int set)
+{
+	unsigned int offset = CYGNUS_GPIO_REG(gpio, reg);
+	unsigned int shift = CYGNUS_GPIO_SHIFT(gpio);
+	u32 val;
+
+	val = cygnus_readl(chip, offset);
+	if (set)
+		val |= BIT(shift);
+	else
+		val &= ~BIT(shift);
+	cygnus_writel(chip, offset, val);
+}
+
+static int cygnus_get_bit(struct cygnus_gpio *chip, unsigned int reg,
+			  unsigned gpio)
+{
+	unsigned int offset = CYGNUS_GPIO_REG(gpio, reg);
+	unsigned int shift = CYGNUS_GPIO_SHIFT(gpio);
+	u32 val;
+
+	val = cygnus_readl(chip, offset) & BIT(shift);
+	if (val)
+		return 1;
+	else
+		return 0;
+}
+
+static void cygnus_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+	struct gpio_chip *gc = irq_desc_get_handler_data(desc);
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	struct irq_chip *irq_chip = irq_desc_get_chip(desc);
+	int i, bit;
+
+	chained_irq_enter(irq_chip, desc);
+
+	/* go through the entire GPIO banks and handle all interrupts */
+	for (i = 0; i < chip->num_banks; i++) {
+		unsigned long val = cygnus_readl(chip,
+				(i * GPIO_BANK_SIZE) +
+				CYGNUS_GPIO_INT_MSTAT_OFFSET);
+
+		for_each_set_bit(bit, &val, NGPIOS_PER_BANK) {
+			unsigned pin = NGPIOS_PER_BANK * i + bit;
+			int child_irq = irq_find_mapping(gc->irqdomain, pin);
+
+			/*
+			 * Clear the interrupt before invoking the
+			 * handler, so we do not leave any window
+			 */
+			cygnus_writel(chip, (i * GPIO_BANK_SIZE) +
+				      CYGNUS_GPIO_INT_CLR_OFFSET, BIT(bit));
+
+			generic_handle_irq(child_irq);
+		}
+	}
+
+	chained_irq_exit(irq_chip, desc);
+}
+
+
+static void cygnus_gpio_irq_ack(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned gpio = d->hwirq;
+	unsigned int offset = CYGNUS_GPIO_REG(gpio,
+			CYGNUS_GPIO_INT_CLR_OFFSET);
+	unsigned int shift = CYGNUS_GPIO_SHIFT(gpio);
+	u32 val = BIT(shift);
+
+	cygnus_writel(chip, offset, val);
+}
+
+/**
+ *  cygnus_gpio_irq_set_mask - mask/unmask a GPIO interrupt
+ *
+ *  @d: IRQ chip data
+ *  @mask: mask/unmask GPIO interrupt. 0 - mask (disable); 1 - unmask (enable)
+ */
+static void cygnus_gpio_irq_set_mask(struct irq_data *d, int mask)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned gpio = d->hwirq;
+
+	cygnus_set_bit(chip, CYGNUS_GPIO_INT_MSK_OFFSET, gpio, mask);
+}
+
+static void cygnus_gpio_irq_mask(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	cygnus_gpio_irq_set_mask(d, 0);
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static void cygnus_gpio_irq_unmask(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	cygnus_gpio_irq_set_mask(d, 1);
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int cygnus_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned gpio = d->hwirq;
+	int int_type = 0, dual_edge = 0, edge_lvl = 0;
+	unsigned long flags;
+
+	switch (type & IRQ_TYPE_SENSE_MASK) {
+	case IRQ_TYPE_EDGE_RISING:
+		edge_lvl = 1;
+		break;
+
+	case IRQ_TYPE_EDGE_FALLING:
+		break;
+
+	case IRQ_TYPE_EDGE_BOTH:
+		dual_edge = 1;
+		break;
+
+	case IRQ_TYPE_LEVEL_HIGH:
+		int_type = 1;
+		edge_lvl = 1;
+		break;
+
+	case IRQ_TYPE_LEVEL_LOW:
+		int_type = 1;
+		break;
+
+	default:
+		dev_err(chip->dev, "invalid GPIO IRQ type 0x%x\n",
+			type);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&chip->lock, flags);
+	cygnus_set_bit(chip, CYGNUS_GPIO_IN_TYPE_OFFSET, gpio, int_type);
+	cygnus_set_bit(chip, CYGNUS_GPIO_INT_DE_OFFSET, gpio, dual_edge);
+	cygnus_set_bit(chip, CYGNUS_GPIO_INT_EDGE_OFFSET, gpio,
+		       edge_lvl);
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	dev_dbg(chip->dev,
+		"gpio:%u set int_type:%d dual_edge:%d edge_lvl:%d\n",
+		gpio, int_type, dual_edge, edge_lvl);
+
+	return 0;
+}
+
+static struct irq_chip cygnus_gpio_irq_chip = {
+	.name = "bcm-cygnus-gpio",
+	.irq_ack = cygnus_gpio_irq_ack,
+	.irq_mask = cygnus_gpio_irq_mask,
+	.irq_unmask = cygnus_gpio_irq_unmask,
+	.irq_set_type = cygnus_gpio_irq_set_type,
+};
+
+/*
+ * Request the Cygnus IOMUX pinmux controller to mux individual pins to GPIO
+ */
+static int cygnus_gpio_request(struct gpio_chip *gc, unsigned offset)
+{
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned gpio = gc->base + offset;
+
+	/* not all Cygnus GPIO pins can be muxed individually */
+	if (!chip->pinmux_is_supported)
+		return 0;
+
+	return pinctrl_request_gpio(gpio);
+}
+
+static void cygnus_gpio_free(struct gpio_chip *gc, unsigned offset)
+{
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned gpio = gc->base + offset;
+
+	if (!chip->pinmux_is_supported)
+		return;
+
+	pinctrl_free_gpio(gpio);
+}
+
+static int cygnus_gpio_direction_input(struct gpio_chip *gc, unsigned gpio)
+{
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	cygnus_set_bit(chip, CYGNUS_GPIO_OUT_EN_OFFSET, gpio, 0);
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	dev_dbg(chip->dev, "gpio:%u set input\n", gpio);
+
+	return 0;
+}
+
+static int cygnus_gpio_direction_output(struct gpio_chip *gc, unsigned gpio,
+					int value)
+{
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	cygnus_set_bit(chip, CYGNUS_GPIO_OUT_EN_OFFSET, gpio, 1);
+	cygnus_set_bit(chip, CYGNUS_GPIO_DATA_OUT_OFFSET, gpio, value);
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	dev_dbg(chip->dev, "gpio:%u set output, value:%d\n", gpio, value);
+
+	return 0;
+}
+
+static void cygnus_gpio_set(struct gpio_chip *gc, unsigned gpio, int value)
+{
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	cygnus_set_bit(chip, CYGNUS_GPIO_DATA_OUT_OFFSET, gpio, value);
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	dev_dbg(chip->dev, "gpio:%u set, value:%d\n", gpio, value);
+}
+
+static int cygnus_gpio_get(struct gpio_chip *gc, unsigned gpio)
+{
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned int offset = CYGNUS_GPIO_REG(gpio,
+					      CYGNUS_GPIO_DATA_IN_OFFSET);
+	unsigned int shift = CYGNUS_GPIO_SHIFT(gpio);
+
+	return !!(cygnus_readl(chip, offset) & BIT(shift));
+}
+
+static int cygnus_get_groups_count(struct pinctrl_dev *pctldev)
+{
+	return 1;

drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c line 396

Quoted 10 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +10 -0
+}
+
+/*
+ * Only one group: "gpio_grp", since this local pinctrl device only performs
+ * GPIO specific PINCONF configurations
+ */
+static const char *cygnus_get_group_name(struct pinctrl_dev *pctldev,
+					 unsigned selector)
+{
+

Extra blank line.

Quoted 315 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +315 -0
+	return "gpio_grp";
+}
+
+static const struct pinctrl_ops cygnus_pctrl_ops = {
+	.get_groups_count = cygnus_get_groups_count,
+	.get_group_name = cygnus_get_group_name,
+	.dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
+	.dt_free_map = pinctrl_utils_dt_free_map,
+};
+
+static int cygnus_gpio_set_pull(struct cygnus_gpio *chip, unsigned gpio,
+				int disable, int pull_up)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	if (disable) {
+		cygnus_set_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio, 0);
+	} else {
+		cygnus_set_bit(chip, CYGNUS_GPIO_PAD_RES_OFFSET, gpio,
+			       pull_up);
+		cygnus_set_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio, 1);
+	}
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	dev_dbg(chip->dev, "gpio:%u set pullup:%d\n", gpio, pull_up);
+
+	return 0;
+}
+
+static void cygnus_gpio_get_pull(struct cygnus_gpio *chip, unsigned gpio,
+				 int *disable, int *pull_up)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	*disable = !cygnus_get_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio);
+	*pull_up = cygnus_get_bit(chip, CYGNUS_GPIO_PAD_RES_OFFSET, gpio);
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int cygnus_gpio_set_strength(struct cygnus_gpio *chip, unsigned gpio,
+				    unsigned strength)
+{
+	void __iomem *base;
+	unsigned int i, offset, shift;
+	u32 val;
+	unsigned long flags;
+
+	/* make sure drive strength is supported */
+	if (strength < 2 ||  strength > 16 || (strength % 2))
+		return -ENOTSUPP;
+
+	if (chip->io_ctrl) {
+		base = chip->io_ctrl;
+		offset = CYGNUS_GPIO_DRV0_CTRL_OFFSET;
+	} else {
+		base = chip->base;
+		offset = CYGNUS_GPIO_REG(gpio,
+					 CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET);
+	}
+
+	shift = CYGNUS_GPIO_SHIFT(gpio);
+
+	dev_dbg(chip->dev, "gpio:%u set drive strength:%d mA\n", gpio,
+		strength);
+
+	spin_lock_irqsave(&chip->lock, flags);
+	strength = (strength / 2) - 1;
+	for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) {
+		val = readl(base + offset);
+		val &= ~BIT(shift);
+		val |= ((strength >> i) & 0x1) << shift;
+		writel(val, base + offset);
+		offset += 4;
+	}
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	return 0;
+}
+
+static int cygnus_gpio_get_strength(struct cygnus_gpio *chip, unsigned gpio,
+				    u16 *strength)
+{
+	void __iomem *base;
+	unsigned int i, offset, shift;
+	u32 val;
+	unsigned long flags;
+
+	if (chip->io_ctrl) {
+		base = chip->io_ctrl;
+		offset = CYGNUS_GPIO_DRV0_CTRL_OFFSET;
+	} else {
+		base = chip->base;
+		offset = CYGNUS_GPIO_REG(gpio,
+					 CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET);
+	}
+
+	shift = CYGNUS_GPIO_SHIFT(gpio);
+
+	spin_lock_irqsave(&chip->lock, flags);
+	*strength = 0;
+	for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) {
+		val = readl(base + offset) & BIT(shift);
+		val >>= shift;
+		*strength += (val << i);
+		offset += 4;
+	}
+
+	/* convert to mA */
+	*strength = (*strength + 1) * 2;
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	return 0;
+}
+
+static int cygnus_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin,
+				 unsigned long *config)
+{
+	struct cygnus_gpio *chip = pinctrl_dev_get_drvdata(pctldev);
+	enum pin_config_param param = pinconf_to_config_param(*config);
+	unsigned gpio = cygnus_pin_to_gpio(pin);
+	u16 arg;
+	int disable, pull_up, ret;
+
+	switch (param) {
+	case PIN_CONFIG_BIAS_DISABLE:
+		cygnus_gpio_get_pull(chip, gpio, &disable, &pull_up);
+		if (disable)
+			return 0;
+		else
+			return -EINVAL;
+
+	case PIN_CONFIG_BIAS_PULL_UP:
+		cygnus_gpio_get_pull(chip, gpio, &disable, &pull_up);
+		if (!disable && pull_up)
+			return 0;
+		else
+			return -EINVAL;
+
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		cygnus_gpio_get_pull(chip, gpio, &disable, &pull_up);
+		if (!disable && !pull_up)
+			return 0;
+		else
+			return -EINVAL;
+
+	case PIN_CONFIG_DRIVE_STRENGTH:
+		ret = cygnus_gpio_get_strength(chip, gpio, &arg);
+		if (ret)
+			return ret;
+		else
+			*config = pinconf_to_config_packed(param, arg);
+
+		return 0;
+
+	default:
+		return -ENOTSUPP;
+	}
+
+	return -ENOTSUPP;
+}
+
+static int cygnus_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin,
+				 unsigned long *configs, unsigned num_configs)
+{
+	struct cygnus_gpio *chip = pinctrl_dev_get_drvdata(pctldev);
+	enum pin_config_param param;
+	u16 arg;
+	unsigned i, gpio = cygnus_pin_to_gpio(pin);
+	int ret = -ENOTSUPP;
+
+	for (i = 0; i < num_configs; i++) {
+		param = pinconf_to_config_param(configs[i]);
+		arg = pinconf_to_config_argument(configs[i]);
+
+		switch (param) {
+		case PIN_CONFIG_BIAS_DISABLE:
+			ret = cygnus_gpio_set_pull(chip, gpio, 1, 0);
+			if (ret < 0)
+				goto out;
+			break;
+
+		case PIN_CONFIG_BIAS_PULL_UP:
+			ret = cygnus_gpio_set_pull(chip, gpio, 0, 1);
+			if (ret < 0)
+				goto out;
+			break;
+
+		case PIN_CONFIG_BIAS_PULL_DOWN:
+			ret = cygnus_gpio_set_pull(chip, gpio, 0, 0);
+			if (ret < 0)
+				goto out;
+			break;
+
+		case PIN_CONFIG_DRIVE_STRENGTH:
+			ret = cygnus_gpio_set_strength(chip, gpio, arg);
+			if (ret < 0)
+				goto out;
+			break;
+
+		default:
+			dev_err(chip->dev, "invalid configuration\n");
+			return -ENOTSUPP;
+		}
+	} /* for each config */
+
+out:
+	return ret;
+}
+
+static const struct pinconf_ops cygnus_pconf_ops = {
+	.is_generic = true,
+	.pin_config_get = cygnus_pin_config_get,
+	.pin_config_set = cygnus_pin_config_set,
+};
+
+/*
+ * Map a GPIO in the local gpio_chip pin space to a pin in the Cygnus IOMUX
+ * pinctrl pin space
+ */
+struct cygnus_gpio_pin_range {
+	unsigned offset;
+	unsigned pin_base;
+	unsigned num_pins;
+};
+
+#define CYGNUS_PINRANGE(o, p, n) { .offset = o, .pin_base = p, .num_pins = n }
+
+/*
+ * Pin mapping table for mapping local GPIO pins to Cygnus IOMUX pinctrl pins
+ */
+static const struct cygnus_gpio_pin_range cygnus_gpio_pintable[] = {
+	CYGNUS_PINRANGE(0, 42, 1),
+	CYGNUS_PINRANGE(1, 44, 3),
+	CYGNUS_PINRANGE(4, 48, 1),
+	CYGNUS_PINRANGE(5, 50, 3),
+	CYGNUS_PINRANGE(8, 126, 1),
+	CYGNUS_PINRANGE(9, 155, 1),
+	CYGNUS_PINRANGE(10, 152, 1),
+	CYGNUS_PINRANGE(11, 154, 1),
+	CYGNUS_PINRANGE(12, 153, 1),
+	CYGNUS_PINRANGE(13, 127, 3),
+	CYGNUS_PINRANGE(16, 140, 1),
+	CYGNUS_PINRANGE(17, 145, 7),
+	CYGNUS_PINRANGE(24, 130, 10),
+	CYGNUS_PINRANGE(34, 141, 4),
+	CYGNUS_PINRANGE(38, 54, 1),
+	CYGNUS_PINRANGE(39, 56, 3),
+	CYGNUS_PINRANGE(42, 60, 3),
+	CYGNUS_PINRANGE(45, 64, 3),
+	CYGNUS_PINRANGE(48, 68, 2),
+	CYGNUS_PINRANGE(50, 84, 6),
+	CYGNUS_PINRANGE(56, 94, 6),
+	CYGNUS_PINRANGE(62, 72, 1),
+	CYGNUS_PINRANGE(63, 70, 1),
+	CYGNUS_PINRANGE(64, 80, 1),
+	CYGNUS_PINRANGE(65, 74, 3),
+	CYGNUS_PINRANGE(68, 78, 1),
+	CYGNUS_PINRANGE(69, 82, 1),
+	CYGNUS_PINRANGE(70, 156, 17),
+	CYGNUS_PINRANGE(87, 104, 12),
+	CYGNUS_PINRANGE(99, 102, 2),
+	CYGNUS_PINRANGE(101, 90, 4),
+	CYGNUS_PINRANGE(105, 116, 10),
+	CYGNUS_PINRANGE(123, 11, 1),
+	CYGNUS_PINRANGE(124, 38, 4),
+	CYGNUS_PINRANGE(128, 43, 1),
+	CYGNUS_PINRANGE(129, 47, 1),
+	CYGNUS_PINRANGE(130, 49, 1),
+	CYGNUS_PINRANGE(131, 53, 1),
+	CYGNUS_PINRANGE(132, 55, 1),
+	CYGNUS_PINRANGE(133, 59, 1),
+	CYGNUS_PINRANGE(134, 63, 1),
+	CYGNUS_PINRANGE(135, 67, 1),
+	CYGNUS_PINRANGE(136, 71, 1),
+	CYGNUS_PINRANGE(137, 73, 1),
+	CYGNUS_PINRANGE(138, 77, 1),
+	CYGNUS_PINRANGE(139, 79, 1),
+	CYGNUS_PINRANGE(140, 81, 1),
+	CYGNUS_PINRANGE(141, 83, 1),
+	CYGNUS_PINRANGE(142, 10, 1)
+};
+
+/*
+ * The Cygnus IOMUX controller mainly supports group based mux configuration,
+ * but certain pins can be muxed to GPIO individually. Only the ASIU GPIO
+ * controller can support this, so it's an optional configuration
+ *
+ * Return -ENODEV means no support and that's fine
+ */
+static int cygnus_gpio_pinmux_add_range(struct cygnus_gpio *chip)
+{
+	struct device_node *node = chip->dev->of_node;
+	struct device_node *pinmux_node;
+	struct platform_device *pinmux_pdev;
+	struct gpio_chip *gc = &chip->gc;
+	int i, ret;
+
+	/* parse DT to find the phandle to the pinmux controller */
+	pinmux_node = of_parse_phandle(node, "pinmux", 0);
+	if (!pinmux_node)
+		return -ENODEV;
+
+	pinmux_pdev = of_find_device_by_node(pinmux_node);
+	if (!pinmux_pdev) {
+		dev_err(chip->dev, "failed to get pinmux device\n");
+		return -EINVAL;
+	}
+
+	/* now need to create the mapping between local GPIO and PINMUX pins */
+	for (i = 0; i < ARRAY_SIZE(cygnus_gpio_pintable); i++) {
+		ret = gpiochip_add_pin_range(gc, dev_name(&pinmux_pdev->dev),

drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c line 721

Quoted 10 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +10 -0
+					     cygnus_gpio_pintable[i].offset,
+					     cygnus_gpio_pintable[i].pin_base,
+					     cygnus_gpio_pintable[i].num_pins);
+		if (ret) {
+			dev_err(chip->dev, "unable to add GPIO pin range\n");
+			goto err_rm_pin_range;
+		}
+	}
+
+	chip->pinmux_is_supported = 1;
	chip->pinmux_is_supported = true;

?

drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c line 725

Quoted 4 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +4 -0
+	return 0;
+
+err_rm_pin_range:
+	gpiochip_remove_pin_ranges(gc);

I think you need:

	put_dveice(&pinmux_pdev->dev);

since of_find_device_by_node calls bus_find_device() that takes reference to found device.

... And now that I look at this majority of users of of_find_device_by_node() is broken like that :(

BTW, it looks like you only need pinmux_dev for it's name so you probably need to drop reference in success path as well.

Quoted 23 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +23 -0
+	return ret;
+}
+
+static void cygnus_gpio_pinmux_remove_range(struct cygnus_gpio *chip)
+{
+	struct gpio_chip *gc = &chip->gc;
+
+	if (chip->pinmux_is_supported)
+		gpiochip_remove_pin_ranges(gc);
+}
+
+/*
+ * Cygnus GPIO controller supports some PINCONF related configurations such as
+ * pull up, pull down, and drive strength, when the pin is configured to GPIO
+ *
+ * Here a local pinctrl device is created with simple 1-to-1 pin mapping to the
+ * local GPIO pins
+ */
+static int cygnus_gpio_register_pinconf(struct cygnus_gpio *chip)
+{
+	struct pinctrl_desc *pctldesc = &chip->pctldesc;
+	struct pinctrl_pin_desc *pins;
+	struct gpio_chip *gc = &chip->gc;

drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c line 758

Quoted 10 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +10 -0
+	int i, ret;
+
+	pins = devm_kcalloc(chip->dev, gc->ngpio, sizeof(*pins), GFP_KERNEL);
+	if (!pins)
+		return -ENOMEM;
+	chip->pins = pins;
+
+	for (i = 0; i < gc->ngpio; i++) {
+		pins[i].number = i;
+		pins[i].name = kasprintf(GFP_KERNEL, "gpio-%d", i);

We have devm_kasprintf().

Quoted 28 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +28 -0
+		if (!pins[i].name) {
+			ret = -ENOMEM;
+			goto err_kfree;
+		}
+	}
+
+	pctldesc->name = dev_name(chip->dev);
+	pctldesc->pctlops = &cygnus_pctrl_ops;
+	pctldesc->pins = pins;
+	pctldesc->npins = gc->ngpio;
+	pctldesc->confops = &cygnus_pconf_ops;
+
+	chip->pctl = pinctrl_register(pctldesc, chip->dev, chip);
+	if (!chip->pctl) {
+		dev_err(chip->dev, "unable to register pinctrl device\n");
+		ret = -EINVAL;
+		goto err_kfree;
+	}
+
+	return 0;
+
+err_kfree:
+	for (i = 0; i < gc->ngpio; i++)
+		kfree(pins[i].name);
+
+	return ret;
+}
+

drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c line 796

Quoted 10 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +10 -0
+static void cygnus_gpio_unregister_pinconf(struct cygnus_gpio *chip)
+{
+	struct gpio_chip *gc = &chip->gc;
+	int i;
+
+	if (chip->pctl)
+		pinctrl_unregister(chip->pctl);
+
+	for (i = 0; i < gc->ngpio; i++)
+		kfree(chip->pins[i].name);

Should not be needed if you use devm_kasprintf.

Quoted 103 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +103 -0
+}
+
+static const struct of_device_id cygnus_gpio_of_match[] = {
+	{ .compatible = "brcm,cygnus-gpio" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, cygnus_gpio_of_match);
+
+static int cygnus_gpio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct cygnus_gpio *chip;
+	struct gpio_chip *gc;
+	u32 ngpios;
+	int irq, ret;
+
+	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->dev = dev;
+	platform_set_drvdata(pdev, chip);
+
+	if (of_property_read_u32(dev->of_node, "ngpios", &ngpios)) {
+		dev_err(dev, "missing ngpios DT property\n");
+		return -ENODEV;
+	}
+	chip->num_banks = (ngpios + NGPIOS_PER_BANK - 1) / NGPIOS_PER_BANK;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	chip->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(chip->base)) {
+		dev_err(dev, "unable to map I/O memory\n");
+		return PTR_ERR(chip->base);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (res) {
+		chip->io_ctrl = devm_ioremap_resource(dev, res);
+		if (IS_ERR(chip->io_ctrl)) {
+			dev_err(dev, "unable to map I/O memory\n");
+			return PTR_ERR(chip->io_ctrl);
+		}
+	}
+
+	spin_lock_init(&chip->lock);
+
+	gc = &chip->gc;
+	gc->base = -1;
+	gc->ngpio = ngpios;
+	gc->label = dev_name(dev);
+	gc->dev = dev;
+	gc->of_node = dev->of_node;
+	gc->request = cygnus_gpio_request;
+	gc->free = cygnus_gpio_free;
+	gc->direction_input = cygnus_gpio_direction_input;
+	gc->direction_output = cygnus_gpio_direction_output;
+	gc->set = cygnus_gpio_set;
+	gc->get = cygnus_gpio_get;
+
+	ret = gpiochip_add(gc);
+	if (ret < 0) {
+		dev_err(dev, "unable to add GPIO chip\n");
+		return ret;
+	}
+
+	ret = cygnus_gpio_pinmux_add_range(chip);
+	if (ret && ret != -ENODEV) {
+		dev_err(dev, "unable to add GPIO pin range\n");
+		goto err_rm_gpiochip;
+	}
+
+	ret = cygnus_gpio_register_pinconf(chip);
+	if (ret) {
+		dev_err(dev, "unable to register pinconf\n");
+		goto err_rm_range;
+	}
+
+	/* optional GPIO interrupt support */
+	irq = platform_get_irq(pdev, 0);
+	if (irq) {
+		ret = gpiochip_irqchip_add(gc, &cygnus_gpio_irq_chip, 0,
+					   handle_simple_irq, IRQ_TYPE_NONE);
+		if (ret) {
+			dev_err(dev, "no GPIO irqchip\n");
+			goto err_unregister_pinconf;
+		}
+
+		gpiochip_set_chained_irqchip(gc, &cygnus_gpio_irq_chip, irq,
+					     cygnus_gpio_irq_handler);
+	}
+
+	return 0;
+
+err_unregister_pinconf:
+	cygnus_gpio_unregister_pinconf(chip);
+
+err_rm_range:
+	cygnus_gpio_pinmux_remove_range(chip);
+
+err_rm_gpiochip:
+	gpiochip_remove(gc);

drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c line 909

Quoted 10 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +10 -0
+
+	return ret;
+}
+
+static struct platform_driver cygnus_gpio_driver = {
+	.driver = {
+		.name = "cygnus-gpio",
+		.of_match_table = cygnus_gpio_of_match,
+	},
+	.probe = cygnus_gpio_probe,

The same comment about suppress_bind_attrs.

Quoted 14 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +11 -0
+};
+
+static int __init cygnus_gpio_init(void)
+{
+	return platform_driver_probe(&cygnus_gpio_driver, cygnus_gpio_probe);
+}
+arch_initcall_sync(cygnus_gpio_init);
+
+MODULE_AUTHOR("Ray Jui <[email protected]>");
+MODULE_DESCRIPTION("Broadcom Cygnus GPIO Driver");
+MODULE_LICENSE("GPL v2");
Signature · 1 line
1.7.9.5

Thanks.

Signature · 1 line
Dmitry
author maintainer
#309 lore raw

Re: [PATCH v7 2/4] pinctrl: cygnus: add gpio/pinconf driver

Quoted 158 lines · Dmitry Torokhov
Quoted 157 lines · Ray Jui

This adds the initial support of the Broadcom Cygnus GPIO/PINCONF driver that supports all 3 GPIO controllers on Cygnus including the ASIU GPIO controller, the chipCommonG GPIO controller, and the always-on GPIO controller. Basic PINCONF configurations such as bias pull up/down, and drive strength are also supported in this driver.

Pins from the ASIU GPIO controller can be individually muxed to GPIO function, through interaction with the Cygnus IOMUX controller

---
 drivers/pinctrl/bcm/Kconfig               |   22 +
 drivers/pinctrl/bcm/Makefile              |    1 +
 drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c |  920 +++++++++++++++++++++++++++++
 3 files changed, 943 insertions(+)
 create mode 100644 drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c
drivers/pinctrl/bcm/Kconfig +22 -0
index eb13201..cd11d4d 100644
@@ -20,6 +20,28 @@ config PINCTRL_BCM2835
 	select PINMUX
 	select PINCONF
 
+config PINCTRL_CYGNUS_GPIO
+	bool "Broadcom Cygnus GPIO (with PINCONF) driver"
+	depends on OF_GPIO && ARCH_BCM_CYGNUS
+	select GPIOLIB_IRQCHIP
+	select PINCONF
+	select GENERIC_PINCONF
+	default ARCH_BCM_CYGNUS
+	help
+	  Say yes here to enable the Broadcom Cygnus GPIO driver.
+
+	  The Broadcom Cygnus SoC has 3 GPIO controllers including the ASIU
+	  GPIO controller (ASIU), the chipCommonG GPIO controller (CCM), and
+	  the always-ON GPIO controller (CRMU/AON). All 3 GPIO controllers are
+	  supported by this driver.
+
+	  All 3 Cygnus GPIO controllers support basic PINCONF functions such
+	  as bias pull up, pull down, and drive strength configurations, when
+	  these pins are muxed to GPIO.
+
+	  Pins from the ASIU GPIO can be individually muxed to GPIO function,
+	  through interaction with the Cygnus IOMUX controller.
+
 config PINCTRL_CYGNUS_MUX
 	bool "Broadcom Cygnus IOMUX driver"
 	depends on (ARCH_BCM_CYGNUS || COMPILE_TEST)
drivers/pinctrl/bcm/Makefile +1 -0
index bb6beb6..2b2f70e 100644
@@ -2,4 +2,5 @@
 
 obj-$(CONFIG_PINCTRL_BCM281XX)		+= pinctrl-bcm281xx.o
 obj-$(CONFIG_PINCTRL_BCM2835)		+= pinctrl-bcm2835.o
+obj-$(CONFIG_PINCTRL_CYGNUS_GPIO)	+= pinctrl-cygnus-gpio.o
 obj-$(CONFIG_PINCTRL_CYGNUS_MUX)	+= pinctrl-cygnus-mux.o
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +90 -0
new file mode 100644
index 0000000..cfe4478
@@ -0,0 +1,920 @@
+/*
+ * Copyright (C) 2014-2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * This file contains the Broadcom Cygnus GPIO driver that supports 3
+ * GPIO controllers on Cygnus including the ASIU GPIO controller, the
+ * chipCommonG GPIO controller, and the always-on GPIO controller. Basic
+ * PINCONF such as bias pull up/down, and drive strength are also supported
+ * in this driver.
+ *
+ * Pins from the ASIU GPIO can be individually muxed to GPIO function,
+ * through the interaction with the Cygnus IOMUX controller
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/ioport.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+
+#include "../pinctrl-utils.h"
+
+#define CYGNUS_GPIO_DATA_IN_OFFSET   0x00
+#define CYGNUS_GPIO_DATA_OUT_OFFSET  0x04
+#define CYGNUS_GPIO_OUT_EN_OFFSET    0x08
+#define CYGNUS_GPIO_IN_TYPE_OFFSET   0x0c
+#define CYGNUS_GPIO_INT_DE_OFFSET    0x10
+#define CYGNUS_GPIO_INT_EDGE_OFFSET  0x14
+#define CYGNUS_GPIO_INT_MSK_OFFSET   0x18
+#define CYGNUS_GPIO_INT_STAT_OFFSET  0x1c
+#define CYGNUS_GPIO_INT_MSTAT_OFFSET 0x20
+#define CYGNUS_GPIO_INT_CLR_OFFSET   0x24
+#define CYGNUS_GPIO_PAD_RES_OFFSET   0x34
+#define CYGNUS_GPIO_RES_EN_OFFSET    0x38
+
+/* drive strength control for ASIU GPIO */
+#define CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58
+
+/* drive strength control for CCM/CRMU (AON) GPIO */
+#define CYGNUS_GPIO_DRV0_CTRL_OFFSET  0x00
+
+#define GPIO_BANK_SIZE 0x200
+#define NGPIOS_PER_BANK 32
+#define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK)
+
+#define CYGNUS_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + (reg))
+#define CYGNUS_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK)
+
+#define GPIO_DRV_STRENGTH_BIT_SHIFT  20
+#define GPIO_DRV_STRENGTH_BITS       3
+#define GPIO_DRV_STRENGTH_BIT_MASK   ((1 << GPIO_DRV_STRENGTH_BITS) - 1)
+
+/*
+ * Cygnus GPIO core
+ *
+ * @dev: pointer to device
+ * @base: I/O register base for Cygnus GPIO controller
+ * @io_ctrl: I/O register base for certain type of Cygnus GPIO controller that
+ * has the PINCONF support implemented outside of the GPIO block
+ * @lock: lock to protect access to I/O registers
+ * @gc: GPIO chip
+ * @num_banks: number of GPIO banks, each bank supports up to 32 GPIOs
+ * @pinmux_is_supported: flag to indicate this GPIO controller contains pins
+ * that can be individually muxed to GPIO
+ * @pctl: pointer to pinctrl_dev
+ * @pctldesc: pinctrl descriptor
+ * @pins: pointer to array of pins
+ */
+struct cygnus_gpio {
+	struct device *dev;
+
+	void __iomem *base;
+	void __iomem *io_ctrl;
+
Quoted 9 lines

drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c line 96

Quoted 6 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +6 -0
+	spinlock_t lock;
+
+	struct gpio_chip gc;
+	unsigned num_banks;
+
+	int pinmux_is_supported;

bool?

Yes. Will do.

Quoted 294 lines
Quoted 294 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +294 -0
+
+	struct pinctrl_dev *pctl;
+	struct pinctrl_desc pctldesc;
+	struct pinctrl_pin_desc *pins;
+};
+
+static struct cygnus_gpio *to_cygnus_gpio(struct gpio_chip *gc)
+{
+	return container_of(gc, struct cygnus_gpio, gc);
+}
+
+/*
+ * Mapping from PINCONF pins to GPIO pins is 1-to-1
+ */
+static unsigned cygnus_pin_to_gpio(unsigned pin)
+{
+	return pin;
+}
+
+static u32 cygnus_readl(struct cygnus_gpio *chip, unsigned int offset)
+{
+	return readl(chip->base + offset);
+}
+
+static void cygnus_writel(struct cygnus_gpio *chip, unsigned int offset,
+			  u32 val)
+{
+	writel(val, chip->base + offset);
+}
+
+/**
+ *  cygnus_set_bit - set or clear one bit (corresponding to the GPIO pin) in a
+ *  Cygnus GPIO register
+ *
+ *  @cygnus_gpio: Cygnus GPIO device
+ *  @reg: register offset
+ *  @gpio: GPIO pin
+ *  @set: set or clear. 1 - set; 0 -clear
+ */
+static void cygnus_set_bit(struct cygnus_gpio *chip, unsigned int reg,
+			   unsigned gpio, int set)
+{
+	unsigned int offset = CYGNUS_GPIO_REG(gpio, reg);
+	unsigned int shift = CYGNUS_GPIO_SHIFT(gpio);
+	u32 val;
+
+	val = cygnus_readl(chip, offset);
+	if (set)
+		val |= BIT(shift);
+	else
+		val &= ~BIT(shift);
+	cygnus_writel(chip, offset, val);
+}
+
+static int cygnus_get_bit(struct cygnus_gpio *chip, unsigned int reg,
+			  unsigned gpio)
+{
+	unsigned int offset = CYGNUS_GPIO_REG(gpio, reg);
+	unsigned int shift = CYGNUS_GPIO_SHIFT(gpio);
+	u32 val;
+
+	val = cygnus_readl(chip, offset) & BIT(shift);
+	if (val)
+		return 1;
+	else
+		return 0;
+}
+
+static void cygnus_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+	struct gpio_chip *gc = irq_desc_get_handler_data(desc);
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	struct irq_chip *irq_chip = irq_desc_get_chip(desc);
+	int i, bit;
+
+	chained_irq_enter(irq_chip, desc);
+
+	/* go through the entire GPIO banks and handle all interrupts */
+	for (i = 0; i < chip->num_banks; i++) {
+		unsigned long val = cygnus_readl(chip,
+				(i * GPIO_BANK_SIZE) +
+				CYGNUS_GPIO_INT_MSTAT_OFFSET);
+
+		for_each_set_bit(bit, &val, NGPIOS_PER_BANK) {
+			unsigned pin = NGPIOS_PER_BANK * i + bit;
+			int child_irq = irq_find_mapping(gc->irqdomain, pin);
+
+			/*
+			 * Clear the interrupt before invoking the
+			 * handler, so we do not leave any window
+			 */
+			cygnus_writel(chip, (i * GPIO_BANK_SIZE) +
+				      CYGNUS_GPIO_INT_CLR_OFFSET, BIT(bit));
+
+			generic_handle_irq(child_irq);
+		}
+	}
+
+	chained_irq_exit(irq_chip, desc);
+}
+
+
+static void cygnus_gpio_irq_ack(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned gpio = d->hwirq;
+	unsigned int offset = CYGNUS_GPIO_REG(gpio,
+			CYGNUS_GPIO_INT_CLR_OFFSET);
+	unsigned int shift = CYGNUS_GPIO_SHIFT(gpio);
+	u32 val = BIT(shift);
+
+	cygnus_writel(chip, offset, val);
+}
+
+/**
+ *  cygnus_gpio_irq_set_mask - mask/unmask a GPIO interrupt
+ *
+ *  @d: IRQ chip data
+ *  @mask: mask/unmask GPIO interrupt. 0 - mask (disable); 1 - unmask (enable)
+ */
+static void cygnus_gpio_irq_set_mask(struct irq_data *d, int mask)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned gpio = d->hwirq;
+
+	cygnus_set_bit(chip, CYGNUS_GPIO_INT_MSK_OFFSET, gpio, mask);
+}
+
+static void cygnus_gpio_irq_mask(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	cygnus_gpio_irq_set_mask(d, 0);
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static void cygnus_gpio_irq_unmask(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	cygnus_gpio_irq_set_mask(d, 1);
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int cygnus_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned gpio = d->hwirq;
+	int int_type = 0, dual_edge = 0, edge_lvl = 0;
+	unsigned long flags;
+
+	switch (type & IRQ_TYPE_SENSE_MASK) {
+	case IRQ_TYPE_EDGE_RISING:
+		edge_lvl = 1;
+		break;
+
+	case IRQ_TYPE_EDGE_FALLING:
+		break;
+
+	case IRQ_TYPE_EDGE_BOTH:
+		dual_edge = 1;
+		break;
+
+	case IRQ_TYPE_LEVEL_HIGH:
+		int_type = 1;
+		edge_lvl = 1;
+		break;
+
+	case IRQ_TYPE_LEVEL_LOW:
+		int_type = 1;
+		break;
+
+	default:
+		dev_err(chip->dev, "invalid GPIO IRQ type 0x%x\n",
+			type);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&chip->lock, flags);
+	cygnus_set_bit(chip, CYGNUS_GPIO_IN_TYPE_OFFSET, gpio, int_type);
+	cygnus_set_bit(chip, CYGNUS_GPIO_INT_DE_OFFSET, gpio, dual_edge);
+	cygnus_set_bit(chip, CYGNUS_GPIO_INT_EDGE_OFFSET, gpio,
+		       edge_lvl);
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	dev_dbg(chip->dev,
+		"gpio:%u set int_type:%d dual_edge:%d edge_lvl:%d\n",
+		gpio, int_type, dual_edge, edge_lvl);
+
+	return 0;
+}
+
+static struct irq_chip cygnus_gpio_irq_chip = {
+	.name = "bcm-cygnus-gpio",
+	.irq_ack = cygnus_gpio_irq_ack,
+	.irq_mask = cygnus_gpio_irq_mask,
+	.irq_unmask = cygnus_gpio_irq_unmask,
+	.irq_set_type = cygnus_gpio_irq_set_type,
+};
+
+/*
+ * Request the Cygnus IOMUX pinmux controller to mux individual pins to GPIO
+ */
+static int cygnus_gpio_request(struct gpio_chip *gc, unsigned offset)
+{
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned gpio = gc->base + offset;
+
+	/* not all Cygnus GPIO pins can be muxed individually */
+	if (!chip->pinmux_is_supported)
+		return 0;
+
+	return pinctrl_request_gpio(gpio);
+}
+
+static void cygnus_gpio_free(struct gpio_chip *gc, unsigned offset)
+{
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned gpio = gc->base + offset;
+
+	if (!chip->pinmux_is_supported)
+		return;
+
+	pinctrl_free_gpio(gpio);
+}
+
+static int cygnus_gpio_direction_input(struct gpio_chip *gc, unsigned gpio)
+{
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	cygnus_set_bit(chip, CYGNUS_GPIO_OUT_EN_OFFSET, gpio, 0);
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	dev_dbg(chip->dev, "gpio:%u set input\n", gpio);
+
+	return 0;
+}
+
+static int cygnus_gpio_direction_output(struct gpio_chip *gc, unsigned gpio,
+					int value)
+{
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	cygnus_set_bit(chip, CYGNUS_GPIO_OUT_EN_OFFSET, gpio, 1);
+	cygnus_set_bit(chip, CYGNUS_GPIO_DATA_OUT_OFFSET, gpio, value);
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	dev_dbg(chip->dev, "gpio:%u set output, value:%d\n", gpio, value);
+
+	return 0;
+}
+
+static void cygnus_gpio_set(struct gpio_chip *gc, unsigned gpio, int value)
+{
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	cygnus_set_bit(chip, CYGNUS_GPIO_DATA_OUT_OFFSET, gpio, value);
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	dev_dbg(chip->dev, "gpio:%u set, value:%d\n", gpio, value);
+}
+
+static int cygnus_gpio_get(struct gpio_chip *gc, unsigned gpio)
+{
+	struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+	unsigned int offset = CYGNUS_GPIO_REG(gpio,
+					      CYGNUS_GPIO_DATA_IN_OFFSET);
+	unsigned int shift = CYGNUS_GPIO_SHIFT(gpio);
+
+	return !!(cygnus_readl(chip, offset) & BIT(shift));
+}
+
+static int cygnus_get_groups_count(struct pinctrl_dev *pctldev)
+{
+	return 1;
+}
+
+/*
+ * Only one group: "gpio_grp", since this local pinctrl device only performs
Quoted 9 lines

drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c line 396

Quoted 6 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +6 -0
+ * GPIO specific PINCONF configurations
+ */
+static const char *cygnus_get_group_name(struct pinctrl_dev *pctldev,
+					 unsigned selector)
+{
+

Extra blank line.

Thanks!

Quoted 339 lines
Quoted 315 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +315 -0
+	return "gpio_grp";
+}
+
+static const struct pinctrl_ops cygnus_pctrl_ops = {
+	.get_groups_count = cygnus_get_groups_count,
+	.get_group_name = cygnus_get_group_name,
+	.dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
+	.dt_free_map = pinctrl_utils_dt_free_map,
+};
+
+static int cygnus_gpio_set_pull(struct cygnus_gpio *chip, unsigned gpio,
+				int disable, int pull_up)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	if (disable) {
+		cygnus_set_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio, 0);
+	} else {
+		cygnus_set_bit(chip, CYGNUS_GPIO_PAD_RES_OFFSET, gpio,
+			       pull_up);
+		cygnus_set_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio, 1);
+	}
+
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	dev_dbg(chip->dev, "gpio:%u set pullup:%d\n", gpio, pull_up);
+
+	return 0;
+}
+
+static void cygnus_gpio_get_pull(struct cygnus_gpio *chip, unsigned gpio,
+				 int *disable, int *pull_up)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lock, flags);
+	*disable = !cygnus_get_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio);
+	*pull_up = cygnus_get_bit(chip, CYGNUS_GPIO_PAD_RES_OFFSET, gpio);
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int cygnus_gpio_set_strength(struct cygnus_gpio *chip, unsigned gpio,
+				    unsigned strength)
+{
+	void __iomem *base;
+	unsigned int i, offset, shift;
+	u32 val;
+	unsigned long flags;
+
+	/* make sure drive strength is supported */
+	if (strength < 2 ||  strength > 16 || (strength % 2))
+		return -ENOTSUPP;
+
+	if (chip->io_ctrl) {
+		base = chip->io_ctrl;
+		offset = CYGNUS_GPIO_DRV0_CTRL_OFFSET;
+	} else {
+		base = chip->base;
+		offset = CYGNUS_GPIO_REG(gpio,
+					 CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET);
+	}
+
+	shift = CYGNUS_GPIO_SHIFT(gpio);
+
+	dev_dbg(chip->dev, "gpio:%u set drive strength:%d mA\n", gpio,
+		strength);
+
+	spin_lock_irqsave(&chip->lock, flags);
+	strength = (strength / 2) - 1;
+	for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) {
+		val = readl(base + offset);
+		val &= ~BIT(shift);
+		val |= ((strength >> i) & 0x1) << shift;
+		writel(val, base + offset);
+		offset += 4;
+	}
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	return 0;
+}
+
+static int cygnus_gpio_get_strength(struct cygnus_gpio *chip, unsigned gpio,
+				    u16 *strength)
+{
+	void __iomem *base;
+	unsigned int i, offset, shift;
+	u32 val;
+	unsigned long flags;
+
+	if (chip->io_ctrl) {
+		base = chip->io_ctrl;
+		offset = CYGNUS_GPIO_DRV0_CTRL_OFFSET;
+	} else {
+		base = chip->base;
+		offset = CYGNUS_GPIO_REG(gpio,
+					 CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET);
+	}
+
+	shift = CYGNUS_GPIO_SHIFT(gpio);
+
+	spin_lock_irqsave(&chip->lock, flags);
+	*strength = 0;
+	for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) {
+		val = readl(base + offset) & BIT(shift);
+		val >>= shift;
+		*strength += (val << i);
+		offset += 4;
+	}
+
+	/* convert to mA */
+	*strength = (*strength + 1) * 2;
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	return 0;
+}
+
+static int cygnus_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin,
+				 unsigned long *config)
+{
+	struct cygnus_gpio *chip = pinctrl_dev_get_drvdata(pctldev);
+	enum pin_config_param param = pinconf_to_config_param(*config);
+	unsigned gpio = cygnus_pin_to_gpio(pin);
+	u16 arg;
+	int disable, pull_up, ret;
+
+	switch (param) {
+	case PIN_CONFIG_BIAS_DISABLE:
+		cygnus_gpio_get_pull(chip, gpio, &disable, &pull_up);
+		if (disable)
+			return 0;
+		else
+			return -EINVAL;
+
+	case PIN_CONFIG_BIAS_PULL_UP:
+		cygnus_gpio_get_pull(chip, gpio, &disable, &pull_up);
+		if (!disable && pull_up)
+			return 0;
+		else
+			return -EINVAL;
+
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		cygnus_gpio_get_pull(chip, gpio, &disable, &pull_up);
+		if (!disable && !pull_up)
+			return 0;
+		else
+			return -EINVAL;
+
+	case PIN_CONFIG_DRIVE_STRENGTH:
+		ret = cygnus_gpio_get_strength(chip, gpio, &arg);
+		if (ret)
+			return ret;
+		else
+			*config = pinconf_to_config_packed(param, arg);
+
+		return 0;
+
+	default:
+		return -ENOTSUPP;
+	}
+
+	return -ENOTSUPP;
+}
+
+static int cygnus_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin,
+				 unsigned long *configs, unsigned num_configs)
+{
+	struct cygnus_gpio *chip = pinctrl_dev_get_drvdata(pctldev);
+	enum pin_config_param param;
+	u16 arg;
+	unsigned i, gpio = cygnus_pin_to_gpio(pin);
+	int ret = -ENOTSUPP;
+
+	for (i = 0; i < num_configs; i++) {
+		param = pinconf_to_config_param(configs[i]);
+		arg = pinconf_to_config_argument(configs[i]);
+
+		switch (param) {
+		case PIN_CONFIG_BIAS_DISABLE:
+			ret = cygnus_gpio_set_pull(chip, gpio, 1, 0);
+			if (ret < 0)
+				goto out;
+			break;
+
+		case PIN_CONFIG_BIAS_PULL_UP:
+			ret = cygnus_gpio_set_pull(chip, gpio, 0, 1);
+			if (ret < 0)
+				goto out;
+			break;
+
+		case PIN_CONFIG_BIAS_PULL_DOWN:
+			ret = cygnus_gpio_set_pull(chip, gpio, 0, 0);
+			if (ret < 0)
+				goto out;
+			break;
+
+		case PIN_CONFIG_DRIVE_STRENGTH:
+			ret = cygnus_gpio_set_strength(chip, gpio, arg);
+			if (ret < 0)
+				goto out;
+			break;
+
+		default:
+			dev_err(chip->dev, "invalid configuration\n");
+			return -ENOTSUPP;
+		}
+	} /* for each config */
+
+out:
+	return ret;
+}
+
+static const struct pinconf_ops cygnus_pconf_ops = {
+	.is_generic = true,
+	.pin_config_get = cygnus_pin_config_get,
+	.pin_config_set = cygnus_pin_config_set,
+};
+
+/*
+ * Map a GPIO in the local gpio_chip pin space to a pin in the Cygnus IOMUX
+ * pinctrl pin space
+ */
+struct cygnus_gpio_pin_range {
+	unsigned offset;
+	unsigned pin_base;
+	unsigned num_pins;
+};
+
+#define CYGNUS_PINRANGE(o, p, n) { .offset = o, .pin_base = p, .num_pins = n }
+
+/*
+ * Pin mapping table for mapping local GPIO pins to Cygnus IOMUX pinctrl pins
+ */
+static const struct cygnus_gpio_pin_range cygnus_gpio_pintable[] = {
+	CYGNUS_PINRANGE(0, 42, 1),
+	CYGNUS_PINRANGE(1, 44, 3),
+	CYGNUS_PINRANGE(4, 48, 1),
+	CYGNUS_PINRANGE(5, 50, 3),
+	CYGNUS_PINRANGE(8, 126, 1),
+	CYGNUS_PINRANGE(9, 155, 1),
+	CYGNUS_PINRANGE(10, 152, 1),
+	CYGNUS_PINRANGE(11, 154, 1),
+	CYGNUS_PINRANGE(12, 153, 1),
+	CYGNUS_PINRANGE(13, 127, 3),
+	CYGNUS_PINRANGE(16, 140, 1),
+	CYGNUS_PINRANGE(17, 145, 7),
+	CYGNUS_PINRANGE(24, 130, 10),
+	CYGNUS_PINRANGE(34, 141, 4),
+	CYGNUS_PINRANGE(38, 54, 1),
+	CYGNUS_PINRANGE(39, 56, 3),
+	CYGNUS_PINRANGE(42, 60, 3),
+	CYGNUS_PINRANGE(45, 64, 3),
+	CYGNUS_PINRANGE(48, 68, 2),
+	CYGNUS_PINRANGE(50, 84, 6),
+	CYGNUS_PINRANGE(56, 94, 6),
+	CYGNUS_PINRANGE(62, 72, 1),
+	CYGNUS_PINRANGE(63, 70, 1),
+	CYGNUS_PINRANGE(64, 80, 1),
+	CYGNUS_PINRANGE(65, 74, 3),
+	CYGNUS_PINRANGE(68, 78, 1),
+	CYGNUS_PINRANGE(69, 82, 1),
+	CYGNUS_PINRANGE(70, 156, 17),
+	CYGNUS_PINRANGE(87, 104, 12),
+	CYGNUS_PINRANGE(99, 102, 2),
+	CYGNUS_PINRANGE(101, 90, 4),
+	CYGNUS_PINRANGE(105, 116, 10),
+	CYGNUS_PINRANGE(123, 11, 1),
+	CYGNUS_PINRANGE(124, 38, 4),
+	CYGNUS_PINRANGE(128, 43, 1),
+	CYGNUS_PINRANGE(129, 47, 1),
+	CYGNUS_PINRANGE(130, 49, 1),
+	CYGNUS_PINRANGE(131, 53, 1),
+	CYGNUS_PINRANGE(132, 55, 1),
+	CYGNUS_PINRANGE(133, 59, 1),
+	CYGNUS_PINRANGE(134, 63, 1),
+	CYGNUS_PINRANGE(135, 67, 1),
+	CYGNUS_PINRANGE(136, 71, 1),
+	CYGNUS_PINRANGE(137, 73, 1),
+	CYGNUS_PINRANGE(138, 77, 1),
+	CYGNUS_PINRANGE(139, 79, 1),
+	CYGNUS_PINRANGE(140, 81, 1),
+	CYGNUS_PINRANGE(141, 83, 1),
+	CYGNUS_PINRANGE(142, 10, 1)
+};
+
+/*
+ * The Cygnus IOMUX controller mainly supports group based mux configuration,
+ * but certain pins can be muxed to GPIO individually. Only the ASIU GPIO
+ * controller can support this, so it's an optional configuration
+ *
+ * Return -ENODEV means no support and that's fine
+ */
+static int cygnus_gpio_pinmux_add_range(struct cygnus_gpio *chip)
+{
+	struct device_node *node = chip->dev->of_node;
+	struct device_node *pinmux_node;
+	struct platform_device *pinmux_pdev;
+	struct gpio_chip *gc = &chip->gc;
+	int i, ret;
+
+	/* parse DT to find the phandle to the pinmux controller */
+	pinmux_node = of_parse_phandle(node, "pinmux", 0);
+	if (!pinmux_node)
+		return -ENODEV;
+
+	pinmux_pdev = of_find_device_by_node(pinmux_node);
+	if (!pinmux_pdev) {
+		dev_err(chip->dev, "failed to get pinmux device\n");
+		return -EINVAL;
+	}
+
+	/* now need to create the mapping between local GPIO and PINMUX pins */
+	for (i = 0; i < ARRAY_SIZE(cygnus_gpio_pintable); i++) {
+		ret = gpiochip_add_pin_range(gc, dev_name(&pinmux_pdev->dev),

drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c line 721

Quoted 10 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +10 -0
+					     cygnus_gpio_pintable[i].offset,
+					     cygnus_gpio_pintable[i].pin_base,
+					     cygnus_gpio_pintable[i].num_pins);
+		if (ret) {
+			dev_err(chip->dev, "unable to add GPIO pin range\n");
+			goto err_rm_pin_range;
+		}
+	}
+
+	chip->pinmux_is_supported = 1;
	chip->pinmux_is_supported = true;

?

drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c line 725

Quoted 4 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +4 -0
+	return 0;
+
+err_rm_pin_range:
+	gpiochip_remove_pin_ranges(gc);

I think you need:

	put_dveice(&pinmux_pdev->dev);
Quoted 9 lines

since of_find_device_by_node calls bus_find_device() that takes reference to found device.

... And now that I look at this majority of users of of_find_device_by_node() is broken like that :(

BTW, it looks like you only need pinmux_dev for it's name so you probably need to drop reference in success path as well.

Oh wow! Really good to know. Will fix!

Quoted 27 lines
Quoted 27 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +27 -0
+	return ret;
+}
+
+static void cygnus_gpio_pinmux_remove_range(struct cygnus_gpio *chip)
+{
+	struct gpio_chip *gc = &chip->gc;
+
+	if (chip->pinmux_is_supported)
+		gpiochip_remove_pin_ranges(gc);
+}
+
+/*
+ * Cygnus GPIO controller supports some PINCONF related configurations such as
+ * pull up, pull down, and drive strength, when the pin is configured to GPIO
+ *
+ * Here a local pinctrl device is created with simple 1-to-1 pin mapping to the
+ * local GPIO pins
+ */
+static int cygnus_gpio_register_pinconf(struct cygnus_gpio *chip)
+{
+	struct pinctrl_desc *pctldesc = &chip->pctldesc;
+	struct pinctrl_pin_desc *pins;
+	struct gpio_chip *gc = &chip->gc;
+	int i, ret;
+
+	pins = devm_kcalloc(chip->dev, gc->ngpio, sizeof(*pins), GFP_KERNEL);
+	if (!pins)
Quoted 9 lines

drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c line 758

Quoted 6 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +6 -0
+		return -ENOMEM;
+	chip->pins = pins;
+
+	for (i = 0; i < gc->ngpio; i++) {
+		pins[i].number = i;
+		pins[i].name = kasprintf(GFP_KERNEL, "gpio-%d", i);

We have devm_kasprintf().

I was not aware of that. Okay I'll change to devm_kasprintf so I can get rid of some memory free code below. Thanks!

Quoted 32 lines
Quoted 32 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +32 -0
+		if (!pins[i].name) {
+			ret = -ENOMEM;
+			goto err_kfree;
+		}
+	}
+
+	pctldesc->name = dev_name(chip->dev);
+	pctldesc->pctlops = &cygnus_pctrl_ops;
+	pctldesc->pins = pins;
+	pctldesc->npins = gc->ngpio;
+	pctldesc->confops = &cygnus_pconf_ops;
+
+	chip->pctl = pinctrl_register(pctldesc, chip->dev, chip);
+	if (!chip->pctl) {
+		dev_err(chip->dev, "unable to register pinctrl device\n");
+		ret = -EINVAL;
+		goto err_kfree;
+	}
+
+	return 0;
+
+err_kfree:
+	for (i = 0; i < gc->ngpio; i++)
+		kfree(pins[i].name);
+
+	return ret;
+}
+
+static void cygnus_gpio_unregister_pinconf(struct cygnus_gpio *chip)
+{
+	struct gpio_chip *gc = &chip->gc;
+	int i;
Quoted 9 lines

drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c line 796

Quoted 6 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +6 -0
+
+	if (chip->pctl)
+		pinctrl_unregister(chip->pctl);
+
+	for (i = 0; i < gc->ngpio; i++)
+		kfree(chip->pins[i].name);

Should not be needed if you use devm_kasprintf.

Yes!

Quoted 107 lines
Quoted 107 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +107 -0
+}
+
+static const struct of_device_id cygnus_gpio_of_match[] = {
+	{ .compatible = "brcm,cygnus-gpio" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, cygnus_gpio_of_match);
+
+static int cygnus_gpio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct cygnus_gpio *chip;
+	struct gpio_chip *gc;
+	u32 ngpios;
+	int irq, ret;
+
+	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->dev = dev;
+	platform_set_drvdata(pdev, chip);
+
+	if (of_property_read_u32(dev->of_node, "ngpios", &ngpios)) {
+		dev_err(dev, "missing ngpios DT property\n");
+		return -ENODEV;
+	}
+	chip->num_banks = (ngpios + NGPIOS_PER_BANK - 1) / NGPIOS_PER_BANK;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	chip->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(chip->base)) {
+		dev_err(dev, "unable to map I/O memory\n");
+		return PTR_ERR(chip->base);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (res) {
+		chip->io_ctrl = devm_ioremap_resource(dev, res);
+		if (IS_ERR(chip->io_ctrl)) {
+			dev_err(dev, "unable to map I/O memory\n");
+			return PTR_ERR(chip->io_ctrl);
+		}
+	}
+
+	spin_lock_init(&chip->lock);
+
+	gc = &chip->gc;
+	gc->base = -1;
+	gc->ngpio = ngpios;
+	gc->label = dev_name(dev);
+	gc->dev = dev;
+	gc->of_node = dev->of_node;
+	gc->request = cygnus_gpio_request;
+	gc->free = cygnus_gpio_free;
+	gc->direction_input = cygnus_gpio_direction_input;
+	gc->direction_output = cygnus_gpio_direction_output;
+	gc->set = cygnus_gpio_set;
+	gc->get = cygnus_gpio_get;
+
+	ret = gpiochip_add(gc);
+	if (ret < 0) {
+		dev_err(dev, "unable to add GPIO chip\n");
+		return ret;
+	}
+
+	ret = cygnus_gpio_pinmux_add_range(chip);
+	if (ret && ret != -ENODEV) {
+		dev_err(dev, "unable to add GPIO pin range\n");
+		goto err_rm_gpiochip;
+	}
+
+	ret = cygnus_gpio_register_pinconf(chip);
+	if (ret) {
+		dev_err(dev, "unable to register pinconf\n");
+		goto err_rm_range;
+	}
+
+	/* optional GPIO interrupt support */
+	irq = platform_get_irq(pdev, 0);
+	if (irq) {
+		ret = gpiochip_irqchip_add(gc, &cygnus_gpio_irq_chip, 0,
+					   handle_simple_irq, IRQ_TYPE_NONE);
+		if (ret) {
+			dev_err(dev, "no GPIO irqchip\n");
+			goto err_unregister_pinconf;
+		}
+
+		gpiochip_set_chained_irqchip(gc, &cygnus_gpio_irq_chip, irq,
+					     cygnus_gpio_irq_handler);
+	}
+
+	return 0;
+
+err_unregister_pinconf:
+	cygnus_gpio_unregister_pinconf(chip);
+
+err_rm_range:
+	cygnus_gpio_pinmux_remove_range(chip);
+
+err_rm_gpiochip:
+	gpiochip_remove(gc);
+
+	return ret;
+}
+
Quoted 9 lines

drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c line 909

Quoted 6 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +6 -0
+static struct platform_driver cygnus_gpio_driver = {
+	.driver = {
+		.name = "cygnus-gpio",
+		.of_match_table = cygnus_gpio_of_match,
+	},
+	.probe = cygnus_gpio_probe,

The same comment about suppress_bind_attrs.

Okay. Will do!

Quoted 8 lines
Quoted 8 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +8 -0
+};
+
+static int __init cygnus_gpio_init(void)
+{
+	return platform_driver_probe(&cygnus_gpio_driver, cygnus_gpio_probe);
+}
+arch_initcall_sync(cygnus_gpio_init);
+
Quoted 9 lines
Quoted 6 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +3 -0
+MODULE_AUTHOR("Ray Jui <[email protected]>");
+MODULE_DESCRIPTION("Broadcom Cygnus GPIO Driver");
+MODULE_LICENSE("GPL v2");
Signature · 1 line
1.7.9.5

Thanks.

Thanks for the review, Dmitry!

Ray

author maintainer
#310 lore raw

PATCH v7 3/4 ARM: dts: enable GPIO for Broadcom Cygnus

In reply to Ray Jui

This enables all 3 GPIO controllers including the ASIU GPIO, the chipcommonG GPIO, and the ALWAYS-ON GPIO, for Broadcom Cygnus SoC

---
 arch/arm/boot/dts/bcm-cygnus.dtsi |   33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)
arch/arm/boot/dts/bcm-cygnus.dtsi +33 -0
index b014ce5..a3b8621 100644
@@ -60,6 +60,39 @@
 		      <0x0301d24c 0x2c>;
 	};
 
+	gpio_crmu: gpio@03024800 {
+		compatible = "brcm,cygnus-gpio";
+		reg = <0x03024800 0x50>,
+		      <0x03024008 0x18>;
+		ngpios = <6>;
+		#gpio-cells = <2>;
+		gpio-controller;
+	};
+
+	gpio_ccm: gpio@1800a000 {
+		compatible = "brcm,cygnus-gpio";
+		reg = <0x1800a000 0x50>,
+		      <0x0301d164 0x20>;
+		ngpios = <24>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-controller;
+	};
+
+	gpio_asiu: gpio@180a5000 {
+		compatible = "brcm,cygnus-gpio";
+		reg = <0x180a5000 0x668>;
+		ngpios = <146>;
+		#gpio-cells = <2>;
+		gpio-controller;
+
+		pinmux = <&pinctrl>;
+
+		interrupt-controller;
+		interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+	};
+
 	amba {
 		#address-cells = <1>;
 		#size-cells = <1>;
Signature · 1 line
1.7.9.5
author maintainer
#311 lore raw

PATCH v7 4/4 ARM: dts: cygnus: enable GPIO based hook detection

In reply to Ray Jui

This enables GPIO based phone hook detection for Broadcom BCM911360 phone factor board (bcm911360_entphn)

---
 arch/arm/boot/dts/bcm911360_entphn.dts |   13 +++++++++++++
 1 file changed, 13 insertions(+)
arch/arm/boot/dts/bcm911360_entphn.dts +13 -0
index d2ee952..7db4843 100644
@@ -33,6 +33,7 @@
 /dts-v1/;
 
 #include "bcm-cygnus.dtsi"
+#include "dt-bindings/input/input.h"
 
 / {
 	model = "Cygnus Enterprise Phone (BCM911360_ENTPHN)";
@@ -50,4 +51,16 @@
 	uart3: serial@18023000 {
 		status = "okay";
 	};
+
+	gpio_keys {
+		compatible = "gpio-keys";
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		hook {
+			label = "HOOK";
+			linux,code = <KEY_O>;
+			gpios = <&gpio_asiu 48 0>;
+		};
+	};
 };
Signature · 1 line
1.7.9.5
author maintainer
#312 lore raw

v4 cover Add pinctrl support to Broadcom Cygnus SoC

In reply to a message not in the archive

This patchset contains the initial pinctrl (IOMUX) support for the Broadcom Cygnus SoC. The Cygnus IOMUX controller supports group based mux configuration and allows certain pins to be muxed to GPIO function individually

Changes from v3:
- Fix the driver to have more proper use of "const" in various places
- Other minor improvements

Changes from v2:
- Consolidate all Broadcom pinctrl drivers into drivers/pinctrl/bcm/*
- Change the Cygnus IOMUX driver to use standard Linux pinctrl subnode
properties such as "function" and "groups" for pinmux configuration, instead of non-standard properties such as "brcm,function" and "brcm,group"
- Use real function names like "spi0", "lcd", "key", and etc. instead of HW
specific mux names like "alt1", "alt2", "alt3", and etc.
- Add suffix "grp" to all group names
- Add support to allow individual pins to be muxed to GPIO function through
subsystem callbacks "gpio_request_enable" and "gpio_disable_free", and get rid of all GPIO groups
- Other minor improvements in the driver

Changes from v1:
- Fix a typo in device tree binding document

Ray Jui (4):
pinctrl: bcm: consolidate Broadcom pinctrl drivers
pinctrl: Broadcom Cygnus pinctrl device tree binding
pinctrl: cygnus: add initial IOMUX driver support
ARM: dts: enable IOMUX for Broadcom Cygnus

 .../bindings/pinctrl/brcm,cygnus-pinmux.txt        |  157 +++
 arch/arm/boot/dts/bcm-cygnus.dtsi                  |    6 +
 drivers/pinctrl/Kconfig                            |   19 +-
 drivers/pinctrl/Makefile                           |    4 +-
 drivers/pinctrl/bcm/Kconfig                        |   34 +
 drivers/pinctrl/bcm/Makefile                       |    5 +
 drivers/pinctrl/bcm/pinctrl-bcm281xx.c             | 1455 ++++++++++++++++++++
 drivers/pinctrl/bcm/pinctrl-bcm2835.c              | 1072 ++++++++++++++
 drivers/pinctrl/bcm/pinctrl-cygnus-mux.c           | 1088 +++++++++++++++
 drivers/pinctrl/pinctrl-bcm281xx.c                 | 1455 --------------------
 drivers/pinctrl/pinctrl-bcm2835.c                  | 1072 --------------
 11 files changed, 3820 insertions(+), 2547 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinmux.txt
 create mode 100644 drivers/pinctrl/bcm/Kconfig
 create mode 100644 drivers/pinctrl/bcm/Makefile
 create mode 100644 drivers/pinctrl/bcm/pinctrl-bcm281xx.c
 create mode 100644 drivers/pinctrl/bcm/pinctrl-bcm2835.c
 create mode 100644 drivers/pinctrl/bcm/pinctrl-cygnus-mux.c
 delete mode 100644 drivers/pinctrl/pinctrl-bcm281xx.c
 delete mode 100644 drivers/pinctrl/pinctrl-bcm2835.c
Signature · 1 line
1.7.9.5
author maintainer
#313 lore raw

PATCH v4 1/4 pinctrl: bcm: consolidate Broadcom pinctrl drivers

Consolidate Broadcom pinctrl drivers into drivers/pinctrl/bcm/*

---
 drivers/pinctrl/Kconfig                |   19 +-
 drivers/pinctrl/Makefile               |    4 +-
 drivers/pinctrl/bcm/Kconfig            |   21 +
 drivers/pinctrl/bcm/Makefile           |    4 +
 drivers/pinctrl/bcm/pinctrl-bcm281xx.c | 1455 ++++++++++++++++++++++++++++++++
 drivers/pinctrl/bcm/pinctrl-bcm2835.c  | 1072 +++++++++++++++++++++++
 drivers/pinctrl/pinctrl-bcm281xx.c     | 1455 --------------------------------
 drivers/pinctrl/pinctrl-bcm2835.c      | 1072 -----------------------
 8 files changed, 2555 insertions(+), 2547 deletions(-)
 create mode 100644 drivers/pinctrl/bcm/Kconfig
 create mode 100644 drivers/pinctrl/bcm/Makefile
 create mode 100644 drivers/pinctrl/bcm/pinctrl-bcm281xx.c
 create mode 100644 drivers/pinctrl/bcm/pinctrl-bcm2835.c
 delete mode 100644 drivers/pinctrl/pinctrl-bcm281xx.c
 delete mode 100644 drivers/pinctrl/pinctrl-bcm2835.c
drivers/pinctrl/Kconfig +1 -18
index d014f22..6cfdad7 100644
@@ -67,24 +67,6 @@ config PINCTRL_AT91
 	help
 	  Say Y here to enable the at91 pinctrl driver
 
-config PINCTRL_BCM2835
-	bool
-	select PINMUX
-	select PINCONF
-
-config PINCTRL_BCM281XX
-	bool "Broadcom BCM281xx pinctrl driver"
-	depends on OF && (ARCH_BCM_MOBILE || COMPILE_TEST)
-	select PINMUX
-	select PINCONF
-	select GENERIC_PINCONF
-	select REGMAP_MMIO
-	help
-	  Say Y here to support Broadcom BCM281xx pinctrl driver, which is used
-	  for the BCM281xx SoC family, including BCM11130, BCM11140, BCM11351,
-	  BCM28145, and BCM28155 SoCs.  This driver requires the pinctrl
-	  framework.  GPIO is provided by a separate GPIO driver.
-
 config PINCTRL_LANTIQ
 	bool
 	depends on LANTIQ
@@ -191,6 +173,7 @@ config PINCTRL_PALMAS
 	  open drain configuration for the Palmas series devices like
 	  TPS65913, TPS80036 etc.
 
+source "drivers/pinctrl/bcm/Kconfig"
 source "drivers/pinctrl/berlin/Kconfig"
 source "drivers/pinctrl/freescale/Kconfig"
 source "drivers/pinctrl/intel/Kconfig"
drivers/pinctrl/Makefile +2 -2
index c030b3d..c018bbf 100644
@@ -14,8 +14,6 @@ obj-$(CONFIG_PINCTRL_AS3722)	+= pinctrl-as3722.o
 obj-$(CONFIG_PINCTRL_BF54x)	+= pinctrl-adi2-bf54x.o
 obj-$(CONFIG_PINCTRL_BF60x)	+= pinctrl-adi2-bf60x.o
 obj-$(CONFIG_PINCTRL_AT91)	+= pinctrl-at91.o
-obj-$(CONFIG_PINCTRL_BCM2835)	+= pinctrl-bcm2835.o
-obj-$(CONFIG_PINCTRL_BCM281XX)	+= pinctrl-bcm281xx.o
 obj-$(CONFIG_PINCTRL_FALCON)	+= pinctrl-falcon.o
 obj-$(CONFIG_PINCTRL_PALMAS)	+= pinctrl-palmas.o
 obj-$(CONFIG_PINCTRL_ROCKCHIP)	+= pinctrl-rockchip.o
@@ -36,6 +34,8 @@ obj-$(CONFIG_PINCTRL_LANTIQ)	+= pinctrl-lantiq.o
 obj-$(CONFIG_PINCTRL_TB10X)	+= pinctrl-tb10x.o
 obj-$(CONFIG_PINCTRL_ST) 	+= pinctrl-st.o
 
+obj-$(CONFIG_ARCH_BCM)		+= bcm/
+obj-$(CONFIG_ARCH_BCM2835)	+= bcm/
 obj-$(CONFIG_ARCH_BERLIN)	+= berlin/
 obj-y				+= freescale/
 obj-$(CONFIG_X86)		+= intel/
drivers/pinctrl/bcm/Kconfig new file +21 -0
new file mode 100644
index 0000000..bc6d048
@@ -0,0 +1,21 @@
+#
+# Broadcom pinctrl drivers
+#
+
+config PINCTRL_BCM281XX
+	bool "Broadcom BCM281xx pinctrl driver"
+	depends on OF && (ARCH_BCM_MOBILE || COMPILE_TEST)
+	select PINMUX
+	select PINCONF
+	select GENERIC_PINCONF
+	select REGMAP_MMIO
+	help
+	  Say Y here to support Broadcom BCM281xx pinctrl driver, which is used
+	  for the BCM281xx SoC family, including BCM11130, BCM11140, BCM11351,
+	  BCM28145, and BCM28155 SoCs.  This driver requires the pinctrl
+	  framework.  GPIO is provided by a separate GPIO driver.
+
+config PINCTRL_BCM2835
+	bool
+	select PINMUX
+	select PINCONF
drivers/pinctrl/bcm/Makefile new file +4 -0
new file mode 100644
index 0000000..7ba80a3
@@ -0,0 +1,4 @@
+# Broadcom pinctrl support
+
+obj-$(CONFIG_PINCTRL_BCM281XX)	+= pinctrl-bcm281xx.o
+obj-$(CONFIG_PINCTRL_BCM2835)	+= pinctrl-bcm2835.o
drivers/pinctrl/bcm/pinctrl-bcm281xx.c new file +1455 -0

1,455 lines not on this page · raw message

drivers/pinctrl/bcm/pinctrl-bcm2835.c new file +1072 -0

1,072 lines not on this page · raw message

drivers/pinctrl/pinctrl-bcm281xx.c deleted +0 -1455

401 lines not on this page · raw message

drivers/pinctrl/pinctrl-bcm2835.c deleted +0 -1072

1,072 lines not on this page · raw message

The rest of this diff is not shown. View the raw message.

Signature · 1 line
1.7.9.5

Part of the diff is not shown here. View the raw message.

#314 lore raw

Re: [PATCH v4 1/4] pinctrl: bcm: consolidate Broadcom pinctrl drivers

Quoted 3 lines · Ray Jui

Consolidate Broadcom pinctrl drivers into drivers/pinctrl/bcm/*

This patch doesn't apply so it needs to be rebased on v4.0-rc1 and resent.

And also when just moving files, always do this:

git format-patch -M ...

So that file movement is detected, and the patch will apply nicely even if the contents of the file(s) have changed.

Can you also combine this series with the v9 GPIO etc series and send out ONE big patch set that I can look at because right now I'm getting really confused ...

I'll look over the rest and see if there is some remaining technical comments (hopefully mainly patch mechanics remains).

Yours,
Linus Walleij

author maintainer
#315 lore raw

Re: [PATCH v4 1/4] pinctrl: bcm: consolidate Broadcom pinctrl drivers

Hi Linus,

Quoted 19 lines · Linus Walleij
Quoted 3 lines · Ray Jui

Consolidate Broadcom pinctrl drivers into drivers/pinctrl/bcm/*

This patch doesn't apply so it needs to be rebased on v4.0-rc1 and resent.

And also when just moving files, always do this:

git format-patch -M ...

So that file movement is detected, and the patch will apply nicely even if the contents of the file(s) have changed.

Can you also combine this series with the v9 GPIO etc series and send out ONE big patch set that I can look at because right now I'm getting really confused ...

Quoted 6 lines

I'll look over the rest and see if there is some remaining technical comments (hopefully mainly patch mechanics remains).

Yours,
Linus Walleij

Okay. I'll rebase to v4.0-rc1 and consolidate both pinmux and gpio pinctrl patch series into one, which will be PATCH v5.

Thanks,

Ray

author maintainer
#316 lore raw

PATCH v4 2/4 pinctrl: Broadcom Cygnus pinctrl device tree binding

In reply to Ray Jui

Device tree binding documentation for Broadcom Cygnus IOMUX driver

---
 .../bindings/pinctrl/brcm,cygnus-pinmux.txt        |  157 ++++++++++++++++++++
 1 file changed, 157 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinmux.txt
Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinmux.txt new file +157 -0
new file mode 100644
index 0000000..985ad90
@@ -0,0 +1,157 @@
+Broadcom Cygnus IOMUX Controller
+
+The Cygnus IOMUX controller supports group based mux configuration. In
+addition, certain pins can be muxed to GPIO function individually.
+
+Required properties:
+
+- compatible:
+    Must be "brcm,cygnus-pinmux"
+
+- reg:
+    Define the base and range of the I/O address space that contains the Cygnus
+IOMUX registers
+
+Required properties in child nodes:
+
+- function:
+    The mux function to select
+
+- groups:
+    The list of groups to select with a given function
+
+Each child node represents a configuration. Client devices reference the child
+node to enable a mux configuration
+
+For more details, refer to
+Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
+
+For example:
+
+	pinmux: pinmux@0x0301d0c8 {
+		compatible = "brcm,cygnus-pinmux";
+		reg = <0x0301d0c8 0x1b0>;
+
+		i2s_0: i2s_0 {
+			function = "i2s0";
+			groups = "i2s0_0_grp", "i2s0_1_grp";
+		};
+
+		i2s_1: i2s_1 {
+			function = "i2s1";
+			groups = "i2s1_0_grp", "i2s1_1_grp";
+		};
+
+		i2s_2: i2s_2 {
+			function = "i2s2";
+			groups = "i2s2_0_grp", "i2s2_1_grp", "i2s2_2_grp",
+				 "i2s2_3_grp", "i2s2_4_grp";
+		};
+
+		spi_0: spi_0 {
+			function = "spi0";
+			groups = "spi0_grp";
+		};
+	}
+
+	spi0@18028000 {
+			compatible = "arm,pl022", "arm,primecell";
+			reg = <0x18028000 0x1000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-0 = <&spi_0>;
+			clocks = <&axi81_clk>;
+			clock-names = "apb_pclk";
+	};
+
+List of supported functions and groups in Cygnus:
+
+"i2s0": "i2s0_0_grp", "i2s0_1_grp"
+
+"i2s1": "i2s1_0_grp", "i2s1_1_grp"
+
+"i2s2": "i2s2_0_grp", "i2s2_1_grp", "i2s2_2_grp", "i2s2_3_grp", "i2s2_4_grp"
+
+"spdif": "spdif_grp"
+
+"pwm0": "pwm0_grp"
+
+"pwm1": "pwm1_grp"
+
+"pwm2": "pwm2_grp"
+
+"pwm3": "pwm3_grp"
+
+"pwm4": "pwm4_grp"
+
+"pwm5": "pwm5_grp"
+
+"key": "key0_grp", "key1_grp", "key2_grp", "key3_grp", "key4_grp", "key5_grp",
+"key6_grp", "key7_grp", "key8_grp", "key9_grp", "key10_grp", "key11_grp",
+"key12_grp", "key13_grp", "key14_grp", "key15_grp"
+
+"audio_dte": "audio_dte0_grp", "audio_dte1_grp", "audio_dte2_grp", "audio_dte3_grp"
+
+"smart_card0": "smart_card0_grp", "smart_card0_fcb_grp"
+
+"smart_card1": "smart_card1_grp", "smart_card1_fcb_grp"
+
+"spi0": "spi0_grp"
+
+"spi1": "spi1_grp"
+
+"spi2": "spi2_grp"
+
+"spi3": "spi3_grp"
+
+"spi4": "spi4_0_grp", "spi4_1_grp"
+
+"spi5": "spi5_grp"
+
+"sw_led0": "sw_led0_0_grp", "sw_led0_1_grp"
+
+"sw_led1": "sw_led1_grp"
+
+"sw_led2": "sw_led2_0_grp", "sw_led2_1_grp"
+
+"d1w": "d1w_grp"
+
+"lcd": "lcd_grp"
+
+"sram": "sram_0_grp", "sram_1_grp"
+
+"uart0": "uart0_grp"
+
+"uart1": "uart1_grp", "uart1_dte_grp"
+
+"uart2": "uart2_grp"
+
+"uart3": "uart3_grp"
+
+"uart4": "uart4_grp"
+
+"qspi": "qspi_0_grp", "qspi_1_grp"
+
+"nand": "nand_grp"
+
+"sdio0": "sdio0_grp", "sdio0_cd_grp", "sdio0_mmc_grp"
+
+"sdio1": "sdio1_data_0_grp", "sdio1_data_1_grp", "sdio1_cd_grp",
+"sdio1_led_grp", "sdio1_mmc_grp"
+
+"can0": "can0_grp"
+
+"can1": "can1_grp"
+
+"cam": "cam_led_grp", "cam_0_grp", "cam_1_grp"
+
+"bsc1": "bsc1_grp"
+
+"pcie_clkreq": "pcie_clkreq_grp"
+
+"usb0_oc": "usb0_oc_grp"
+
+"usb1_oc": "usb1_oc_grp"
+
+"usb2_oc": "usb2_oc_grp"
Signature · 1 line
1.7.9.5
author maintainer
#317 lore raw

PATCH v4 3/4 pinctrl: cygnus: add initial IOMUX driver support

In reply to Ray Jui

This adds the initial driver support for the Broadcom Cygnus IOMUX controller. The Cygnus IOMUX controller supports group based mux configuration but allows certain pins to be muxed to GPIO individually

---
 drivers/pinctrl/bcm/Kconfig              |   13 +
 drivers/pinctrl/bcm/Makefile             |    5 +-
 drivers/pinctrl/bcm/pinctrl-cygnus-mux.c | 1088 ++++++++++++++++++++++++++++++
 3 files changed, 1104 insertions(+), 2 deletions(-)
 create mode 100644 drivers/pinctrl/bcm/pinctrl-cygnus-mux.c
drivers/pinctrl/bcm/Kconfig +13 -0
index bc6d048..eb13201 100644
@@ -19,3 +19,16 @@ config PINCTRL_BCM2835
 	bool
 	select PINMUX
 	select PINCONF
+
+config PINCTRL_CYGNUS_MUX
+	bool "Broadcom Cygnus IOMUX driver"
+	depends on (ARCH_BCM_CYGNUS || COMPILE_TEST)
+	select PINMUX
+	select GENERIC_PINCONF
+	default ARCH_BCM_CYGNUS
+	help
+	  Say yes here to enable the Broadcom Cygnus IOMUX driver.
+
+	  The Broadcom Cygnus IOMUX driver supports group based IOMUX
+	  configuration, with the exception that certain individual pins
+	  can be overrided to GPIO function
drivers/pinctrl/bcm/Makefile +3 -2
index 7ba80a3..bb6beb6 100644
@@ -1,4 +1,5 @@
 # Broadcom pinctrl support
 
-obj-$(CONFIG_PINCTRL_BCM281XX)	+= pinctrl-bcm281xx.o
-obj-$(CONFIG_PINCTRL_BCM2835)	+= pinctrl-bcm2835.o
+obj-$(CONFIG_PINCTRL_BCM281XX)		+= pinctrl-bcm281xx.o
+obj-$(CONFIG_PINCTRL_BCM2835)		+= pinctrl-bcm2835.o
+obj-$(CONFIG_PINCTRL_CYGNUS_MUX)	+= pinctrl-cygnus-mux.o
drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file +1088 -0

1,088 lines not on this page · raw message

Signature · 1 line
1.7.9.5
author maintainer
#318 lore raw

PATCH v4 4/4 ARM: dts: enable IOMUX for Broadcom Cygnus

In reply to Ray Jui

This enables the IOMUX support for Broadcom Cygnus SoC

---
 arch/arm/boot/dts/bcm-cygnus.dtsi |    6 ++++++
 1 file changed, 6 insertions(+)
arch/arm/boot/dts/bcm-cygnus.dtsi +6 -0
index 5126f9e..b014ce5 100644
@@ -54,6 +54,12 @@
 
 	/include/ "bcm-cygnus-clock.dtsi"
 
+	pinctrl: pinctrl@0x0301d0c8 {
+		compatible = "brcm,cygnus-pinmux";
+		reg = <0x0301d0c8 0x30>,
+		      <0x0301d24c 0x2c>;
+	};
+
 	amba {
 		#address-cells = <1>;
 		#size-cells = <1>;
Signature · 1 line
1.7.9.5
Tested-by on the v4 series
#319 lore raw

Re: [PATCH v4 0/4] Add pinctrl support to Broadcom Cygnus SoC

In reply to Ray Jui
Quoted 21 lines · Ray Jui

This patchset contains the initial pinctrl (IOMUX) support for the Broadcom Cygnus SoC. The Cygnus IOMUX controller supports group based mux configuration and allows certain pins to be muxed to GPIO function individually

Changes from v3:
- Fix the driver to have more proper use of "const" in various places
- Other minor improvements

Changes from v2:
- Consolidate all Broadcom pinctrl drivers into drivers/pinctrl/bcm/*
- Change the Cygnus IOMUX driver to use standard Linux pinctrl subnode
properties such as "function" and "groups" for pinmux configuration, instead of non-standard properties such as "brcm,function" and "brcm,group"
- Use real function names like "spi0", "lcd", "key", and etc. instead of HW
specific mux names like "alt1", "alt2", "alt3", and etc.
- Add suffix "grp" to all group names
- Add support to allow individual pins to be muxed to GPIO function through
subsystem callbacks "gpio_request_enable" and "gpio_disable_free", and get rid of all GPIO groups
- Other minor improvements in the driver

Quoted 2 lines

Changes from v1:
- Fix a typo in device tree binding document

FWIW I tested this series on BCM958305K SVK.

Thanks.

Signature · 1 line
Dmitry
author maintainer
#320 lore raw

v8 cover Add gpio/pinconf support to Broadcom Cygnus SoC

In reply to a message not in the archive

This patchset contains the initial GPIO/PINCONF support for the Broadcom Cygnus SoC.

Cygnus has 3 GPIO controllers: 1) the ASIU GPIO; 2) the chipCommonG GPIO; and 3) the ALWAYS-ON GPIO. All 3 types of GPIO controllers are supported by the this driver.

All 3 Cygnus GPIO controllers support basic PINCONF functions such as bias pull up, pull down, and drive strength configurations, when these pins are muxed to GPIO.

Pins from the ASIU GPIO can be individually muxed to GPIO function, through interaction with the Cygnus IOMUX controller.

Note this patchset has a dependency on the other patchset "Add pinctrl support to Broadcom Cygnus SoC" that is also under review

Changes from v7:
- Use 'bool' instead of 'int' for flag that indicates pinmux support in the driver
- Call put_device to drop reference to the pinmux dev after call to of_find_device_by_node
- Replace kasprintf with devm_kasprintf and remove memory deallocation logic in the driver
- Set suppress_bind_attrs to true for the driver

Changes from v6:
- Move the driver from drivers/gpio/* to drivers/pinctrl/* since this driver supports both GPIO and some basic PINCONF features
- Support PINCONF features through standard DT subnodes properties including "bias-disable", "bias-pull-up", "bias-pull-down", and "drive-strength", by creating local PINCONF controller
- Add support to allow individual ASIU GPIO pins to be muxed as GPIO, through interactions with the Cygnus IOMUX driver
- Convert the driver to use standard GPIOCHIP_IRQ APIs. This helps to reduce customized code in the driver
- Other miscellaneous imrpovements in the driver
- Enable GPIO based phone hook detection support for BCM911360 phone factor board

Changes from v5:
- Get rid of DT property "linux,gpio-base". Use dynamic allocation for GPIO base number

Changes from v4:
- Use DT property "linux,gpio-base" to define GPIO base number
- factorize common code to improve code readability and reduce code size
- remove "bcm_" prefix on function and struct names
- improve debugging prints
- default GPIO_BCM_CYGNUS to y in Kconfig (it still depends on ARCH_BCM_CYGNUS). This way we do not need to select it from the arch/arm/mach-bcm/Kconfig
- Get rid of redundant MAINTAINER entry for this driver. It will be maintained by Broadcom iProc/Cygnus maintainers
- Update device tree document based on driver changes

Changes from v3:
- Fix dt property tpyo
- Fix incorrect GPIO compatible ID in device tree binding document example

Changes from v2:
- Consolidate different compatible IDs into "brcm,cygnus-gpio"
- Get rid of redundant "no-interrupt" property

Changes from v1:
- Get rid of inline qualifier
- Get rid of redundant check in the ISR
- Other minor fixes to imrove code readability

Ray Jui (4):
pinctrl: Cygnus: define Broadcom Cygnus GPIO/PINCONF binding
pinctrl: cygnus: add gpio/pinconf driver
ARM: dts: enable GPIO for Broadcom Cygnus
ARM: dts: cygnus: enable GPIO based hook detection

 .../bindings/pinctrl/brcm,cygnus-gpio.txt          |  102 +++
 arch/arm/boot/dts/bcm-cygnus.dtsi                  |   33 +
 arch/arm/boot/dts/bcm911360_entphn.dts             |   13 +
 drivers/pinctrl/bcm/Kconfig                        |   22 +
 drivers/pinctrl/bcm/Makefile                       |    1 +
 drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c          |  907 ++++++++++++++++++++
 6 files changed, 1078 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-gpio.txt
 create mode 100644 drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c
Signature · 1 line
1.7.9.5
author maintainer
#321 lore raw

PATCH v8 1/4 pinctrl: Cygnus: define Broadcom Cygnus GPIO/PINCONF binding

Document the GPIO/PINCONF device tree binding for Broadcom Cygnus SoC

---
 .../bindings/pinctrl/brcm,cygnus-gpio.txt          |  102 ++++++++++++++++++++
 1 file changed, 102 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/brcm,cygnus-gpio.txt
Documentation/devicetree/bindings/pinctrl/brcm,cygnus-gpio.txt new file +102 -0
new file mode 100644
index 0000000..9b9196c
@@ -0,0 +1,102 @@
+Broadcom Cygnus GPIO/PINCONF Controller
+
+Required properties:
+
+- compatible:
+    Must be "brcm,cygnus-gpio"
+
+- reg:
+    Define the base and range of the I/O address space that contains the Cygnus
+GPIO/PINCONF controller registers
+
+- ngpios:
+    Total number of GPIOs the controller provides
+
+- #gpio-cells:
+    Must be two. The first cell is the GPIO pin number (within the
+controller's pin space) and the second cell is used for the following:
+    bit[0]: polarity (0 for active high and 1 for active low)
+
+- gpio-controller:
+    Specifies that the node is a GPIO controller
+
+Optional properties:
+
+- interrupts:
+    Interrupt ID
+
+- interrupt-controller:
+    Specifies that the node is an interrupt controller
+
+- pinmux:
+    Specifies the phandle to the IOMUX device, where pins can be individually
+muxed to GPIO
+
+Supported generic PINCONF properties in child nodes:
+
+- pins:
+    The list of pins (within the controller's own pin space) that properties
+in the node apply to. Pin names are "gpio-<pin>"
+
+- bias-disable:
+    Disable pin bias
+
+- bias-pull-up:
+    Enable internal pull up resistor
+
+- bias-pull-down:
+    Enable internal pull down resistor
+
+- drive-strength:
+    Valid drive strength values include 2, 4, 6, 8, 10, 12, 14, 16 (mA)
+
+Example:
+	gpio_ccm: gpio@1800a000 {
+		compatible = "brcm,cygnus-gpio";
+		reg = <0x1800a000 0x50>,
+		      <0x0301d164 0x20>;
+		ngpios = <24>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-controller;
+
+		touch_pins: touch_pins {
+			pwr: pwr {
+				pins = "gpio-0";
+				drive-strength = <16>;
+			};
+
+			event: event {
+				pins = "gpio-1";
+				bias-pull-up;
+			};
+		};
+	};
+
+	gpio_asiu: gpio@180a5000 {
+		compatible = "brcm,cygnus-gpio";
+		reg = <0x180a5000 0x668>;
+		ngpios = <146>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-controller;
+	};
+
+	/*
+	 * Touchscreen that uses the CCM GPIO 0 and 1
+	 */
+	tsc {
+		...
+		...
+		gpio-pwr = <&gpio_ccm 0 0>;
+		gpio-event = <&gpio_ccm 1 0>;
+	};
+
+	/* Bluetooth that uses the ASIU GPIO 5, with polarity inverted */
+	bluetooth {
+		...
+		...
+		bcm,rfkill-bank-sel = <&gpio_asiu 5 1>
+	}
Signature · 1 line
1.7.9.5
author maintainer
#322 lore raw

PATCH v8 2/4 pinctrl: cygnus: add gpio/pinconf driver

In reply to Ray Jui

This adds the initial support of the Broadcom Cygnus GPIO/PINCONF driver that supports all 3 GPIO controllers on Cygnus including the ASIU GPIO controller, the chipCommonG GPIO controller, and the always-on GPIO controller. Basic PINCONF configurations such as bias pull up/down, and drive strength are also supported in this driver.

Pins from the ASIU GPIO controller can be individually muxed to GPIO function, through interaction with the Cygnus IOMUX controller

---
 drivers/pinctrl/bcm/Kconfig               |   22 +
 drivers/pinctrl/bcm/Makefile              |    1 +
 drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c |  907 +++++++++++++++++++++++++++++
 3 files changed, 930 insertions(+)
 create mode 100644 drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c
drivers/pinctrl/bcm/Kconfig +22 -0
index eb13201..cd11d4d 100644
@@ -20,6 +20,28 @@ config PINCTRL_BCM2835
 	select PINMUX
 	select PINCONF
 
+config PINCTRL_CYGNUS_GPIO
+	bool "Broadcom Cygnus GPIO (with PINCONF) driver"
+	depends on OF_GPIO && ARCH_BCM_CYGNUS
+	select GPIOLIB_IRQCHIP
+	select PINCONF
+	select GENERIC_PINCONF
+	default ARCH_BCM_CYGNUS
+	help
+	  Say yes here to enable the Broadcom Cygnus GPIO driver.
+
+	  The Broadcom Cygnus SoC has 3 GPIO controllers including the ASIU
+	  GPIO controller (ASIU), the chipCommonG GPIO controller (CCM), and
+	  the always-ON GPIO controller (CRMU/AON). All 3 GPIO controllers are
+	  supported by this driver.
+
+	  All 3 Cygnus GPIO controllers support basic PINCONF functions such
+	  as bias pull up, pull down, and drive strength configurations, when
+	  these pins are muxed to GPIO.
+
+	  Pins from the ASIU GPIO can be individually muxed to GPIO function,
+	  through interaction with the Cygnus IOMUX controller.
+
 config PINCTRL_CYGNUS_MUX
 	bool "Broadcom Cygnus IOMUX driver"
 	depends on (ARCH_BCM_CYGNUS || COMPILE_TEST)
drivers/pinctrl/bcm/Makefile +1 -0
index bb6beb6..2b2f70e 100644
@@ -2,4 +2,5 @@
 
 obj-$(CONFIG_PINCTRL_BCM281XX)		+= pinctrl-bcm281xx.o
 obj-$(CONFIG_PINCTRL_BCM2835)		+= pinctrl-bcm2835.o
+obj-$(CONFIG_PINCTRL_CYGNUS_GPIO)	+= pinctrl-cygnus-gpio.o
 obj-$(CONFIG_PINCTRL_CYGNUS_MUX)	+= pinctrl-cygnus-mux.o
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +907 -0

907 lines not on this page · raw message

Signature · 1 line
1.7.9.5
#323 lore raw

Re: [PATCH v8 2/4] pinctrl: cygnus: add gpio/pinconf driver

Hi Ray,

Quoted 25 lines · Ray Jui
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +25 -0
+static int cygnus_gpio_pinmux_add_range(struct cygnus_gpio *chip)
+{
+	struct device_node *node = chip->dev->of_node;
+	struct device_node *pinmux_node;
+	struct platform_device *pinmux_pdev;
+	struct gpio_chip *gc = &chip->gc;
+	int i, ret;
+
+	/* parse DT to find the phandle to the pinmux controller */
+	pinmux_node = of_parse_phandle(node, "pinmux", 0);
+	if (!pinmux_node)
+		return -ENODEV;
+
+	pinmux_pdev = of_find_device_by_node(pinmux_node);
+	if (!pinmux_pdev) {
+		dev_err(chip->dev, "failed to get pinmux device\n");
+		return -EINVAL;
+	}
+
+	/* now need to create the mapping between local GPIO and PINMUX pins */
+	for (i = 0; i < ARRAY_SIZE(cygnus_gpio_pintable); i++) {
+		ret = gpiochip_add_pin_range(gc, dev_name(&pinmux_pdev->dev),
+					     cygnus_gpio_pintable[i].offset,
+					     cygnus_gpio_pintable[i].pin_base,
+					     cygnus_gpio_pintable[i].num_pins);

drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c line 721

Quoted 10 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +10 -0
+		if (ret) {
+			dev_err(chip->dev, "unable to add GPIO pin range\n");
+			goto err_put_device;
+		}
+	}
+
+	chip->pinmux_is_supported = true;
+
+	/* no need for pinmux_pdev device reference anymore */
+	put_device(&pinmux_pdev->dev);

Sorry I did not notice it before, but of_parse_phandle() takes reference to the returned device node, so you need to "put" it here and in error path as well. Actually you can do:

	int ret = 0;

	pinmux_node = of_parse_phandle(node, "pinmux", 0);
	if (!pinmux_node)
		return -ENODEV;

	pinmux_pdev = of_find_device_by_node(pinmux_node);
	/* We do not longer need pinmux node */
	of_node_put(pinmux_node);

	if (!pinmux_dev)
		....

	for (..) {
		...
		if (ret) {
			dev_err(...);
			break;
		}
	}

	chip->pinmux_is_supported = (ret == 0);
	put_device(..);
	return ret;
}

This way you free resources in the same path.

...

drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c line 901

Quoted 13 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +13 -0
+
+static struct platform_driver cygnus_gpio_driver = {
+	.driver = {
+		.name = "cygnus-gpio",
+		.of_match_table = cygnus_gpio_of_match,
+		.suppress_bind_attrs = true,
+	},
+	.probe = cygnus_gpio_probe,
+};
+
+static int __init cygnus_gpio_init(void)
+{
+	return platform_driver_probe(&cygnus_gpio_driver, cygnus_gpio_probe);

When I asked you to add ".suppress_bind_attrs = true" I missed the fact that you were using platform_driver_probe() which already does this internally. However platform_driver_probe() can't handle deferred probing, which may or may not be OK. Is there a chance that any of the resources needed by the driver return -EPROBE_DEFER? If not then it is safe to continue using platform_driver_probe() and you can drop suppress_bind_attrs assignment, otherwise it may be better to switch to platform_driver_register().

Thanks.

Signature · 1 line
Dmitry
author maintainer
#324 lore raw

Re: [PATCH v8 2/4] pinctrl: cygnus: add gpio/pinconf driver

Quoted 69 lines · Dmitry Torokhov

Hi Ray,

Quoted 25 lines · Ray Jui
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +25 -0
+static int cygnus_gpio_pinmux_add_range(struct cygnus_gpio *chip)
+{
+	struct device_node *node = chip->dev->of_node;
+	struct device_node *pinmux_node;
+	struct platform_device *pinmux_pdev;
+	struct gpio_chip *gc = &chip->gc;
+	int i, ret;
+
+	/* parse DT to find the phandle to the pinmux controller */
+	pinmux_node = of_parse_phandle(node, "pinmux", 0);
+	if (!pinmux_node)
+		return -ENODEV;
+
+	pinmux_pdev = of_find_device_by_node(pinmux_node);
+	if (!pinmux_pdev) {
+		dev_err(chip->dev, "failed to get pinmux device\n");
+		return -EINVAL;
+	}
+
+	/* now need to create the mapping between local GPIO and PINMUX pins */
+	for (i = 0; i < ARRAY_SIZE(cygnus_gpio_pintable); i++) {
+		ret = gpiochip_add_pin_range(gc, dev_name(&pinmux_pdev->dev),
+					     cygnus_gpio_pintable[i].offset,
+					     cygnus_gpio_pintable[i].pin_base,
+					     cygnus_gpio_pintable[i].num_pins);

drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c line 721

Quoted 10 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +10 -0
+		if (ret) {
+			dev_err(chip->dev, "unable to add GPIO pin range\n");
+			goto err_put_device;
+		}
+	}
+
+	chip->pinmux_is_supported = true;
+
+	/* no need for pinmux_pdev device reference anymore */
+	put_device(&pinmux_pdev->dev);

Sorry I did not notice it before, but of_parse_phandle() takes reference to the returned device node, so you need to "put" it here and in error path as well. Actually you can do:

	int ret = 0;

	pinmux_node = of_parse_phandle(node, "pinmux", 0);
	if (!pinmux_node)
		return -ENODEV;

	pinmux_pdev = of_find_device_by_node(pinmux_node);
	/* We do not longer need pinmux node */
	of_node_put(pinmux_node);

	if (!pinmux_dev)
		....

	for (..) {
		...
		if (ret) {
			dev_err(...);
			break;
		}
	}

	chip->pinmux_is_supported = (ret == 0);
	put_device(..);
	return ret;
}
Quoted 2 lines

This way you free resources in the same path.

Thanks. I'll make the change.

Quoted 25 lines

...

drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c line 901

Quoted 13 lines
drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file +13 -0
+
+static struct platform_driver cygnus_gpio_driver = {
+	.driver = {
+		.name = "cygnus-gpio",
+		.of_match_table = cygnus_gpio_of_match,
+		.suppress_bind_attrs = true,
+	},
+	.probe = cygnus_gpio_probe,
+};
+
+static int __init cygnus_gpio_init(void)
+{
+	return platform_driver_probe(&cygnus_gpio_driver, cygnus_gpio_probe);

When I asked you to add ".suppress_bind_attrs = true" I missed the fact that you were using platform_driver_probe() which already does this internally. However platform_driver_probe() can't handle deferred probing, which may or may not be OK. Is there a chance that any of the resources needed by the driver return -EPROBE_DEFER? If not then it is safe to continue using platform_driver_probe() and you can drop suppress_bind_attrs assignment, otherwise it may be better to switch to platform_driver_register().

Quoted 2 lines

Thanks.

No I do not expect any resource that this driver depends on to return -EPROBE_DEFER. The IOMUX driver that this driver depends on should be initialized before this driver.

I'll drop .suppress_bind_attrs then. Thanks.

Ray

author maintainer
#325 lore raw

PATCH v8 3/4 ARM: dts: enable GPIO for Broadcom Cygnus

In reply to Ray Jui

This enables all 3 GPIO controllers including the ASIU GPIO, the chipcommonG GPIO, and the ALWAYS-ON GPIO, for Broadcom Cygnus SoC

---
 arch/arm/boot/dts/bcm-cygnus.dtsi |   33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)
arch/arm/boot/dts/bcm-cygnus.dtsi +33 -0
index b014ce5..a3b8621 100644
@@ -60,6 +60,39 @@
 		      <0x0301d24c 0x2c>;
 	};
 
+	gpio_crmu: gpio@03024800 {
+		compatible = "brcm,cygnus-gpio";
+		reg = <0x03024800 0x50>,
+		      <0x03024008 0x18>;
+		ngpios = <6>;
+		#gpio-cells = <2>;
+		gpio-controller;
+	};
+
+	gpio_ccm: gpio@1800a000 {
+		compatible = "brcm,cygnus-gpio";
+		reg = <0x1800a000 0x50>,
+		      <0x0301d164 0x20>;
+		ngpios = <24>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-controller;
+	};
+
+	gpio_asiu: gpio@180a5000 {
+		compatible = "brcm,cygnus-gpio";
+		reg = <0x180a5000 0x668>;
+		ngpios = <146>;
+		#gpio-cells = <2>;
+		gpio-controller;
+
+		pinmux = <&pinctrl>;
+
+		interrupt-controller;
+		interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+	};
+
 	amba {
 		#address-cells = <1>;
 		#size-cells = <1>;
Signature · 1 line
1.7.9.5
author maintainer
#326 lore raw

PATCH v8 4/4 ARM: dts: cygnus: enable GPIO based hook detection

In reply to Ray Jui

This enables GPIO based phone hook detection for Broadcom BCM911360 phone factor board (bcm911360_entphn)

---
 arch/arm/boot/dts/bcm911360_entphn.dts |   13 +++++++++++++
 1 file changed, 13 insertions(+)
arch/arm/boot/dts/bcm911360_entphn.dts +13 -0
index d2ee952..7db4843 100644
@@ -33,6 +33,7 @@
 /dts-v1/;
 
 #include "bcm-cygnus.dtsi"
+#include "dt-bindings/input/input.h"
 
 / {
 	model = "Cygnus Enterprise Phone (BCM911360_ENTPHN)";
@@ -50,4 +51,16 @@
 	uart3: serial@18023000 {
 		status = "okay";
 	};
+
+	gpio_keys {
+		compatible = "gpio-keys";
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		hook {
+			label = "HOOK";
+			linux,code = <KEY_O>;
+			gpios = <&gpio_asiu 48 0>;
+		};
+	};
 };
Signature · 1 line
1.7.9.5
author maintainer
#327 lore raw

v5 cover Add common clock support for Broadcom iProc architecture

In reply to a message not in the archive

This patchset contains the initial common clock support for Broadcom's iProc family of SoCs. The iProc clock architecture comprises of various PLLs, e.g., ARMPLL, GENPLL, LCPLL0, MIPIPLL, and etc. An onboard crystal serves as the basic reference clock for these PLLs. Each PLL may have several leaf clocks. One special group of clocks is the ASIU clocks, which are dervied directly from the crystal reference clock.

This patchset also contains the basic clock support for the Broadcom Cygnus SoC, which implements the iProc clock architecture

Changes from v4:
- Add of_clk_get_parent_rate helper function into the clock framework
- Switch to use of_clk_get_parent_rate in the iProc PLL clock driver

Changes from v3:
- Fix incorrect use of passing in of_clk_src_onecell_get when adding ARM PLL and other iProc PLLs as clock provider. These PLLs have zero cells in DT and thefore of_clk_src_simple_get should be used instead
- Rename Cygnus MIPI PLL Channel 2 clock from BCM_CYGNUS_MIPIPLL_CH2_UNUSED to BCM_CYGNUS_MIPIPLL_CH2_V3D, since a 3D graphic rendering engine has been integrated into Cygnus revision B0 and has its core clock running off MIPI PLL Channel 2
- Changed default MIPI PLL VCO frequency from 1.75 GHz to 2.1 GHz. This allows us to derive 300 MHz V3D clock from channel 2 through the post divisor

Changes from v2:
- Re-arrange Cygnus clock/pll init functions so each init function is right next to its clock table
- Removed #defines for number of clocks in Cygnus. Have the number of clocks automatically determined based on array size of the clock table

Changes from v1:
- Separate drivers/clk/Makefile change for drivers/clk/bcm out to a standalone patch

Ray Jui (6):
clk: add of_clk_get_parent_rate function
clk: iproc: define Broadcom iProc clock binding
clk: iproc: add initial common clock support
clk: Change bcm clocks build dependency
clk: cygnus: add clock support for Broadcom Cygnus
ARM: dts: enable clock support for Broadcom Cygnus

 arch/arm/boot/dts/bcm-cygnus-clock.dtsi |  110 ++++++--
 arch/arm/boot/dts/bcm-cygnus.dtsi       |    2 +-
 brcm,iproc-clocks.txt                   |  178 ++++++++++++
 drivers/clk/Makefile                    |    2 +-
 drivers/clk/bcm/Kconfig                 |    9 +
 drivers/clk/bcm/Makefile                |    2 +
 drivers/clk/bcm/clk-cygnus.c            |  277 +++++++++++++++++++
 drivers/clk/bcm/clk-iproc-armpll.c      |  282 +++++++++++++++++++
 drivers/clk/bcm/clk-iproc-asiu.c        |  275 ++++++++++++++++++
 drivers/clk/bcm/clk-iproc-clk.c         |  238 ++++++++++++++++
 drivers/clk/bcm/clk-iproc-pll.c         |  461 +++++++++++++++++++++++++++++++
 drivers/clk/bcm/clk-iproc.h             |  157 +++++++++++
 drivers/clk/clk.c                       |   17 ++
 include/dt-bindings/clock/bcm-cygnus.h  |   65 +++++
 include/linux/clk-provider.h            |    6 +-
 15 files changed, 2053 insertions(+), 28 deletions(-)
 create mode 100644 brcm,iproc-clocks.txt
 create mode 100644 drivers/clk/bcm/clk-cygnus.c
 create mode 100644 drivers/clk/bcm/clk-iproc-armpll.c
 create mode 100644 drivers/clk/bcm/clk-iproc-asiu.c
 create mode 100644 drivers/clk/bcm/clk-iproc-clk.c
 create mode 100644 drivers/clk/bcm/clk-iproc-pll.c
 create mode 100644 drivers/clk/bcm/clk-iproc.h
 create mode 100644 include/dt-bindings/clock/bcm-cygnus.h
Signature · 1 line
1.7.9.5
author maintainer
#328 lore raw

PATCH v5 1/6 clk: add of_clk_get_parent_rate function

Sometimes a clock needs to know the rate of its parent before itself is registered to the framework. An example is that a PLL may need to initialize itself to a specific VCO frequency, before registering to the framework. The parent rate needs to be known, for PLL multipliers and divisors to be configured properly.

Introduce helper function of_clk_get_parent_rate, which can be used to obtain the parent rate of a clock, given a device node and index.

---
 drivers/clk/clk.c            |   17 +++++++++++++++++
 include/linux/clk-provider.h |    6 +++++-
 2 files changed, 22 insertions(+), 1 deletion(-)
drivers/clk/clk.c +17 -0
index d48ac71..e1893a2 100644
@@ -2581,6 +2581,23 @@ const char *of_clk_get_parent_name(struct device_node *np, int index)
 }
 EXPORT_SYMBOL_GPL(of_clk_get_parent_name);
 
+unsigned long of_clk_get_parent_rate(struct device_node *np, int index)
+{
+	const char *parent_name;
+	struct clk *parent_clk;
+
+	parent_name = of_clk_get_parent_name(np, index);
+	if (!parent_name)
+		return 0;
+
+	parent_clk = __clk_lookup(parent_name);
+	if (!parent_clk)
+		return 0;
+
+	return clk_get_rate(parent_clk);
+}
+EXPORT_SYMBOL_GPL(of_clk_get_parent_rate);
+
 struct clock_provider {
 	of_clk_init_cb_t clk_init_cb;
 	struct device_node *np;
include/linux/clk-provider.h +5 -1
index d936409..e1e2d95 100644
@@ -585,7 +585,7 @@ struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec,
 struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data);
 int of_clk_get_parent_count(struct device_node *np);
 const char *of_clk_get_parent_name(struct device_node *np, int index);
-
+unsigned long of_clk_get_parent_rate(struct device_node *np, int index);
 void of_clk_init(const struct of_device_id *matches);
 
 #else /* !CONFIG_OF */
@@ -614,6 +614,10 @@ static inline const char *of_clk_get_parent_name(struct device_node *np,
 {
 	return NULL;
 }
+static unsigned long of_clk_get_parent_rate(struct device_node *np, int index)
+{
+	return 0;
+}
 #define of_clk_init(matches) \
 	{ while (0); }
 #endif /* CONFIG_OF */
Signature · 1 line
1.7.9.5
Reviewed-by on v5 1/6
#329 lore raw

Re: [PATCH v5 1/6] clk: add of_clk_get_parent_rate function

Quoted 10 lines · Ray Jui

Sometimes a clock needs to know the rate of its parent before itself is registered to the framework. An example is that a PLL may need to initialize itself to a specific VCO frequency, before registering to the framework. The parent rate needs to be known, for PLL multipliers and divisors to be configured properly.

Introduce helper function of_clk_get_parent_rate, which can be used to obtain the parent rate of a clock, given a device node and index.

Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
#330 lore raw

Re: [PATCH v5 1/6] clk: add of_clk_get_parent_rate function

In reply to Ray Jui

Hi Ray,

Quoted 8 lines · Ray Jui

Sometimes a clock needs to know the rate of its parent before itself is registered to the framework. An example is that a PLL may need to initialize itself to a specific VCO frequency, before registering to the framework. The parent rate needs to be known, for PLL multipliers and divisors to be configured properly.

Introduce helper function of_clk_get_parent_rate, which can be used to obtain the parent rate of a clock, given a device node and index.

I can't see how this patch helps you. First it's not guaranteed that the parent is already registered, what do you do in this case? Then the clock framework doesn't require that you initialize the PLL before registering. That can be done in the clk ops later.

Sascha

Signature · 4 lines
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
author maintainer
#331 lore raw

Re: [PATCH v5 1/6] clk: add of_clk_get_parent_rate function

Hi Sascha,

Quoted 9 lines · Sascha Hauer

Hi Ray,

Quoted 6 lines · Ray Jui

Sometimes a clock needs to know the rate of its parent before itself is registered to the framework. An example is that a PLL may need to initialize itself to a specific VCO frequency, before registering to the framework. The parent rate needs to be known, for PLL multipliers and divisors to be configured properly.

Quoted 5 lines
Quoted 2 lines

Introduce helper function of_clk_get_parent_rate, which can be used to obtain the parent rate of a clock, given a device node and index.

I can't see how this patch helps you. First it's not guaranteed that the parent is already registered, what do you do in this case?

In the case when clock parent is not found, as you can see from the code, it simply returns zero, just like other clk get rate APIs.

I thought the order of clock registration is based on order of the clock nodes in device tree. It makes sense to me to declare the parent clock before a child clock, so it's guaranteed that the parent is registered before the child.

Quoted 2 lines

Then the clock framework doesn't require that you initialize the PLL before registering. That can be done in the clk ops later.

Sure it's not mandatory. But what's wrong with me choosing to initialize the PLL clock to a known frequency before registering it to the framework?

Ray

#332 lore raw

Re: [PATCH v5 1/6] clk: add of_clk_get_parent_rate function

Quoted 12 lines · Ray Jui

Hi Sascha,

Quoted 9 lines · Sascha Hauer

Hi Ray,

Quoted 6 lines · Ray Jui

Sometimes a clock needs to know the rate of its parent before itself is registered to the framework. An example is that a PLL may need to initialize itself to a specific VCO frequency, before registering to the framework. The parent rate needs to be known, for PLL multipliers and divisors to be configured properly.

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

Introduce helper function of_clk_get_parent_rate, which can be used to obtain the parent rate of a clock, given a device node and index.

I can't see how this patch helps you. First it's not guaranteed that the parent is already registered, what do you do in this case?

In the case when clock parent is not found, as you can see from the code, it simply returns zero, just like other clk get rate APIs.

Yes, but what do you do with the 0 result then in your PLL initialization?

Quoted 5 lines

I thought the order of clock registration is based on order of the clock nodes in device tree. It makes sense to me to declare the parent clock before a child clock, so it's guaranteed that the parent is registered before the child.

No, you can't rely on that. The order of the device nodes may happen to define the order of clock initialization now, but that may change. device nodes are usually ordered by bus addresses, not by intended initialization order. Even if you reorder them everything must still work.

Quoted 6 lines
Quoted 3 lines

Then the clock framework doesn't require that you initialize the PLL before registering. That can be done in the clk ops later.

Sure it's not mandatory. But what's wrong with me choosing to initialize the PLL clock to a known frequency before registering it to the framework?

Appearantly you don't know the (input) frequency of the PLL when registering it to the framework, so the question must be: What's wrong with keeping it uninitialized?

If the PLL is unused then you don't care about it's initialization status. If it happens to be enabled by a bootloader and still unused at late_initcall time the clock framework will disable it so you have a known state then. If a consumer for the PLL appears it's its job to initialize it through the clk api.

Sascha

Signature · 4 lines
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
author maintainer
#333 lore raw

Re: [PATCH v5 1/6] clk: add of_clk_get_parent_rate function

Quoted 16 lines · Sascha Hauer
Quoted 15 lines · Ray Jui

Hi Sascha,

Quoted 12 lines · Sascha Hauer

Hi Ray,

Quoted 8 lines · Ray Jui

Sometimes a clock needs to know the rate of its parent before itself is registered to the framework. An example is that a PLL may need to initialize itself to a specific VCO frequency, before registering to the framework. The parent rate needs to be known, for PLL multipliers and divisors to be configured properly.

Introduce helper function of_clk_get_parent_rate, which can be used to obtain the parent rate of a clock, given a device node and index.

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

I can't see how this patch helps you. First it's not guaranteed that the parent is already registered, what do you do in this case?

In the case when clock parent is not found, as you can see from the code, it simply returns zero, just like other clk get rate APIs.

Yes, but what do you do with the 0 result then in your PLL initialization?

As of the current code, it fails the PLL frequency initialization and bails out. Thinking about it more, it actually makes more sense to just warn and still go ahead to register the clock, in which case it will use whatever default frequency after chip power on reset or a frequency configured in the bootloader.

Quoted 6 lines
Quoted 5 lines

I thought the order of clock registration is based on order of the clock nodes in device tree. It makes sense to me to declare the parent clock before a child clock, so it's guaranteed that the parent is registered before the child.

Quoted 6 lines

No, you can't rely on that. The order of the device nodes may happen to define the order of clock initialization now, but that may change. device nodes are usually ordered by bus addresses, not by intended initialization order. Even if you reorder them everything must still work.

Okay I get your point that the order of device nodes may not be relied on for device initialization order. But then another mechanism should be deployed to give developers the option to decide on the clock initialization sequence. It can be optional but it should be there.

Quoted 11 lines
Quoted 6 lines
Quoted 3 lines

Then the clock framework doesn't require that you initialize the PLL before registering. That can be done in the clk ops later.

Sure it's not mandatory. But what's wrong with me choosing to initialize the PLL clock to a known frequency before registering it to the framework?

Appearantly you don't know the (input) frequency of the PLL when registering it to the framework, so the question must be: What's wrong with keeping it uninitialized?

Quoted 8 lines

If the PLL is unused then you don't care about it's initialization status. If it happens to be enabled by a bootloader and still unused at late_initcall time the clock framework will disable it so you have a known state then. If a consumer for the PLL appears it's its job to initialize it through the clk api.

Sascha

Okay, what we need here is to initialize the PLL to a desired frequency, based on device tree settings (since it will be configured differently, among different boards). This is a PLL that 1) has limited options of frequencies which it can be configured to, and 2) has multiple child clocks, where is a more suitable place to initialize it to the desired frequency than right before registering it to the framework? I know a lot of people do it in the bootloader, but I thought we should be given the flexibility of configuring it in the kernel.

When you say "consumers", do you mean 1) the device driver that uses the PLL; or 2) the device driver that use the child clock of the PLL? If it's case 1), then we don't really have a device driver that directly uses the PLL, and I thought that's quite normal, as most PLLs don't directly feed into any peripherals.

We do have multiple device drivers that use the child clocks of the PLL, but it makes no sense to configure the PLL clock in any of those drivers.

Ray

#334 lore raw

Re: [PATCH v5 1/6] clk: add of_clk_get_parent_rate function

Quoted 79 lines · Ray Jui
Quoted 16 lines · Sascha Hauer
Quoted 15 lines · Ray Jui

Hi Sascha,

Quoted 12 lines · Sascha Hauer

Hi Ray,

Quoted 8 lines · Ray Jui

Sometimes a clock needs to know the rate of its parent before itself is registered to the framework. An example is that a PLL may need to initialize itself to a specific VCO frequency, before registering to the framework. The parent rate needs to be known, for PLL multipliers and divisors to be configured properly.

Introduce helper function of_clk_get_parent_rate, which can be used to obtain the parent rate of a clock, given a device node and index.

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

I can't see how this patch helps you. First it's not guaranteed that the parent is already registered, what do you do in this case?

In the case when clock parent is not found, as you can see from the code, it simply returns zero, just like other clk get rate APIs.

Yes, but what do you do with the 0 result then in your PLL initialization?

As of the current code, it fails the PLL frequency initialization and bails out. Thinking about it more, it actually makes more sense to just warn and still go ahead to register the clock, in which case it will use whatever default frequency after chip power on reset or a frequency configured in the bootloader.

Quoted 6 lines
Quoted 5 lines

I thought the order of clock registration is based on order of the clock nodes in device tree. It makes sense to me to declare the parent clock before a child clock, so it's guaranteed that the parent is registered before the child.

Quoted 6 lines

No, you can't rely on that. The order of the device nodes may happen to define the order of clock initialization now, but that may change. device nodes are usually ordered by bus addresses, not by intended initialization order. Even if you reorder them everything must still work.

Okay I get your point that the order of device nodes may not be relied on for device initialization order. But then another mechanism should be deployed to give developers the option to decide on the clock initialization sequence. It can be optional but it should be there.

Quoted 11 lines
Quoted 6 lines
Quoted 3 lines

Then the clock framework doesn't require that you initialize the PLL before registering. That can be done in the clk ops later.

Sure it's not mandatory. But what's wrong with me choosing to initialize the PLL clock to a known frequency before registering it to the framework?

Appearantly you don't know the (input) frequency of the PLL when registering it to the framework, so the question must be: What's wrong with keeping it uninitialized?

Quoted 8 lines

If the PLL is unused then you don't care about it's initialization status. If it happens to be enabled by a bootloader and still unused at late_initcall time the clock framework will disable it so you have a known state then. If a consumer for the PLL appears it's its job to initialize it through the clk api.

Sascha

Okay, what we need here is to initialize the PLL to a desired frequency, based on device tree settings (since it will be configured differently, among different boards). This is a PLL that 1) has limited options of frequencies which it can be configured to, and 2) has multiple child clocks, where is a more suitable place to initialize it to the desired frequency than right before registering it to the framework? I know a lot of people do it in the bootloader, but I thought we should be given the flexibility of configuring it in the kernel.

Quoted 5 lines

When you say "consumers", do you mean 1) the device driver that uses the PLL; or 2) the device driver that use the child clock of the PLL? If it's case 1), then we don't really have a device driver that directly uses the PLL, and I thought that's quite normal, as most PLLs don't directly feed into any peripherals.

I meant 1) and 2). Before a consumer comes along the state of the PLL doesn't matter. When a consumer shows up it has to call clk_prepare_enable which (directly or indirectly) will enable your PLL. Then it's still time to apply the default settings you found out during probe of the PLL.

Sascha

Signature · 4 lines
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
#335 lore raw

Re: [PATCH v5 1/6] clk: add of_clk_get_parent_rate function

Quoting Sascha Hauer (2015-02-26 00:43:19)

Quoted 86 lines · Ray Jui
Quoted 79 lines · Sascha Hauer
Quoted 16 lines · Ray Jui
Quoted 15 lines

Hi Sascha,

Quoted 12 lines · Sascha Hauer

Hi Ray,

Quoted 8 lines · Ray Jui

Sometimes a clock needs to know the rate of its parent before itself is registered to the framework. An example is that a PLL may need to initialize itself to a specific VCO frequency, before registering to the framework. The parent rate needs to be known, for PLL multipliers and divisors to be configured properly.

Introduce helper function of_clk_get_parent_rate, which can be used to obtain the parent rate of a clock, given a device node and index.

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

I can't see how this patch helps you. First it's not guaranteed that the parent is already registered, what do you do in this case?

In the case when clock parent is not found, as you can see from the code, it simply returns zero, just like other clk get rate APIs.

Yes, but what do you do with the 0 result then in your PLL initialization?

As of the current code, it fails the PLL frequency initialization and bails out. Thinking about it more, it actually makes more sense to just warn and still go ahead to register the clock, in which case it will use whatever default frequency after chip power on reset or a frequency configured in the bootloader.

Quoted 6 lines
Quoted 5 lines

I thought the order of clock registration is based on order of the clock nodes in device tree. It makes sense to me to declare the parent clock before a child clock, so it's guaranteed that the parent is registered before the child.

Quoted 6 lines

No, you can't rely on that. The order of the device nodes may happen to define the order of clock initialization now, but that may change. device nodes are usually ordered by bus addresses, not by intended initialization order. Even if you reorder them everything must still work.

Okay I get your point that the order of device nodes may not be relied on for device initialization order. But then another mechanism should be deployed to give developers the option to decide on the clock initialization sequence. It can be optional but it should be there.

Quoted 11 lines
Quoted 6 lines
Quoted 3 lines

Then the clock framework doesn't require that you initialize the PLL before registering. That can be done in the clk ops later.

Sure it's not mandatory. But what's wrong with me choosing to initialize the PLL clock to a known frequency before registering it to the framework?

Appearantly you don't know the (input) frequency of the PLL when registering it to the framework, so the question must be: What's wrong with keeping it uninitialized?

Quoted 8 lines

If the PLL is unused then you don't care about it's initialization status. If it happens to be enabled by a bootloader and still unused at late_initcall time the clock framework will disable it so you have a known state then. If a consumer for the PLL appears it's its job to initialize it through the clk api.

Sascha

Okay, what we need here is to initialize the PLL to a desired frequency, based on device tree settings (since it will be configured differently, among different boards). This is a PLL that 1) has limited options of frequencies which it can be configured to, and 2) has multiple child clocks, where is a more suitable place to initialize it to the desired frequency than right before registering it to the framework? I know a lot of people do it in the bootloader, but I thought we should be given the flexibility of configuring it in the kernel.

Quoted 5 lines

When you say "consumers", do you mean 1) the device driver that uses the PLL; or 2) the device driver that use the child clock of the PLL? If it's case 1), then we don't really have a device driver that directly uses the PLL, and I thought that's quite normal, as most PLLs don't directly feed into any peripherals.

Quoted 5 lines

I meant 1) and 2). Before a consumer comes along the state of the PLL doesn't matter. When a consumer shows up it has to call clk_prepare_enable which (directly or indirectly) will enable your PLL. Then it's still time to apply the default settings you found out during probe of the PLL.

My review comments are really for iproc_pll_setup() in patch #3, but the discussion is here so I'll respond to this thread.

I think the root of this problem is that your pll clk_ops does not support .set_rate. That is why your clock driver hacks in a call to pll_set_rate in iproc_pll_setup.

Due to the above shortcoming you also do not use the assigned-clock-rate infrastructure to set your pll rate at registration-time. There is no reason for your driver to re-invent this logic. iproc_pll_setup is fetching the clock-frequency property from DT and then trying to set that rate. Instead please use the generic code.

The right way to handle this is to support a .set_rate callback (looks like you're 90% of the way there with pll_set_rate) and then use the assigned-clock-rates property to specify this from DT.

Regards,
Mike

Quoted 8 lines

Sascha

Signature · 4 lines
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
author maintainer
#336 lore raw

Re: [PATCH v5 1/6] clk: add of_clk_get_parent_rate function

Hi Mike,

Quoted 106 lines · Mike Turquette

Quoting Sascha Hauer (2015-02-26 00:43:19)

Quoted 86 lines · Ray Jui
Quoted 79 lines · Sascha Hauer
Quoted 16 lines · Ray Jui
Quoted 15 lines

Hi Sascha,

Quoted 12 lines · Sascha Hauer

Hi Ray,

Quoted 8 lines · Ray Jui

Sometimes a clock needs to know the rate of its parent before itself is registered to the framework. An example is that a PLL may need to initialize itself to a specific VCO frequency, before registering to the framework. The parent rate needs to be known, for PLL multipliers and divisors to be configured properly.

Introduce helper function of_clk_get_parent_rate, which can be used to obtain the parent rate of a clock, given a device node and index.

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

I can't see how this patch helps you. First it's not guaranteed that the parent is already registered, what do you do in this case?

In the case when clock parent is not found, as you can see from the code, it simply returns zero, just like other clk get rate APIs.

Yes, but what do you do with the 0 result then in your PLL initialization?

As of the current code, it fails the PLL frequency initialization and bails out. Thinking about it more, it actually makes more sense to just warn and still go ahead to register the clock, in which case it will use whatever default frequency after chip power on reset or a frequency configured in the bootloader.

Quoted 6 lines
Quoted 5 lines

I thought the order of clock registration is based on order of the clock nodes in device tree. It makes sense to me to declare the parent clock before a child clock, so it's guaranteed that the parent is registered before the child.

Quoted 6 lines

No, you can't rely on that. The order of the device nodes may happen to define the order of clock initialization now, but that may change. device nodes are usually ordered by bus addresses, not by intended initialization order. Even if you reorder them everything must still work.

Okay I get your point that the order of device nodes may not be relied on for device initialization order. But then another mechanism should be deployed to give developers the option to decide on the clock initialization sequence. It can be optional but it should be there.

Quoted 11 lines
Quoted 6 lines
Quoted 3 lines

Then the clock framework doesn't require that you initialize the PLL before registering. That can be done in the clk ops later.

Sure it's not mandatory. But what's wrong with me choosing to initialize the PLL clock to a known frequency before registering it to the framework?

Appearantly you don't know the (input) frequency of the PLL when registering it to the framework, so the question must be: What's wrong with keeping it uninitialized?

Quoted 8 lines

If the PLL is unused then you don't care about it's initialization status. If it happens to be enabled by a bootloader and still unused at late_initcall time the clock framework will disable it so you have a known state then. If a consumer for the PLL appears it's its job to initialize it through the clk api.

Sascha

Okay, what we need here is to initialize the PLL to a desired frequency, based on device tree settings (since it will be configured differently, among different boards). This is a PLL that 1) has limited options of frequencies which it can be configured to, and 2) has multiple child clocks, where is a more suitable place to initialize it to the desired frequency than right before registering it to the framework? I know a lot of people do it in the bootloader, but I thought we should be given the flexibility of configuring it in the kernel.

Quoted 5 lines

When you say "consumers", do you mean 1) the device driver that uses the PLL; or 2) the device driver that use the child clock of the PLL? If it's case 1), then we don't really have a device driver that directly uses the PLL, and I thought that's quite normal, as most PLLs don't directly feed into any peripherals.

Quoted 5 lines

I meant 1) and 2). Before a consumer comes along the state of the PLL doesn't matter. When a consumer shows up it has to call clk_prepare_enable which (directly or indirectly) will enable your PLL. Then it's still time to apply the default settings you found out during probe of the PLL.

My review comments are really for iproc_pll_setup() in patch #3, but the discussion is here so I'll respond to this thread.

I think the root of this problem is that your pll clk_ops does not support .set_rate. That is why your clock driver hacks in a call to pll_set_rate in iproc_pll_setup.

Due to the above shortcoming you also do not use the assigned-clock-rate infrastructure to set your pll rate at registration-time. There is no reason for your driver to re-invent this logic. iproc_pll_setup is fetching the clock-frequency property from DT and then trying to set that rate. Instead please use the generic code.

Quoted 7 lines

The right way to handle this is to support a .set_rate callback (looks like you're 90% of the way there with pll_set_rate) and then use the assigned-clock-rates property to specify this from DT.

Regards,
Mike

Okay. It's good to know that "assigned-clock-rate" can be used and serve exactly what we need here. I'll update my patch series to use that instead.

In this case, do you think I should still keep of_clk_get_parent_rate in the patch series?

Thanks,

Ray

Quoted 8 lines
Quoted 8 lines

Sascha

Signature · 4 lines
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
#337 lore raw

Re: [PATCH v5 1/6] clk: add of_clk_get_parent_rate function

Quoting Ray Jui (2015-03-06 12:07:13)

Quoted 117 lines

Hi Mike,

Quoted 106 lines · Mike Turquette

Quoting Sascha Hauer (2015-02-26 00:43:19)

Quoted 86 lines · Ray Jui
Quoted 79 lines · Sascha Hauer
Quoted 16 lines · Ray Jui
Quoted 15 lines

Hi Sascha,

Quoted 12 lines · Sascha Hauer

Hi Ray,

Quoted 8 lines · Ray Jui

Sometimes a clock needs to know the rate of its parent before itself is registered to the framework. An example is that a PLL may need to initialize itself to a specific VCO frequency, before registering to the framework. The parent rate needs to be known, for PLL multipliers and divisors to be configured properly.

Introduce helper function of_clk_get_parent_rate, which can be used to obtain the parent rate of a clock, given a device node and index.

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

I can't see how this patch helps you. First it's not guaranteed that the parent is already registered, what do you do in this case?

In the case when clock parent is not found, as you can see from the code, it simply returns zero, just like other clk get rate APIs.

Yes, but what do you do with the 0 result then in your PLL initialization?

As of the current code, it fails the PLL frequency initialization and bails out. Thinking about it more, it actually makes more sense to just warn and still go ahead to register the clock, in which case it will use whatever default frequency after chip power on reset or a frequency configured in the bootloader.

Quoted 6 lines
Quoted 5 lines

I thought the order of clock registration is based on order of the clock nodes in device tree. It makes sense to me to declare the parent clock before a child clock, so it's guaranteed that the parent is registered before the child.

Quoted 6 lines

No, you can't rely on that. The order of the device nodes may happen to define the order of clock initialization now, but that may change. device nodes are usually ordered by bus addresses, not by intended initialization order. Even if you reorder them everything must still work.

Okay I get your point that the order of device nodes may not be relied on for device initialization order. But then another mechanism should be deployed to give developers the option to decide on the clock initialization sequence. It can be optional but it should be there.

Quoted 11 lines
Quoted 6 lines
Quoted 3 lines

Then the clock framework doesn't require that you initialize the PLL before registering. That can be done in the clk ops later.

Sure it's not mandatory. But what's wrong with me choosing to initialize the PLL clock to a known frequency before registering it to the framework?

Appearantly you don't know the (input) frequency of the PLL when registering it to the framework, so the question must be: What's wrong with keeping it uninitialized?

Quoted 8 lines

If the PLL is unused then you don't care about it's initialization status. If it happens to be enabled by a bootloader and still unused at late_initcall time the clock framework will disable it so you have a known state then. If a consumer for the PLL appears it's its job to initialize it through the clk api.

Sascha

Okay, what we need here is to initialize the PLL to a desired frequency, based on device tree settings (since it will be configured differently, among different boards). This is a PLL that 1) has limited options of frequencies which it can be configured to, and 2) has multiple child clocks, where is a more suitable place to initialize it to the desired frequency than right before registering it to the framework? I know a lot of people do it in the bootloader, but I thought we should be given the flexibility of configuring it in the kernel.

Quoted 5 lines

When you say "consumers", do you mean 1) the device driver that uses the PLL; or 2) the device driver that use the child clock of the PLL? If it's case 1), then we don't really have a device driver that directly uses the PLL, and I thought that's quite normal, as most PLLs don't directly feed into any peripherals.

Quoted 5 lines

I meant 1) and 2). Before a consumer comes along the state of the PLL doesn't matter. When a consumer shows up it has to call clk_prepare_enable which (directly or indirectly) will enable your PLL. Then it's still time to apply the default settings you found out during probe of the PLL.

My review comments are really for iproc_pll_setup() in patch #3, but the discussion is here so I'll respond to this thread.

I think the root of this problem is that your pll clk_ops does not support .set_rate. That is why your clock driver hacks in a call to pll_set_rate in iproc_pll_setup.

Due to the above shortcoming you also do not use the assigned-clock-rate infrastructure to set your pll rate at registration-time. There is no reason for your driver to re-invent this logic. iproc_pll_setup is fetching the clock-frequency property from DT and then trying to set that rate. Instead please use the generic code.

Quoted 7 lines

The right way to handle this is to support a .set_rate callback (looks like you're 90% of the way there with pll_set_rate) and then use the assigned-clock-rates property to specify this from DT.

Regards,
Mike

Quoted 5 lines

Okay. It's good to know that "assigned-clock-rate" can be used and serve exactly what we need here. I'll update my patch series to use that instead.

In this case, do you think I should still keep of_clk_get_parent_rate in the patch series?

No. Without a user we should drop it, and I also do not like its use of clk_lookup.

Thanks,
Mike

Quoted 13 lines

Thanks,

Ray

Quoted 8 lines
Quoted 8 lines

Sascha

Signature · 4 lines
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
author maintainer
#338 lore raw

PATCH v5 2/6 clk: iproc: define Broadcom iProc clock binding

In reply to Ray Jui

Document the device tree binding for Broadcom iProc architecture based clock controller

---
 brcm,iproc-clocks.txt |  178 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 178 insertions(+)
 create mode 100644 brcm,iproc-clocks.txt
brcm,iproc-clocks.txt new file +178 -0
new file mode 100644
index 0000000..cc64fd2
@@ -0,0 +1,178 @@
+Broadcom iProc Family Clocks
+
+This binding uses the common clock binding:
+    Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+The iProc clock controller manages clocks that are common to the iProc family.
+An SoC from the iProc family may have several PPLs, e.g., ARMPLL, GENPLL,
+LCPLL0, MIPIPLL, and etc., all derived from an onboard crystal. Each PLL
+comprises of several leaf clocks
+
+Required properties for PLLs:
+- compatible:
+    Should have a value of the form "brcm,<soc>-<pll>". For example, GENPLL on
+Cygnus has a compatible string of "brcm,cygnus-genpll"
+
+- #clock-cells:
+    Must be <0>
+
+- reg:
+    Define the base and range of the I/O address space that contain the iProc
+clock control registers required for the PLL
+
+- clocks:
+    The input parent clock phandle for the PLL. For all iProc PLLs, this is an
+onboard crystal with a fixed rate
+
+Optional Properties for PLLs:
+- clock-frequency:
+    PLL frequency in Hz. If specified, PLL will be configured to run at
+<clock-frequency> instead of the default frequency after chip reset, provided
+that <clock-frequency> and its parameters are defined in the SoC specific
+frequency parameter table
+
+Example:
+
+	osc: oscillator {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <25000000>;
+	};
+
+	genpll: genpll {
+		#clock-cells = <0>;
+		compatible = "brcm,cygnus-genpll";
+		reg = <0x0301d000 0x2c>,
+			<0x0301c020 0x4>;
+		clocks = <&osc>;
+	};
+
+Required properties for leaf clocks of a PLL:
+
+- compatible:
+    Should have a value of the form "brcm,<soc>-<pll>-clk". For example, leaf
+clocks derived from the GENPLL on Cygnus SoC have a compatible string of
+"brcm,cygnus-genpll-clk"
+
+- #clock-cells:
+    Have a value of <1> since there are more than 1 leaf clock of a
+given PLL
+
+- reg:
+    Define the base and range of the I/O address space that contain the iProc
+clock control registers required for the PLL leaf clocks
+
+- clocks:
+    The input parent PLL phandle for the leaf clock
+
+- clock-output-names:
+    An ordered list of strings defining the names of the leaf clocks
+
+Example:
+
+	genpll: genpll {
+		#clock-cells = <0>;
+		compatible = "brcm,cygnus-genpll";
+		reg = <0x0301d000 0x2c>,
+			<0x0301c020 0x4>;
+		clocks = <&osc>;
+	};
+
+	genpll_clks: genpll_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-genpll-clk";
+		reg = <0x0301d000 0x2c>;
+		clocks = <&genpll>;
+		clock-output-names = "axi21", "250mhz", "ihost_sys",
+			"enet_sw", "audio_125", "can";
+	};
+
+Required properties for ASIU clocks:
+
+ASIU clocks are a special case. These clocks are derived directly from the
+reference clock of the onboard crystal
+
+- compatible:
+    Should have a value of the form "brcm,<soc>-asiu-clk". For example, ASIU
+clocks for Cygnus have a compatible string of "brcm,cygnus-asiu-clk"
+
+- #clock-cells:
+    Have a value of <1> since there are more than 1 ASIU clocks
+
+- reg:
+    Define the base and range of the I/O address space that contain the iProc
+clock control registers required for ASIU clocks
+
+- clocks:
+    The input parent clock phandle for the ASIU clock, i.e., the onboard
+crystal
+
+- clock-output-names:
+    An ordered list of strings defining the names of the ASIU clocks
+
+Example:
+
+	osc: oscillator {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <25000000>;
+	};
+
+	asiu_clks: asiu_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-asiu-clk";
+		reg = <0x0301d048 0xc>,
+			<0x180aa024 0x4>;
+		clocks = <&osc>;
+		clock-output-names = "keypad", "adc/touch", "pwm";
+	};
+
+Cygnus
+------
+PLL and leaf clock compatible strings for Cygnus are:
+    "brcm,cygnus-armpll"
+    "brcm,cygnus-genpll"
+    "brcm,cygnus-lcpll0"
+    "brcm,cygnus-mipipll"
+    "brcm,cygnus-genpll-clk"
+    "brcm,cygnus-lcpll0-clk"
+    "brcm,cygnus-mipipll-clk"
+    "brcm,cygnus-asiu-clk"
+
+The following table defines the set of PLL/clock index and ID for Cygnus.
+These clock IDs are defined in:
+    "include/dt-bindings/clock/bcm-cygnus.h"
+
+    Clock      Source           Index   ID
+    ---        -----            -----   ---------
+    crystal    N/A              N/A     N/A
+
+    armpll     crystal          N/A     N/A
+    genpll     crystal          N/A     N/A
+    lcpll0     crystal          N/A     N/A
+    mipipll    crystal          N/A     N/A
+
+    keypad     crystal (ASIU)   0       BCM_CYGNUS_ASIU_KEYPAD_CLK
+    adc/tsc    crystal (ASIU)   1       BCM_CYGNUS_ASIU_ADC_CLK
+    pwm        crystal (ASIU)   2       BCM_CYGNUS_ASIU_PWM_CLK
+
+    axi21      genpll           0       BCM_CYGNUS_GENPLL_AXI21_CLK
+    250mhz     genpll           1       BCM_CYGNUS_GENPLL_250MHZ_CLK
+    ihost_sys  genpll           2       BCM_CYGNUS_GENPLL_IHOST_SYS_CLK
+    enet_sw    genpll           3       BCM_CYGNUS_GENPLL_ENET_SW_CLK
+    audio_125  genpll           4       BCM_CYGNUS_GENPLL_AUDIO_125_CLK
+    can        genpll           5       BCM_CYGNUS_GENPLL_CAN_CLK
+
+    pcie_phy   lcpll0           0       BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK
+    ddr_phy    lcpll0           1       BCM_CYGNUS_LCPLL0_DDR_PHY_CLK
+    sdio       lcpll0           2       BCM_CYGNUS_LCPLL0_SDIO_CLK
+    usb_phy    lcpll0           3       BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK
+    smart_card lcpll0           4       BCM_CYGNUS_LCPLL0_SMART_CARD_CLK
+    ch5        lcpll0           5       BCM_CYGNUS_LCPLL0_CH5_UNUSED
+
+    ch0_unused mipipll          0       BCM_CYGNUS_MIPIPLL_CH0_UNUSED
+    ch1_lcd    mipipll          1       BCM_CYGNUS_MIPIPLL_CH1_LCD
+    ch2_unused mipipll          2       BCM_CYGNUS_MIPIPLL_CH2_UNUSED
+    ch3_unused mipipll          3       BCM_CYGNUS_MIPIPLL_CH3_UNUSED
+    ch4_unused mipipll          4       BCM_CYGNUS_MIPIPLL_CH4_UNUSED
+    ch5_unused mipipll          5       BCM_CYGNUS_MIPIPLL_CH5_UNUSED
Signature · 1 line
1.7.9.5
author maintainer
#339 lore raw

PATCH v5 3/6 clk: iproc: add initial common clock support

In reply to Ray Jui

This adds basic and generic support for various iProc PLLs and clocks including the ARMPLL, GENPLL, LCPLL, MIPIPLL, and ASIU clocks.

SoCs under the iProc architecture can define their specific register offsets and clock parameters for their PLL and clock controllers. These parameters can be passed as arugments into the generic iProc PLL and clock setup functions

Derived from code originally provided by Jonathan Richardson <[email protected]>

---
 drivers/clk/bcm/Kconfig            |    9 +
 drivers/clk/bcm/Makefile           |    1 +
 drivers/clk/bcm/clk-iproc-armpll.c |  282 ++++++++++++++++++++++
 drivers/clk/bcm/clk-iproc-asiu.c   |  275 +++++++++++++++++++++
 drivers/clk/bcm/clk-iproc-clk.c    |  238 +++++++++++++++++++
 drivers/clk/bcm/clk-iproc-pll.c    |  461 ++++++++++++++++++++++++++++++++++++
 drivers/clk/bcm/clk-iproc.h        |  157 ++++++++++++
 7 files changed, 1423 insertions(+)
 create mode 100644 drivers/clk/bcm/clk-iproc-armpll.c
 create mode 100644 drivers/clk/bcm/clk-iproc-asiu.c
 create mode 100644 drivers/clk/bcm/clk-iproc-clk.c
 create mode 100644 drivers/clk/bcm/clk-iproc-pll.c
 create mode 100644 drivers/clk/bcm/clk-iproc.h
drivers/clk/bcm/Kconfig +9 -0
index 75506e5..66b5b7f 100644
@@ -7,3 +7,12 @@ config CLK_BCM_KONA
 	  Enable common clock framework support for Broadcom SoCs
 	  using "Kona" style clock control units, including those
 	  in the BCM281xx and BCM21664 families.
+
+config COMMON_CLK_IPROC
+	bool "Broadcom iProc clock support"
+	depends on ARCH_BCM_IPROC
+	depends on COMMON_CLK
+	default y
+	help
+	  Enable common clock framework support for Broadcom SoCs
+	  based on the "iProc" architecture
drivers/clk/bcm/Makefile +1 -0
index 6297d05..6926636 100644
@@ -2,3 +2,4 @@ obj-$(CONFIG_CLK_BCM_KONA)	+= clk-kona.o
 obj-$(CONFIG_CLK_BCM_KONA)	+= clk-kona-setup.o
 obj-$(CONFIG_CLK_BCM_KONA)	+= clk-bcm281xx.o
 obj-$(CONFIG_CLK_BCM_KONA)	+= clk-bcm21664.o
+obj-$(CONFIG_COMMON_CLK_IPROC)	+= clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-clk.o clk-iproc-asiu.o
drivers/clk/bcm/clk-iproc-armpll.c new file +282 -0

282 lines not on this page · raw message

drivers/clk/bcm/clk-iproc-asiu.c new file +275 -0

275 lines not on this page · raw message

drivers/clk/bcm/clk-iproc-clk.c new file +238 -0

238 lines not on this page · raw message

drivers/clk/bcm/clk-iproc-pll.c new file +461 -0

461 lines not on this page · raw message

drivers/clk/bcm/clk-iproc.h new file +157 -0

157 lines not on this page · raw message

Signature · 1 line
1.7.9.5
author maintainer
#340 lore raw

PATCH v5 4/6 clk: Change bcm clocks build dependency

In reply to Ray Jui

The clock code under drivers/clk/bcm now contains code for both the Broadcom mobile SoCs and the iProc SoCs. Change the the makefile dependency to be under config flag CONFIG_ARCH_BCM that's enabled for both families of SoCs

---
 drivers/clk/Makefile |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/clk/Makefile +1 -1
index d5fba5b..eff0213 100644
@@ -41,7 +41,7 @@ obj-$(CONFIG_ARCH_VT8500)		+= clk-vt8500.o
 obj-$(CONFIG_COMMON_CLK_WM831X)		+= clk-wm831x.o
 obj-$(CONFIG_COMMON_CLK_XGENE)		+= clk-xgene.o
 obj-$(CONFIG_COMMON_CLK_AT91)		+= at91/
-obj-$(CONFIG_ARCH_BCM_MOBILE)		+= bcm/
+obj-$(CONFIG_ARCH_BCM)			+= bcm/
 obj-$(CONFIG_ARCH_BERLIN)		+= berlin/
 obj-$(CONFIG_ARCH_HI3xxx)		+= hisilicon/
 obj-$(CONFIG_ARCH_HIP04)		+= hisilicon/
Signature · 1 line
1.7.9.5
author maintainer
#341 lore raw

PATCH v5 5/6 clk: cygnus: add clock support for Broadcom Cygnus

In reply to Ray Jui

The Broadcom Cygnus SoC is architected under the iProc architecture. It has the following PLLs: ARMPLL, GENPLL, LCPLL0, MIPIPLL, all dervied from an onboard crystal. Cygnus also has various ASIU clocks that are derived directly from the onboard crystal.

---
 drivers/clk/bcm/Makefile               |    1 +
 drivers/clk/bcm/clk-cygnus.c           |  277 ++++++++++++++++++++++++++++++++
 include/dt-bindings/clock/bcm-cygnus.h |   65 ++++++++
 3 files changed, 343 insertions(+)
 create mode 100644 drivers/clk/bcm/clk-cygnus.c
 create mode 100644 include/dt-bindings/clock/bcm-cygnus.h
drivers/clk/bcm/Makefile +1 -0
index 6926636..afcbe55 100644
@@ -3,3 +3,4 @@ obj-$(CONFIG_CLK_BCM_KONA)	+= clk-kona-setup.o
 obj-$(CONFIG_CLK_BCM_KONA)	+= clk-bcm281xx.o
 obj-$(CONFIG_CLK_BCM_KONA)	+= clk-bcm21664.o
 obj-$(CONFIG_COMMON_CLK_IPROC)	+= clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-clk.o clk-iproc-asiu.o
+obj-$(CONFIG_ARCH_BCM_CYGNUS)	+= clk-cygnus.o
drivers/clk/bcm/clk-cygnus.c new file +277 -0

277 lines not on this page · raw message

include/dt-bindings/clock/bcm-cygnus.h new file +65 -0

65 lines not on this page · raw message

Signature · 1 line
1.7.9.5
author maintainer
#342 lore raw

PATCH v5 6/6 ARM: dts: enable clock support for Broadcom Cygnus

In reply to Ray Jui

Replace current device tree dummy clocks with real clock support for Broadcom Cygnus SoC

---
 arch/arm/boot/dts/bcm-cygnus-clock.dtsi |  110 ++++++++++++++++++++++++-------
 arch/arm/boot/dts/bcm-cygnus.dtsi       |    2 +-
 2 files changed, 86 insertions(+), 26 deletions(-)
arch/arm/boot/dts/bcm-cygnus-clock.dtsi +85 -25
index 60d8389..abb8a3f 100644
@@ -36,56 +36,116 @@ clocks {
 	ranges;
 
 	osc: oscillator {
+		#clock-cells = <0>;
 		compatible = "fixed-clock";
-		#clock-cells = <1>;
 		clock-frequency = <25000000>;
 	};
 
-	apb_clk: apb_clk {
-		compatible = "fixed-clock";
+	/* Cygnus ARM PLL */
+	armpll: armpll {
 		#clock-cells = <0>;
-		clock-frequency = <1000000000>;
+		compatible = "brcm,cygnus-armpll";
+		clocks = <&osc>;
+		reg = <0x19000000 0x1000>;
 	};
 
-	periph_clk: periph_clk {
-		compatible = "fixed-clock";
+	/* peripheral clock for system timer */
+	arm_periph_clk: arm_periph_clk {
 		#clock-cells = <0>;
-		clock-frequency = <500000000>;
+		compatible = "fixed-factor-clock";
+		clocks = <&armpll>;
+		clock-div = <2>;
+		clock-mult = <1>;
 	};
 
-	sdio_clk: lcpll_ch2 {
-		compatible = "fixed-clock";
+	/* APB bus clock */
+	apb_clk: apb_clk {
 		#clock-cells = <0>;
-		clock-frequency = <200000000>;
+		compatible = "fixed-factor-clock";
+		clocks = <&armpll>;
+		clock-div = <4>;
+		clock-mult = <1>;
 	};
 
-	axi81_clk: axi81_clk {
-		compatible = "fixed-clock";
+	genpll: genpll {
 		#clock-cells = <0>;
-		clock-frequency = <100000000>;
+		compatible = "brcm,cygnus-genpll";
+		reg = <0x0301d000 0x2c>,
+			<0x0301c020 0x4>;
+		clocks = <&osc>;
 	};
 
-	keypad_clk: keypad_clk {
-		compatible = "fixed-clock";
+	/* various clocks running off the GENPLL */
+	genpll_clks: genpll_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-genpll-clk";
+		reg = <0x0301d000 0x2c>;
+		clocks = <&genpll>;
+		clock-output-names = "axi21", "250mhz", "ihost_sys",
+			"enet_sw", "audio_125", "can";
+	};
+
+	/* always 1/2 of the axi21 clock */
+	axi41_clk: axi41_clk {
 		#clock-cells = <0>;
-		clock-frequency = <31806>;
+		compatible = "fixed-factor-clock";
+		clocks = <&genpll_clks 0>;
+		clock-div = <2>;
+		clock-mult = <1>;
 	};
 
-	adc_clk: adc_clk {
-		compatible = "fixed-clock";
+	/* always 1/4 of the axi21 clock */
+	axi81_clk: axi81_clk {
 		#clock-cells = <0>;
-		clock-frequency = <1562500>;
+		compatible = "fixed-factor-clock";
+		clocks = <&genpll_clks 0>;
+		clock-div = <4>;
+		clock-mult = <1>;
 	};
 
-	pwm_clk: pwm_clk {
-		compatible = "fixed-clock";
+	lcpll0: lcpll0 {
 		#clock-cells = <0>;
-		clock-frequency = <1000000>;
+		compatible = "brcm,cygnus-lcpll0";
+		reg = <0x0301d02c 0x1c>,
+			<0x0301c020 0x4>;
+		clocks = <&osc>;
 	};
 
-	lcd_clk: mipipll_ch1 {
-		compatible = "fixed-clock";
+	/* various clocks running off the LCPLL0 */
+	lcpll0_clks: lcpll0_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-lcpll0-clk";
+		reg = <0x0301d02c 0x1c>;
+		clocks = <&lcpll0>;
+		clock-output-names = "pcie_phy", "ddr_phy", "sdio",
+			"usb_phy", "smart_card", "ch5";
+	};
+
+	mipipll: mipipll {
 		#clock-cells = <0>;
-		clock-frequency = <100000000>;
+		compatible = "brcm,cygnus-mipipll";
+		reg = <0x180a9800 0x2c>,
+			<0x0301c020 0x4>,
+			<0x180aa024 0x4>;
+		clock-frequency = <2100000000>;
+		clocks = <&osc>;
+	};
+
+	mipipll_clks: mipipll_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-mipipll-clk";
+		reg = <0x180a9800 0x2c>;
+		clocks = <&mipipll>;
+		clock-output-names = "ch0_unused", "ch1_lcd", "ch2_unused",
+			"ch3_unused", "ch4_unused", "ch5_unused";
+	};
+
+	asiu_clks: asiu_clks {
+		#clock-cells = <1>;
+		compatible = "brcm,cygnus-asiu-clk";
+		reg = <0x0301d048 0xc>,
+			<0x180aa024 0x4>;
+		clocks = <&osc>;
+		clock-output-names = "keypad", "adc/touch", "pwm";
 	};
 };
arch/arm/boot/dts/bcm-cygnus.dtsi +1 -1
index 5126f9e..2b99e9c 100644
@@ -134,7 +134,7 @@
 		compatible = "arm,cortex-a9-global-timer";
 		reg = <0x19020200 0x100>;
 		interrupts = <GIC_PPI 11 IRQ_TYPE_LEVEL_HIGH>;
-		clocks = <&periph_clk>;
+		clocks = <&arm_periph_clk>;
 	};
 
 };
Signature · 1 line
1.7.9.5
Tested-by on the v5 series
#343 lore raw

Re: [PATCH v5 0/6] Add common clock support for Broadcom iProc architecture

In reply to Ray Jui
Quoted 25 lines · Ray Jui

This patchset contains the initial common clock support for Broadcom's iProc family of SoCs. The iProc clock architecture comprises of various PLLs, e.g., ARMPLL, GENPLL, LCPLL0, MIPIPLL, and etc. An onboard crystal serves as the basic reference clock for these PLLs. Each PLL may have several leaf clocks. One special group of clocks is the ASIU clocks, which are dervied directly from the crystal reference clock.

This patchset also contains the basic clock support for the Broadcom Cygnus SoC, which implements the iProc clock architecture

Changes from v4:
- Add of_clk_get_parent_rate helper function into the clock framework
- Switch to use of_clk_get_parent_rate in the iProc PLL clock driver

Changes from v3:
- Fix incorrect use of passing in of_clk_src_onecell_get when adding ARM PLL and other iProc PLLs as clock provider. These PLLs have zero cells in DT and thefore of_clk_src_simple_get should be used instead
- Rename Cygnus MIPI PLL Channel 2 clock from BCM_CYGNUS_MIPIPLL_CH2_UNUSED to BCM_CYGNUS_MIPIPLL_CH2_V3D, since a 3D graphic rendering engine has been integrated into Cygnus revision B0 and has its core clock running off MIPI PLL Channel 2
- Changed default MIPI PLL VCO frequency from 1.75 GHz to 2.1 GHz. This allows us to derive 300 MHz V3D clock from channel 2 through the post divisor

Quoted 8 lines

Changes from v2:
- Re-arrange Cygnus clock/pll init functions so each init function is right next to its clock table
- Removed #defines for number of clocks in Cygnus. Have the number of clocks automatically determined based on array size of the clock table

Changes from v1:
- Separate drivers/clk/Makefile change for drivers/clk/bcm out to a standalone patch

FWIW I tested this series on BCM958305K SVK.

Thanks.

Signature · 1 line
Dmitry
author maintainer
#344 lore raw

v8 cover Add I2C support to Broadcom iProc

In reply to a message not in the archive

This patchset contains the initial I2C support for Broadcom iProc family of SoCs.

The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100 kHz) and fast mode (400 kHz)

Changes from v7:
- Remove redundant 10-bit address check in the driver
- Fix the driver that accidentally emits 1-byte of data with zero content in the case of SMBUS quick command
- Improve debugging prints in the driver
- Other minor improvements

Changes from v6:
- Get rid of unnecessary atomic variable usage in the driver
- Improve the "waiting for transaction to complete" logic further by making sure there's no pending/ongoing interrupt by the time when flag 'xfer_is_done' is checked
- After disabling interrupt with 'writel', add 'readl' to the same register to flush the bus to ensure the write has gone through

Changes from v5:
- Improve the "waiting for transaction to be complete" logic to take care of the corner case when an interrupt fires after wait_for_completion_timeout times out
- Improve the logic to disable I2C interrupt in the remove function. Make it more generic so it works for both dedicated and shared interrupt

Changes from v4:
- Remove redundant header file includes
- Change the logic that waits for the host controller to be idle to simply return -EBUSY
- Use proper print level and error codes in the driver
- Allow zero length message in the driver to support I2C_SMBUS_QUICK
- Change back to use devm_request_irq. Disable interrupt in the remove function so there's no outstanding I2C interrupt when the driver is being removed from the framework
- Other minor miscellaneous improvements and fixes

Changes from v3:
- Add config dependency to COMPILE_TEST to allow the driver to be build tested by other platforms
- Improve CPU utilization efficiency in the loop of waiting for bus to idle
- Add more comment in the driver to clarify the way how the "start busy" interrupt is triggered from the I2C controller
- Fix inconsistent coding style and format
- Improve the bus speed validation logic in the driver
- Add code to free the interrupt line in driver's remove function. Also change to use non-devm API to request the interrupt line
- Other miscellaneous improvements and fixes

Changes from v2:
- Have the I2C driver default to y so it does not need to be selected from ARCH_BCM_IPROC. This also helps to get rid of one patch. The driver still depends on ARCH_BCM_IPROC
- Get rid of redundant check on resource returned by platform_get_resource

Changes from v1:
- Fix function argument parenthesis
- Get rid of redundant driver owner field

Ray Jui (3):
i2c: iProc: define Broadcom iProc I2C binding
i2c: iproc: Add Broadcom iProc I2C Driver
ARM: dts: add I2C device nodes for Broadcom Cygnus

 .../devicetree/bindings/i2c/brcm,iproc-i2c.txt     |   37 ++
 arch/arm/boot/dts/bcm-cygnus.dtsi                  |   20 +
 drivers/i2c/busses/Kconfig                         |   10 +
 drivers/i2c/busses/Makefile                        |    1 +
 drivers/i2c/busses/i2c-bcm-iproc.c                 |  494 ++++++++++++++++++++
 5 files changed, 562 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt
 create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c
Signature · 1 line
1.7.9.5
author maintainer
#345 lore raw

PATCH v8 1/3 i2c: iProc: define Broadcom iProc I2C binding

Document the I2C device tree binding for Broadcom iProc family of SoCs

---
 .../devicetree/bindings/i2c/brcm,iproc-i2c.txt     |   37 ++++++++++++++++++++
 1 file changed, 37 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt
Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt new file +37 -0
new file mode 100644
index 0000000..81f982c
@@ -0,0 +1,37 @@
+Broadcom iProc I2C controller
+
+Required properties:
+
+- compatible:
+    Must be "brcm,iproc-i2c"
+
+- reg:
+    Define the base and range of the I/O address space that contain the iProc
+    I2C controller registers
+
+- interrupts:
+    Should contain the I2C interrupt
+
+- clock-frequency:
+    This is the I2C bus clock. Need to be either 100000 or 400000
+
+- #address-cells:
+    Always 1 (for I2C addresses)
+
+- #size-cells:
+    Always 0
+
+Example:
+	i2c0: i2c@18008000 {
+		compatible = "brcm,iproc-i2c";
+		reg = <0x18008000 0x100>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>;
+		clock-frequency = <100000>;
+
+		codec: wm8750@1a {
+			compatible = "wlf,wm8750";
+			reg = <0x1a>;
+		};
+	};
Signature · 1 line
1.7.9.5
author maintainer
#346 lore raw

PATCH v8 2/3 i2c: iproc: Add Broadcom iProc I2C Driver

In reply to Ray Jui

Add initial support to the Broadcom iProc I2C controller found in the iProc family of SoCs.

The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100kHz) and fast mode (400kHz)

---
 drivers/i2c/busses/Kconfig         |   10 +
 drivers/i2c/busses/Makefile        |    1 +
 drivers/i2c/busses/i2c-bcm-iproc.c |  494 ++++++++++++++++++++++++++++++++++++
 3 files changed, 505 insertions(+)
 create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c
drivers/i2c/busses/Kconfig +10 -0
index ab838d9..3d08731 100644
@@ -372,6 +372,16 @@ config I2C_BCM2835
 	  This support is also available as a module.  If so, the module
 	  will be called i2c-bcm2835.
 
+config I2C_BCM_IPROC
+	tristate "Broadcom iProc I2C controller"
+	depends on ARCH_BCM_IPROC || COMPILE_TEST
+	default ARCH_BCM_IPROC
+	help
+	  If you say yes to this option, support will be included for the
+	  Broadcom iProc I2C controller.
+
+	  If you don't know what to do here, say N.
+
 config I2C_BCM_KONA
 	tristate "BCM Kona I2C adapter"
 	depends on ARCH_BCM_MOBILE
drivers/i2c/busses/Makefile +1 -0
index 56388f6..d93b509 100644
@@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91)		+= i2c-at91.o
 obj-$(CONFIG_I2C_AU1550)	+= i2c-au1550.o
 obj-$(CONFIG_I2C_AXXIA)		+= i2c-axxia.o
 obj-$(CONFIG_I2C_BCM2835)	+= i2c-bcm2835.o
+obj-$(CONFIG_I2C_BCM_IPROC)	+= i2c-bcm-iproc.o
 obj-$(CONFIG_I2C_BLACKFIN_TWI)	+= i2c-bfin-twi.o
 obj-$(CONFIG_I2C_CADENCE)	+= i2c-cadence.o
 obj-$(CONFIG_I2C_CBUS_GPIO)	+= i2c-cbus-gpio.o
drivers/i2c/busses/i2c-bcm-iproc.c new file +494 -0

494 lines not on this page · raw message

Signature · 1 line
1.7.9.5
#347 lore raw

Re: [PATCH v8 2/3] i2c: iproc: Add Broadcom iProc I2C Driver

Hi Ray,

Quoted 6 lines · Ray Jui

Add initial support to the Broadcom iProc I2C controller found in the iProc family of SoCs.

The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100kHz) and fast mode (400kHz)

Mostly looking good.

drivers/i2c/busses/i2c-bcm-iproc.c line 21

Quoted 8 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +8 -0
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/delay.h>

Please sort the includes.

drivers/i2c/busses/i2c-bcm-iproc.c line 128

Quoted 8 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +8 -0
+static bool bcm_iproc_i2c_bus_busy(struct bcm_iproc_i2c_dev *iproc_i2c)
+{
+	if (readl(iproc_i2c->base + M_CMD_OFFSET) &
+	    (1 << M_CMD_START_BUSY_SHIFT))
+		return true;
+	else
+		return false;
+}

Minor: return !!(readl(...))? You decide.

drivers/i2c/busses/i2c-bcm-iproc.c line 139

Quoted 11 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +11 -0
+
+static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *iproc_i2c,
+				     struct i2c_msg *msg, u8 *addr)
+{
+	*addr = msg->addr << 1;
+
+	if (msg->flags & I2C_M_RD)
+		*addr |= 1;
+
+	return 0;
+}

I'd suggest a oneliner.

*addr = msg->addr << 1 | (msg->flags & I2C_M_RD ? 1 : 0)

Or use !! like above.

Don't do an extra function for that. It is only used once and it also doesn't need to be int since it can't fail anyhow.

(Note to self: I should make a macro for that in i2c.h)

drivers/i2c/busses/i2c-bcm-iproc.c line 188

Quoted 6 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +6 -0
+	/* need to reserve one byte in the FIFO for the slave address */
+	if (msg->len > M_TX_RX_FIFO_SIZE - 1) {
+		dev_err(iproc_i2c->device,
+			"only support data length up to %u bytes\n",
+			M_TX_RX_FIFO_SIZE - 1);
+		return -EINVAL;

-EOPNOTSUPP

Is it really a HW limitation? Could the driver later be extended to continue filling the FIFO if a certain threshold is reached?

drivers/i2c/busses/i2c-bcm-iproc.c line 283

Quoted 4 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +4 -0
+	dev_dbg(iproc_i2c->device, "xfer %c, addr=0x%02x, len=%d\n",
+		(msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr,
+		msg->len);
+	dev_dbg(iproc_i2c->device, "*** data: %*ph\n", msg->len, msg->buf);

Not really needed. We have tracing for that.

Quoted 7 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +7 -0
+	if (bus_speed < 100000) {
+		dev_err(iproc_i2c->device, "%d Hz bus speed not supported\n",
+			bus_speed);
+		dev_err(iproc_i2c->device,
+			"valid speeds are 100khz and 400khz\n");
+		return -EINVAL;
+	} else if (bus_speed < 400000) {

drivers/i2c/busses/i2c-bcm-iproc.c line 344

Quoted 10 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +10 -0
+		bus_speed = 100000;
+		speed_bit = 0;
+	} else {
+		bus_speed = 400000;
+		speed_bit = 1;
+	}
+
+	val = readl(iproc_i2c->base + TIM_CFG_OFFSET);
+	val &= ~(1 << TIM_CFG_MODE_400_SHIFT);
+	val |= speed_bit << TIM_CFG_MODE_400_SHIFT;

val |= (bus_speed == 400000) ...

and skip speed_bit? You decide.

Quoted 7 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +7 -0
+static void bcm_iproc_i2c_enable(struct bcm_iproc_i2c_dev *iproc_i2c)
+{
+	u32 val;
+
+	val = readl(iproc_i2c->base + CFG_OFFSET);
+	val |= 1 << CFG_EN_SHIFT;
+	writel(val, iproc_i2c->base + CFG_OFFSET);

drivers/i2c/busses/i2c-bcm-iproc.c line 398

Quoted 10 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +10 -0
+}
+
+static void bcm_iproc_i2c_disable(struct bcm_iproc_i2c_dev *iproc_i2c)
+{
+	u32 val;
+
+	val = readl(iproc_i2c->base + CFG_OFFSET);
+	val &= ~(1 << CFG_EN_SHIFT);
+	writel(val, iproc_i2c->base + CFG_OFFSET);
+}

Extra functions? They are self explaining and only used once. You decide.

Rest looks fine, thanks!

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150207/e4a4fb2e/attachment-0001.sig>

author maintainer
#348 lore raw

Re: [PATCH v8 2/3] i2c: iproc: Add Broadcom iProc I2C Driver

Quoted 12 lines · Wolfram Sang

Hi Ray,

Quoted 6 lines · Ray Jui

Add initial support to the Broadcom iProc I2C controller found in the iProc family of SoCs.

The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100kHz) and fast mode (400kHz)

Mostly looking good.

Quoted 11 lines

drivers/i2c/busses/i2c-bcm-iproc.c line 21

Quoted 8 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +8 -0
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/delay.h>

Please sort the includes.

Will do.

Quoted 11 lines

drivers/i2c/busses/i2c-bcm-iproc.c line 128

Quoted 8 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +8 -0
+static bool bcm_iproc_i2c_bus_busy(struct bcm_iproc_i2c_dev *iproc_i2c)
+{
+	if (readl(iproc_i2c->base + M_CMD_OFFSET) &
+	    (1 << M_CMD_START_BUSY_SHIFT))
+		return true;
+	else
+		return false;
+}

Minor: return !!(readl(...))? You decide.

Okay will do that. Will also remove this function since now it becomes one line and is used only once.

Quoted 14 lines

drivers/i2c/busses/i2c-bcm-iproc.c line 139

Quoted 11 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +11 -0
+
+static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *iproc_i2c,
+				     struct i2c_msg *msg, u8 *addr)
+{
+	*addr = msg->addr << 1;
+
+	if (msg->flags & I2C_M_RD)
+		*addr |= 1;
+
+	return 0;
+}

I'd suggest a oneliner.

Quoted 9 lines

*addr = msg->addr << 1 | (msg->flags & I2C_M_RD ? 1 : 0)

Or use !! like above.

Don't do an extra function for that. It is only used once and it also doesn't need to be int since it can't fail anyhow.

(Note to self: I should make a macro for that in i2c.h)

Yes will change. Thanks.

Quoted 9 lines

drivers/i2c/busses/i2c-bcm-iproc.c line 188

Quoted 6 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +6 -0
+	/* need to reserve one byte in the FIFO for the slave address */
+	if (msg->len > M_TX_RX_FIFO_SIZE - 1) {
+		dev_err(iproc_i2c->device,
+			"only support data length up to %u bytes\n",
+			M_TX_RX_FIFO_SIZE - 1);
+		return -EINVAL;

-EOPNOTSUPP

Quoted 3 lines

Is it really a HW limitation? Could the driver later be extended to continue filling the FIFO if a certain threshold is reached?

Will return -EOPNOTSUPP. This really depends on whether or not we expect one sequence of START + SLV ADDR + DATA + STOP per i2c message. I can later extend the driver to refill/re-drain the FIFO for data size >= 64 bytes, if one sequence of SATRT...STOP per message is not a requirement.

Quoted 7 lines

drivers/i2c/busses/i2c-bcm-iproc.c line 283

Quoted 4 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +4 -0
+	dev_dbg(iproc_i2c->device, "xfer %c, addr=0x%02x, len=%d\n",
+		(msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr,
+		msg->len);
+	dev_dbg(iproc_i2c->device, "*** data: %*ph\n", msg->len, msg->buf);

Not really needed. We have tracing for that.

Will remove.

Quoted 20 lines
Quoted 7 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +7 -0
+	if (bus_speed < 100000) {
+		dev_err(iproc_i2c->device, "%d Hz bus speed not supported\n",
+			bus_speed);
+		dev_err(iproc_i2c->device,
+			"valid speeds are 100khz and 400khz\n");
+		return -EINVAL;
+	} else if (bus_speed < 400000) {

drivers/i2c/busses/i2c-bcm-iproc.c line 344

Quoted 10 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +10 -0
+		bus_speed = 100000;
+		speed_bit = 0;
+	} else {
+		bus_speed = 400000;
+		speed_bit = 1;
+	}
+
+	val = readl(iproc_i2c->base + TIM_CFG_OFFSET);
+	val &= ~(1 << TIM_CFG_MODE_400_SHIFT);
+	val |= speed_bit << TIM_CFG_MODE_400_SHIFT;

val |= (bus_speed == 400000) ...

Quoted 2 lines

and skip speed_bit? You decide.

Okay, I'll get rid of speed_bit.

Quoted 12 lines
Quoted 12 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +12 -0
+static void bcm_iproc_i2c_enable(struct bcm_iproc_i2c_dev *iproc_i2c)
+{
+	u32 val;
+
+	val = readl(iproc_i2c->base + CFG_OFFSET);
+	val |= 1 << CFG_EN_SHIFT;
+	writel(val, iproc_i2c->base + CFG_OFFSET);
+}
+
+static void bcm_iproc_i2c_disable(struct bcm_iproc_i2c_dev *iproc_i2c)
+{
+	u32 val;
Quoted 8 lines

drivers/i2c/busses/i2c-bcm-iproc.c line 380

Quoted 5 lines
drivers/i2c/busses/i2c-bcm-iproc.c new file +5 -0
+
+	val = readl(iproc_i2c->base + CFG_OFFSET);
+	val &= ~(1 << CFG_EN_SHIFT);
+	writel(val, iproc_i2c->base + CFG_OFFSET);
+}

Extra functions? They are self explaining and only used once. You decide.

In fact I'll keep the function, since it will likely be needed later when we add suspend/resume support to the driver. But I'll combine the two functions and make it a single function called bcm_iproc_i2c_enable_disable.

Quoted 3 lines

Rest looks fine, thanks!

Thanks for the review!

Ray

#349 lore raw

Re: [PATCH v8 2/3] i2c: iproc: Add Broadcom iProc I2C Driver

Quoted 9 lines
Quoted 4 lines

Is it really a HW limitation? Could the driver later be extended to continue filling the FIFO if a certain threshold is reached?

Will return -EOPNOTSUPP. This really depends on whether or not we expect one sequence of START + SLV ADDR + DATA + STOP per i2c message. I can later extend the driver to refill/re-drain the FIFO for data size >= 64 bytes, if one sequence of SATRT...STOP per message is not a requirement.

It is important to have the terminology clear here: One transfer can consist of multiple messages. The transfer uses START/STOP at the beginning/end, the messages within the transfer only REPEATED_START.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150208/14e75449/attachment.sig>

author maintainer
#350 lore raw

Re: [PATCH v8 2/3] i2c: iproc: Add Broadcom iProc I2C Driver

Quoted 5 lines · Wolfram Sang
Quoted 5 lines
Quoted 4 lines

Is it really a HW limitation? Could the driver later be extended to continue filling the FIFO if a certain threshold is reached?

Quoted 9 lines
Quoted 4 lines

Will return -EOPNOTSUPP. This really depends on whether or not we expect one sequence of START + SLV ADDR + DATA + STOP per i2c message. I can later extend the driver to refill/re-drain the FIFO for data size >= 64 bytes, if one sequence of SATRT...STOP per message is not a requirement.

It is important to have the terminology clear here: One transfer can consist of multiple messages. The transfer uses START/STOP at the beginning/end, the messages within the transfer only REPEATED_START.

Okay. Let me check with our ASIC engineer to see if there's a way to get the driver extended to support the case when data size is larger than the FIFO size. From my understanding based on the data sheet I have, I don't think that can be done with this controller. But if the ASIC engineers tells me the opposite, I'll add it as a separate patch later.

Thanks,

Ray

#351 lore raw

Re: [PATCH v8 2/3] i2c: iproc: Add Broadcom iProc I2C Driver

Quoted 6 lines

Okay. Let me check with our ASIC engineer to see if there's a way to get the driver extended to support the case when data size is larger than the FIFO size. From my understanding based on the data sheet I have, I don't think that can be done with this controller. But if the ASIC engineers tells me the opposite, I'll add it as a separate patch later.

Perfect, thanks!

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150209/b20c9302/attachment.sig>

author maintainer
#352 lore raw

PATCH v8 3/3 ARM: dts: add I2C device nodes for Broadcom Cygnus

In reply to Ray Jui

Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep them disabled there. Individual I2C devices can be enabled in board specific dts file when I2C slave devices are enabled in the future

---
 arch/arm/boot/dts/bcm-cygnus.dtsi |   20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)
arch/arm/boot/dts/bcm-cygnus.dtsi +20 -0
index 5126f9e..ff5fb6a 100644
@@ -70,6 +70,26 @@
 		};
 	};
 
+	i2c0: i2c@18008000 {
+		compatible = "brcm,cygnus-iproc-i2c", "brcm,iproc-i2c";
+		reg = <0x18008000 0x100>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>;
+		clock-frequency = <100000>;
+		status = "disabled";
+	};
+
+	i2c1: i2c@1800b000 {
+		compatible = "brcm,cygnus-iproc-i2c", "brcm,iproc-i2c";
+		reg = <0x1800b000 0x100>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>;
+		clock-frequency = <100000>;
+		status = "disabled";
+	};
+
 	uart0: serial@18020000 {
 		compatible = "snps,dw-apb-uart";
 		reg = <0x18020000 0x100>;
Signature · 1 line
1.7.9.5
author maintainer
#353 lore raw

v9 cover Add I2C support to Broadcom iProc

In reply to a message not in the archive

This patchset contains the initial I2C support for Broadcom iProc family of SoCs.

The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100 kHz) and fast mode (400 kHz)

Synced code base to Linux 3.19-rc7

Changes from v8:
- Sort header includes in alphabetical order
- Use correct error code
- Get rid of redundant functions and combine functions to make driver more slim
- Get rid of redundant debugging prints that are already available from the I2C framework
- Other minor improvements

Changes from v7:
- Remove redundant 10-bit address check in the driver
- Fix the driver that accidentally emits 1-byte of data with zero content in the case of SMBUS quick command
- Improve debugging prints in the driver
- Other minor improvements

Changes from v6:
- Get rid of unnecessary atomic variable usage in the driver
- Improve the "waiting for transaction to complete" logic further by making sure there's no pending/ongoing interrupt by the time when flag 'xfer_is_done' is checked
- After disabling interrupt with 'writel', add 'readl' to the same register to flush the bus to ensure the write has gone through

Changes from v5:
- Improve the "waiting for transaction to be complete" logic to take care of the corner case when an interrupt fires after wait_for_completion_timeout times out
- Improve the logic to disable I2C interrupt in the remove function. Make it more generic so it works for both dedicated and shared interrupt

Changes from v4:
- Remove redundant header file includes
- Change the logic that waits for the host controller to be idle to simply return -EBUSY
- Use proper print level and error codes in the driver
- Allow zero length message in the driver to support I2C_SMBUS_QUICK
- Change back to use devm_request_irq. Disable interrupt in the remove function so there's no outstanding I2C interrupt when the driver is being removed from the framework
- Other minor miscellaneous improvements and fixes

Changes from v3:
- Add config dependency to COMPILE_TEST to allow the driver to be build tested by other platforms
- Improve CPU utilization efficiency in the loop of waiting for bus to idle
- Add more comment in the driver to clarify the way how the "start busy" interrupt is triggered from the I2C controller
- Fix inconsistent coding style and format
- Improve the bus speed validation logic in the driver
- Add code to free the interrupt line in driver's remove function. Also change to use non-devm API to request the interrupt line
- Other miscellaneous improvements and fixes

Changes from v2:
- Have the I2C driver default to y so it does not need to be selected from ARCH_BCM_IPROC. This also helps to get rid of one patch. The driver still depends on ARCH_BCM_IPROC
- Get rid of redundant check on resource returned by platform_get_resource

Changes from v1:
- Fix function argument parenthesis
- Get rid of redundant driver owner field

Ray Jui (3):
i2c: iProc: define Broadcom iProc I2C binding
i2c: iproc: Add Broadcom iProc I2C Driver
ARM: dts: add I2C device nodes for Broadcom Cygnus

 .../devicetree/bindings/i2c/brcm,iproc-i2c.txt     |   37 ++
 arch/arm/boot/dts/bcm-cygnus.dtsi                  |   20 +
 drivers/i2c/busses/Kconfig                         |   10 +
 drivers/i2c/busses/Makefile                        |    1 +
 drivers/i2c/busses/i2c-bcm-iproc.c                 |  461 ++++++++++++++++++++
 5 files changed, 529 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt
 create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c
Signature · 1 line
1.7.9.5
author maintainer
#354 lore raw

PATCH v9 1/3 i2c: iProc: define Broadcom iProc I2C binding

Document the I2C device tree binding for Broadcom iProc family of SoCs

---
 .../devicetree/bindings/i2c/brcm,iproc-i2c.txt     |   37 ++++++++++++++++++++
 1 file changed, 37 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt
Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt new file +37 -0
new file mode 100644
index 0000000..81f982c
@@ -0,0 +1,37 @@
+Broadcom iProc I2C controller
+
+Required properties:
+
+- compatible:
+    Must be "brcm,iproc-i2c"
+
+- reg:
+    Define the base and range of the I/O address space that contain the iProc
+    I2C controller registers
+
+- interrupts:
+    Should contain the I2C interrupt
+
+- clock-frequency:
+    This is the I2C bus clock. Need to be either 100000 or 400000
+
+- #address-cells:
+    Always 1 (for I2C addresses)
+
+- #size-cells:
+    Always 0
+
+Example:
+	i2c0: i2c@18008000 {
+		compatible = "brcm,iproc-i2c";
+		reg = <0x18008000 0x100>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>;
+		clock-frequency = <100000>;
+
+		codec: wm8750@1a {
+			compatible = "wlf,wm8750";
+			reg = <0x1a>;
+		};
+	};
Signature · 1 line
1.7.9.5
Applied
#355 lore raw

Re: [PATCH v9 1/3] i2c: iProc: define Broadcom iProc I2C binding

Quoted 6 lines · Ray Jui

Document the I2C device tree binding for Broadcom iProc family of SoCs

Applied to for-next, thanks!

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150209/f4265f58/attachment-0001.sig>

author maintainer
#356 lore raw

PATCH v9 2/3 i2c: iproc: Add Broadcom iProc I2C Driver

In reply to Ray Jui

Add initial support to the Broadcom iProc I2C controller found in the iProc family of SoCs.

The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100kHz) and fast mode (400kHz)

---
 drivers/i2c/busses/Kconfig         |   10 +
 drivers/i2c/busses/Makefile        |    1 +
 drivers/i2c/busses/i2c-bcm-iproc.c |  461 ++++++++++++++++++++++++++++++++++++
 3 files changed, 472 insertions(+)
 create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c
drivers/i2c/busses/Kconfig +10 -0
index ab838d9..3d08731 100644
@@ -372,6 +372,16 @@ config I2C_BCM2835
 	  This support is also available as a module.  If so, the module
 	  will be called i2c-bcm2835.
 
+config I2C_BCM_IPROC
+	tristate "Broadcom iProc I2C controller"
+	depends on ARCH_BCM_IPROC || COMPILE_TEST
+	default ARCH_BCM_IPROC
+	help
+	  If you say yes to this option, support will be included for the
+	  Broadcom iProc I2C controller.
+
+	  If you don't know what to do here, say N.
+
 config I2C_BCM_KONA
 	tristate "BCM Kona I2C adapter"
 	depends on ARCH_BCM_MOBILE
drivers/i2c/busses/Makefile +1 -0
index 56388f6..d93b509 100644
@@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91)		+= i2c-at91.o
 obj-$(CONFIG_I2C_AU1550)	+= i2c-au1550.o
 obj-$(CONFIG_I2C_AXXIA)		+= i2c-axxia.o
 obj-$(CONFIG_I2C_BCM2835)	+= i2c-bcm2835.o
+obj-$(CONFIG_I2C_BCM_IPROC)	+= i2c-bcm-iproc.o
 obj-$(CONFIG_I2C_BLACKFIN_TWI)	+= i2c-bfin-twi.o
 obj-$(CONFIG_I2C_CADENCE)	+= i2c-cadence.o
 obj-$(CONFIG_I2C_CBUS_GPIO)	+= i2c-cbus-gpio.o
drivers/i2c/busses/i2c-bcm-iproc.c new file +461 -0

461 lines not on this page · raw message

Signature · 1 line
1.7.9.5
#357 lore raw

Re: [PATCH v9 2/3] i2c: iproc: Add Broadcom iProc I2C Driver

Quoted 10 lines · Ray Jui

Add initial support to the Broadcom iProc I2C controller found in the iProc family of SoCs.

The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100kHz) and fast mode (400kHz)

Looks good. What kind of tests have you done with exactly this version of the driver (not earlier ones)?

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150208/15f140da/attachment.sig>

author maintainer
#358 lore raw

Re: [PATCH v9 2/3] i2c: iproc: Add Broadcom iProc I2C Driver

Quoted 8 lines · Wolfram Sang
Quoted 7 lines · Ray Jui

Add initial support to the Broadcom iProc I2C controller found in the iProc family of SoCs.

The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100kHz) and fast mode (400kHz)

Quoted 7 lines
Quoted 3 lines

Looks good. What kind of tests have you done with exactly this version of the driver (not earlier ones)?

I did build test and ran i2cdetect on Cygnus BCM958300K combo board with the 3.19 rc7 kernel, and then I back ported this driver to our 3.10 version of the production kernel (where we have complete audio driver support), to run some audio playback tests, which involves using the wolfson codec (through i2c). The only line I added when back porting to the 3.10 kernel is of_i2c_register_devices at the end of probe.

Thanks,

Ray

Applied
#359 lore raw

Re: [PATCH v9 2/3] i2c: iproc: Add Broadcom iProc I2C Driver

In reply to Ray Jui
Quoted 10 lines · Ray Jui

Add initial support to the Broadcom iProc I2C controller found in the iProc family of SoCs.

The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100kHz) and fast mode (400kHz)

Applied to for-next, thanks!

Next time, please send new patches as seperate threads, not as a reply to the old series.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150209/ede3c1de/attachment.sig>

author maintainer
#360 lore raw

Re: [PATCH v9 2/3] i2c: iproc: Add Broadcom iProc I2C Driver

Quoted 8 lines · Wolfram Sang
Quoted 7 lines · Ray Jui

Add initial support to the Broadcom iProc I2C controller found in the iProc family of SoCs.

The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100kHz) and fast mode (400kHz)

Quoted 9 lines
Quoted 3 lines

Applied to for-next, thanks!

Next time, please send new patches as seperate threads, not as a reply to the old series.

Thanks, Wolfram. I think I'm missing something here and would like to get it clarified, so I don't make the same mistake again in the future:
I thought I've already sent these patches as a new thread, i.e., [PATCH v9 ...], isn't it?

Thanks,

Ray

#361 lore raw

Re: [PATCH v9 2/3] i2c: iproc: Add Broadcom iProc I2C Driver

Quoted 5 lines

Thanks, Wolfram. I think I'm missing something here and would like to get it clarified, so I don't make the same mistake again in the future:
I thought I've already sent these patches as a new thread, i.e., [PATCH v9 ...], isn't it?

Yes, but "In-Reply-To" was set and so my MUAs (mutt and Thunderbird) threaded them to your first submission. Most people I know prefer to have this not threaded.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150210/05111ede/attachment.sig>

author maintainer
#362 lore raw

Re: [PATCH v9 2/3] i2c: iproc: Add Broadcom iProc I2C Driver

Quoted 10 lines · Wolfram Sang
Quoted 5 lines

Thanks, Wolfram. I think I'm missing something here and would like to get it clarified, so I don't make the same mistake again in the future:
I thought I've already sent these patches as a new thread, i.e., [PATCH v9 ...], isn't it?

Yes, but "In-Reply-To" was set and so my MUAs (mutt and Thunderbird) threaded them to your first submission. Most people I know prefer to have this not threaded.

Okay I got it now. Thanks.

Ray

author maintainer
#363 lore raw

PATCH v9 3/3 ARM: dts: add I2C device nodes for Broadcom Cygnus

In reply to Ray Jui

Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep them disabled there. Individual I2C devices can be enabled in board specific dts file when I2C slave devices are enabled in the future

---
 arch/arm/boot/dts/bcm-cygnus.dtsi |   20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)
arch/arm/boot/dts/bcm-cygnus.dtsi +20 -0
index 5126f9e..ff5fb6a 100644
@@ -70,6 +70,26 @@
 		};
 	};
 
+	i2c0: i2c@18008000 {
+		compatible = "brcm,cygnus-iproc-i2c", "brcm,iproc-i2c";
+		reg = <0x18008000 0x100>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>;
+		clock-frequency = <100000>;
+		status = "disabled";
+	};
+
+	i2c1: i2c@1800b000 {
+		compatible = "brcm,cygnus-iproc-i2c", "brcm,iproc-i2c";
+		reg = <0x1800b000 0x100>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>;
+		clock-frequency = <100000>;
+		status = "disabled";
+	};
+
 	uart0: serial@18020000 {
 		compatible = "snps,dw-apb-uart";
 		reg = <0x18020000 0x100>;
Signature · 1 line
1.7.9.5
#364 lore raw

Re: [PATCH v9 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus

Quoted 7 lines · Ray Jui

Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep them disabled there. Individual I2C devices can be enabled in board specific dts file when I2C slave devices are enabled in the future

I usually don't take DTS patches. They should go via arm-soc. Please say so if there are reasons I should take them.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150209/c0a1ad9e/attachment.sig>

author maintainer
#365 lore raw

Re: [PATCH v9 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus

Quoted 5 lines · Wolfram Sang
Quoted 4 lines · Ray Jui

Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep them disabled there. Individual I2C devices can be enabled in board specific dts file when I2C slave devices are enabled in the future

Quoted 7 lines
Quoted 3 lines

I usually don't take DTS patches. They should go via arm-soc. Please say so if there are reasons I should take them.

Okay. I'll send this as an individual patch to arm-soc.

Thanks,

Ray

#366 lore raw

Re: [PATCH v9 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus

Le 09/02/2015 21:24, Ray Jui a ?crit :

Quoted 8 lines · Wolfram Sang
Quoted 5 lines · Ray Jui
Quoted 4 lines

Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep them disabled there. Individual I2C devices can be enabled in board specific dts file when I2C slave devices are enabled in the future

Quoted 8 lines
Quoted 7 lines
Quoted 3 lines

I usually don't take DTS patches. They should go via arm-soc. Please say so if there are reasons I should take them.

Okay. I'll send this as an individual patch to arm-soc.

Could you put in in a branch somewhere on github.com?/cygnus-linus.git so it's easy for me to pull from there?
--
Florian

author maintainer
#367 lore raw

Re: [PATCH v9 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus

Quoted 16 lines · Florian Fainelli

Le 09/02/2015 21:24, Ray Jui a ?crit :

Quoted 15 lines · Wolfram Sang
Quoted 12 lines · Ray Jui
Quoted 7 lines

Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep them disabled there. Individual I2C devices can be enabled in board specific dts file when I2C slave devices are enabled in the future

I usually don't take DTS patches. They should go via arm-soc. Please say so if there are reasons I should take them.

Quoted 7 lines
Quoted 1 line

Okay. I'll send this as an individual patch to arm-soc.

Could you put in in a branch somewhere on github.com?/cygnus-linus.git so it's easy for me to pull from there?
--
Florian

Hi Florian,

It's in branch cygnus-i2c-v9.

Thanks!

Ray