2013-06-06 19:08:12

by Heiko Stübner

[permalink] [raw]
Subject: [PATCH v2 0/8] arm: add basic support for Rockchip Cortex-A9 SoCs

Second version of basic Rockchip A9 support.


Changes since v1:
- addressed Linus Walleij's comments to the pinctrl driver, including the
move to generic pinconfig (hopefully I did catch all)
- renamed the clocks to use the SoC name of the initial user
as suggested by Olof Johansson
- fixed the uart address, found by Arnd Bergmann
- address Arnd's comments on the board file (use of_clk_init and friends,
remove map_io, use real soc names)
- removed Makefile.boot as suggested by Thomas Petazzoni


Dependencies:
- the irqdomain support for the generic_irq from Thomas Gleixner is required
by the pinctrl driver, which is strangely not in linux-next, only in tip.git
- "mmc: dw_mmc: Add support DW SD/MMC driver on SOCFPGA" moves the
SDMMC_CMD_USE_HOLD_REG constant into the common header, which is required
on these Rockchip SoCs
- the db_apb_timer enhancements sent separately
- the pinctrl-generic option for pin-default pulls, sent today


Heiko Stuebner (8):
clk: flag to use upper half of the register as change indicator
clk: divider: add flag to limit possible dividers to even numbers
mmc: dw_mmc-pltfm: remove static from dw_mci_pltfm_remove
mmc: dw_mmc-pltfm: add Rockchip variant
pinctrl: add pinctrl driver for Rockchip SoCs
clk: add basic Rockchip rk3066a clock support
arm: add debug uarts for rockchip rk29xx and rk3xxx series
arm: add basic support for Rockchip RK3066a boards

.../bindings/pinctrl/rockchip,pinctrl.txt | 97 ++
arch/arm/Kconfig | 2 +
arch/arm/Kconfig.debug | 34 +
arch/arm/Makefile | 1 +
arch/arm/boot/dts/rk3066a-clocks.dtsi | 460 +++++++
arch/arm/boot/dts/rk3066a.dtsi | 359 +++++
arch/arm/include/debug/rockchip.S | 42 +
arch/arm/mach-rockchip/Kconfig | 17 +
arch/arm/mach-rockchip/Makefile | 1 +
arch/arm/mach-rockchip/rockchip.c | 54 +
drivers/clk/Makefile | 1 +
drivers/clk/clk-divider.c | 29 +-
drivers/clk/clk-gate.c | 24 +-
drivers/clk/clk-mux.c | 15 +-
drivers/clk/rockchip/Makefile | 6 +
drivers/clk/rockchip/clk-rockchip-pll.c | 131 ++
drivers/clk/rockchip/clk-rockchip-pll.h | 19 +
drivers/clk/rockchip/clk-rockchip.c | 313 +++++
drivers/mmc/host/dw_mmc-pltfm.c | 48 +-
drivers/pinctrl/Kconfig | 6 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-rockchip.c | 1364 ++++++++++++++++++++
include/dt-bindings/pinctrl/rockchip.h | 37 +
include/linux/clk-provider.h | 18 +
24 files changed, 3053 insertions(+), 26 deletions(-)
create mode 100644 Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
create mode 100644 arch/arm/boot/dts/rk3066a-clocks.dtsi
create mode 100644 arch/arm/boot/dts/rk3066a.dtsi
create mode 100644 arch/arm/include/debug/rockchip.S
create mode 100644 arch/arm/mach-rockchip/Kconfig
create mode 100644 arch/arm/mach-rockchip/Makefile
create mode 100644 arch/arm/mach-rockchip/rockchip.c
create mode 100644 drivers/clk/rockchip/Makefile
create mode 100644 drivers/clk/rockchip/clk-rockchip-pll.c
create mode 100644 drivers/clk/rockchip/clk-rockchip-pll.h
create mode 100644 drivers/clk/rockchip/clk-rockchip.c
create mode 100644 drivers/pinctrl/pinctrl-rockchip.c
create mode 100644 include/dt-bindings/pinctrl/rockchip.h

--
1.7.2.3


2013-06-06 19:08:49

by Heiko Stübner

[permalink] [raw]
Subject: [PATCH v2 1/8] clk: flag to use upper half of the register as change indicator

There exist platforms, namely at least all Rockchip Cortex-A9 based ones,
that don't use the paradigm of reading-changing-writing the register contents,
but instead only write the changes to the register with a mask that indicates
the changed bits.

This patch adds flags and code to support the case where the lower 16 bit of
hold the information and the upper 16 bit are used as mask to indicate the
written changes.

As hardware-specific flags should not be added to the common clk flags, the
flags are added to gate, mux and divider clocks individually.

Signed-off-by: Heiko Stuebner <[email protected]>
---
drivers/clk/clk-divider.c | 15 +++++++++++++--
drivers/clk/clk-gate.c | 24 ++++++++++++++++++------
drivers/clk/clk-mux.c | 15 +++++++++++++--
include/linux/clk-provider.h | 16 ++++++++++++++++
4 files changed, 60 insertions(+), 10 deletions(-)

diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index 6d96741..e37c48a 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -217,8 +217,12 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
if (divider->lock)
spin_lock_irqsave(divider->lock, flags);

- val = readl(divider->reg);
- val &= ~(div_mask(divider) << divider->shift);
+ if (divider->flags & CLK_DIVIDER_MASK_UPPER_HALF) {
+ val = div_mask(divider) << (divider->shift + 16);
+ } else {
+ val = readl(divider->reg);
+ val &= ~(div_mask(divider) << divider->shift);
+ }
val |= value << divider->shift;
writel(val, divider->reg);

@@ -245,6 +249,13 @@ static struct clk *_register_divider(struct device *dev, const char *name,
struct clk *clk;
struct clk_init_data init;

+ if ((clk_divider_flags & CLK_DIVIDER_MASK_UPPER_HALF) &&
+ (shift + width > 15)) {
+ pr_err("%s: shift %d + width %d invalid\n", __func__,
+ shift, width);
+ return ERR_PTR(-EINVAL);
+ }
+
/* allocate the divider */
div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
if (!div) {
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
index 15114fe..e553fa7 100644
--- a/drivers/clk/clk-gate.c
+++ b/drivers/clk/clk-gate.c
@@ -53,12 +53,19 @@ static void clk_gate_endisable(struct clk_hw *hw, int enable)
if (gate->lock)
spin_lock_irqsave(gate->lock, flags);

- reg = readl(gate->reg);
-
- if (set)
- reg |= BIT(gate->bit_idx);
- else
- reg &= ~BIT(gate->bit_idx);
+ if (gate->flags & CLK_GATE_MASK_UPPER_HALF) {
+ reg = BIT(gate->bit_idx + 16);
+
+ if (set)
+ reg |= BIT(gate->bit_idx);
+ } else {
+ reg = readl(gate->reg);
+
+ if (set)
+ reg |= BIT(gate->bit_idx);
+ else
+ reg &= ~BIT(gate->bit_idx);
+ }

writel(reg, gate->reg);

@@ -121,6 +128,11 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
struct clk *clk;
struct clk_init_data init;

+ if ((clk_gate_flags & CLK_GATE_MASK_UPPER_HALF) && bit_idx > 15) {
+ pr_err("%s: bit_idx %d invalid\n", __func__, bit_idx);
+ return ERR_PTR(-EINVAL);
+ }
+
/* allocate the gate */
gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
if (!gate) {
diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
index 25b1734..fce26f5 100644
--- a/drivers/clk/clk-mux.c
+++ b/drivers/clk/clk-mux.c
@@ -86,8 +86,12 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
if (mux->lock)
spin_lock_irqsave(mux->lock, flags);

- val = readl(mux->reg);
- val &= ~(mux->mask << mux->shift);
+ if (mux->flags & CLK_MUX_MASK_UPPER_HALF) {
+ val = mux->mask << (mux->shift + 16);
+ } else {
+ val = readl(mux->reg);
+ val &= ~(mux->mask << mux->shift);
+ }
val |= index << mux->shift;
writel(val, mux->reg);

@@ -112,6 +116,13 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name,
struct clk *clk;
struct clk_init_data init;

+ if (!mask || ((clk_mux_flags & CLK_MUX_MASK_UPPER_HALF) &&
+ (__fls(mask << shift) > 15))) {
+ pr_err("%s: shift %d + mask %d invalid\n", __func__,
+ shift, mask);
+ return ERR_PTR(-EINVAL);
+ }
+
/* allocate the mux */
mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
if (!mux) {
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 265f384..420a187 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -210,6 +210,11 @@ void of_fixed_clk_setup(struct device_node *np);
* CLK_GATE_SET_TO_DISABLE - by default this clock sets the bit at bit_idx to
* enable the clock. Setting this flag does the opposite: setting the bit
* disable the clock and clearing it enables the clock
+ * CLK_GATE_MASK_UPPER_HALF - this clock only uses the lower 16 bit of the
+ * register to hold the gate bits, while using the upper 16 bit to
+ * indicate the bits that get changed during a write. So gating the
+ * clock in bit 2 would write BIT(18) | BIT(2) to the register, while
+ * ungating this clock would write only BIT(18) to the register.
*/
struct clk_gate {
struct clk_hw hw;
@@ -220,6 +225,7 @@ struct clk_gate {
};

#define CLK_GATE_SET_TO_DISABLE BIT(0)
+#define CLK_GATE_MASK_UPPER_HALF BIT(1)

extern const struct clk_ops clk_gate_ops;
struct clk *clk_register_gate(struct device *dev, const char *name,
@@ -257,6 +263,11 @@ struct clk_div_table {
* Some hardware implementations gracefully handle this case and allow a
* zero divisor by not modifying their input clock
* (divide by one / bypass).
+ * CLK_DIVIDER_MASK_UPPER_HALF - this clock only uses the lower 16 bit of the
+ * register to hold the divider bits, while using the upper 16 bit to
+ * indicate the bits that get changed during a write. So for a clock with
+ * shift 0 and width 2, setting the divider to 2 would result in a write
+ * of (3 << 16) | (2 << 0).
*/
struct clk_divider {
struct clk_hw hw;
@@ -271,6 +282,7 @@ struct clk_divider {
#define CLK_DIVIDER_ONE_BASED BIT(0)
#define CLK_DIVIDER_POWER_OF_TWO BIT(1)
#define CLK_DIVIDER_ALLOW_ZERO BIT(2)
+#define CLK_DIVIDER_MASK_UPPER_HALF BIT(3)

extern const struct clk_ops clk_divider_ops;
struct clk *clk_register_divider(struct device *dev, const char *name,
@@ -299,6 +311,9 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name,
* Flags:
* CLK_MUX_INDEX_ONE - register index starts at 1, not 0
* CLK_MUX_INDEX_BIT - register index is a single bit (power of two)
+ * CLK_MUX_MASK_UPPER_HALF - this clock only uses the lower 16 bit of the
+ * register to hold the mux bits, while using the upper 16 bit to
+ * indicate the bits that get changed during a write.
*/
struct clk_mux {
struct clk_hw hw;
@@ -312,6 +327,7 @@ struct clk_mux {

#define CLK_MUX_INDEX_ONE BIT(0)
#define CLK_MUX_INDEX_BIT BIT(1)
+#define CLK_MUX_MASK_UPPER_HALF BIT(3)

extern const struct clk_ops clk_mux_ops;

--
1.7.2.3

2013-06-06 19:39:35

by Heiko Stübner

[permalink] [raw]
Subject: [PATCH v2 5/8] pinctrl: add pinctrl driver for Rockchip SoCs

This driver adds support the Cortex-A9 based SoCs from Rockchip,
so at least the RK2928, RK3066 (a and b) and RK3188.
Earlier Rockchip SoCs seem to use similar mechanics for gpio
handling so should be supportable with relative small changes.
Pull handling on the rk3188 is currently a stub, due to it being
a bit different to the earlier SoCs.

Pinmuxing as well as gpio (and interrupt-) handling tested on
a rk3066a based machine.

Signed-off-by: Heiko Stuebner <[email protected]>
---
.../bindings/pinctrl/rockchip,pinctrl.txt | 97 ++
drivers/pinctrl/Kconfig | 6 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-rockchip.c | 1364 ++++++++++++++++++++
include/dt-bindings/pinctrl/rockchip.h | 37 +
5 files changed, 1505 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
create mode 100644 drivers/pinctrl/pinctrl-rockchip.c
create mode 100644 include/dt-bindings/pinctrl/rockchip.h

diff --git a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
new file mode 100644
index 0000000..ded0eed
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
@@ -0,0 +1,97 @@
+* Rockchip Pinmux Controller
+
+The Rockchip Pinmux Controller, enables the IC
+to share one PAD to several functional blocks. The sharing is done by
+multiplexing the PAD input/output signals. For each PAD there are up to
+4 muxing options with option 0 being the use as a GPIO.
+
+Please refer to pinctrl-bindings.txt in this directory for details of the
+common pinctrl bindings used by client devices, including the meaning of the
+phrase "pin configuration node".
+
+The Rockchip pin configuration node is a node of a group of pins which can be
+used for a specific device or function. This node represents both mux and
+config of the pins in that group. The 'pins' selects the function mode(also
+named pin mode) this pin can work on and the 'config' configures various pad
+settings such as pull-up, etc.
+
+The pins are grouped into up to 5 individual pin banks which need to be
+defined as gpio sub-nodes of the pinmux controller.
+
+Required properties for iomux controller:
+ - compatible: one of "rockchip,rk2928-pinctrl", "rockchip,rk3066a-pinctrl"
+ "rockchip,rk3066b-pinctrl", "rockchip,rk3188-pinctrl"
+
+Required properties for gpio sub nodes:
+ - compatible: "rockchip,gpio-bank"
+ - reg: register of the gpio bank (different than the iomux registerset)
+ - interrupts: base interrupt of the gpio bank in the interrupt controller
+ - clocks: clock that drives this bank
+ - gpio-controller: identifies the node as a gpio controller and pin bank.
+ - #gpio-cells: number of cells in GPIO specifier. Since the generic GPIO
+ binding is used, the amount of cells must be specified as 2. See generic
+ GPIO binding documentation for description of particular cells.
+ - interrupt-controller: identifies the controller node as interrupt-parent.
+ - #interrupt-cells: the value of this property should be 2 and the interrupt
+ cells should use the standard two-cell scheme described in
+ bindings/interrupt-controller/interrupts.txt
+
+Required properties for pin configuration node:
+ - rockchip,pins: 4 integers array, represents a group of pins mux and config
+ setting. The format is rockchip,pins = <PIN_BANK PIN_BANK_NUM MUX CONFIG>.
+ The MUX 0 means gpio and MUX 1 to 3 mean the specific device function
+
+Bits used for CONFIG:
+PULL_AUTO (1 << 0): indicate this pin needs a pull setting for SoCs
+ that determine the pull up or down themselfs
+PULL_UP (1 << 1): indicate this pin needs a pull up
+PULL_DOWN (1 << 2): indicate this pin needs a pull down
+
+Examples:
+
+#include <dt-bindings/pinctrl/rockchip.h>
+
+...
+
+pinctrl@20008000 {
+ compatible = "rockchip,rk3066a-pinctrl";
+ reg = <0x20008000 0x150>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ gpio0: gpio0@20034000 {
+ compatible = "rockchip,gpio-bank";
+ reg = <0x20034000 0x100>;
+ interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates8 9>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ ...
+
+ uart2 {
+ uart2_xfer: uart2-xfer {
+ rockchip,pins = <RK_GPIO1 8 1 RK_PINCTRL_PULL_AUTO>,
+ <RK_GPIO1 9 1 RK_PINCTRL_PULL_AUTO>;
+ };
+ };
+};
+
+uart2: serial@20064000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x20064000 0x400>;
+ interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <1>;
+ clocks = <&mux_uart2>;
+ status = "okay";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart2_xfer>;
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 5259d40..dec3387 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -158,6 +158,12 @@ config PINCTRL_DB8540
bool "DB8540 pin controller driver"
depends on PINCTRL_NOMADIK && ARCH_U8500

+config PINCTRL_ROCKCHIP
+ bool
+ select PINMUX
+ select GENERIC_PINCONF
+ select GENERIC_IRQ_CHIP
+
config PINCTRL_SINGLE
tristate "One-register-per-pin type device tree based pinctrl driver"
depends on OF
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 3b26e3c..16c3f8bc 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_PINCTRL_NOMADIK) += pinctrl-nomadik.o
obj-$(CONFIG_PINCTRL_STN8815) += pinctrl-nomadik-stn8815.o
obj-$(CONFIG_PINCTRL_DB8500) += pinctrl-nomadik-db8500.o
obj-$(CONFIG_PINCTRL_DB8540) += pinctrl-nomadik-db8540.o
+obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
obj-$(CONFIG_PINCTRL_SIRF) += sirf/
obj-$(CONFIG_PINCTRL_SUNXI) += pinctrl-sunxi.o
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
new file mode 100644
index 0000000..b77e241
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -0,0 +1,1364 @@
+/*
+ * Pinctrl driver for Rockchip SoCs
+ *
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * With some ideas taken from pinctrl-samsung:
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ * Copyright (c) 2012 Linaro Ltd
+ * http://www.linaro.org
+ *
+ * and pinctrl-at91:
+ * Copyright (C) 2011-2012 Jean-Christophe PLAGNIOL-VILLARD <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/gpio.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/clk-provider.h>
+#include <dt-bindings/pinctrl/rockchip.h>
+
+#include "core.h"
+
+/* GPIO control registers */
+#define GPIO_SWPORT_DR 0x00
+#define GPIO_SWPORT_DDR 0x04
+#define GPIO_INTEN 0x30
+#define GPIO_INTMASK 0x34
+#define GPIO_INTTYPE_LEVEL 0x38
+#define GPIO_INT_POLARITY 0x3c
+#define GPIO_INT_STATUS 0x40
+#define GPIO_INT_RAWSTATUS 0x44
+#define GPIO_DEBOUNCE 0x48
+#define GPIO_PORTS_EOI 0x4c
+#define GPIO_EXT_PORT 0x50
+#define GPIO_LS_SYNC 0x60
+
+/**
+ * @reg_base: register base of the gpio bank
+ * @clk: clock of the gpio bank
+ * @irq: interrupt of the gpio bank
+ * @pin_base: first pin number
+ * @nr_pins: number of pins in this bank
+ * @name: name of the bank
+ * @bank_num: number of the bank, to account for holes
+ * @valid: are all necessary informations present
+ * @of_node: dt node of this bank
+ * @drvdata: common pinctrl basedata
+ * @domain: irqdomain of the gpio bank
+ * @gpio_chip: gpiolib chip
+ * @grange: gpio range
+ * @slock: spinlock for the gpio bank
+ */
+struct rockchip_pin_bank {
+ void __iomem *reg_base;
+ struct clk *clk;
+ int irq;
+ u32 pin_base;
+ u8 nr_pins;
+ char *name;
+ u8 bank_num;
+ bool valid;
+ struct device_node *of_node;
+ struct rockchip_pinctrl *drvdata;
+ struct irq_domain *domain;
+ struct gpio_chip gpio_chip;
+ struct pinctrl_gpio_range grange;
+ spinlock_t slock;
+
+};
+
+#define PIN_BANK(id, pins, label) \
+ { \
+ .bank_num = id, \
+ .nr_pins = pins, \
+ .name = label, \
+ }
+
+/**
+ * @pull_auto: some SoCs don't allow pulls to be specified as up or down, but
+ * instead decide this automatically based on the pad-type.
+ */
+struct rockchip_pin_ctrl {
+ struct rockchip_pin_bank *pin_banks;
+ u32 nr_banks;
+ u32 nr_pins;
+ char *label;
+ int mux_offset;
+ int pull_offset;
+ bool pull_auto;
+ int pull_bank_stride;
+};
+
+/**
+ * struct rockchip_pin_group: represent group of pins of a pinmux function.
+ * @name: name of the pin group, used to lookup the group.
+ * @pins: the pins included in this group.
+ * @npins: number of pins included in this group.
+ * @func: the mux function number to be programmed when selected.
+ * @configs: the config values to be set for each pin
+ */
+struct rockchip_pin_group {
+ const char *name;
+ unsigned int *pins;
+ unsigned int npins;
+ unsigned int *func;
+ unsigned long *configs;
+};
+
+/**
+ * struct rockchip_pmx_func: represent a pin function.
+ * @name: name of the pin function, used to lookup the function.
+ * @groups: one or more names of pin groups that provide this function.
+ * @num_groups: number of groups included in @groups.
+ */
+struct rockchip_pmx_func {
+ const char *name;
+ const char **groups;
+ u8 ngroups;
+};
+
+struct rockchip_pinctrl {
+ void __iomem *reg_base;
+ struct device *dev;
+ struct rockchip_pin_ctrl *ctrl;
+ struct pinctrl_desc pctl;
+ struct pinctrl_dev *pctl_dev;
+ struct rockchip_pin_group *groups;
+ unsigned int ngroups;
+ struct rockchip_pmx_func *functions;
+ unsigned int nfunctions;
+};
+
+static inline struct rockchip_pin_bank *gc_to_pin_bank(struct gpio_chip *gc)
+{
+ return container_of(gc, struct rockchip_pin_bank, gpio_chip);
+}
+
+static const inline struct rockchip_pin_group *pinctrl_name_to_group(
+ const struct rockchip_pinctrl *info,
+ const char *name)
+{
+ const struct rockchip_pin_group *grp = NULL;
+ int i;
+
+ for (i = 0; i < info->ngroups; i++) {
+ if (strcmp(info->groups[i].name, name))
+ continue;
+
+ grp = &info->groups[i];
+ break;
+ }
+
+ return grp;
+}
+
+/*
+ * given a pin number that is local to a pin controller, find out the pin bank
+ * and the register base of the pin bank.
+ */
+static struct rockchip_pin_bank *pin_to_bank(struct rockchip_pinctrl *info,
+ unsigned pin)
+{
+ struct rockchip_pin_bank *b = info->ctrl->pin_banks;
+
+ while ((pin >= b->pin_base) &&
+ ((b->pin_base + b->nr_pins - 1) < pin))
+ b++;
+
+ return b;
+}
+
+static struct rockchip_pin_bank *bank_num_to_bank(
+ struct rockchip_pinctrl *info,
+ unsigned num)
+{
+ struct rockchip_pin_bank *b = info->ctrl->pin_banks;
+ int i;
+
+ for (i = 0; i < info->ctrl->nr_banks; i++) {
+ if (b->bank_num == num)
+ break;
+
+ b++;
+ }
+
+ if (b->bank_num != num)
+ return ERR_PTR(-EINVAL);
+
+ return b;
+}
+
+/*
+ * Pinctrl_ops handling
+ */
+
+static int rockchip_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ return info->ngroups;
+}
+
+static const char *rockchip_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned selector)
+{
+ struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ return info->groups[selector].name;
+}
+
+static int rockchip_get_group_pins(struct pinctrl_dev *pctldev,
+ unsigned selector, const unsigned **pins,
+ unsigned *npins)
+{
+ struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ if (selector >= info->ngroups)
+ return -EINVAL;
+
+ *pins = info->groups[selector].pins;
+ *npins = info->groups[selector].npins;
+
+ return 0;
+}
+
+static int rockchip_dt_node_to_map(struct pinctrl_dev *pctldev,
+ struct device_node *np,
+ struct pinctrl_map **map, unsigned *num_maps)
+{
+ struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+ const struct rockchip_pin_group *grp;
+ struct pinctrl_map *new_map;
+ struct device_node *parent;
+ int map_num = 1;
+ int i;
+
+ /*
+ * first find the group of this node and check if we need to create
+ * config maps for pins
+ */
+ grp = pinctrl_name_to_group(info, np->name);
+ if (!grp) {
+ dev_err(info->dev, "unable to find group for node %s\n",
+ np->name);
+ return -EINVAL;
+ }
+
+ map_num += grp->npins;
+ new_map = devm_kzalloc(pctldev->dev, sizeof(*new_map) * map_num,
+ GFP_KERNEL);
+ if (!new_map)
+ return -ENOMEM;
+
+ *map = new_map;
+ *num_maps = map_num;
+
+ /* create mux map */
+ parent = of_get_parent(np);
+ if (!parent) {
+ devm_kfree(pctldev->dev, new_map);
+ return -EINVAL;
+ }
+ new_map[0].type = PIN_MAP_TYPE_MUX_GROUP;
+ new_map[0].data.mux.function = parent->name;
+ new_map[0].data.mux.group = np->name;
+ of_node_put(parent);
+
+ /* create config map */
+ new_map++;
+ for (i = 0; i < grp->npins; i++) {
+ new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN;
+ new_map[i].data.configs.group_or_pin =
+ pin_get_name(pctldev, grp->pins[i]);
+ new_map[i].data.configs.configs = &grp->configs[i];
+ new_map[i].data.configs.num_configs = 1;
+ }
+
+ dev_dbg(pctldev->dev, "maps: function %s group %s num %d\n",
+ (*map)->data.mux.function, (*map)->data.mux.group, map_num);
+
+ return 0;
+}
+
+static void rockchip_dt_free_map(struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps)
+{
+}
+
+static const struct pinctrl_ops rockchip_pctrl_ops = {
+ .get_groups_count = rockchip_get_groups_count,
+ .get_group_name = rockchip_get_group_name,
+ .get_group_pins = rockchip_get_group_pins,
+ .dt_node_to_map = rockchip_dt_node_to_map,
+ .dt_free_map = rockchip_dt_free_map,
+};
+
+/*
+ * Hardware access
+ */
+
+/*
+ * Set a new mux function for a pin.
+ *
+ * The register is divided into the upper and lower 16 bit. When changing
+ * a value, the previous register value is not read and changed. Instead
+ * it seems the changed bits are marked in the upper 16 bit, while the
+ * changed value gets set in the same offset in the lower 16 bit.
+ * All pin settings seem to be 2 bit wide in both the upper and lower
+ * parts.
+ * @bank: pin bank to change
+ * @pin: pin to change
+ * @mux: new mux function to set
+ */
+static void rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+ void __iomem *reg = info->reg_base + info->ctrl->mux_offset;
+ unsigned long flags;
+ u8 bit;
+ u32 data;
+
+ dev_dbg(info->dev, "setting mux of GPIO%d-%d to %d\n",
+ bank->bank_num, pin, mux);
+
+ /* get basic quadrupel of mux registers and the correct reg inside */
+ reg += bank->bank_num * 0x10;
+ reg += (pin / 8) * 4;
+ bit = (pin % 8) * 2;
+
+ spin_lock_irqsave(&bank->slock, flags);
+
+ data = (3 << (bit + 16));
+ data |= (mux & 3) << bit;
+ writel(data, reg);
+
+ spin_unlock_irqrestore(&bank->slock, flags);
+}
+
+static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+ struct rockchip_pin_ctrl *ctrl = info->ctrl;
+ void __iomem *reg;
+ u8 bit;
+
+ /* rk3066b does support any pulls */
+ if (!ctrl->pull_offset)
+ return PIN_CONFIG_BIAS_DISABLE;
+
+ reg = info->reg_base + ctrl->pull_offset;
+
+ if (ctrl->pull_auto) {
+ reg += bank->bank_num * ctrl->pull_bank_stride;
+ reg += (pin_num / 16) * 4;
+ bit = pin_num % 16;
+
+ return !(readl_relaxed(reg) & BIT(bit))
+ ? PIN_CONFIG_BIAS_PULL_PIN_DEFAULT
+ : PIN_CONFIG_BIAS_DISABLE;
+ } else {
+ dev_err(info->dev, "pull support for rk31xx not implemented\n");
+ return -EIO;
+ }
+}
+
+static int rockchip_set_pull(struct rockchip_pin_bank *bank,
+ int pin_num, int pull)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+ struct rockchip_pin_ctrl *ctrl = info->ctrl;
+ void __iomem *reg;
+ unsigned long flags;
+ u8 bit;
+ u32 data;
+
+ dev_dbg(info->dev, "setting pull of GPIO%d-%d to %d\n",
+ bank->bank_num, pin_num, pull);
+
+ /* rk3066b does support any pulls */
+ if (!ctrl->pull_offset)
+ return pull ? -EINVAL : 0;
+
+ reg = info->reg_base + ctrl->pull_offset;
+
+ if (ctrl->pull_auto) {
+ if (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT &&
+ pull != PIN_CONFIG_BIAS_DISABLE) {
+ dev_err(info->dev, "only PIN_DEFAULT and DISABLE allowed\n");
+ return -EINVAL;
+ }
+
+ reg += bank->bank_num * ctrl->pull_bank_stride;
+ reg += (pin_num / 16) * 4;
+ bit = pin_num % 16;
+
+ spin_lock_irqsave(&bank->slock, flags);
+
+ data = BIT(bit + 16);
+ if (pull == PIN_CONFIG_BIAS_DISABLE)
+ data |= BIT(bit);
+ writel(data, reg);
+
+ spin_unlock_irqrestore(&bank->slock, flags);
+ } else {
+ if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) {
+ dev_err(info->dev, "pull direction (up/down) needs to be specified\n");
+ return -EINVAL;
+ }
+
+ dev_err(info->dev, "pull support for rk31xx not implemented\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * Pinmux_ops handling
+ */
+
+static int rockchip_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
+{
+ struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ return info->nfunctions;
+}
+
+static const char *rockchip_pmx_get_func_name(struct pinctrl_dev *pctldev,
+ unsigned selector)
+{
+ struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ return info->functions[selector].name;
+}
+
+static int rockchip_pmx_get_groups(struct pinctrl_dev *pctldev,
+ unsigned selector, const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+
+ *groups = info->functions[selector].groups;
+ *num_groups = info->functions[selector].ngroups;
+
+ return 0;
+}
+
+static int rockchip_pmx_enable(struct pinctrl_dev *pctldev, unsigned selector,
+ unsigned group)
+{
+ struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+ const unsigned int *pins = info->groups[group].pins;
+ struct rockchip_pin_bank *bank;
+ int cnt;
+
+ dev_dbg(info->dev, "enable function %s group %s\n",
+ info->functions[selector].name, info->groups[group].name);
+
+ /*
+ * for each pin in the pin group selected, program the correspoding pin
+ * pin function number in the config register.
+ */
+ for (cnt = 0; cnt < info->groups[group].npins; cnt++) {
+ bank = pin_to_bank(info, pins[cnt]);
+ rockchip_set_mux(bank, pins[cnt] - bank->pin_base,
+ info->groups[group].func[cnt]);
+ }
+
+ return 0;
+}
+
+static void rockchip_pmx_disable(struct pinctrl_dev *pctldev,
+ unsigned selector, unsigned group)
+{
+ struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+ const unsigned int *pins = info->groups[group].pins;
+ struct rockchip_pin_bank *bank;
+ int cnt;
+
+ dev_dbg(info->dev, "disable function %s group %s\n",
+ info->functions[selector].name, info->groups[group].name);
+
+ for (cnt = 0; cnt < info->groups[group].npins; cnt++) {
+ bank = pin_to_bank(info, pins[cnt]);
+ rockchip_set_mux(bank, pins[cnt] - bank->pin_base, 0);
+ }
+}
+
+/*
+ * The calls to gpio_direction_output() and gpio_direction_input()
+ * leads to this function call (via the pinctrl_gpio_direction_{input|output}()
+ * function called from the gpiolib interface).
+ */
+static int rockchip_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned offset, bool input)
+{
+ struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+ struct rockchip_pin_bank *bank;
+ struct gpio_chip *chip;
+ int pin;
+ u32 data;
+
+ chip = range->gc;
+ bank = gc_to_pin_bank(chip);
+ pin = offset - chip->base;
+
+ dev_dbg(info->dev, "gpio_direction for pin %u as %s-%d to %s\n",
+ offset, range->name, pin, input ? "input" : "output");
+
+ rockchip_set_mux(bank, pin, RK_FUNC_GPIO);
+
+ data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
+ /* set bit to 1 for output, 0 for input */
+ if (!input)
+ data |= BIT(pin);
+ else
+ data &= ~BIT(pin);
+ writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR);
+
+ return 0;
+}
+
+static const struct pinmux_ops rockchip_pmx_ops = {
+ .get_functions_count = rockchip_pmx_get_funcs_count,
+ .get_function_name = rockchip_pmx_get_func_name,
+ .get_function_groups = rockchip_pmx_get_groups,
+ .enable = rockchip_pmx_enable,
+ .disable = rockchip_pmx_disable,
+ .gpio_set_direction = rockchip_pmx_gpio_set_direction,
+};
+
+/*
+ * Pinconf_ops handling
+ */
+
+/* set the pin config settings for a specified pin */
+static int rockchip_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
+ unsigned long config)
+{
+ struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+ struct rockchip_pin_bank *bank = pin_to_bank(info, pin);
+ enum pin_config_param param = pinconf_to_config_param(config);
+
+ switch(param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ case PIN_CONFIG_BIAS_PULL_UP:
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
+ return rockchip_set_pull(bank, pin - bank->pin_base, param);
+ break;
+ default:
+ return -ENOTSUPP;
+ break;
+ }
+
+ return 0;
+}
+
+/* get the pin config settings for a specified pin */
+static int rockchip_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
+ unsigned long *config)
+{
+ struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
+ struct rockchip_pin_bank *bank = pin_to_bank(info, pin);
+ enum pin_config_param param = pinconf_to_config_param(*config);
+ unsigned int pull;
+
+ switch(param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ case PIN_CONFIG_BIAS_PULL_UP:
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
+ pull = rockchip_get_pull(bank, pin - bank->pin_base);
+
+ if (pull != param)
+ return -EINVAL;
+
+ *config = 0;
+ break;
+ default:
+ return -ENOTSUPP;
+ break;
+ }
+
+ return 0;
+}
+
+static const struct pinconf_ops rockchip_pinconf_ops = {
+ .pin_config_get = rockchip_pinconf_get,
+ .pin_config_set = rockchip_pinconf_set,
+};
+
+static const char *gpio_compat = "rockchip,gpio-bank";
+
+static void rockchip_pinctrl_child_count(struct rockchip_pinctrl *info,
+ struct device_node *np)
+{
+ struct device_node *child;
+
+ for_each_child_of_node(np, child) {
+ if (of_device_is_compatible(child, gpio_compat))
+ continue;
+
+ info->nfunctions++;
+ info->ngroups += of_get_child_count(child);
+ }
+}
+
+static int rockchip_pinctrl_parse_groups(struct device_node *np,
+ struct rockchip_pin_group *grp,
+ struct rockchip_pinctrl *info,
+ u32 index)
+{
+ struct rockchip_pin_bank *bank;
+ int size;
+ const __be32 *list;
+ int num;
+ int i, j;
+ int pin;
+ int bias;
+
+ dev_dbg(info->dev, "group(%d): %s\n", index, np->name);
+
+ /* Initialise group */
+ grp->name = np->name;
+
+ /*
+ * the binding format is rockchip,pins = <bank pin mux CONFIG>,
+ * do sanity check and calculate pins number
+ */
+ list = of_get_property(np, "rockchip,pins", &size);
+ /* we do not check return since it's safe node passed down */
+ size /= sizeof(*list);
+ if (!size || size % 4) {
+ dev_err(info->dev, "wrong pins number or pins and configs should be by 4\n");
+ return -EINVAL;
+ }
+
+ grp->npins = size / 4;
+
+ grp->configs = devm_kzalloc(info->dev, grp->npins *
+ sizeof(unsigned long),
+ GFP_KERNEL);
+ grp->pins = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int),
+ GFP_KERNEL);
+ grp->func = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int),
+ GFP_KERNEL);
+ if (!grp->configs || !grp->pins)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < size; i += 4, j++) {
+ unsigned long pinconf;
+
+ num = be32_to_cpu(*list++);
+ bank = bank_num_to_bank(info, num);
+ if (IS_ERR(bank))
+ return PTR_ERR(bank);
+
+ pin = be32_to_cpu(*list++);
+ grp->pins[j] = bank->pin_base + pin;
+ grp->func[j] = be32_to_cpu(*list++);
+
+ pinconf = be32_to_cpu(*list++);
+ switch(pinconf) {
+ case RK_PINCTRL_NONE:
+ bias = PIN_CONFIG_BIAS_DISABLE;
+ break;
+ case RK_PINCTRL_PULL_PIN_DEFAULT:
+ bias = PIN_CONFIG_BIAS_PULL_PIN_DEFAULT;
+ break;
+ case RK_PINCTRL_PULL_UP:
+ bias = PIN_CONFIG_BIAS_PULL_UP;
+ break;
+ case RK_PINCTRL_PULL_DOWN:
+ bias = PIN_CONFIG_BIAS_PULL_DOWN;
+ break;
+ }
+
+ grp->configs[j] = pinconf_to_config_packed(bias, 0);
+ }
+
+ return 0;
+}
+
+static int rockchip_pinctrl_parse_functions(struct device_node *np,
+ struct rockchip_pinctrl *info,
+ u32 index)
+{
+ struct device_node *child;
+ struct rockchip_pmx_func *func;
+ struct rockchip_pin_group *grp;
+ int ret;
+ static u32 grp_index;
+ u32 i = 0;
+
+ dev_dbg(info->dev, "parse function(%d): %s\n", index, np->name);
+
+ func = &info->functions[index];
+
+ /* Initialise function */
+ func->name = np->name;
+ func->ngroups = of_get_child_count(np);
+ if (func->ngroups <= 0) {
+ dev_err(info->dev, "no groups defined for %s\n", np->name);
+ return -EINVAL;
+ }
+ func->groups = devm_kzalloc(info->dev,
+ func->ngroups * sizeof(char *), GFP_KERNEL);
+ if (!func->groups)
+ return -ENOMEM;
+
+ for_each_child_of_node(np, child) {
+ func->groups[i] = child->name;
+ grp = &info->groups[grp_index++];
+ ret = rockchip_pinctrl_parse_groups(child, grp, info, i++);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rockchip_pinctrl_parse_dt(struct platform_device *pdev,
+ struct rockchip_pinctrl *info)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct device_node *child;
+ int ret;
+ int i;
+
+ rockchip_pinctrl_child_count(info, np);
+
+ dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions);
+ dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups);
+
+ info->functions = devm_kzalloc(dev, info->nfunctions *
+ sizeof(struct rockchip_pmx_func),
+ GFP_KERNEL);
+ if (!info->functions) {
+ dev_err(dev, "failed to allocate memory for function list\n");
+ return -EINVAL;
+ }
+
+ info->groups = devm_kzalloc(dev, info->ngroups *
+ sizeof(struct rockchip_pin_group),
+ GFP_KERNEL);
+ if (!info->groups) {
+ dev_err(dev, "failed allocate memory for ping group list\n");
+ return -EINVAL;
+ }
+
+ i = 0;
+
+ for_each_child_of_node(np, child) {
+ if (of_device_is_compatible(child, gpio_compat))
+ continue;
+ ret = rockchip_pinctrl_parse_functions(child, info, i++);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to parse function\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int rockchip_pinctrl_register(struct platform_device *pdev,
+ struct rockchip_pinctrl *info)
+{
+ struct pinctrl_desc *ctrldesc = &info->pctl;
+ struct pinctrl_pin_desc *pindesc, *pdesc;
+ struct rockchip_pin_bank *pin_bank;
+ int pin, bank, ret;
+ int k;
+
+ ctrldesc->name = "rockchip-pinctrl";
+ ctrldesc->owner = THIS_MODULE;
+ ctrldesc->pctlops = &rockchip_pctrl_ops;
+ ctrldesc->pmxops = &rockchip_pmx_ops;
+ ctrldesc->confops = &rockchip_pinconf_ops;
+
+ pindesc = devm_kzalloc(&pdev->dev, sizeof(*pindesc) *
+ info->ctrl->nr_pins, GFP_KERNEL);
+ if (!pindesc) {
+ dev_err(&pdev->dev, "mem alloc for pin descriptors failed\n");
+ return -ENOMEM;
+ }
+ ctrldesc->pins = pindesc;
+ ctrldesc->npins = info->ctrl->nr_pins;
+
+ pdesc = pindesc;
+ for (bank = 0 , k = 0; bank < info->ctrl->nr_banks; bank++) {
+ pin_bank = &info->ctrl->pin_banks[bank];
+ for (pin = 0; pin < pin_bank->nr_pins; pin++, k++) {
+ pdesc->number = k;
+ pdesc->name = kasprintf(GFP_KERNEL, "%s-%d",
+ pin_bank->name, pin);
+ pdesc++;
+ }
+ }
+
+ info->pctl_dev = pinctrl_register(ctrldesc, &pdev->dev, info);
+ if (!info->pctl_dev) {
+ dev_err(&pdev->dev, "could not register pinctrl driver\n");
+ return -EINVAL;
+ }
+
+ for (bank = 0; bank < info->ctrl->nr_banks; ++bank) {
+ pin_bank = &info->ctrl->pin_banks[bank];
+ pin_bank->grange.name = pin_bank->name;
+ pin_bank->grange.id = bank;
+ pin_bank->grange.pin_base = pin_bank->pin_base;
+ pin_bank->grange.base = pin_bank->gpio_chip.base;
+ pin_bank->grange.npins = pin_bank->gpio_chip.ngpio;
+ pin_bank->grange.gc = &pin_bank->gpio_chip;
+ pinctrl_add_gpio_range(info->pctl_dev, &pin_bank->grange);
+ }
+
+ ret = rockchip_pinctrl_parse_dt(pdev, info);
+ if (ret) {
+ pinctrl_unregister(info->pctl_dev);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * GPIO handling
+ */
+
+static void rockchip_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
+{
+ struct rockchip_pin_bank *bank = gc_to_pin_bank(gc);
+ void __iomem *reg = bank->reg_base + GPIO_SWPORT_DR;
+ unsigned long flags;
+ u32 data;
+
+ spin_lock_irqsave(&bank->slock, flags);
+
+ data = readl(reg);
+ data &= ~BIT(offset);
+ if (value)
+ data |= BIT(offset);
+ writel(data, reg);
+
+ spin_unlock_irqrestore(&bank->slock, flags);
+}
+
+/*
+ * Returns the level of the pin for input direction and setting of the DR
+ * register for output gpios.
+ */
+static int rockchip_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct rockchip_pin_bank *bank = gc_to_pin_bank(gc);
+ u32 data;
+
+ data = readl(bank->reg_base + GPIO_EXT_PORT);
+ data >>= offset;
+ data &= 1;
+ return data;
+}
+
+/*
+ * gpiolib gpio_direction_input callback function. The setting of the pin
+ * mux function as 'gpio input' will be handled by the pinctrl susbsystem
+ * interface.
+ */
+static int rockchip_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+ return pinctrl_gpio_direction_input(gc->base + offset);
+}
+
+/*
+ * gpiolib gpio_direction_output callback function. The setting of the pin
+ * mux function as 'gpio output' will be handled by the pinctrl susbsystem
+ * interface.
+ */
+static int rockchip_gpio_direction_output(struct gpio_chip *gc,
+ unsigned offset, int value)
+{
+ rockchip_gpio_set(gc, offset, value);
+ return pinctrl_gpio_direction_output(gc->base + offset);
+}
+
+/*
+ * gpiolib gpio_to_irq callback function. Creates a mapping between a GPIO pin
+ * and a virtual IRQ, if not already present.
+ */
+static int rockchip_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+ struct rockchip_pin_bank *bank = gc_to_pin_bank(gc);
+ unsigned int virq;
+
+ if (!bank->domain)
+ return -ENXIO;
+
+ virq = irq_create_mapping(bank->domain, offset);
+
+ return (virq) ? : -ENXIO;
+}
+
+static const struct gpio_chip rockchip_gpiolib_chip = {
+ .set = rockchip_gpio_set,
+ .get = rockchip_gpio_get,
+ .direction_input = rockchip_gpio_direction_input,
+ .direction_output = rockchip_gpio_direction_output,
+ .to_irq = rockchip_gpio_to_irq,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * Interrupt handling
+ */
+
+static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_get_chip(irq);
+ struct rockchip_pin_bank *bank = irq_get_handler_data(irq);
+ u32 pend;
+
+ dev_dbg(bank->drvdata->dev, "got irq for bank %s\n", bank->name);
+
+ chained_irq_enter(chip, desc);
+
+ pend = readl_relaxed(bank->reg_base + GPIO_INT_STATUS);
+
+ while (pend) {
+ unsigned int virq;
+
+ irq = __ffs(pend);
+ pend &= ~BIT(irq);
+ virq = irq_linear_revmap(bank->domain, irq);
+
+ if (!virq) {
+ dev_err(bank->drvdata->dev, "unmapped irq %d\n", irq);
+ continue;
+ }
+
+ dev_dbg(bank->drvdata->dev, "handling irq %d\n", irq);
+
+ generic_handle_irq(virq);
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct rockchip_pin_bank *bank = gc->private;
+ u32 mask = BIT(d->hwirq);
+ u32 polarity;
+ u32 level;
+ u32 data;
+
+ if (type & IRQ_TYPE_EDGE_BOTH)
+ __irq_set_handler_locked(d->irq, handle_edge_irq);
+ else
+ __irq_set_handler_locked(d->irq, handle_level_irq);
+
+ irq_gc_lock(gc);
+
+ level = readl_relaxed(gc->reg_base + GPIO_INTTYPE_LEVEL);
+ polarity = readl_relaxed(gc->reg_base + GPIO_INT_POLARITY);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ level |= mask;
+ polarity |= mask;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ level |= mask;
+ polarity &= ~mask;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ level &= ~mask;
+ polarity |= mask;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ level &= ~mask;
+ polarity &= ~mask;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ writel_relaxed(level, gc->reg_base + GPIO_INTTYPE_LEVEL);
+ writel_relaxed(polarity, gc->reg_base + GPIO_INT_POLARITY);
+
+ irq_gc_unlock(gc);
+
+ /* make sure the pin is configured as gpio input */
+ rockchip_set_mux(bank, d->hwirq, RK_FUNC_GPIO);
+ data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
+ data &= ~mask;
+ writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR);
+
+ return 0;
+}
+
+static int rockchip_interrupts_register(struct platform_device *pdev,
+ struct rockchip_pinctrl *info)
+{
+ struct rockchip_pin_ctrl *ctrl = info->ctrl;
+ struct rockchip_pin_bank *bank = ctrl->pin_banks;
+ unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
+ struct irq_chip_generic *gc;
+ int ret;
+ int i;
+
+ for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
+ if (!bank->valid) {
+ dev_warn(&pdev->dev, "bank %s is not valid\n",
+ bank->name);
+ continue;
+ }
+
+ bank->domain = irq_domain_add_linear(bank->of_node, 32,
+ &irq_generic_chip_ops, NULL);
+ if (!bank->domain) {
+ dev_warn(&pdev->dev, "could not initialize irq domain for bank %s\n",
+ bank->name);
+ continue;
+ }
+
+ ret = irq_alloc_domain_generic_chips(bank->domain, 32, 1,
+ "rockchip_gpio_irq", handle_level_irq,
+ clr, 0, IRQ_GC_INIT_MASK_CACHE);
+ if (ret) {
+ dev_err(&pdev->dev, "could not alloc generic chips for bank %s\n",
+ bank->name);
+ irq_domain_remove(bank->domain);
+ continue;
+ }
+
+ gc = irq_get_domain_generic_chip(bank->domain, 0);
+ gc->reg_base = bank->reg_base;
+ gc->private = bank;
+ gc->chip_types[0].regs.mask = GPIO_INTEN;
+ gc->chip_types[0].regs.ack = GPIO_PORTS_EOI;
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
+ gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake;
+ gc->chip_types[0].chip.irq_set_type = rockchip_irq_set_type;
+
+ irq_set_handler_data(bank->irq, bank);
+ irq_set_chained_handler(bank->irq, rockchip_irq_demux);
+ }
+
+ return 0;
+}
+
+static int rockchip_gpiolib_register(struct platform_device *pdev,
+ struct rockchip_pinctrl *info)
+{
+ struct rockchip_pin_ctrl *ctrl = info->ctrl;
+ struct rockchip_pin_bank *bank = ctrl->pin_banks;
+ struct gpio_chip *gc;
+ int ret;
+ int i;
+
+ for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
+ if (!bank->valid) {
+ dev_warn(&pdev->dev, "bank %s is not valid\n",
+ bank->name);
+ continue;
+ }
+
+ bank->gpio_chip = rockchip_gpiolib_chip;
+
+ gc = &bank->gpio_chip;
+ gc->base = bank->pin_base;
+ gc->ngpio = bank->nr_pins;
+ gc->dev = &pdev->dev;
+ gc->of_node = bank->of_node;
+ gc->label = bank->name;
+
+ ret = gpiochip_add(gc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register gpio_chip %s, error code: %d\n",
+ gc->label, ret);
+ goto fail;
+ }
+ }
+
+ rockchip_interrupts_register(pdev, info);
+
+ return 0;
+
+fail:
+ for (--i, --bank; i >= 0; --i, --bank) {
+ if (!bank->valid)
+ continue;
+
+ if (gpiochip_remove(&bank->gpio_chip))
+ dev_err(&pdev->dev, "gpio chip %s remove failed\n",
+ bank->gpio_chip.label);
+ }
+ return ret;
+}
+
+static int rockchip_gpiolib_unregister(struct platform_device *pdev,
+ struct rockchip_pinctrl *info)
+{
+ struct rockchip_pin_ctrl *ctrl = info->ctrl;
+ struct rockchip_pin_bank *bank = ctrl->pin_banks;
+ int ret = 0;
+ int i;
+
+ for (i = 0; !ret && i < ctrl->nr_banks; ++i, ++bank) {
+ if (!bank->valid)
+ continue;
+
+ ret = gpiochip_remove(&bank->gpio_chip);
+ }
+
+ if (ret)
+ dev_err(&pdev->dev, "gpio chip remove failed\n");
+
+ return ret;
+}
+
+static int rockchip_get_bank_data(struct rockchip_pin_bank *bank,
+ struct device *dev)
+{
+ struct resource res;
+
+ if (of_address_to_resource(bank->of_node, 0, &res)) {
+ dev_err(dev, "cannot find IO resource for bank\n");
+ return -ENOENT;
+ }
+
+ bank->reg_base = devm_ioremap_resource(dev, &res);
+ if (IS_ERR(bank->reg_base))
+ return PTR_ERR(bank->reg_base);
+
+ bank->irq = irq_of_parse_and_map(bank->of_node, 0);
+
+ bank->clk = of_clk_get(bank->of_node, 0);
+ if (IS_ERR(bank->clk))
+ return PTR_ERR(bank->clk);
+
+ return clk_prepare_enable(bank->clk);
+}
+
+static const struct of_device_id rockchip_pinctrl_dt_match[];
+
+/* retrieve the soc specific data */
+static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
+ struct rockchip_pinctrl *d,
+ struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct device_node *node = pdev->dev.of_node;
+ struct device_node *np;
+ struct rockchip_pin_ctrl *ctrl;
+ struct rockchip_pin_bank *bank;
+ int i;
+
+ match = of_match_node(rockchip_pinctrl_dt_match, node);
+ ctrl = (struct rockchip_pin_ctrl *)match->data;
+
+ for_each_child_of_node(node, np) {
+ if (!of_find_property(np, "gpio-controller", NULL))
+ continue;
+
+ bank = ctrl->pin_banks;
+ for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
+ if (!strcmp(bank->name, np->name)) {
+ bank->of_node = np;
+
+ if (!rockchip_get_bank_data(bank, &pdev->dev))
+ bank->valid = true;
+
+ break;
+ }
+ }
+ }
+
+ bank = ctrl->pin_banks;
+ for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
+ spin_lock_init(&bank->slock);
+ bank->drvdata = d;
+ bank->pin_base = ctrl->nr_pins;
+ ctrl->nr_pins += bank->nr_pins;
+ }
+
+ return ctrl;
+}
+
+static int rockchip_pinctrl_probe(struct platform_device *pdev)
+{
+ struct rockchip_pinctrl *info;
+ struct device *dev = &pdev->dev;
+ struct rockchip_pin_ctrl *ctrl;
+ struct resource *res;
+ int ret;
+
+ if (!dev->of_node) {
+ dev_err(dev, "device tree node not found\n");
+ return -ENODEV;
+ }
+
+ info = devm_kzalloc(dev, sizeof(struct rockchip_pinctrl), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ ctrl = rockchip_pinctrl_get_soc_data(info, pdev);
+ if (!ctrl) {
+ dev_err(dev, "driver data not available\n");
+ return -EINVAL;
+ }
+ info->ctrl = ctrl;
+ info->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "cannot find IO resource\n");
+ return -ENOENT;
+ }
+
+ info->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(info->reg_base))
+ return PTR_ERR(info->reg_base);
+
+ ret = rockchip_gpiolib_register(pdev, info);
+ if (ret)
+ return ret;
+
+ ret = rockchip_pinctrl_register(pdev, info);
+ if (ret) {
+ rockchip_gpiolib_unregister(pdev, info);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, info);
+
+ return 0;
+}
+
+static struct rockchip_pin_bank rk2928_pin_banks[] = {
+ PIN_BANK(0, 32, "gpio0"),
+ PIN_BANK(1, 32, "gpio1"),
+ PIN_BANK(2, 32, "gpio2"),
+ PIN_BANK(3, 32, "gpio3"),
+};
+
+static struct rockchip_pin_ctrl rk2928_pin_ctrl = {
+ .pin_banks = rk2928_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk2928_pin_banks),
+ .label = "RK2928-GPIO",
+ .mux_offset = 0xa8,
+ .pull_offset = 0x118,
+ .pull_auto = 1,
+ .pull_bank_stride = 8,
+};
+
+static struct rockchip_pin_bank rk3066a_pin_banks[] = {
+ PIN_BANK(0, 32, "gpio0"),
+ PIN_BANK(1, 32, "gpio1"),
+ PIN_BANK(2, 32, "gpio2"),
+ PIN_BANK(3, 32, "gpio3"),
+ PIN_BANK(4, 32, "gpio4"),
+ PIN_BANK(6, 16, "gpio6"),
+};
+
+static struct rockchip_pin_ctrl rk3066a_pin_ctrl = {
+ .pin_banks = rk3066a_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3066a_pin_banks),
+ .label = "RK3066a-GPIO",
+ .mux_offset = 0xa8,
+ .pull_offset = 0x118,
+ .pull_auto = 1,
+ .pull_bank_stride = 8,
+};
+
+static struct rockchip_pin_bank rk3066b_pin_banks[] = {
+ PIN_BANK(0, 32, "gpio0"),
+ PIN_BANK(1, 32, "gpio1"),
+ PIN_BANK(2, 32, "gpio2"),
+ PIN_BANK(3, 32, "gpio3"),
+};
+
+static struct rockchip_pin_ctrl rk3066b_pin_ctrl = {
+ .pin_banks = rk3066b_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3066b_pin_banks),
+ .label = "RK3066b-GPIO",
+ .mux_offset = 0x60,
+ .pull_offset = -EINVAL,
+};
+
+static struct rockchip_pin_bank rk3188_pin_banks[] = {
+ PIN_BANK(0, 32, "gpio0"),
+ PIN_BANK(1, 32, "gpio1"),
+ PIN_BANK(2, 32, "gpio2"),
+ PIN_BANK(3, 32, "gpio3"),
+};
+
+static struct rockchip_pin_ctrl rk3188_pin_ctrl = {
+ .pin_banks = rk3188_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3188_pin_banks),
+ .label = "RK3188-GPIO",
+ .mux_offset = 0x68,
+ .pull_offset = 0x164,
+ .pull_bank_stride = 16,
+};
+
+static const struct of_device_id rockchip_pinctrl_dt_match[] = {
+ { .compatible = "rockchip,rk2928-pinctrl",
+ .data = (void *)&rk2928_pin_ctrl },
+ { .compatible = "rockchip,rk3066a-pinctrl",
+ .data = (void *)&rk3066a_pin_ctrl },
+ { .compatible = "rockchip,rk3066b-pinctrl",
+ .data = (void *)&rk3066b_pin_ctrl },
+ { .compatible = "rockchip,rk3188-pinctrl",
+ .data = (void *)&rk3188_pin_ctrl },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rockchip_pinctrl_dt_match);
+
+static struct platform_driver rockchip_pinctrl_driver = {
+ .probe = rockchip_pinctrl_probe,
+ .driver = {
+ .name = "rockchip-pinctrl",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(rockchip_pinctrl_dt_match),
+ },
+};
+
+static int __init rockchip_pinctrl_drv_register(void)
+{
+ return platform_driver_register(&rockchip_pinctrl_driver);
+}
+postcore_initcall(rockchip_pinctrl_drv_register);
+
+MODULE_AUTHOR("Heiko Stuebner <[email protected]>");
+MODULE_DESCRIPTION("Rockchip pinctrl driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/dt-bindings/pinctrl/rockchip.h b/include/dt-bindings/pinctrl/rockchip.h
new file mode 100644
index 0000000..a354298
--- /dev/null
+++ b/include/dt-bindings/pinctrl/rockchip.h
@@ -0,0 +1,37 @@
+/*
+ * Header providing constants for Rockchip pinctrl bindings.
+ *
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __DT_BINDINGS_ROCKCHIP_PINCTRL_H__
+#define __DT_BINDINGS_ROCKCHIP_PINCTRL_H__
+
+#define RK_GPIO0 0
+#define RK_GPIO1 1
+#define RK_GPIO2 2
+#define RK_GPIO3 3
+#define RK_GPIO4 4
+#define RK_GPIO6 6
+
+#define RK_FUNC_GPIO 0
+#define RK_FUNC_1 1
+#define RK_FUNC_2 2
+
+#define RK_PINCTRL_NONE 0
+#define RK_PINCTRL_PULL_PIN_DEFAULT (1 << 0)
+#define RK_PINCTRL_PULL_UP (1 << 1)
+#define RK_PINCTRL_PULL_DOWN (1 << 2)
+
+#endif
--
1.7.2.3

2013-06-06 19:39:47

by Heiko Stübner

[permalink] [raw]
Subject: [PATCH v2 4/8] mmc: dw_mmc-pltfm: add Rockchip variant

Cortex-A9 SoCs from Rockchip use a slightly modified variant of dw_mmc
controllers that seems to require the SDMMC_CMD_USE_HOLD_REG bit to
always be set.

There also seem to be no other modifications (additional register etc)
present, so to keep the footprint low, add this small variant to the
pltfm driver.

Signed-off-by: Heiko Stuebner <[email protected]>
---
drivers/mmc/host/dw_mmc-pltfm.c | 48 +++++++++++++++++++++++++++-----------
1 files changed, 34 insertions(+), 14 deletions(-)

diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index 0048da8..7d041b5 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -24,6 +24,16 @@

#include "dw_mmc.h"

+
+static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr)
+{
+ *cmdr |= SDMMC_CMD_USE_HOLD_REG;
+}
+
+static const struct dw_mci_drv_data rockchip_drv_data = {
+ .prepare_command = dw_mci_rockchip_prepare_command,
+};
+
int dw_mci_pltfm_register(struct platform_device *pdev,
const struct dw_mci_drv_data *drv_data)
{
@@ -63,20 +73,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
}
EXPORT_SYMBOL_GPL(dw_mci_pltfm_register);

-static int dw_mci_pltfm_probe(struct platform_device *pdev)
-{
- return dw_mci_pltfm_register(pdev, NULL);
-}
-
-int dw_mci_pltfm_remove(struct platform_device *pdev)
-{
- struct dw_mci *host = platform_get_drvdata(pdev);
-
- dw_mci_remove(host);
- return 0;
-}
-EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove);
-
#ifdef CONFIG_PM_SLEEP
/*
* TODO: we should probably disable the clock to the card in the suspend path.
@@ -114,10 +110,34 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops);

static const struct of_device_id dw_mci_pltfm_match[] = {
{ .compatible = "snps,dw-mshc", },
+ { .compatible = "rockchip,cortex-a9-dw-mshc",
+ .data = &rockchip_drv_data },
{},
};
MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);

+static int dw_mci_pltfm_probe(struct platform_device *pdev)
+{
+ const struct dw_mci_drv_data *drv_data = NULL;
+ const struct of_device_id *match;
+
+ if (pdev->dev.of_node) {
+ match = of_match_node(dw_mci_pltfm_match, pdev->dev.of_node);
+ drv_data = match->data;
+ }
+
+ return dw_mci_pltfm_register(pdev, drv_data);
+}
+
+int dw_mci_pltfm_remove(struct platform_device *pdev)
+{
+ struct dw_mci *host = platform_get_drvdata(pdev);
+
+ dw_mci_remove(host);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove);
+
static struct platform_driver dw_mci_pltfm_driver = {
.probe = dw_mci_pltfm_probe,
.remove = dw_mci_pltfm_remove,
--
1.7.2.3

2013-06-06 19:39:41

by Heiko Stübner

[permalink] [raw]
Subject: [PATCH v2 7/8] arm: add debug uarts for rockchip rk29xx and rk3xxx series

Uarts on all recent Rockchip SoCs are Synopsis DesignWare 8250 types.
Only their addresses vary very much.

This patch adds the necessary definitions to use any of the uart ports
for early debug purposes.

Signed-off-by: Heiko Stuebner <[email protected]>
---
arch/arm/Kconfig.debug | 34 +++++++++++++++++++++++++++++
arch/arm/include/debug/rockchip.S | 42 +++++++++++++++++++++++++++++++++++++
2 files changed, 76 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/include/debug/rockchip.S

diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 29f7623..2060cd5 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -360,6 +360,13 @@ choice
their output to the standard serial port on the RealView
PB1176 platform.

+ config DEBUG_ROCKCHIP_UART
+ bool "Kernel low-level debugging messages via Rockchip UART"
+ depends on ARCH_ROCKCHIP
+ help
+ Say Y here if you want kernel low-level debugging support
+ on Rockchip based platforms.
+
config DEBUG_S3C_UART0
depends on PLAT_SAMSUNG
select DEBUG_EXYNOS_UART if ARCH_EXYNOS
@@ -597,6 +604,32 @@ endchoice

choice
prompt "Low-level debug console UART"
+ depends on DEBUG_ROCKCHIP_UART
+
+ config DEBUG_RK29_UART0
+ bool "RK29 UART0"
+
+ config DEBUG_RK29_UART1
+ bool "RK29 UART1"
+
+ config DEBUG_RK29_UART2
+ bool "RK29 UART2"
+
+ config DEBUG_RK3X_UART0
+ bool "RK3X UART0"
+
+ config DEBUG_RK3X_UART1
+ bool "RK3X UART1"
+
+ config DEBUG_RK3X_UART2
+ bool "RK3X UART2"
+
+ config DEBUG_RK3X_UART3
+ bool "RK3X UART3"
+endchoice
+
+choice
+ prompt "Low-level debug console UART"
depends on DEBUG_LL && DEBUG_TEGRA_UART

config TEGRA_DEBUG_UART_AUTO_ODMDATA
@@ -648,6 +681,7 @@ config DEBUG_LL_INCLUDE
default "debug/picoxcell.S" if DEBUG_PICOXCELL_UART
default "debug/pxa.S" if DEBUG_PXA_UART1 || DEBUG_MMP_UART2 || \
DEBUG_MMP_UART3
+ default "debug/rockchip.S" if DEBUG_ROCKCHIP_UART
default "debug/sirf.S" if DEBUG_SIRFPRIMA2_UART1 || DEBUG_SIRFMARCO_UART1
default "debug/socfpga.S" if DEBUG_SOCFPGA_UART
default "debug/sunxi.S" if DEBUG_SUNXI_UART0 || DEBUG_SUNXI_UART1
diff --git a/arch/arm/include/debug/rockchip.S b/arch/arm/include/debug/rockchip.S
new file mode 100644
index 0000000..cfd883e
--- /dev/null
+++ b/arch/arm/include/debug/rockchip.S
@@ -0,0 +1,42 @@
+/*
+ * Early serial output macro for Rockchip SoCs
+ *
+ * Copyright (C) 2012 Maxime Ripard
+ *
+ * Maxime Ripard <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#if defined(CONFIG_DEBUG_RK29_UART0)
+#define ROCKCHIP_UART_DEBUG_PHYS_BASE 0x20060000
+#define ROCKCHIP_UART_DEBUG_VIRT_BASE 0xfed60000
+#elif defined(CONFIG_DEBUG_RK29_UART1)
+#define ROCKCHIP_UART_DEBUG_PHYS_BASE 0x20064000
+#define ROCKCHIP_UART_DEBUG_VIRT_BASE 0xfed64000
+#elif defined(CONFIG_DEBUG_RK29_UART2)
+#define ROCKCHIP_UART_DEBUG_PHYS_BASE 0x20068000
+#define ROCKCHIP_UART_DEBUG_VIRT_BASE 0xfed68000
+#elif defined(CONFIG_DEBUG_RK3X_UART0)
+#define ROCKCHIP_UART_DEBUG_PHYS_BASE 0x10124000
+#define ROCKCHIP_UART_DEBUG_VIRT_BASE 0xfeb24000
+#elif defined(CONFIG_DEBUG_RK3X_UART1)
+#define ROCKCHIP_UART_DEBUG_PHYS_BASE 0x10126000
+#define ROCKCHIP_UART_DEBUG_VIRT_BASE 0xfeb26000
+#elif defined(CONFIG_DEBUG_RK3X_UART2)
+#define ROCKCHIP_UART_DEBUG_PHYS_BASE 0x20064000
+#define ROCKCHIP_UART_DEBUG_VIRT_BASE 0xfed64000
+#elif defined(CONFIG_DEBUG_RK3X_UART3)
+#define ROCKCHIP_UART_DEBUG_PHYS_BASE 0x20068000
+#define ROCKCHIP_UART_DEBUG_VIRT_BASE 0xfed68000
+#endif
+
+ .macro addruart, rp, rv, tmp
+ ldr \rp, =ROCKCHIP_UART_DEBUG_PHYS_BASE
+ ldr \rv, =ROCKCHIP_UART_DEBUG_VIRT_BASE
+ .endm
+
+#define UART_SHIFT 2
+#include <asm/hardware/debug-8250.S>
--
1.7.2.3

2013-06-06 19:39:45

by Heiko Stübner

[permalink] [raw]
Subject: [PATCH v2 6/8] clk: add basic Rockchip rk3066a clock support

This adds basic support for clocks on Rockchip rk3066 SoCs.
The clock handling thru small dt nodes is heavily inspired by the
sunxi clk code.

The plls are currently read-only, as their setting needs more
investigation. This also results in slow cpu speeds, as the apll starts
at a default of 600mhz.

Signed-off-by: Heiko Stuebner <[email protected]>
---
arch/arm/boot/dts/rk3066a-clocks.dtsi | 460 +++++++++++++++++++++++++++++++
drivers/clk/Makefile | 1 +
drivers/clk/rockchip/Makefile | 6 +
drivers/clk/rockchip/clk-rockchip-pll.c | 131 +++++++++
drivers/clk/rockchip/clk-rockchip-pll.h | 19 ++
drivers/clk/rockchip/clk-rockchip.c | 313 +++++++++++++++++++++
6 files changed, 930 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/boot/dts/rk3066a-clocks.dtsi
create mode 100644 drivers/clk/rockchip/Makefile
create mode 100644 drivers/clk/rockchip/clk-rockchip-pll.c
create mode 100644 drivers/clk/rockchip/clk-rockchip-pll.h
create mode 100644 drivers/clk/rockchip/clk-rockchip.c

diff --git a/arch/arm/boot/dts/rk3066a-clocks.dtsi b/arch/arm/boot/dts/rk3066a-clocks.dtsi
new file mode 100644
index 0000000..3877055
--- /dev/null
+++ b/arch/arm/boot/dts/rk3066a-clocks.dtsi
@@ -0,0 +1,460 @@
+/*
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/ {
+ clocks {
+ compatible = "rockchip,clocks";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ /*
+ * This is a dummy clock, to be used as placeholder on
+ * other mux clocks when a specific parent clock is not
+ * yet implemented. It should be dropped when the driver
+ * is complete.
+ */
+ dummy: dummy {
+ compatible = "fixed-clock";
+ clock-frequency = <0>;
+ #clock-cells = <0>;
+ };
+
+ xin24m: xin24m {
+ compatible = "fixed-clock";
+ clock-frequency = <24000000>;
+ #clock-cells = <0>;
+ };
+
+ apll: apll@20000000 {
+ compatible = "rockchip,rk3066a-apll";
+ reg = <0x20000000 0x10>,
+ <0x20000040 0x04>;
+ clocks = <&xin24m>;
+ #clock-cells = <0>;
+ };
+
+ dpll: dpll@20000000 {
+ compatible = "rockchip,rk3066a-dpll";
+ reg = <0x20000010 0x10>,
+ <0x20000040 0x04>;
+ clocks = <&xin24m>;
+ #clock-cells = <0>;
+ };
+
+ cpll: cpll@20000000 {
+ compatible = "rockchip,rk3066a-cpll";
+ reg = <0x20000020 0x10>,
+ <0x20000040 0x04>;
+ clocks = <&xin24m>;
+ #clock-cells = <0>;
+ };
+
+ gpll: gpll@20000000 {
+ compatible = "rockchip,rk3066a-gpll";
+ reg = <0x20000030 0x10>,
+ <0x20000040 0x04>;
+ clocks = <&xin24m>;
+ #clock-cells = <0>;
+ };
+
+ mux_aclk_periph: mux-aclk-periph@2000006c {
+ compatible = "rockchip,rk2928-gpll-cpll-bit15-mux";
+ reg = <0x2000006c 0x04>;
+ clocks = <&gpll>, <&cpll>;
+ #clock-cells = <0>;
+ };
+
+ mux_uart_pll: mux-uart_pll@20000074 {
+ compatible = "rockchip,rk2928-gpll-cpll-bit15-mux";
+ reg = <0x20000074 0x04>;
+ clocks = <&gpll>, <&cpll>;
+ #clock-cells = <0>;
+ };
+
+ div_uart0: div-uart0@20000078 {
+ compatible = "rockchip,rk2928-uart-divider";
+ reg = <0x20000078 0x04>;
+ clocks = <&mux_uart_pll>;
+ #clock-cells = <0>;
+ };
+
+ mux_uart0: mux-uart0@20000078 {
+ compatible = "rockchip,rk2928-uart-mux";
+ reg = <0x20000078 0x04>;
+ clocks = <&clk_gates1 8>, <&dummy>, <&xin24m>; /* dummy is uart0_frac_div */
+ #clock-cells = <0>;
+ };
+
+ div_uart1: div-uart1@2000007c {
+ compatible = "rockchip,rk2928-uart-divider";
+ reg = <0x2000007c 0x04>;
+ clocks = <&mux_uart_pll>;
+ #clock-cells = <0>;
+ };
+
+ mux_uart1: mux-uart1@2000007c {
+ compatible = "rockchip,rk2928-uart-mux";
+ reg = <0x2000007c 0x04>;
+ clocks = <&clk_gates1 10>, <&dummy>, <&xin24m>;
+ #clock-cells = <0>;
+ };
+
+ div_uart2: div-uart2@20000080 {
+ compatible = "rockchip,rk2928-uart-divider";
+ reg = <0x20000080 0x04>;
+ clocks = <&mux_uart_pll>;
+ #clock-cells = <0>;
+ };
+
+ mux_uart2: mux-uart2@20000080 {
+ compatible = "rockchip,rk2928-uart-mux";
+ reg = <0x20000080 0x04>;
+ clocks = <&clk_gates1 12>, <&dummy>, <&xin24m>;
+ #clock-cells = <0>;
+ };
+
+ div_uart3: div-uart3@20000084 {
+ compatible = "rockchip,rk2928-uart-divider";
+ reg = <0x20000084 0x04>;
+ clocks = <&mux_uart_pll>;
+ #clock-cells = <0>;
+ };
+
+ mux_uart3: mux-uart3@20000084 {
+ compatible = "rockchip,rk2928-uart-mux";
+ reg = <0x20000084 0x04>;
+ clocks = <&clk_gates1 14>, <&dummy>, <&xin24m>;
+ #clock-cells = <0>;
+ };
+
+ mux_cpu: mux-cpu@20000044 {
+ compatible = "rockchip,rk3066-cpu-mux";
+ reg = <0x20000044 0x4>;
+ clocks = <&apll>, <&dummy> /* cpu_gpll_path */;
+ #clock-cells = <0>;
+ };
+
+ div_cpu: div-cpu@20000044 {
+ compatible = "rockchip,rk3066a-cpu-divider";
+ reg = <0x20000044 0x4>;
+ clocks = <&mux_cpu>;
+ #clock-cells = <0>;
+ };
+
+ div_aclk_cpu: div-aclk-cpu@20000048 {
+ compatible = "rockchip,rk3066a-aclk-cpu-divider";
+ reg = <0x20000048 0x4>;
+ clocks = <&div_cpu>;
+ #clock-cells = <0>;
+ };
+
+ div_aclk_periph: div-aclk-periph@2000006c {
+ compatible = "rockchip,rk2928-aclk-periph-divider";
+ reg = <0x2000006c 0x4>;
+ clocks = <&mux_aclk_periph>;
+ #clock-cells = <0>;
+ };
+
+ div_hclk_periph: div-hclk-periph@2000006c {
+ compatible = "rockchip,rk2928-hclk-divider";
+ reg = <0x2000006c 0x4>;
+ clocks = <&clk_gates2 1>;
+ #clock-cells = <0>;
+ };
+
+ div_pclk_periph: div-pclk-periph@2000006c {
+ compatible = "rockchip,rk2928-pclk-divider";
+ reg = <0x2000006c 0x4>;
+ clocks = <&clk_gates2 1>;
+ #clock-cells = <0>;
+ };
+
+ div_hclk_cpu: div-hclk-cpu@20000048 {
+ compatible = "rockchip,rk2928-hclk-divider";
+ reg = <0x20000048 0x4>;
+ clocks = <&clk_gates0 3>;
+ #clock-cells = <0>;
+ };
+
+ div_pclk_cpu: div-pclk-cpu@20000048 {
+ compatible = "rockchip,rk2928-pclk-divider";
+ reg = <0x20000048 0x4>;
+ clocks = <&clk_gates0 3>;
+ #clock-cells = <0>;
+ };
+
+ div_mmc0: div-mmc0@20000070 {
+ compatible = "rockchip,rk2928-mmc-divider";
+ reg = <0x20000070 0x4>;
+ clocks = <&clk_gates2 2>;
+ #clock-cells = <0>;
+ };
+
+ div_mmc1: div-mmc1@20000074 {
+ compatible = "rockchip,rk2928-mmc-divider";
+ reg = <0x20000074 0x4>;
+ clocks = <&clk_gates2 2>;
+ #clock-cells = <0>;
+ };
+
+ clk_gates0: gate-clk@200000d0 {
+ compatible = "rockchip,rk2928-gate-clk";
+ reg = <0x200000d0 0x4>;
+ clocks = <&dummy>, <&dummy>,
+ <&dummy>, <&div_aclk_cpu>,
+ <&div_hclk_cpu>, <&div_pclk_cpu>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>;
+
+ clock-output-names =
+ "gate_core_periph", "gate_cpu_gpll",
+ "gate_ddrphy", "gate_aclk_cpu",
+ "gate_hclk_cpu", "gate_pclk_cpu",
+ "gate_atclk_cpu", "gate_i2s0",
+ "gate_i2s0_frac", "gate_i2s1",
+ "gate_i2s1_frac", "gate_i2s2",
+ "gate_i2s2_frac", "gate_spdif",
+ "gate_spdif_frac", "gate_testclk";
+
+ #clock-cells = <1>;
+ };
+
+ clk_gates1: gate-clk@200000d4 {
+ compatible = "rockchip,rk2928-gate-clk";
+ reg = <0x200000d4 0x4>;
+ clocks = <&xin24m>, <&xin24m>,
+ <&xin24m>, <&dummy>,
+ <&dummy>, <&xin24m>,
+ <&xin24m>, <&dummy>,
+ <&div_uart0>, <&dummy>,
+ <&div_uart1>, <&dummy>,
+ <&div_uart2>, <&dummy>,
+ <&div_uart3>, <&dummy>;
+
+ clock-output-names =
+ "gate_timer0", "gate_timer1",
+ "gate_timer2", "gate_jtag",
+ "gate_aclk_lcdc1_src", "gate_otgphy0",
+ "gate_otgphy1", "gate_ddr_gpll",
+ "gate_uart0", "gate_frac_uart0",
+ "gate_uart1", "gate_frac_uart1",
+ "gate_uart2", "gate_frac_uart2",
+ "gate_uart3", "gate_frac_uart3";
+
+ #clock-cells = <1>;
+ };
+
+ clk_gates2: gate-clk@200000d8 {
+ compatible = "rockchip,rk2928-gate-clk";
+ reg = <0x200000d8 0x4>;
+ clocks = <&clk_gates2 1>, <&div_aclk_periph>,
+ <&div_hclk_periph>, <&div_pclk_periph>,
+ <&dummy>, <&dummy>,
+ <&clk_gates2 3>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&div_mmc0>,
+ <&dummy>, <&div_mmc1>,
+ <&dummy>, <&dummy>;
+
+ clock-output-names =
+ "gate_periph_src", "gate_aclk_periph",
+ "gate_hclk_periph", "gate_pclk_periph",
+ "gate_smc", "gate_mac",
+ "gate_hsadc", "gate_hsadc_frac",
+ "gate_saradc", "gate_spi0",
+ "gate_spi1", "gate_mmc0",
+ "gate_mac_lbtest", "gate_mmc1",
+ "gate_emmc", "gate_tsadc";
+
+ #clock-cells = <1>;
+ };
+
+ clk_gates3: gate-clk@200000dc {
+ compatible = "rockchip,rk2928-gate-clk";
+ reg = <0x200000dc 0x4>;
+ clocks = <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&dummy>;
+
+ clock-output-names =
+ "gate_aclk_lcdc0_src", "gate_dclk_lcdc0",
+ "gate_dclk_lcdc1", "gate_pclkin_cif0",
+ "gate_pclkin_cif1", "reserved",
+ "reserved", "gate_cif0_out",
+ "gate_cif1_out", "gate_aclk_vepu",
+ "gate_hclk_vepu", "gate_aclk_vdpu",
+ "gate_hclk_vdpu", "gate_gpu_src",
+ "reserved", "gate_xin27m";
+
+ #clock-cells = <1>;
+ };
+
+ clk_gates4: gate-clk@200000e0 {
+ compatible = "rockchip,rk2928-gate-clk";
+ reg = <0x200000e0 0x4>;
+ clocks = <&clk_gates2 2>, <&clk_gates2 3>,
+ <&clk_gates2 1>, <&clk_gates2 1>,
+ <&clk_gates2 1>, <&clk_gates2 2>,
+ <&clk_gates2 2>, <&clk_gates2 2>,
+ <&clk_gates0 4>, <&clk_gates0 4>,
+ <&clk_gates0 3>, <&clk_gates0 3>,
+ <&clk_gates0 3>, <&clk_gates2 3>,
+ <&clk_gates0 4>;
+
+ clock-output-names =
+ "gate_hclk_peri_axi_matrix", "gate_pclk_peri_axi_matrix",
+ "gate_aclk_cpu_peri", "gate_aclk_peri_axi_matrix",
+ "gate_aclk_pei_niu", "gate_hclk_usb_peri",
+ "gate_hclk_peri_ahb_arbi", "gate_hclk_emem_peri",
+ "gate_hclk_cpubus", "gate_hclk_ahb2apb",
+ "gate_aclk_strc_sys", "gate_aclk_l2mem_con",
+ "gate_aclk_intmem", "gate_pclk_tsadc",
+ "gate_hclk_hdmi";
+
+ #clock-cells = <1>;
+ };
+
+ clk_gates5: gate-clk@200000e4 {
+ compatible = "rockchip,rk2928-gate-clk";
+ reg = <0x200000e4 0x4>;
+ clocks = <&clk_gates0 3>, <&clk_gates2 1>,
+ <&clk_gates0 5>, <&clk_gates0 5>,
+ <&clk_gates0 5>, <&clk_gates0 5>,
+ <&clk_gates0 4>, <&clk_gates0 5>,
+ <&clk_gates2 1>, <&clk_gates2 2>,
+ <&clk_gates2 2>, <&clk_gates2 2>,
+ <&clk_gates2 2>, <&clk_gates4 5>,
+ <&clk_gates4 5>, <&dummy>;
+
+ clock-output-names =
+ "gate_aclk_dmac1", "gate_aclk_dmac2",
+ "gate_pclk_efuse", "gate_pclk_tzpc",
+ "gate_pclk_grf", "gate_pclk_pmu",
+ "gate_hclk_rom", "gate_pclk_ddrupctl",
+ "gate_aclk_smc", "gate_hclk_nandc",
+ "gate_hclk_mmc0", "gate_hclk_mmc1",
+ "gate_hclk_emmc", "gate_hclk_otg0",
+ "gate_hclk_otg1", "gate_aclk_gpu";
+
+ #clock-cells = <1>;
+ };
+
+ clk_gates6: gate-clk@200000e8 {
+ compatible = "rockchip,rk2928-gate-clk";
+ reg = <0x200000e8 0x4>;
+ clocks = <&clk_gates3 0>, <&clk_gates0 4>,
+ <&clk_gates0 4>, <&clk_gates1 4>,
+ <&clk_gates0 4>, <&clk_gates3 0>,
+ <&clk_gates0 4>, <&clk_gates1 4>,
+ <&clk_gates3 0>, <&clk_gates0 4>,
+ <&clk_gates0 4>, <&clk_gates1 4>,
+ <&clk_gates0 4>, <&clk_gates3 0>,
+ <&dummy>, <&dummy>;
+
+ clock-output-names =
+ "gate_aclk_lcdc0", "gate_hclk_lcdc0",
+ "gate_hclk_lcdc1", "gate_aclk_lcdc1",
+ "gate_hclk_cif0", "gate_aclk_cif0",
+ "gate_hclk_cif1", "gate_aclk_cif1",
+ "gate_aclk_ipp", "gate_hclk_ipp",
+ "gate_hclk_rga", "gate_aclk_rga",
+ "gate_hclk_vio_bus", "gate_aclk_vio0",
+ "gate_aclk_vcodec", "gate_shclk_vio_h2h";
+
+ #clock-cells = <1>;
+ };
+
+ clk_gates7: gate-clk@200000ec {
+ compatible = "rockchip,rk2928-gate-clk";
+ reg = <0x200000ec 0x4>;
+ clocks = <&clk_gates2 2>, <&clk_gates0 4>,
+ <&clk_gates0 4>, <&clk_gates0 4>,
+ <&clk_gates0 4>, <&clk_gates2 2>,
+ <&clk_gates2 2>, <&clk_gates0 5>,
+ <&clk_gates0 5>, <&clk_gates0 5>,
+ <&clk_gates0 5>, <&clk_gates2 3>,
+ <&clk_gates2 3>, <&clk_gates2 3>,
+ <&clk_gates2 3>, <&clk_gates2 3>;
+
+ clock-output-names =
+ "gate_hclk_emac", "gate_hclk_spdif",
+ "gate_hclk_i2s0_2ch", "gate_hclk_i2s1_2ch",
+ "gate_hclk_i2s_8ch", "gate_hclk_hsadc",
+ "gate_hclk_pidf", "gate_pclk_timer0",
+ "gate_pclk_timer1", "gate_pclk_timer2",
+ "gate_pclk_pwm01", "gate_pclk_pwm23",
+ "gate_pclk_spi0", "gate_pclk_spi1",
+ "gate_pclk_saradc", "gate_pclk_wdt";
+
+ #clock-cells = <1>;
+ };
+
+ clk_gates8: gate-clk@200000f0 {
+ compatible = "rockchip,rk2928-gate-clk";
+ reg = <0x200000f0 0x4>;
+ clocks = <&clk_gates0 5>, <&clk_gates0 5>,
+ <&clk_gates2 3>, <&clk_gates2 3>,
+ <&clk_gates0 5>, <&clk_gates0 5>,
+ <&clk_gates2 3>, <&clk_gates2 3>,
+ <&clk_gates2 3>, <&clk_gates0 5>,
+ <&clk_gates0 5>, <&clk_gates0 5>,
+ <&clk_gates2 3>, <&clk_gates2 3>,
+ <&dummy>, <&clk_gates0 5>;
+
+ clock-output-names =
+ "gate_pclk_uart0", "gate_pclk_uart1",
+ "gate_pclk_uart2", "gate_pclk_uart3",
+ "gate_pclk_i2c0", "gate_pclk_i2c1",
+ "gate_pclk_i2c2", "gate_pclk_i2c3",
+ "gate_pclk_i2c4", "gate_pclk_gpio0",
+ "gate_pclk_gpio1", "gate_pclk_gpio2",
+ "gate_pclk_gpio3", "gate_pclk_gpio4",
+ "reserved", "gate_pclk_gpio6";
+
+ #clock-cells = <1>;
+ };
+
+ clk_gates9: gate-clk@200000f4 {
+ compatible = "rockchip,rk2928-gate-clk";
+ reg = <0x200000f4 0x4>;
+ clocks = <&dummy>, <&clk_gates0 5>,
+ <&dummy>, <&dummy>,
+ <&dummy>, <&clk_gates1 4>,
+ <&clk_gates0 5>, <&dummy>,
+ <&dummy>, <&dummy>,
+ <&dummy>;
+
+ clock-output-names =
+ "gate_clk_core_dbg", "gate_pclk_dbg",
+ "gate_clk_trace", "gate_atclk",
+ "gate_clk_l2c", "gate_aclk_vio1",
+ "gate_pclk_publ", "gate_aclk_intmem0",
+ "gate_aclk_intmem1", "gate_aclk_intmem2",
+ "gate_aclk_intmem3";
+
+ #clock-cells = <1>;
+ };
+ };
+
+};
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index f51b52b..2e2e957 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -25,6 +25,7 @@ ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_MMP) += mmp/
endif
obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
+obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-$(CONFIG_ARCH_U8500) += ux500/
obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
new file mode 100644
index 0000000..e0fbafb
--- /dev/null
+++ b/drivers/clk/rockchip/Makefile
@@ -0,0 +1,6 @@
+#
+# Rockchip Clock specific Makefile
+#
+
+obj-y += clk-rockchip.o
+obj-y += clk-rockchip-pll.o
diff --git a/drivers/clk/rockchip/clk-rockchip-pll.c b/drivers/clk/rockchip/clk-rockchip-pll.c
new file mode 100644
index 0000000..4456445
--- /dev/null
+++ b/drivers/clk/rockchip/clk-rockchip-pll.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/div64.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clk-private.h>
+
+#define RK3X_PLL_MODE_MASK 0x3
+#define RK3X_PLL_MODE_SLOW 0x0
+#define RK3X_PLL_MODE_NORM 0x1
+#define RK3X_PLL_MODE_DEEP 0x2
+
+#define RK3X_PLLCON0_OD_MASK 0xf
+#define RK3X_PLLCON0_OD_SHIFT 0
+#define RK3X_PLLCON0_NR_MASK 0x3f
+#define RK3X_PLLCON0_NR_SHIFT 8
+
+#define RK3X_PLLCON1_NF_MASK 0x1fff
+#define RK3X_PLLCON1_NF_SHIFT 0
+
+#define RK3X_PLLCON3_REST (1 << 5)
+#define RK3X_PLLCON3_BYPASS (1 << 0)
+
+struct rockchip_clk_pll {
+ struct clk_hw hw;
+ void __iomem *reg_base;
+ void __iomem *reg_mode;
+ unsigned int shift_mode;
+ spinlock_t *lock;
+};
+
+#define to_clk_pll(_hw) container_of(_hw, struct rockchip_clk_pll, hw)
+
+static unsigned long rk3x_generic_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rockchip_clk_pll *pll = to_clk_pll(hw);
+ u32 pll_con0 = readl_relaxed(pll->reg_base);
+ u32 pll_con1 = readl_relaxed(pll->reg_base + 0x4);
+ u32 pll_con3 = readl_relaxed(pll->reg_base + 0xc);
+ u32 mode_con = readl_relaxed(pll->reg_mode) >> pll->shift_mode;
+ u64 pll_nf;
+ u64 pll_nr;
+ u64 pll_no;
+ u64 rate64;
+
+ if (pll_con3 & RK3X_PLLCON3_BYPASS) {
+ pr_debug("%s: pll %s is bypassed\n", __func__,
+ __clk_get_name(hw->clk));
+ return parent_rate;
+ }
+
+ mode_con &= RK3X_PLL_MODE_MASK;
+ if (mode_con != RK3X_PLL_MODE_NORM) {
+ pr_debug("%s: pll %s not in normal mode: %d\n", __func__,
+ __clk_get_name(hw->clk), mode_con);
+ return parent_rate;
+ }
+
+ pll_nf = (pll_con1 >> RK3X_PLLCON1_NF_SHIFT);
+ pll_nf &= RK3X_PLLCON1_NF_MASK;
+ pll_nf++;
+ rate64 = (u64)parent_rate * pll_nf;
+
+ pll_nr = (pll_con0 >> RK3X_PLLCON0_NR_SHIFT);
+ pll_nr &= RK3X_PLLCON0_NR_MASK;
+ pll_nr++;
+ do_div(rate64, pll_nr);
+
+ pll_no = (pll_con0 >> RK3X_PLLCON0_OD_SHIFT);
+ pll_no &= RK3X_PLLCON0_OD_MASK;
+ pll_no++;
+ do_div(rate64, pll_no);
+
+ return (unsigned long)rate64;
+}
+
+static const struct clk_ops rk3x_generic_pll_clk_ops = {
+ .recalc_rate = rk3x_generic_pll_recalc_rate,
+};
+
+struct clk * __init rockchip_clk_register_rk3x_pll(const char *name,
+ const char *pname, void __iomem *reg_base,
+ void __iomem *reg_mode, unsigned int shift_mode,
+ spinlock_t *lock)
+{
+ struct rockchip_clk_pll *pll;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll) {
+ pr_err("%s: could not allocate pll clk %s\n", __func__, name);
+ return NULL;
+ }
+
+ init.name = name;
+ init.ops = &rk3x_generic_pll_clk_ops;
+ init.flags = CLK_GET_RATE_NOCACHE;
+ init.parent_names = &pname;
+ init.num_parents = 1;
+
+ pll->hw.init = &init;
+ pll->reg_base = reg_base;
+ pll->reg_mode = reg_mode;
+ pll->shift_mode = shift_mode;
+ pll->lock = lock;
+
+ clk = clk_register(NULL, &pll->hw);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register pll clock %s\n", __func__,
+ name);
+ kfree(pll);
+ }
+
+ return clk;
+}
diff --git a/drivers/clk/rockchip/clk-rockchip-pll.h b/drivers/clk/rockchip/clk-rockchip-pll.h
new file mode 100644
index 0000000..a63288a
--- /dev/null
+++ b/drivers/clk/rockchip/clk-rockchip-pll.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+extern struct clk * __init rockchip_clk_register_rk3x_pll(const char *name,
+ const char *pname, const void __iomem *reg_base,
+ const void __iomem *reg_mode, unsigned int shift_mode,
+ spinlock_t *lock);
diff --git a/drivers/clk/rockchip/clk-rockchip.c b/drivers/clk/rockchip/clk-rockchip.c
new file mode 100644
index 0000000..048f8ae
--- /dev/null
+++ b/drivers/clk/rockchip/clk-rockchip.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include "clk-rockchip-pll.h"
+
+static DEFINE_SPINLOCK(clk_lock);
+
+struct rockchip_pll_data {
+ int mode_shift;
+};
+
+struct rockchip_pll_data rk3066a_apll_data = {
+ .mode_shift = 0,
+};
+
+struct rockchip_pll_data rk3066a_dpll_data = {
+ .mode_shift = 4,
+};
+
+struct rockchip_pll_data rk3066a_cpll_data = {
+ .mode_shift = 8,
+};
+
+struct rockchip_pll_data rk3066a_gpll_data = {
+ .mode_shift = 12,
+};
+
+/* Matches for plls */
+static const __initconst struct of_device_id clk_pll_match[] = {
+ { .compatible = "rockchip,rk3066a-apll", .data = &rk3066a_apll_data },
+ { .compatible = "rockchip,rk3066a-dpll", .data = &rk3066a_dpll_data },
+ { .compatible = "rockchip,rk3066a-cpll", .data = &rk3066a_cpll_data },
+ { .compatible = "rockchip,rk3066a-gpll", .data = &rk3066a_gpll_data },
+ {}
+};
+
+static void __init rockchip_pll_setup(struct device_node *node,
+ struct rockchip_pll_data *data)
+{
+ struct clk *clk;
+ const char *clk_name = node->name;
+ const char *clk_parent;
+ void __iomem *reg_base;
+ void __iomem *reg_mode;
+ u32 rate;
+
+ reg_base = of_iomap(node, 0);
+ reg_mode = of_iomap(node, 1);
+
+ clk_parent = of_clk_get_parent_name(node, 0);
+
+ pr_debug("%s: adding %s as child of %s\n",
+ __func__, clk_name, clk_parent);
+
+ clk = rockchip_clk_register_rk3x_pll(clk_name, clk_parent, reg_base,
+ reg_mode, data->mode_shift,
+ &clk_lock);
+ if (clk) {
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+
+ /* optionally set a target frequency for the pll */
+ if (!of_property_read_u32(node, "clock-frequency", &rate))
+ clk_set_rate(clk, rate);
+ }
+}
+
+/*
+ * Mux clocks
+ */
+
+struct rockchip_mux_data {
+ int shift;
+ int width;
+};
+
+#define RK_MUX(n, s, w) \
+static const __initconst struct rockchip_mux_data n = { \
+ .shift = s, \
+ .width = w, \
+}
+
+RK_MUX(gpll_cpll_15_mux_data, 15, 1);
+RK_MUX(uart_mux_data, 8, 2);
+RK_MUX(cpu_mux_data, 8, 1);
+
+static const __initconst struct of_device_id clk_mux_match[] = {
+ { .compatible = "rockchip,rk2928-gpll-cpll-bit15-mux",
+ .data = &gpll_cpll_15_mux_data },
+ { .compatible = "rockchip,rk2928-uart-mux",
+ .data = &uart_mux_data },
+ { .compatible = "rockchip,rk3066-cpu-mux",
+ .data = &cpu_mux_data },
+ {}
+};
+
+static void __init rockchip_mux_clk_setup(struct device_node *node,
+ struct rockchip_mux_data *data)
+{
+ struct clk *clk;
+ const char *clk_name = node->name;
+ void __iomem *reg;
+ int max_parents = (1 << data->width);
+ const char *parents[max_parents];
+ int flags;
+ int i = 0;
+
+ reg = of_iomap(node, 0);
+
+ while (i < max_parents &&
+ (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
+ i++;
+
+ flags = CLK_MUX_MASK_UPPER_HALF;
+
+ clk = clk_register_mux(NULL, clk_name, parents, i, 0,
+ reg, data->shift, data->width,
+ flags, &clk_lock);
+ if (clk)
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+
+/*
+ * Divider clocks
+ */
+
+struct rockchip_div_data {
+ int shift;
+ int width;
+ int max;
+ int flags;
+};
+
+#define RK_DIV(n, s, w, f) \
+static const __initconst struct rockchip_div_data n = { \
+ .shift = s, \
+ .width = w, \
+ .flags = f, \
+}
+
+RK_DIV(cpu_div_data, 0, 5, 0);
+RK_DIV(aclk_periph_div_data, 0, 5, 0);
+RK_DIV(aclk_cpu_div_data, 0, 3, 0);
+RK_DIV(hclk_div_data, 8, 2, CLK_DIVIDER_POWER_OF_TWO);
+RK_DIV(pclk_div_data, 12, 2, CLK_DIVIDER_POWER_OF_TWO);
+RK_DIV(mmc_div_data, 0, 6, CLK_DIVIDER_EVEN);
+RK_DIV(uart_div_data, 0, 7, 0);
+
+static const __initconst struct of_device_id clk_divider_match[] = {
+ { .compatible = "rockchip,rk3066a-cpu-divider",
+ .data = &cpu_div_data },
+ { .compatible = "rockchip,rk2928-aclk-periph-divider",
+ .data = &aclk_periph_div_data },
+ { .compatible = "rockchip,rk3066a-aclk-cpu-divider",
+ .data = &aclk_cpu_div_data },
+ { .compatible = "rockchip,rk2928-hclk-divider",
+ .data = &hclk_div_data },
+ { .compatible = "rockchip,rk2928-pclk-divider",
+ .data = &pclk_div_data },
+ { .compatible = "rockchip,rk2928-mmc-divider",
+ .data = &mmc_div_data },
+ { .compatible = "rockchip,rk2928-uart-divider",
+ .data = &uart_div_data },
+ {}
+};
+
+static void __init rockchip_divider_clk_setup(struct device_node *node,
+ struct rockchip_div_data *data)
+{
+ struct clk *clk;
+ const char *clk_name = node->name;
+ const char *clk_parent;
+ void __iomem *reg;
+ int flags;
+
+ reg = of_iomap(node, 0);
+
+ clk_parent = of_clk_get_parent_name(node, 0);
+
+ flags = data->flags;
+ flags |= CLK_DIVIDER_MASK_UPPER_HALF;
+
+ clk = clk_register_divider(NULL, clk_name, clk_parent, 0,
+ reg, data->shift, data->width,
+ flags, &clk_lock);
+ if (clk)
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+
+/*
+ * Gate clocks
+ */
+
+static void __init rockchip_gate_clk_setup(struct device_node *node,
+ void *data)
+{
+ struct clk_onecell_data *clk_data;
+ const char *clk_parent;
+ const char *clk_name;
+ void __iomem *reg;
+ void __iomem *reg_idx;
+ int flags;
+ int qty;
+ int reg_bit;
+ int clkflags = CLK_SET_RATE_PARENT;
+ int i;
+
+ qty = of_property_count_strings(node, "clock-output-names");
+ if (qty < 0) {
+ pr_err("%s: error in clock-output-names %d\n", __func__, qty);
+ return;
+ }
+
+ if (qty == 0) {
+ pr_info("%s: nothing to do\n", __func__);
+ return;
+ }
+
+ reg = of_iomap(node, 0);
+
+ clk_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);
+ if (!clk_data)
+ return;
+
+ clk_data->clks = kzalloc(qty * sizeof(struct clk *), GFP_KERNEL);
+ if (!clk_data->clks) {
+ kfree(clk_data);
+ return;
+ }
+
+ flags = CLK_GATE_MASK_UPPER_HALF | CLK_GATE_SET_TO_DISABLE;
+
+ for (i = 0; i < qty; i++) {
+ of_property_read_string_index(node, "clock-output-names",
+ i, &clk_name);
+
+ /* ignore empty slots */
+ if (!strcmp("reserved", clk_name))
+ continue;
+
+ clk_parent = of_clk_get_parent_name(node, i);
+
+ /* keep all gates untouched for now */
+ clkflags |= CLK_IGNORE_UNUSED;
+
+ reg_idx = reg + (4 * (i / 16));
+ reg_bit = (i % 16);
+
+ clk_data->clks[i] = clk_register_gate(NULL, clk_name,
+ clk_parent, clkflags,
+ reg_idx, reg_bit,
+ flags,
+ &clk_lock);
+ WARN_ON(IS_ERR(clk_data->clks[i]));
+ }
+
+ clk_data->clk_num = qty;
+
+ of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+}
+
+static const __initconst struct of_device_id clk_gate_match[] = {
+ { .compatible = "rockchip,rk2928-gate-clk" },
+ {}
+};
+
+void __init of_rockchip_clk_table_clock_setup(
+ const struct of_device_id *clk_match,
+ void *function)
+{
+ struct device_node *np;
+ const struct div_data *data;
+ const struct of_device_id *match;
+ void (*setup_function)(struct device_node *, const void *) = function;
+
+ for_each_matching_node(np, clk_match) {
+ match = of_match_node(clk_match, np);
+ data = match->data;
+ setup_function(np, data);
+ }
+}
+
+void __init rockchip_init_clocks(struct device_node *node)
+{
+ of_rockchip_clk_table_clock_setup(clk_pll_match,
+ rockchip_pll_setup);
+
+ of_rockchip_clk_table_clock_setup(clk_mux_match,
+ rockchip_mux_clk_setup);
+
+ of_rockchip_clk_table_clock_setup(clk_gate_match,
+ rockchip_gate_clk_setup);
+
+ of_rockchip_clk_table_clock_setup(clk_divider_match,
+ rockchip_divider_clk_setup);
+}
+CLK_OF_DECLARE(rockchip_clocks, "rockchip,clocks", rockchip_init_clocks);
--
1.7.2.3

2013-06-06 19:40:18

by Heiko Stübner

[permalink] [raw]
Subject: [PATCH v2 3/8] mmc: dw_mmc-pltfm: remove static from dw_mci_pltfm_remove

dw_mci_pltfm_remove gets exported and used by dw_mmc-exynos, so should
not be static.

Signed-off-by: Heiko Stuebner <[email protected]>
Acked-by: Jaehoon Chung <[email protected]>
Acked-by: Seungwon Jeon <[email protected]>
---
drivers/mmc/host/dw_mmc-pltfm.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index 37873f1..0048da8 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -68,7 +68,7 @@ static int dw_mci_pltfm_probe(struct platform_device *pdev)
return dw_mci_pltfm_register(pdev, NULL);
}

-static int dw_mci_pltfm_remove(struct platform_device *pdev)
+int dw_mci_pltfm_remove(struct platform_device *pdev)
{
struct dw_mci *host = platform_get_drvdata(pdev);

--
1.7.2.3

2013-06-06 19:40:53

by Heiko Stübner

[permalink] [raw]
Subject: [PATCH v2 8/8] arm: add basic support for Rockchip RK3066a boards

This adds a generic devicetree board file and a dtsi for boards
based on the RK3066a SoCs from Rockchip.

Apart from the generic parts (gic, clocks, pinctrl) the only components
currently supported are the timers, uarts and mmc ports (all DesignWare-
based).

Signed-off-by: Heiko Stuebner <[email protected]>
---
arch/arm/Kconfig | 2 +
arch/arm/Makefile | 1 +
arch/arm/boot/dts/rk3066a.dtsi | 359 +++++++++++++++++++++++++++++++++++++
arch/arm/mach-rockchip/Kconfig | 16 ++
arch/arm/mach-rockchip/Makefile | 1 +
arch/arm/mach-rockchip/rockchip.c | 54 ++++++
6 files changed, 433 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/boot/dts/rk3066a.dtsi
create mode 100644 arch/arm/mach-rockchip/Kconfig
create mode 100644 arch/arm/mach-rockchip/Makefile
create mode 100644 arch/arm/mach-rockchip/rockchip.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 4315d18..6355589 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -983,6 +983,8 @@ source "arch/arm/mach-mmp/Kconfig"

source "arch/arm/mach-realview/Kconfig"

+source "arch/arm/mach-rockchip/Kconfig"
+
source "arch/arm/mach-sa1100/Kconfig"

source "arch/arm/plat-samsung/Kconfig"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 3380c4f..0153f60 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -172,6 +172,7 @@ machine-$(CONFIG_ARCH_PICOXCELL) += picoxcell
machine-$(CONFIG_ARCH_PRIMA2) += prima2
machine-$(CONFIG_ARCH_PXA) += pxa
machine-$(CONFIG_ARCH_REALVIEW) += realview
+machine-$(CONFIG_ARCH_ROCKCHIP) += rockchip
machine-$(CONFIG_ARCH_RPC) += rpc
machine-$(CONFIG_ARCH_S3C24XX) += s3c24xx
machine-$(CONFIG_ARCH_S3C64XX) += s3c64xx
diff --git a/arch/arm/boot/dts/rk3066a.dtsi b/arch/arm/boot/dts/rk3066a.dtsi
new file mode 100644
index 0000000..7f4709d
--- /dev/null
+++ b/arch/arm/boot/dts/rk3066a.dtsi
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/pinctrl/rockchip.h>
+#include "skeleton.dtsi"
+#include "rk3066a-clocks.dtsi"
+
+/ {
+ compatible = "rockchip,rk3066a";
+ interrupt-parent = <&gic>;
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ next-level-cache = <&L2>;
+ reg = <0x0>;
+ };
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ next-level-cache = <&L2>;
+ reg = <0x1>;
+ };
+ };
+
+ soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ ranges;
+
+ gic: interrupt-controller@1013d000 {
+ compatible = "arm,cortex-a9-gic";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ reg = <0x1013d000 0x1000>,
+ <0x1013c100 0x0100>;
+ };
+
+ L2: l2-cache-controller@10138000 {
+ compatible = "arm,pl310-cache";
+ reg = <0x10138000 0x1000>;
+ cache-unified;
+ cache-level = <2>;
+ };
+
+ local-timer@1013c600 {
+ compatible = "arm,cortex-a9-twd-timer";
+ reg = <0x1013c600 0x20>;
+ interrupts = <GIC_PPI 13 0x304>;
+ };
+
+ timer@20038000 {
+ compatible = "snps,dw-apb-timer-osc";
+ reg = <0x20038000 0x100>;
+ interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates1 0>, <&clk_gates7 7>;
+ clock-names = "timer", "pclk";
+ };
+
+ timer@2003a000 {
+ compatible = "snps,dw-apb-timer-osc";
+ reg = <0x2003a000 0x100>;
+ interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates1 1>, <&clk_gates7 8>;
+ clock-names = "timer", "pclk";
+ };
+
+ timer@2000e000 {
+ compatible = "snps,dw-apb-timer-osc";
+ reg = <0x2000e000 0x100>;
+ interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates1 2>, <&clk_gates7 9>;
+ clock-names = "timer", "pclk";
+ };
+
+ pinctrl@20008000 {
+ compatible = "rockchip,rk3066a-pinctrl";
+ reg = <0x20008000 0x150>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ gpio0: gpio0@20034000 {
+ compatible = "rockchip,gpio-bank";
+ reg = <0x20034000 0x100>;
+ interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates8 9>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio1: gpio1@2003c000 {
+ compatible = "rockchip,gpio-bank";
+ reg = <0x2003c000 0x100>;
+ interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates8 10>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio2: gpio2@2003e000 {
+ compatible = "rockchip,gpio-bank";
+ reg = <0x2003e000 0x100>;
+ interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates8 11>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio3: gpio3@20080000 {
+ compatible = "rockchip,gpio-bank";
+ reg = <0x20080000 0x100>;
+ interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates8 12>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio4: gpio4@20084000 {
+ compatible = "rockchip,gpio-bank";
+ reg = <0x20084000 0x100>;
+ interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates8 13>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio6: gpio6@2000a000 {
+ compatible = "rockchip,gpio-bank";
+ reg = <0x2000a000 0x100>;
+ interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates8 15>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ uart0 {
+ uart0_xfer: uart0-xfer {
+ rockchip,pins = <RK_GPIO1 0 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>,
+ <RK_GPIO1 1 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+
+ uart0_cts: uart0-cts {
+ rockchip,pins = <RK_GPIO1 2 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+
+ uart0_rts: uart0-rts {
+ rockchip,pins = <RK_GPIO1 3 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+ };
+
+ uart1 {
+ uart1_xfer: uart1-xfer {
+ rockchip,pins = <RK_GPIO1 4 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>,
+ <RK_GPIO1 5 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+
+ uart1_cts: uart1-cts {
+ rockchip,pins = <RK_GPIO1 6 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+
+ uart1_rts: uart1-rts {
+ rockchip,pins = <RK_GPIO1 7 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+ };
+
+ uart2 {
+ uart2_xfer: uart2-xfer {
+ rockchip,pins = <RK_GPIO1 8 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>,
+ <RK_GPIO1 9 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+ /* no rts / cts for uart2 */
+ };
+
+ uart3 {
+ uart3_xfer: uart3-xfer {
+ rockchip,pins = <RK_GPIO3 27 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>,
+ <RK_GPIO3 28 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+
+ uart3_cts: uart3-cts {
+ rockchip,pins = <RK_GPIO3 29 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+
+ uart3_rts: uart3-rts {
+ rockchip,pins = <RK_GPIO3 30 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+ };
+
+ sd0 {
+ sd0_clk: sd0-clk {
+ rockchip,pins = <RK_GPIO3 8 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+
+ sd0_cmd: sd0-cmd {
+ rockchip,pins = <RK_GPIO3 9 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+
+ sd0_cd: sd0-cd {
+ rockchip,pins = <RK_GPIO3 14 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+
+ sd0_wp: sd0-wp {
+ rockchip,pins = <RK_GPIO3 15 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+
+ sd0_bus1: sd0-bus-width1 {
+ rockchip,pins = <RK_GPIO3 10 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+
+ sd0_bus4: sd0-bus-width4 {
+ rockchip,pins = <RK_GPIO3 10 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>,
+ <RK_GPIO3 11 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>,
+ <RK_GPIO3 12 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>,
+ <RK_GPIO3 13 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+ };
+
+ sd1 {
+ sd1_clk: sd1-clk {
+ rockchip,pins = <RK_GPIO3 21 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+
+ sd1_cmd: sd1-cmd {
+ rockchip,pins = <RK_GPIO3 16 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+
+ sd1_cd: sd1-cd {
+ rockchip,pins = <RK_GPIO3 22 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+
+ sd1_wp: sd1-wp {
+ rockchip,pins = <RK_GPIO3 23 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+
+ sd1_bus1: sd1-bus-width1 {
+ rockchip,pins = <RK_GPIO3 17 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+
+ sd1_bus4: sd1-bus-width4 {
+ rockchip,pins = <RK_GPIO3 17 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>,
+ <RK_GPIO3 18 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>,
+ <RK_GPIO3 19 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>,
+ <RK_GPIO3 20 RK_FUNC_1 RK_PINCTRL_PULL_PIN_DEFAULT>;
+ };
+ };
+ };
+
+ uart0: serial@10124000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x10124000 0x400>;
+ interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <1>;
+ clocks = <&mux_uart0>;
+ status = "disabled";
+ };
+
+ uart1: serial@10126000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x10126000 0x400>;
+ interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <1>;
+ clocks = <&mux_uart1>;
+ status = "disabled";
+ };
+
+ uart2: serial@20064000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x20064000 0x400>;
+ interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <1>;
+ clocks = <&mux_uart2>;
+ status = "disabled";
+ };
+
+ uart3: serial@20068000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x20068000 0x400>;
+ interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <1>;
+ clocks = <&mux_uart3>;
+ status = "disabled";
+ };
+
+ dwmmc@10214000 {
+ compatible = "rockchip,cortex-a9-dw-mshc";
+ reg = <0x10214000 0x1000>;
+ interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ clocks = <&clk_gates5 10>, <&clk_gates2 11>;
+ clock-names = "biu", "ciu";
+
+ status = "disabled";
+ };
+
+ dwmmc@10218000 {
+ compatible = "rockchip,cortex-a9-dw-mshc";
+ reg = <0x10218000 0x1000>;
+ interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ clocks = <&clk_gates5 11>, <&clk_gates2 13>;
+ clock-names = "biu", "ciu";
+
+ status = "disabled";
+ };
+ };
+};
diff --git a/arch/arm/mach-rockchip/Kconfig b/arch/arm/mach-rockchip/Kconfig
new file mode 100644
index 0000000..25ee12b
--- /dev/null
+++ b/arch/arm/mach-rockchip/Kconfig
@@ -0,0 +1,16 @@
+config ARCH_ROCKCHIP
+ bool "Rockchip RK2928 and RK3xxx SOCs" if ARCH_MULTI_V7
+ select PINCTRL
+ select PINCTRL_ROCKCHIP
+ select ARCH_REQUIRE_GPIOLIB
+ select ARM_GIC
+ select CACHE_L2X0
+ select HAVE_ARM_TWD if LOCAL_TIMERS
+ select HAVE_SMP
+ select LOCAL_TIMERS if SMP
+ select COMMON_CLK
+ select GENERIC_CLOCKEVENTS
+ select DW_APB_TIMER_OF
+ help
+ Support for Rockchip's Cortex-A9 Single-to-Quad-Core-SoCs
+ containing the RK2928, RK30xx and RK31xx series.
diff --git a/arch/arm/mach-rockchip/Makefile b/arch/arm/mach-rockchip/Makefile
new file mode 100644
index 0000000..1547d4f
--- /dev/null
+++ b/arch/arm/mach-rockchip/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip.o
diff --git a/arch/arm/mach-rockchip/rockchip.c b/arch/arm/mach-rockchip/rockchip.c
new file mode 100644
index 0000000..0933e17
--- /dev/null
+++ b/arch/arm/mach-rockchip/rockchip.c
@@ -0,0 +1,54 @@
+/*
+ * Device Tree support for Rockchip SoCs
+ *
+ * Copyright (c) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/of_platform.h>
+#include <linux/irqchip.h>
+#include <linux/dw_apb_timer.h>
+#include <linux/clk-provider.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/hardware/cache-l2x0.h>
+
+static void __init rockchip_timer_init(void)
+{
+ of_clk_init(NULL);
+ clocksource_of_init();
+}
+
+static void __init rockchip_dt_init(void)
+{
+#ifdef CONFIG_CACHE_L2X0
+ l2x0_of_init(0, ~0UL);
+#endif
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+}
+
+static const char * const rockchip_board_dt_compat[] = {
+ "rockchip,rk2928",
+ "rockchip,rk3066a",
+ "rockchip,rk3066b",
+ "rockchip,rk3188",
+ NULL,
+};
+
+DT_MACHINE_START(ROCKCHIP_DT, "Rockchip Cortex-A9 (Device Tree)")
+ .init_machine = rockchip_dt_init,
+ .init_time = rockchip_timer_init,
+ .dt_compat = rockchip_board_dt_compat,
+MACHINE_END
--
1.7.2.3

2013-06-06 19:41:10

by Heiko Stübner

[permalink] [raw]
Subject: [PATCH v2 2/8] clk: divider: add flag to limit possible dividers to even numbers

SoCs like the Rockchip Cortex-A9 ones contain divider some clocks
that use the regular mechanisms for storage but allow only even
dividers and 1 to be used.

Therefore add a flag that lets _is_valid_div limit the valid dividers
to these values. _get_maxdiv is also adapted to return even values
for the CLK_DIVIDER_ONE_BASED case.

Signed-off-by: Heiko Stuebner <[email protected]>
---
drivers/clk/clk-divider.c | 14 ++++++++++++--
include/linux/clk-provider.h | 2 ++
2 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index e37c48a..adfbd0d 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -45,8 +45,16 @@ static unsigned int _get_table_maxdiv(const struct clk_div_table *table)

static unsigned int _get_maxdiv(struct clk_divider *divider)
{
- if (divider->flags & CLK_DIVIDER_ONE_BASED)
- return div_mask(divider);
+ if (divider->flags & CLK_DIVIDER_ONE_BASED) {
+ unsigned int div = div_mask(divider);
+
+ /* decrease to even number */
+ if (divider->flags & CLK_DIVIDER_EVEN)
+ div--;
+
+ return div;
+ }
+
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
return 1 << div_mask(divider);
if (divider->table)
@@ -141,6 +149,8 @@ static bool _is_valid_div(struct clk_divider *divider, unsigned int div)
return is_power_of_2(div);
if (divider->table)
return _is_valid_table_div(divider->table, div);
+ if (divider->flags & CLK_DIVIDER_EVEN && div != 1 && (div % 2) != 0)
+ return false;
return true;
}

diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 420a187..9fdd60d 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -268,6 +268,7 @@ struct clk_div_table {
* indicate the bits that get changed during a write. So for a clock with
* shift 0 and width 2, setting the divider to 2 would result in a write
* of (3 << 16) | (2 << 0).
+ * CLK_DIVIDER_EVEN - only allow even divider values
*/
struct clk_divider {
struct clk_hw hw;
@@ -283,6 +284,7 @@ struct clk_divider {
#define CLK_DIVIDER_POWER_OF_TWO BIT(1)
#define CLK_DIVIDER_ALLOW_ZERO BIT(2)
#define CLK_DIVIDER_MASK_UPPER_HALF BIT(3)
+#define CLK_DIVIDER_EVEN BIT(4)

extern const struct clk_ops clk_divider_ops;
struct clk *clk_register_divider(struct device *dev, const char *name,
--
1.7.2.3

2013-06-06 20:35:23

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH v2 4/8] mmc: dw_mmc-pltfm: add Rockchip variant

On Thu, Jun 6, 2013 at 10:11 PM, Heiko Stübner <[email protected]> wrote:
> Cortex-A9 SoCs from Rockchip use a slightly modified variant of dw_mmc
> controllers that seems to require the SDMMC_CMD_USE_HOLD_REG bit to
> always be set.
>
> There also seem to be no other modifications (additional register etc)
> present, so to keep the footprint low, add this small variant to the
> pltfm driver.

My comments against v1 of this patch are still valid.

--
With Best Regards,
Andy Shevchenko

2013-06-07 11:46:35

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v2 1/8] clk: flag to use upper half of the register as change indicator

On Thu, Jun 6, 2013 at 9:08 PM, Heiko St?bner <[email protected]> wrote:

> There exist platforms, namely at least all Rockchip Cortex-A9 based ones,
> that don't use the paradigm of reading-changing-writing the register contents,
> but instead only write the changes to the register with a mask that indicates
> the changed bits.
>
> This patch adds flags and code to support the case where the lower 16 bit of
> hold the information and the upper 16 bit are used as mask to indicate the
> written changes.
>
> As hardware-specific flags should not be added to the common clk flags, the
> flags are added to gate, mux and divider clocks individually.
>
> Signed-off-by: Heiko Stuebner <[email protected]>
(...)
> + if ((clk_gate_flags & CLK_GATE_MASK_UPPER_HALF) && bit_idx > 15) {
> + pr_err("%s: bit_idx %d invalid\n", __func__, bit_idx);
> + return ERR_PTR(-EINVAL);
> + }

Now this looks *EXTREMELY* familiar to a patch just sent by Haojian
for HiSilicon.

"[PATCH v2 3/6] clk: divider: add CLK_DIVIDER_HIWORD_MASK flag"
http://marc.info/?l=linux-arm-kernel&m=137035873916777&w=2

What kind of coincidence is this? Are Rockchip and HiSilicon using
the same silicon IP or are they of a common origin? (It is a small
world after all.)

I think you two guys need to read each others patch sets closely
here. I'd like Haojian to look at Heiko's patches and Heiko to look
at Haojian's patches, just to make sure you're not actually writing
two drivers for the same hardware in the end.

Yours,
Linus Walleij

2013-06-07 12:27:33

by Heiko Stübner

[permalink] [raw]
Subject: Re: [PATCH v2 1/8] clk: flag to use upper half of the register as change indicator

Am Freitag, 7. Juni 2013, 13:46:32 schrieb Linus Walleij:
> On Thu, Jun 6, 2013 at 9:08 PM, Heiko St?bner <[email protected]> wrote:
> > There exist platforms, namely at least all Rockchip Cortex-A9 based ones,
> > that don't use the paradigm of reading-changing-writing the register
> > contents, but instead only write the changes to the register with a mask
> > that indicates the changed bits.
> >
> > This patch adds flags and code to support the case where the lower 16 bit
> > of hold the information and the upper 16 bit are used as mask to
> > indicate the written changes.
> >
> > As hardware-specific flags should not be added to the common clk flags,
> > the flags are added to gate, mux and divider clocks individually.
> >
> > Signed-off-by: Heiko Stuebner <[email protected]>
>
> (...)
>
> > + if ((clk_gate_flags & CLK_GATE_MASK_UPPER_HALF) && bit_idx > 15)
> > { + pr_err("%s: bit_idx %d invalid\n", __func__, bit_idx);
> > + return ERR_PTR(-EINVAL);
> > + }
>
> Now this looks *EXTREMELY* familiar to a patch just sent by Haojian
> for HiSilicon.
>
> "[PATCH v2 3/6] clk: divider: add CLK_DIVIDER_HIWORD_MASK flag"
> http://marc.info/?l=linux-arm-kernel&m=137035873916777&w=2
>
> What kind of coincidence is this? Are Rockchip and HiSilicon using
> the same silicon IP or are they of a common origin? (It is a small
> world after all.)

Now this is really interesting :-) and the handling really seems to follow the
same pattern.


> I think you two guys need to read each others patch sets closely
> here. I'd like Haojian to look at Heiko's patches and Heiko to look
> at Haojian's patches, just to make sure you're not actually writing
> two drivers for the same hardware in the end.

I'll take a look


Heiko

2013-06-07 12:53:54

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v2 5/8] pinctrl: add pinctrl driver for Rockchip SoCs

On Thu, Jun 6, 2013 at 9:11 PM, Heiko St?bner <[email protected]> wrote:

> This driver adds support the Cortex-A9 based SoCs from Rockchip,
> so at least the RK2928, RK3066 (a and b) and RK3188.
> Earlier Rockchip SoCs seem to use similar mechanics for gpio
> handling so should be supportable with relative small changes.
> Pull handling on the rk3188 is currently a stub, due to it being
> a bit different to the earlier SoCs.
>
> Pinmuxing as well as gpio (and interrupt-) handling tested on
> a rk3066a based machine.
>
> Signed-off-by: Heiko Stuebner <[email protected]>

This basically looks fine.

(...)
> +Required properties for pin configuration node:
> + - rockchip,pins: 4 integers array, represents a group of pins mux and config
> + setting. The format is rockchip,pins = <PIN_BANK PIN_BANK_NUM MUX CONFIG>.
> + The MUX 0 means gpio and MUX 1 to 3 mean the specific device function
> +
> +Bits used for CONFIG:
> +PULL_AUTO (1 << 0): indicate this pin needs a pull setting for SoCs
> + that determine the pull up or down themselfs
> +PULL_UP (1 << 1): indicate this pin needs a pull up
> +PULL_DOWN (1 << 2): indicate this pin needs a pull down


So I would rather have these (as a separate patch) in
<dt-bindings/pinctrl/pinconfig.h>
The documentation in
Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
and the same bindings to be used for as many systems as
possible utilizing generic pin config.

I will be liberal in accepting patches creating this
infrastructure.

We need to realize that we will be setting example for everyone
else, and everyone else will be following that example.

> + for (i = 0, j = 0; i < size; i += 4, j++) {
> + unsigned long pinconf;
> +
> + num = be32_to_cpu(*list++);
> + bank = bank_num_to_bank(info, num);
> + if (IS_ERR(bank))
> + return PTR_ERR(bank);
> +
> + pin = be32_to_cpu(*list++);
> + grp->pins[j] = bank->pin_base + pin;
> + grp->func[j] = be32_to_cpu(*list++);
> +
> + pinconf = be32_to_cpu(*list++);
> + switch(pinconf) {
> + case RK_PINCTRL_NONE:
> + bias = PIN_CONFIG_BIAS_DISABLE;
> + break;
> + case RK_PINCTRL_PULL_PIN_DEFAULT:
> + bias = PIN_CONFIG_BIAS_PULL_PIN_DEFAULT;
> + break;
> + case RK_PINCTRL_PULL_UP:
> + bias = PIN_CONFIG_BIAS_PULL_UP;
> + break;
> + case RK_PINCTRL_PULL_DOWN:
> + bias = PIN_CONFIG_BIAS_PULL_DOWN;
> + break;
> + }
> +
> + grp->configs[j] = pinconf_to_config_packed(bias, 0);
> + }

I would like this to be added to
drivers/pinctrl/pinconf-generic.c
as a utility function, with the header in
drivers/pinctrl/pinconf.h

Also in a separate patch.

> +++ b/include/dt-bindings/pinctrl/rockchip.h
(...)
> +#define RK_PINCTRL_NONE 0
> +#define RK_PINCTRL_PULL_PIN_DEFAULT (1 << 0)
> +#define RK_PINCTRL_PULL_UP (1 << 1)
> +#define RK_PINCTRL_PULL_DOWN (1 << 2)

So I'd like these moved to /include/dt-bindings/pinctrl/pinconfig.h
and used as a separete include. Drop the RK_* prefix as it
will be universal and use a PINCONFIG_* prefix instead
of PINCTRL_*.

I think that is the route we need to take.

Bonus if you implement more config options from
pinconf-generic.h but otherwise me and others will have
to do it.

Yours,
Linus Walleij

2013-06-07 15:21:04

by Haojian Zhuang

[permalink] [raw]
Subject: Re: [PATCH v2 1/8] clk: flag to use upper half of the register as change indicator

On 7 June 2013 20:27, Heiko St?bner <[email protected]> wrote:
> Am Freitag, 7. Juni 2013, 13:46:32 schrieb Linus Walleij:
>> On Thu, Jun 6, 2013 at 9:08 PM, Heiko St?bner <[email protected]> wrote:
>> > There exist platforms, namely at least all Rockchip Cortex-A9 based ones,
>> > that don't use the paradigm of reading-changing-writing the register
>> > contents, but instead only write the changes to the register with a mask
>> > that indicates the changed bits.
>> >
>> > This patch adds flags and code to support the case where the lower 16 bit
>> > of hold the information and the upper 16 bit are used as mask to
>> > indicate the written changes.
>> >
>> > As hardware-specific flags should not be added to the common clk flags,
>> > the flags are added to gate, mux and divider clocks individually.
>> >
>> > Signed-off-by: Heiko Stuebner <[email protected]>
>>
>> (...)
>>
>> > + if ((clk_gate_flags & CLK_GATE_MASK_UPPER_HALF) && bit_idx > 15)
>> > { + pr_err("%s: bit_idx %d invalid\n", __func__, bit_idx);
>> > + return ERR_PTR(-EINVAL);
>> > + }
>>
>> Now this looks *EXTREMELY* familiar to a patch just sent by Haojian
>> for HiSilicon.
>>
>> "[PATCH v2 3/6] clk: divider: add CLK_DIVIDER_HIWORD_MASK flag"
>> http://marc.info/?l=linux-arm-kernel&m=137035873916777&w=2
>>
>> What kind of coincidence is this? Are Rockchip and HiSilicon using
>> the same silicon IP or are they of a common origin? (It is a small
>> world after all.)
>
> Now this is really interesting :-) and the handling really seems to follow the
> same pattern.
>
>
>> I think you two guys need to read each others patch sets closely
>> here. I'd like Haojian to look at Heiko's patches and Heiko to look
>> at Haojian's patches, just to make sure you're not actually writing
>> two drivers for the same hardware in the end.
>
> I'll take a look
>
>
> Heiko

Me too :)

Regards
Haojian

2013-06-07 23:14:10

by Heiko Stübner

[permalink] [raw]
Subject: Re: [PATCH v2 5/8] pinctrl: add pinctrl driver for Rockchip SoCs

Am Freitag, 7. Juni 2013, 14:53:51 schrieb Linus Walleij:
> On Thu, Jun 6, 2013 at 9:11 PM, Heiko St?bner <[email protected]> wrote:
> > + for (i = 0, j = 0; i < size; i += 4, j++) {
> > + unsigned long pinconf;
> > +
> > + num = be32_to_cpu(*list++);
> > + bank = bank_num_to_bank(info, num);
> > + if (IS_ERR(bank))
> > + return PTR_ERR(bank);
> > +
> > + pin = be32_to_cpu(*list++);
> > + grp->pins[j] = bank->pin_base + pin;
> > + grp->func[j] = be32_to_cpu(*list++);
> > +
> > + pinconf = be32_to_cpu(*list++);
> > + switch(pinconf) {
> > + case RK_PINCTRL_NONE:
> > + bias = PIN_CONFIG_BIAS_DISABLE;
> > + break;
> > + case RK_PINCTRL_PULL_PIN_DEFAULT:
> > + bias = PIN_CONFIG_BIAS_PULL_PIN_DEFAULT;
> > + break;
> > + case RK_PINCTRL_PULL_UP:
> > + bias = PIN_CONFIG_BIAS_PULL_UP;
> > + break;
> > + case RK_PINCTRL_PULL_DOWN:
> > + bias = PIN_CONFIG_BIAS_PULL_DOWN;
> > + break;
> > + }
> > +
> > + grp->configs[j] = pinconf_to_config_packed(bias, 0);
> > + }
>
> I would like this to be added to
> drivers/pinctrl/pinconf-generic.c
> as a utility function, with the header in
> drivers/pinctrl/pinconf.h
>
> Also in a separate patch.
>
> > +++ b/include/dt-bindings/pinctrl/rockchip.h
>
> (...)
>
> > +#define RK_PINCTRL_NONE 0
> > +#define RK_PINCTRL_PULL_PIN_DEFAULT (1 << 0)
> > +#define RK_PINCTRL_PULL_UP (1 << 1)
> > +#define RK_PINCTRL_PULL_DOWN (1 << 2)
>
> So I'd like these moved to /include/dt-bindings/pinctrl/pinconfig.h
> and used as a separete include. Drop the RK_* prefix as it
> will be universal and use a PINCONFIG_* prefix instead
> of PINCTRL_*.
>
> I think that is the route we need to take.
>
> Bonus if you implement more config options from
> pinconf-generic.h but otherwise me and others will have
> to do it.

Gah, damn me for always wanting to get bonus points ;-),

I did play around a bit with the idea you described above and came up
with the stuff a bit below. This is in no way meant to be finished, I
also only was able to compile test it yet, but it looks like it might
work when I test it tomorrow.

So if possible I would just like to get a "looks halfway sane, continue"
or "completely wrong track" please :-) :


constants in dt-bindings/pinctrl/pinconfig.h:
-----------------------------
#define PINCONFIG_NONE 0
#define PINCONFIG_BIAS_DISABLE (1 << 0)
#define PINCONFIG_BIAS_HIGH_IMPEDANCE (1 << 1)
#define PINCONFIG_BIAS_BUS_HOLD (1 << 2)
#define PINCONFIG_BIAS_PULL_PIN_DEFAULT (1 << 3)
#define PINCONFIG_BIAS_PULL_UP (1 << 4)
#define PINCONFIG_BIAS_PULL_DOWN (1 << 5)
#define PINCONFIG_DRIVE_PUSH_PULL (1 << 6)
#define PINCONFIG_DRIVE_OPEN_DRAIN (1 << 7)
#define PINCONFIG_DRIVE_OPEN_SOURCE (1 << 8)
#define PINCONFIG_INPUT_SCHMITT_ENABLE (1 << 9)
#define PINCONFIG_INPUT_SCHMITT_DISABLE (1 << 10)
#define PINCONFIG_LOW_POWER_MODE (1 << 11)
#define PINCONFIG_OUTPUT_LOW (1 << 12)
#define PINCONFIG_OUTPUT_HIGH (1 << 13)


and code including how the rockchip driver could look like:
---------
diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c
index 9a6812b..35c40be 100644
--- a/drivers/pinctrl/pinconf-generic.c
+++ b/drivers/pinctrl/pinconf-generic.c
@@ -139,3 +139,61 @@ void pinconf_generic_dump_config(struct pinctrl_dev *pctldev,
}
EXPORT_SYMBOL_GPL(pinconf_generic_dump_config);
#endif
+
+/*
+ * Maps the devicetree config bits to actual pinconf values.
+ * The array indizes match the bits set in dt-bindings/pinctrl/pinconf.h
+ * and the array should contain an entry for each bit defined there
+ */
+static unsigned long conf_map[] = {
+ PIN_CONF_PACKED(PIN_CONFIG_BIAS_DISABLE, 0),
+ PIN_CONF_PACKED(PIN_CONFIG_BIAS_HIGH_IMPEDANCE, 0),
+ PIN_CONF_PACKED(PIN_CONFIG_BIAS_BUS_HOLD, 0),
+ PIN_CONF_PACKED(PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 0),
+ PIN_CONF_PACKED(PIN_CONFIG_BIAS_PULL_UP, 0),
+ PIN_CONF_PACKED(PIN_CONFIG_BIAS_PULL_DOWN, 0),
+ PIN_CONF_PACKED(PIN_CONFIG_DRIVE_PUSH_PULL, 0),
+ PIN_CONF_PACKED(PIN_CONFIG_DRIVE_OPEN_DRAIN, 0),
+ PIN_CONF_PACKED(PIN_CONFIG_DRIVE_OPEN_SOURCE, 0),
+ PIN_CONF_PACKED(PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1),
+ PIN_CONF_PACKED(PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0),
+ PIN_CONF_PACKED(PIN_CONFIG_LOW_POWER_MODE, 0),
+ PIN_CONF_PACKED(PIN_CONFIG_OUTPUT, 0),
+ PIN_CONF_PACKED(PIN_CONFIG_OUTPUT, 1),
+};
+
+/*
+ * Parse a pinconf bitmap from a devicetree entry into individual pin configs.
+ * @pinconf: the bitmap containing config bits
+ * @configs: after the function returns contains a pointer to an array of
+ * pin configs
+ * @nconfigs: number of entries of configs
+ */
+int pinconf_generic_parse_dt_bitmap(unsigned long pinconf,
+ unsigned long *configs,
+ unsigned int *nconfigs)
+{
+ int bit;
+ int i;
+
+ *nconfigs = hweight_long(pinconf);
+ configs = kzalloc(*nconfigs * sizeof(unsigned long), GFP_KERNEL);
+
+ i = 0;
+ while (pinconf && i < *nconfigs) {
+ bit = __ffs(pinconf);
+ pinconf &= ~BIT(i);
+
+ if (bit > ARRAY_SIZE(conf_map)) {
+ pr_err("%s: unknown bit %d\n", __func__, bit);
+ kfree(configs);
+ return -EINVAL;
+ }
+
+ configs[i] = conf_map[bit];
+
+ i++;
+ }
+
+ return 0;
+}
diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h
index 92c7267..0fab53a 100644
--- a/drivers/pinctrl/pinconf.h
+++ b/drivers/pinctrl/pinconf.h
@@ -123,3 +123,9 @@ static inline void pinconf_generic_dump_config(struct pinctrl_dev *pctldev,
return;
}
#endif
+
+#ifdef CONFIG_GENERIC_PINCONF
+int pinconf_generic_parse_dt_bitmap(unsigned long pinconf,
+ unsigned long *configs,
+ unsigned int *nconfigs);
+#endif
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
index b77e241..88398df 100644
--- a/drivers/pinctrl/pinctrl-rockchip.c
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -40,6 +40,7 @@
#include <dt-bindings/pinctrl/rockchip.h>

#include "core.h"
+#include "pinconf.h"

/* GPIO control registers */
#define GPIO_SWPORT_DR 0x00
@@ -639,7 +640,8 @@ static int rockchip_pinctrl_parse_groups(struct device_node *np,
int num;
int i, j;
int pin;
- int bias;
+ int ret;
+ int nconfigs;

dev_dbg(info->dev, "group(%d): %s\n", index, np->name);

@@ -683,22 +685,16 @@ static int rockchip_pinctrl_parse_groups(struct device_node *np,
grp->func[j] = be32_to_cpu(*list++);

pinconf = be32_to_cpu(*list++);
- switch(pinconf) {
- case RK_PINCTRL_NONE:
- bias = PIN_CONFIG_BIAS_DISABLE;
- break;
- case RK_PINCTRL_PULL_PIN_DEFAULT:
- bias = PIN_CONFIG_BIAS_PULL_PIN_DEFAULT;
- break;
- case RK_PINCTRL_PULL_UP:
- bias = PIN_CONFIG_BIAS_PULL_UP;
- break;
- case RK_PINCTRL_PULL_DOWN:
- bias = PIN_CONFIG_BIAS_PULL_DOWN;
- break;
- }

- grp->configs[j] = pinconf_to_config_packed(bias, 0);
+ ret = pinconf_generic_parse_dt_bitmap(pinconf,
+ &grp->configs[j], &nconfigs);
+ if (ret)
+ return ret;
+
+ if (nconfigs != 1) {
+ pr_err("unexpected number of config entries\n");
+ return -EINVAL;
+ }
}

return 0;

2013-06-07 23:53:27

by Heiko Stübner

[permalink] [raw]
Subject: Re: [PATCH v2 5/8] pinctrl: add pinctrl driver for Rockchip SoCs

Am Samstag, 8. Juni 2013, 01:13:48 schrieb Heiko St?bner:
> Am Freitag, 7. Juni 2013, 14:53:51 schrieb Linus Walleij:
> > On Thu, Jun 6, 2013 at 9:11 PM, Heiko St?bner <[email protected]> wrote:
> > > + for (i = 0, j = 0; i < size; i += 4, j++) {
> > > + unsigned long pinconf;
> > > +
> > > + num = be32_to_cpu(*list++);
> > > + bank = bank_num_to_bank(info, num);
> > > + if (IS_ERR(bank))
> > > + return PTR_ERR(bank);
> > > +
> > > + pin = be32_to_cpu(*list++);
> > > + grp->pins[j] = bank->pin_base + pin;
> > > + grp->func[j] = be32_to_cpu(*list++);
> > > +
> > > + pinconf = be32_to_cpu(*list++);
> > > + switch(pinconf) {
> > > + case RK_PINCTRL_NONE:
> > > + bias = PIN_CONFIG_BIAS_DISABLE;
> > > + break;
> > > + case RK_PINCTRL_PULL_PIN_DEFAULT:
> > > + bias = PIN_CONFIG_BIAS_PULL_PIN_DEFAULT;
> > > + break;
> > > + case RK_PINCTRL_PULL_UP:
> > > + bias = PIN_CONFIG_BIAS_PULL_UP;
> > > + break;
> > > + case RK_PINCTRL_PULL_DOWN:
> > > + bias = PIN_CONFIG_BIAS_PULL_DOWN;
> > > + break;
> > > + }
> > > +
> > > + grp->configs[j] = pinconf_to_config_packed(bias, 0);
> > > + }
> >
> > I would like this to be added to
> > drivers/pinctrl/pinconf-generic.c
> > as a utility function, with the header in
> > drivers/pinctrl/pinconf.h
> >
> > Also in a separate patch.
> >
> > > +++ b/include/dt-bindings/pinctrl/rockchip.h
> >
> > (...)
> >
> > > +#define RK_PINCTRL_NONE 0
> > > +#define RK_PINCTRL_PULL_PIN_DEFAULT (1 << 0)
> > > +#define RK_PINCTRL_PULL_UP (1 << 1)
> > > +#define RK_PINCTRL_PULL_DOWN (1 << 2)
> >
> > So I'd like these moved to /include/dt-bindings/pinctrl/pinconfig.h
> > and used as a separete include. Drop the RK_* prefix as it
> > will be universal and use a PINCONFIG_* prefix instead
> > of PINCTRL_*.
> >
> > I think that is the route we need to take.
> >
> > Bonus if you implement more config options from
> > pinconf-generic.h but otherwise me and others will have
> > to do it.
>
> Gah, damn me for always wanting to get bonus points ;-),
>
> I did play around a bit with the idea you described above and came up
> with the stuff a bit below. This is in no way meant to be finished, I
> also only was able to compile test it yet, but it looks like it might
> work when I test it tomorrow.
>
> So if possible I would just like to get a "looks halfway sane, continue"
> or "completely wrong track" please :-) :
>
>
> constants in dt-bindings/pinctrl/pinconfig.h:
> -----------------------------
> #define PINCONFIG_NONE 0
> #define PINCONFIG_BIAS_DISABLE (1 << 0)
> #define PINCONFIG_BIAS_HIGH_IMPEDANCE (1 << 1)
> #define PINCONFIG_BIAS_BUS_HOLD (1 << 2)
> #define PINCONFIG_BIAS_PULL_PIN_DEFAULT (1 << 3)
> #define PINCONFIG_BIAS_PULL_UP (1 << 4)
> #define PINCONFIG_BIAS_PULL_DOWN (1 << 5)
> #define PINCONFIG_DRIVE_PUSH_PULL (1 << 6)
> #define PINCONFIG_DRIVE_OPEN_DRAIN (1 << 7)
> #define PINCONFIG_DRIVE_OPEN_SOURCE (1 << 8)
> #define PINCONFIG_INPUT_SCHMITT_ENABLE (1 << 9)
> #define PINCONFIG_INPUT_SCHMITT_DISABLE (1 << 10)
> #define PINCONFIG_LOW_POWER_MODE (1 << 11)
> #define PINCONFIG_OUTPUT_LOW (1 << 12)
> #define PINCONFIG_OUTPUT_HIGH (1 << 13)
>
>
> and code including how the rockchip driver could look like:
> ---------
> diff --git a/drivers/pinctrl/pinconf-generic.c
> b/drivers/pinctrl/pinconf-generic.c index 9a6812b..35c40be 100644
> --- a/drivers/pinctrl/pinconf-generic.c
> +++ b/drivers/pinctrl/pinconf-generic.c
> @@ -139,3 +139,61 @@ void pinconf_generic_dump_config(struct pinctrl_dev
> *pctldev, }
> EXPORT_SYMBOL_GPL(pinconf_generic_dump_config);
> #endif
> +
> +/*
> + * Maps the devicetree config bits to actual pinconf values.
> + * The array indizes match the bits set in dt-bindings/pinctrl/pinconf.h
> + * and the array should contain an entry for each bit defined there
> + */
> +static unsigned long conf_map[] = {
> + PIN_CONF_PACKED(PIN_CONFIG_BIAS_DISABLE, 0),
> + PIN_CONF_PACKED(PIN_CONFIG_BIAS_HIGH_IMPEDANCE, 0),
> + PIN_CONF_PACKED(PIN_CONFIG_BIAS_BUS_HOLD, 0),
> + PIN_CONF_PACKED(PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 0),
> + PIN_CONF_PACKED(PIN_CONFIG_BIAS_PULL_UP, 0),
> + PIN_CONF_PACKED(PIN_CONFIG_BIAS_PULL_DOWN, 0),
> + PIN_CONF_PACKED(PIN_CONFIG_DRIVE_PUSH_PULL, 0),
> + PIN_CONF_PACKED(PIN_CONFIG_DRIVE_OPEN_DRAIN, 0),
> + PIN_CONF_PACKED(PIN_CONFIG_DRIVE_OPEN_SOURCE, 0),
> + PIN_CONF_PACKED(PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1),
> + PIN_CONF_PACKED(PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0),
> + PIN_CONF_PACKED(PIN_CONFIG_LOW_POWER_MODE, 0),
> + PIN_CONF_PACKED(PIN_CONFIG_OUTPUT, 0),
> + PIN_CONF_PACKED(PIN_CONFIG_OUTPUT, 1),
> +};
> +
> +/*
> + * Parse a pinconf bitmap from a devicetree entry into individual pin
> configs. + * @pinconf: the bitmap containing config bits
> + * @configs: after the function returns contains a pointer to an array of
> + * pin configs
> + * @nconfigs: number of entries of configs
> + */
> +int pinconf_generic_parse_dt_bitmap(unsigned long pinconf,
> + unsigned long *configs,
> + unsigned int *nconfigs)
> +{
> + int bit;
> + int i;
> +
> + *nconfigs = hweight_long(pinconf);
> + configs = kzalloc(*nconfigs * sizeof(unsigned long), GFP_KERNEL);
> +
> + i = 0;
> + while (pinconf && i < *nconfigs) {
> + bit = __ffs(pinconf);
> + pinconf &= ~BIT(i);
> +
> + if (bit > ARRAY_SIZE(conf_map)) {
> + pr_err("%s: unknown bit %d\n", __func__, bit);
> + kfree(configs);
> + return -EINVAL;
> + }
> +
> + configs[i] = conf_map[bit];
> +
> + i++;
> + }
> +
> + return 0;
> +}
> diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h
> index 92c7267..0fab53a 100644
> --- a/drivers/pinctrl/pinconf.h
> +++ b/drivers/pinctrl/pinconf.h
> @@ -123,3 +123,9 @@ static inline void pinconf_generic_dump_config(struct
> pinctrl_dev *pctldev, return;
> }
> #endif
> +
> +#ifdef CONFIG_GENERIC_PINCONF
> +int pinconf_generic_parse_dt_bitmap(unsigned long pinconf,
> + unsigned long *configs,
> + unsigned int *nconfigs);
> +#endif
> diff --git a/drivers/pinctrl/pinctrl-rockchip.c
> b/drivers/pinctrl/pinctrl-rockchip.c index b77e241..88398df 100644
> --- a/drivers/pinctrl/pinctrl-rockchip.c
> +++ b/drivers/pinctrl/pinctrl-rockchip.c
> @@ -40,6 +40,7 @@
> #include <dt-bindings/pinctrl/rockchip.h>
>
> #include "core.h"
> +#include "pinconf.h"
>
> /* GPIO control registers */
> #define GPIO_SWPORT_DR 0x00
> @@ -639,7 +640,8 @@ static int rockchip_pinctrl_parse_groups(struct
> device_node *np, int num;
> int i, j;
> int pin;
> - int bias;
> + int ret;
> + int nconfigs;
>
> dev_dbg(info->dev, "group(%d): %s\n", index, np->name);
>
> @@ -683,22 +685,16 @@ static int rockchip_pinctrl_parse_groups(struct
> device_node *np, grp->func[j] = be32_to_cpu(*list++);
>
> pinconf = be32_to_cpu(*list++);
> - switch(pinconf) {
> - case RK_PINCTRL_NONE:
> - bias = PIN_CONFIG_BIAS_DISABLE;
> - break;
> - case RK_PINCTRL_PULL_PIN_DEFAULT:
> - bias = PIN_CONFIG_BIAS_PULL_PIN_DEFAULT;
> - break;
> - case RK_PINCTRL_PULL_UP:
> - bias = PIN_CONFIG_BIAS_PULL_UP;
> - break;
> - case RK_PINCTRL_PULL_DOWN:
> - bias = PIN_CONFIG_BIAS_PULL_DOWN;
> - break;
> - }
>
> - grp->configs[j] = pinconf_to_config_packed(bias, 0);
> + ret = pinconf_generic_parse_dt_bitmap(pinconf,
> + &grp->configs[j], &nconfigs);

of course this part is obviously wrong, which I just realized, because the
configs array only contains usigned longs

it must be more like
pinconf_generic_parse_dt_bitmap(pinconf, configs, &nconfigs)
...
grp->configs[j] = configs[0];

or better yet simply carrying the created configs without copying entries or
limiting the number of configs


> + if (ret)
> + return ret;
> +
> + if (nconfigs != 1) {
> + pr_err("unexpected number of config entries\n");
> + return -EINVAL;
> + }
> }
>
> return 0;