From fc33ff43134790ef3cb997ed90048a50b4d1b15e Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sat, 14 Jul 2012 15:26:08 +0800 Subject: arm: at91: at91sam9x5: fix gpio number per bank On the at91sam9x5 SoC series, GPIO banks B and D only have 19 and 22 pins. So add a property to set this parameter. Acked-by: Nicolas Ferre Acked-by: Linus Walleij Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD --- Documentation/devicetree/bindings/gpio/gpio_atmel.txt | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/gpio/gpio_atmel.txt b/Documentation/devicetree/bindings/gpio/gpio_atmel.txt index 66efc804806a..85f8c0d084fa 100644 --- a/Documentation/devicetree/bindings/gpio/gpio_atmel.txt +++ b/Documentation/devicetree/bindings/gpio/gpio_atmel.txt @@ -9,6 +9,10 @@ Required properties: unused). - gpio-controller: Marks the device node as a GPIO controller. +optional properties: +- #gpio-lines: Number of gpio if absent 32. + + Example: pioA: gpio@fffff200 { compatible = "atmel,at91rm9200-gpio"; @@ -16,5 +20,6 @@ Example: interrupts = <2 4>; #gpio-cells = <2>; gpio-controller; + #gpio-lines = <19>; }; -- cgit v1.2.3 From 6732ae5cb47c4f9a72727585956f2a5e069d1637 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Thu, 12 Jul 2012 23:35:02 +0800 Subject: ARM: at91: add pinctrl support This is also include the gpio controller as the IP share both. Each soc will have to describe the SoC limitation and pin configuration via DT. This will allow to do not need to touch the C code when adding new SoC if the IP version is supported. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD --- .../bindings/pinctrl/atmel,at91-pinctrl.txt | 136 ++ arch/arm/Kconfig | 2 + arch/arm/mach-at91/board-dt.c | 2 - arch/arm/mach-at91/gpio.c | 165 +-- drivers/pinctrl/Kconfig | 9 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-at91.c | 1490 ++++++++++++++++++++ 7 files changed, 1643 insertions(+), 162 deletions(-) create mode 100644 Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt create mode 100644 drivers/pinctrl/pinctrl-at91.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt new file mode 100644 index 000000000000..20a987e55a28 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt @@ -0,0 +1,136 @@ +* Atmel AT91 Pinmux Controller + +The AT91 Pinmux Controler, 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 +8 muxing options (called periph modes). Since different modules require +different PAD settings (like pull up, keeper, etc) the contoller controls +also the PAD settings parameters. + +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". + +Atmel AT91 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, multi drive, etc. + +Required properties for iomux controller: +- compatible: "atmel,at91rm9200-pinctrl" +- atmel,mux-mask: array of mask (periph per bank) to describe if a pin can be + configured in this periph mode. All the periph and bank need to be describe. + +How to create such array: + +Each column will represent the possible peripheral of the pinctrl +Each line will represent a pio bank + +Take an example on the 9260 +Peripheral: 2 ( A and B) +Bank: 3 (A, B and C) +=> + + /* A B */ + 0xffffffff 0xffc00c3b /* pioA */ + 0xffffffff 0x7fff3ccf /* pioB */ + 0xffffffff 0x007fffff /* pioC */ + +For each peripheral/bank we will descibe in a u32 if a pin can can be +configured in it by putting 1 to the pin bit (1 << pin) + +Let's take the pioA on peripheral B +From the datasheet Table 10-2. +Peripheral B +PA0 MCDB0 +PA1 MCCDB +PA2 +PA3 MCDB3 +PA4 MCDB2 +PA5 MCDB1 +PA6 +PA7 +PA8 +PA9 +PA10 ETX2 +PA11 ETX3 +PA12 +PA13 +PA14 +PA15 +PA16 +PA17 +PA18 +PA19 +PA20 +PA21 +PA22 ETXER +PA23 ETX2 +PA24 ETX3 +PA25 ERX2 +PA26 ERX3 +PA27 ERXCK +PA28 ECRS +PA29 ECOL +PA30 RXD4 +PA31 TXD4 + +=> 0xffc00c3b + +Required properties for pin configuration node: +- atmel,pins: 4 integers array, represents a group of pins mux and config + setting. The format is atmel,pins = . + The PERIPH 0 means gpio. + +Bits used for CONFIG: +PULL_UP(1 << 0): indicate this pin need a pull up. +MULTIDRIVE(1 << 1): indicate this pin need to be configured as multidrive. + +NOTE: +Some requirements for using atmel,at91rm9200-pinctrl binding: +1. We have pin function node defined under at91 controller node to represent + what pinmux functions this SoC supports. +2. The driver can use the function node's name and pin configuration node's + name describe the pin function and group hierarchy. + For example, Linux at91 pinctrl driver takes the function node's name + as the function name and pin configuration node's name as group name to + create the map table. +3. Each pin configuration node should have a phandle, devices can set pins + configurations by referring to the phandle of that pin configuration node. +4. The gpio controller must be describe in the pinctrl simple-bus. + +Examples: + +pinctrl@fffff400 { + #address-cells = <1>; + #size-cells = <1>; + ranges; + compatible = "atmel,at91rm9200-pinctrl", "simple-bus"; + reg = <0xfffff400 0x600>; + + atmel,mux-mask = < + /* A B */ + 0xffffffff 0xffc00c3b /* pioA */ + 0xffffffff 0x7fff3ccf /* pioB */ + 0xffffffff 0x007fffff /* pioC */ + >; + + /* shared pinctrl settings */ + dbgu { + pinctrl_dbgu: dbgu-0 { + atmel,pins = + <1 14 0x1 0x0 /* PB14 periph A */ + 1 15 0x1 0x1>; /* PB15 periph with pullup */ + }; + }; +}; + +dbgu: serial@fffff200 { + compatible = "atmel,at91sam9260-usart"; + reg = <0xfffff200 0x200>; + interrupts = <1 4 7>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_dbgu>; + status = "disabled"; +}; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 431c3753145a..3d7f11fe610d 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -329,6 +329,8 @@ config ARCH_AT91 select IRQ_DOMAIN select NEED_MACH_GPIO_H select NEED_MACH_IO_H if PCCARD + select PINCTRL + select PINCTRL_AT91 if USE_OF help This enables support for systems based on Atmel AT91RM9200 and AT91SAM9* processors. diff --git a/arch/arm/mach-at91/board-dt.c b/arch/arm/mach-at91/board-dt.c index e8f45c4e0ea8..3b6a94820fa0 100644 --- a/arch/arm/mach-at91/board-dt.c +++ b/arch/arm/mach-at91/board-dt.c @@ -30,8 +30,6 @@ static const struct of_device_id irq_of_match[] __initconst = { { .compatible = "atmel,at91rm9200-aic", .data = at91_aic_of_init }, - { .compatible = "atmel,at91rm9200-gpio", .data = at91_gpio_of_irq_setup }, - { .compatible = "atmel,at91sam9x5-gpio", .data = at91_gpio_of_irq_setup }, { /*sentinel*/ } }; diff --git a/arch/arm/mach-at91/gpio.c b/arch/arm/mach-at91/gpio.c index a34f0ed291c0..c5d7e1e9d757 100644 --- a/arch/arm/mach-at91/gpio.c +++ b/arch/arm/mach-at91/gpio.c @@ -23,8 +23,6 @@ #include #include #include -#include -#include #include @@ -717,80 +715,6 @@ postcore_initcall(at91_gpio_debugfs_init); */ static struct lock_class_key gpio_lock_class; -#if defined(CONFIG_OF) -static int at91_gpio_irq_map(struct irq_domain *h, unsigned int virq, - irq_hw_number_t hw) -{ - struct at91_gpio_chip *at91_gpio = h->host_data; - - irq_set_lockdep_class(virq, &gpio_lock_class); - - /* - * Can use the "simple" and not "edge" handler since it's - * shorter, and the AIC handles interrupts sanely. - */ - irq_set_chip_and_handler(virq, &gpio_irqchip, - handle_simple_irq); - set_irq_flags(virq, IRQF_VALID); - irq_set_chip_data(virq, at91_gpio); - - return 0; -} - -static struct irq_domain_ops at91_gpio_ops = { - .map = at91_gpio_irq_map, - .xlate = irq_domain_xlate_twocell, -}; - -int __init at91_gpio_of_irq_setup(struct device_node *node, - struct device_node *parent) -{ - struct at91_gpio_chip *prev = NULL; - int alias_idx = of_alias_get_id(node, "gpio"); - struct at91_gpio_chip *at91_gpio = &gpio_chip[alias_idx]; - - /* Setup proper .irq_set_type function */ - if (has_pio3()) - gpio_irqchip.irq_set_type = alt_gpio_irq_type; - else - gpio_irqchip.irq_set_type = gpio_irq_type; - - /* Disable irqs of this PIO controller */ - __raw_writel(~0, at91_gpio->regbase + PIO_IDR); - - /* Setup irq domain */ - at91_gpio->domain = irq_domain_add_linear(node, at91_gpio->chip.ngpio, - &at91_gpio_ops, at91_gpio); - if (!at91_gpio->domain) - panic("at91_gpio.%d: couldn't allocate irq domain (DT).\n", - at91_gpio->pioc_idx); - - /* Setup chained handler */ - if (at91_gpio->pioc_idx) - prev = &gpio_chip[at91_gpio->pioc_idx - 1]; - - /* The toplevel handler handles one bank of GPIOs, except - * on some SoC it can handles up to three... - * We only set up the handler for the first of the list. - */ - if (prev && prev->next == at91_gpio) - return 0; - - at91_gpio->pioc_virq = irq_create_mapping(irq_find_host(parent), - at91_gpio->pioc_hwirq); - irq_set_chip_data(at91_gpio->pioc_virq, at91_gpio); - irq_set_chained_handler(at91_gpio->pioc_virq, gpio_irq_handler); - - return 0; -} -#else -int __init at91_gpio_of_irq_setup(struct device_node *node, - struct device_node *parent) -{ - return -EINVAL; -} -#endif - /* * irqdomain initialization: pile up irqdomains on top of AIC range */ @@ -989,85 +913,6 @@ err: return -EINVAL; } -#ifdef CONFIG_OF_GPIO -static void __init of_at91_gpio_init_one(struct device_node *np) -{ - int alias_idx; - struct at91_gpio_chip *at91_gpio; - uint32_t ngpio; - - if (!np) - return; - - alias_idx = of_alias_get_id(np, "gpio"); - if (alias_idx >= MAX_GPIO_BANKS) { - pr_err("at91_gpio, failed alias idx(%d) > MAX_GPIO_BANKS(%d), ignoring.\n", - alias_idx, MAX_GPIO_BANKS); - return; - } - - at91_gpio = &gpio_chip[alias_idx]; - at91_gpio->chip.base = alias_idx * MAX_NB_GPIO_PER_BANK; - - at91_gpio->regbase = of_iomap(np, 0); - if (!at91_gpio->regbase) { - pr_err("at91_gpio.%d, failed to map registers, ignoring.\n", - alias_idx); - return; - } - - /* Get the interrupts property */ - if (of_property_read_u32(np, "interrupts", &at91_gpio->pioc_hwirq)) { - pr_err("at91_gpio.%d, failed to get interrupts property, ignoring.\n", - alias_idx); - goto ioremap_err; - } - - /* Get capabilities from compatibility property */ - if (of_device_is_compatible(np, "atmel,at91sam9x5-gpio")) - at91_gpio_caps |= AT91_GPIO_CAP_PIO3; - - if (!of_property_read_u32(np, "#gpio-lines", &ngpio)) { - if (ngpio >= MAX_NB_GPIO_PER_BANK) - pr_err("at91_gpio.%d, gpio-nb >= %d failback to %d\n", - alias_idx, MAX_NB_GPIO_PER_BANK, MAX_NB_GPIO_PER_BANK); - else - at91_gpio->chip.ngpio = ngpio; - } - - /* Setup clock */ - if (at91_gpio_setup_clk(alias_idx)) - goto ioremap_err; - - at91_gpio->chip.of_node = np; - gpio_banks = max(gpio_banks, alias_idx + 1); - at91_gpio->pioc_idx = alias_idx; - return; - -ioremap_err: - iounmap(at91_gpio->regbase); -} - -static int __init of_at91_gpio_init(void) -{ - struct device_node *np = NULL; - - /* - * This isn't ideal, but it gets things hooked up until this - * driver is converted into a platform_device - */ - for_each_compatible_node(np, NULL, "atmel,at91rm9200-gpio") - of_at91_gpio_init_one(np); - - return gpio_banks > 0 ? 0 : -EINVAL; -} -#else -static int __init of_at91_gpio_init(void) -{ - return -EINVAL; -} -#endif - static void __init at91_gpio_init_one(int idx, u32 regbase, int pioc_hwirq) { struct at91_gpio_chip *at91_gpio = &gpio_chip[idx]; @@ -1102,11 +947,11 @@ void __init at91_gpio_init(struct at91_gpio_bank *data, int nr_banks) BUG_ON(nr_banks > MAX_GPIO_BANKS); - if (of_at91_gpio_init() < 0) { - /* No GPIO controller found in device tree */ - for (i = 0; i < nr_banks; i++) - at91_gpio_init_one(i, data[i].regbase, data[i].id); - } + if (of_have_populated_dt()) + return; + + for (i = 0; i < nr_banks; i++) + at91_gpio_init_one(i, data[i].regbase, data[i].id); for (i = 0; i < gpio_banks; i++) { at91_gpio = &gpio_chip[i]; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 7bf914df6e91..4787f0e4597d 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -26,6 +26,15 @@ config DEBUG_PINCTRL help Say Y here to add some extra checks and diagnostics to PINCTRL calls. +config PINCTRL_AT91 + bool "AT91 pinctrl driver" + depends on OF + depends on ARCH_AT91 + select PINMUX + select PINCONF + help + Say Y here to enable the at91 pinctrl driver + config PINCTRL_BCM2835 bool select PINMUX diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index f395ba5cec25..78a191c85adc 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -9,6 +9,7 @@ ifeq ($(CONFIG_OF),y) obj-$(CONFIG_PINCTRL) += devicetree.o endif obj-$(CONFIG_GENERIC_PINCONF) += pinconf-generic.o +obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o obj-$(CONFIG_PINCTRL_BCM2835) += pinctrl-bcm2835.o obj-$(CONFIG_PINCTRL_IMX) += pinctrl-imx.o obj-$(CONFIG_PINCTRL_IMX35) += pinctrl-imx35.o diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c new file mode 100644 index 000000000000..01bf92459fdc --- /dev/null +++ b/drivers/pinctrl/pinctrl-at91.c @@ -0,0 +1,1490 @@ +/* + * at91 pinctrl driver based on at91 pinmux core + * + * Copyright (C) 2011-2012 Jean-Christophe PLAGNIOL-VILLARD + * + * Under GPLv2 only + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* Since we request GPIOs from ourself */ +#include + +#include + +#include +#include + +#include "core.h" + +#define MAX_NB_GPIO_PER_BANK 32 + +struct at91_pinctrl_mux_ops; + +struct at91_gpio_chip { + struct gpio_chip chip; + struct pinctrl_gpio_range range; + struct at91_gpio_chip *next; /* Bank sharing same clock */ + int pioc_hwirq; /* PIO bank interrupt identifier on AIC */ + int pioc_virq; /* PIO bank Linux virtual interrupt */ + int pioc_idx; /* PIO bank index */ + void __iomem *regbase; /* PIO bank virtual address */ + struct clk *clock; /* associated clock */ + struct irq_domain *domain; /* associated irq domain */ + struct at91_pinctrl_mux_ops *ops; /* ops */ +}; + +#define to_at91_gpio_chip(c) container_of(c, struct at91_gpio_chip, chip) + +static struct at91_gpio_chip *gpio_chips[MAX_GPIO_BANKS]; + +static int gpio_banks; + +#define PULL_UP (0 << 1) +#define MULTI_DRIVE (1 << 1) + +/** + * struct at91_pmx_func - describes AT91 pinmux functions + * @name: the name of this specific function + * @groups: corresponding pin groups + * @ngroups: the number of groups + */ +struct at91_pmx_func { + const char *name; + const char **groups; + unsigned ngroups; +}; + +enum at91_mux { + AT91_MUX_GPIO = 0, + AT91_MUX_PERIPH_A = 1, + AT91_MUX_PERIPH_B = 2, + AT91_MUX_PERIPH_C = 3, + AT91_MUX_PERIPH_D = 4, +}; + +/** + * struct at91_pmx_pin - describes an At91 pin mux + * @bank: the bank of the pin + * @pin: the pin number in the @bank + * @mux: the mux mode : gpio or periph_x of the pin i.e. alternate function. + * @conf: the configuration of the pin: PULL_UP, MULTIDRIVE etc... + */ +struct at91_pmx_pin { + uint32_t bank; + uint32_t pin; + enum at91_mux mux; + unsigned long conf; +}; + +/** + * struct at91_pin_group - describes an At91 pin group + * @name: the name of this specific pin group + * @pins_conf: the mux mode for each pin in this group. The size of this + * array is the same as pins. + * @pins: an array of discrete physical pins used in this group, taken + * from the driver-local pin enumeration space + * @npins: the number of pins in this group array, i.e. the number of + * elements in .pins so we can iterate over that array + */ +struct at91_pin_group { + const char *name; + struct at91_pmx_pin *pins_conf; + unsigned int *pins; + unsigned npins; +}; + +/** + * struct at91_pinctrl_mux_ops - describes an At91 mux ops group + * on new IP with support for periph C and D the way to mux in + * periph A and B has changed + * So provide the right call back + * if not present means the IP does not support it + * @get_periph: return the periph mode configured + * @mux_A_periph: mux as periph A + * @mux_B_periph: mux as periph B + * @mux_C_periph: mux as periph C + * @mux_D_periph: mux as periph D + * @irq_type: return irq type + */ +struct at91_pinctrl_mux_ops { + enum at91_mux (*get_periph)(void __iomem *pio, unsigned mask); + void (*mux_A_periph)(void __iomem *pio, unsigned mask); + void (*mux_B_periph)(void __iomem *pio, unsigned mask); + void (*mux_C_periph)(void __iomem *pio, unsigned mask); + void (*mux_D_periph)(void __iomem *pio, unsigned mask); + /* irq */ + int (*irq_type)(struct irq_data *d, unsigned type); +}; + +static int gpio_irq_type(struct irq_data *d, unsigned type); +static int alt_gpio_irq_type(struct irq_data *d, unsigned type); + +struct at91_pinctrl { + struct device *dev; + struct pinctrl_dev *pctl; + + int nbanks; + + uint32_t *mux_mask; + int nmux; + + struct at91_pmx_func *functions; + int nfunctions; + + struct at91_pin_group *groups; + int ngroups; + + struct at91_pinctrl_mux_ops *ops; +}; + +static const inline struct at91_pin_group *at91_pinctrl_find_group_by_name( + const struct at91_pinctrl *info, + const char *name) +{ + const struct at91_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]; + dev_dbg(info->dev, "%s: %d 0:%d\n", name, grp->npins, grp->pins[0]); + break; + } + + return grp; +} + +static int at91_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->ngroups; +} + +static const char *at91_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->groups[selector].name; +} + +static int at91_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector, + const unsigned **pins, + unsigned *npins) +{ + struct at91_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 void at91_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, + unsigned offset) +{ + seq_printf(s, "%s", dev_name(pctldev->dev)); +} + +static int at91_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, unsigned *num_maps) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + const struct at91_pin_group *grp; + struct pinctrl_map *new_map; + struct device_node *parent; + int map_num = 1; + int i; + struct at91_pmx_pin *pin; + + /* + * first find the group of this node and check if we need create + * config maps for pins + */ + grp = at91_pinctrl_find_group_by_name(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) { + kfree(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++) { + pin = &grp->pins_conf[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->pins_conf[i].conf; + 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 at91_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps) +{ +} + +static struct pinctrl_ops at91_pctrl_ops = { + .get_groups_count = at91_get_groups_count, + .get_group_name = at91_get_group_name, + .get_group_pins = at91_get_group_pins, + .pin_dbg_show = at91_pin_dbg_show, + .dt_node_to_map = at91_dt_node_to_map, + .dt_free_map = at91_dt_free_map, +}; + +static void __iomem * pin_to_controller(struct at91_pinctrl *info, + unsigned int bank) +{ + return gpio_chips[bank]->regbase; +} + +static inline int pin_to_bank(unsigned pin) +{ + return pin /= MAX_NB_GPIO_PER_BANK; +} + +static unsigned pin_to_mask(unsigned int pin) +{ + return 1 << pin; +} + +static void at91_mux_disable_interrupt(void __iomem *pio, unsigned mask) +{ + writel_relaxed(mask, pio + PIO_IDR); +} + +static unsigned at91_mux_get_pullup(void __iomem *pio, unsigned pin) +{ + return (readl_relaxed(pio + PIO_PUSR) >> pin) & 0x1; +} + +static void at91_mux_set_pullup(void __iomem *pio, unsigned mask, bool on) +{ + writel_relaxed(mask, pio + (on ? PIO_PUER : PIO_PUDR)); +} + +static unsigned at91_mux_get_multidrive(void __iomem *pio, unsigned pin) +{ + return (readl_relaxed(pio + PIO_MDSR) >> pin) & 0x1; +} + +static void at91_mux_set_multidrive(void __iomem *pio, unsigned mask, bool on) +{ + writel_relaxed(mask, pio + (on ? PIO_MDER : PIO_MDDR)); +} + +static void at91_mux_set_A_periph(void __iomem *pio, unsigned mask) +{ + writel_relaxed(mask, pio + PIO_ASR); +} + +static void at91_mux_set_B_periph(void __iomem *pio, unsigned mask) +{ + writel_relaxed(mask, pio + PIO_BSR); +} + +static void at91_mux_pio3_set_A_periph(void __iomem *pio, unsigned mask) +{ + + writel_relaxed(readl_relaxed(pio + PIO_ABCDSR1) & ~mask, + pio + PIO_ABCDSR1); + writel_relaxed(readl_relaxed(pio + PIO_ABCDSR2) & ~mask, + pio + PIO_ABCDSR2); +} + +static void at91_mux_pio3_set_B_periph(void __iomem *pio, unsigned mask) +{ + writel_relaxed(readl_relaxed(pio + PIO_ABCDSR1) | mask, + pio + PIO_ABCDSR1); + writel_relaxed(readl_relaxed(pio + PIO_ABCDSR2) & ~mask, + pio + PIO_ABCDSR2); +} + +static void at91_mux_pio3_set_C_periph(void __iomem *pio, unsigned mask) +{ + writel_relaxed(readl_relaxed(pio + PIO_ABCDSR1) & ~mask, pio + PIO_ABCDSR1); + writel_relaxed(readl_relaxed(pio + PIO_ABCDSR2) | mask, pio + PIO_ABCDSR2); +} + +static void at91_mux_pio3_set_D_periph(void __iomem *pio, unsigned mask) +{ + writel_relaxed(readl_relaxed(pio + PIO_ABCDSR1) | mask, pio + PIO_ABCDSR1); + writel_relaxed(readl_relaxed(pio + PIO_ABCDSR2) | mask, pio + PIO_ABCDSR2); +} + +static enum at91_mux at91_mux_pio3_get_periph(void __iomem *pio, unsigned mask) +{ + unsigned select; + + if (readl_relaxed(pio + PIO_PSR) & mask) + return AT91_MUX_GPIO; + + select = !!(readl_relaxed(pio + PIO_ABCDSR1) & mask); + select |= (!!(readl_relaxed(pio + PIO_ABCDSR2) & mask) << 1); + + return select + 1; +} + +static enum at91_mux at91_mux_get_periph(void __iomem *pio, unsigned mask) +{ + unsigned select; + + if (readl_relaxed(pio + PIO_PSR) & mask) + return AT91_MUX_GPIO; + + select = readl_relaxed(pio + PIO_ABSR) & mask; + + return select + 1; +} + +static struct at91_pinctrl_mux_ops at91rm9200_ops = { + .get_periph = at91_mux_get_periph, + .mux_A_periph = at91_mux_set_A_periph, + .mux_B_periph = at91_mux_set_B_periph, + .irq_type = gpio_irq_type, +}; + +static struct at91_pinctrl_mux_ops at91sam9x5_ops = { + .get_periph = at91_mux_pio3_get_periph, + .mux_A_periph = at91_mux_pio3_set_A_periph, + .mux_B_periph = at91_mux_pio3_set_B_periph, + .mux_C_periph = at91_mux_pio3_set_C_periph, + .mux_D_periph = at91_mux_pio3_set_D_periph, + .irq_type = alt_gpio_irq_type, +}; + +static void at91_pin_dbg(const struct device *dev, const struct at91_pmx_pin *pin) +{ + if (pin->mux) { + dev_dbg(dev, "pio%c%d configured as periph%c with conf = 0x%lu\n", + pin->bank + 'A', pin->pin, pin->mux - 1 + 'A', pin->conf); + } else { + dev_dbg(dev, "pio%c%d configured as gpio with conf = 0x%lu\n", + pin->bank + 'A', pin->pin, pin->conf); + } +} + +static int pin_check_config(struct at91_pinctrl *info, const char* name, + int index, const struct at91_pmx_pin *pin) +{ + int mux; + + /* check if it's a valid config */ + if (pin->bank >= info->nbanks) { + dev_err(info->dev, "%s: pin conf %d bank_id %d >= nbanks %d\n", + name, index, pin->bank, info->nbanks); + return -EINVAL; + } + + if (pin->pin >= MAX_NB_GPIO_PER_BANK) { + dev_err(info->dev, "%s: pin conf %d pin_bank_id %d >= %d\n", + name, index, pin->pin, MAX_NB_GPIO_PER_BANK); + return -EINVAL; + } + + if (!pin->mux) + return 0; + + mux = pin->mux - 1; + + if (mux >= info->nmux) { + dev_err(info->dev, "%s: pin conf %d mux_id %d >= nmux %d\n", + name, index, mux, info->nmux); + return -EINVAL; + } + + if (!(info->mux_mask[pin->bank * info->nmux + mux] & 1 << pin->pin)) { + dev_err(info->dev, "%s: pin conf %d mux_id %d not supported for pio%c%d\n", + name, index, mux, pin->bank + 'A', pin->pin); + return -EINVAL; + } + + return 0; +} + +static void at91_mux_gpio_disable(void __iomem *pio, unsigned mask) +{ + writel_relaxed(mask, pio + PIO_PDR); +} + +static void at91_mux_gpio_enable(void __iomem *pio, unsigned mask, bool input) +{ + writel_relaxed(mask, pio + PIO_PER); + writel_relaxed(mask, pio + (input ? PIO_ODR : PIO_OER)); +} + +static int at91_pmx_enable(struct pinctrl_dev *pctldev, unsigned selector, + unsigned group) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + const struct at91_pmx_pin *pins_conf = info->groups[group].pins_conf; + const struct at91_pmx_pin *pin; + uint32_t npins = info->groups[group].npins; + int i, ret; + unsigned mask; + void __iomem *pio; + + dev_dbg(info->dev, "enable function %s group %s\n", + info->functions[selector].name, info->groups[group].name); + + /* first check that all the pins of the group are valid with a valid + * paramter */ + for (i = 0; i < npins; i++) { + pin = &pins_conf[i]; + ret = pin_check_config(info, info->groups[group].name, i, pin); + if (ret) + return ret; + } + + for (i = 0; i < npins; i++) { + pin = &pins_conf[i]; + at91_pin_dbg(info->dev, pin); + pio = pin_to_controller(info, pin->bank); + mask = pin_to_mask(pin->pin); + at91_mux_disable_interrupt(pio, mask); + switch(pin->mux) { + case AT91_MUX_GPIO: + at91_mux_gpio_enable(pio, mask, 1); + break; + case AT91_MUX_PERIPH_A: + info->ops->mux_A_periph(pio, mask); + break; + case AT91_MUX_PERIPH_B: + info->ops->mux_B_periph(pio, mask); + break; + case AT91_MUX_PERIPH_C: + if (!info->ops->mux_C_periph) + return -EINVAL; + info->ops->mux_C_periph(pio, mask); + break; + case AT91_MUX_PERIPH_D: + if (!info->ops->mux_D_periph) + return -EINVAL; + info->ops->mux_D_periph(pio, mask); + break; + } + if (pin->mux) + at91_mux_gpio_disable(pio, mask); + } + + return 0; +} + +static void at91_pmx_disable(struct pinctrl_dev *pctldev, unsigned selector, + unsigned group) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + const struct at91_pmx_pin *pins_conf = info->groups[group].pins_conf; + const struct at91_pmx_pin *pin; + uint32_t npins = info->groups[group].npins; + int i; + unsigned mask; + void __iomem *pio; + + for (i = 0; i < npins; i++) { + pin = &pins_conf[i]; + at91_pin_dbg(info->dev, pin); + pio = pin_to_controller(info, pin->bank); + mask = pin_to_mask(pin->pin); + at91_mux_gpio_enable(pio, mask, 1); + } +} + +static int at91_pmx_get_funcs_count(struct pinctrl_dev *pctldev) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->nfunctions; +} + +static const char *at91_pmx_get_func_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->functions[selector].name; +} + +static int at91_pmx_get_groups(struct pinctrl_dev *pctldev, unsigned selector, + const char * const **groups, + unsigned * const num_groups) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + *groups = info->functions[selector].groups; + *num_groups = info->functions[selector].ngroups; + + return 0; +} + +int at91_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset) +{ + struct at91_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev); + struct at91_gpio_chip *at91_chip; + struct gpio_chip *chip; + unsigned mask; + + if (!range) { + dev_err(npct->dev, "invalid range\n"); + return -EINVAL; + } + if (!range->gc) { + dev_err(npct->dev, "missing GPIO chip in range\n"); + return -EINVAL; + } + chip = range->gc; + at91_chip = container_of(chip, struct at91_gpio_chip, chip); + + dev_dbg(npct->dev, "enable pin %u as GPIO\n", offset); + + mask = 1 << (offset - chip->base); + + dev_dbg(npct->dev, "enable pin %u as PIO%c%d 0x%x\n", + offset, 'A' + range->id, offset - chip->base, mask); + + writel_relaxed(mask, at91_chip->regbase + PIO_PER); + + return 0; +} + +void at91_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset) +{ + struct at91_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev); + + dev_dbg(npct->dev, "disable pin %u as GPIO\n", offset); + /* Set the pin to some default state, GPIO is usually default */ +} + +static struct pinmux_ops at91_pmx_ops = { + .get_functions_count = at91_pmx_get_funcs_count, + .get_function_name = at91_pmx_get_func_name, + .get_function_groups = at91_pmx_get_groups, + .enable = at91_pmx_enable, + .disable = at91_pmx_disable, + .gpio_request_enable = at91_gpio_request_enable, + .gpio_disable_free = at91_gpio_disable_free, +}; + +static int at91_pinconf_get(struct pinctrl_dev *pctldev, + unsigned pin_id, unsigned long *config) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + void __iomem *pio; + unsigned pin; + + dev_dbg(info->dev, "%s:%d, pin_id=%d, config=0x%lx", __func__, __LINE__, pin_id, *config); + pio = pin_to_controller(info, pin_to_bank(pin_id)); + pin = pin_id % MAX_NB_GPIO_PER_BANK; + + if (at91_mux_get_multidrive(pio, pin)) + *config |= MULTI_DRIVE; + + if (at91_mux_get_pullup(pio, pin)) + *config |= PULL_UP; + + return 0; +} + +static int at91_pinconf_set(struct pinctrl_dev *pctldev, + unsigned pin_id, unsigned long config) +{ + struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + unsigned mask; + void __iomem *pio; + + dev_dbg(info->dev, "%s:%d, pin_id=%d, config=0x%lx", __func__, __LINE__, pin_id, config); + pio = pin_to_controller(info, pin_to_bank(pin_id)); + mask = pin_to_mask(pin_id % MAX_NB_GPIO_PER_BANK); + + at91_mux_set_pullup(pio, mask, config & PULL_UP); + at91_mux_set_multidrive(pio, mask, config & MULTI_DRIVE); + return 0; +} + +static void at91_pinconf_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned pin_id) +{ + +} + +static void at91_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned group) +{ +} + +struct pinconf_ops at91_pinconf_ops = { + .pin_config_get = at91_pinconf_get, + .pin_config_set = at91_pinconf_set, + .pin_config_dbg_show = at91_pinconf_dbg_show, + .pin_config_group_dbg_show = at91_pinconf_group_dbg_show, +}; + +static struct pinctrl_desc at91_pinctrl_desc = { + .pctlops = &at91_pctrl_ops, + .pmxops = &at91_pmx_ops, + .confops = &at91_pinconf_ops, + .owner = THIS_MODULE, +}; + +static const char *gpio_compat = "atmel,at91rm9200-gpio"; + +static void __devinit at91_pinctrl_child_count(struct at91_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)) { + info->nbanks++; + } else { + info->nfunctions++; + info->ngroups += of_get_child_count(child); + } + } +} + +static int __devinit at91_pinctrl_mux_mask(struct at91_pinctrl *info, + struct device_node *np) +{ + int ret = 0; + int size; + const const __be32 *list; + + list = of_get_property(np, "atmel,mux-mask", &size); + if (!list) { + dev_err(info->dev, "can not read the mux-mask of %d\n", size); + return -EINVAL; + } + + size /= sizeof(*list); + if (!size || size % info->nbanks) { + dev_err(info->dev, "wrong mux mask array should be by %d\n", info->nbanks); + return -EINVAL; + } + info->nmux = size / info->nbanks; + + info->mux_mask = devm_kzalloc(info->dev, sizeof(u32) * size, GFP_KERNEL); + if (!info->mux_mask) { + dev_err(info->dev, "could not alloc mux_mask\n"); + return -ENOMEM; + } + + ret = of_property_read_u32_array(np, "atmel,mux-mask", + info->mux_mask, size); + if (ret) + dev_err(info->dev, "can not read the mux-mask of %d\n", size); + return ret; +} + +static int __devinit at91_pinctrl_parse_groups(struct device_node *np, + struct at91_pin_group *grp, + struct at91_pinctrl *info, + u32 index) +{ + struct at91_pmx_pin *pin; + int size; + const const __be32 *list; + int i, j; + + dev_dbg(info->dev, "group(%d): %s\n", index, np->name); + + /* Initialise group */ + grp->name = np->name; + + /* + * the binding format is atmel,pins = , + * do sanity check and calculate pins number + */ + list = of_get_property(np, "atmel,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; + pin = grp->pins_conf = devm_kzalloc(info->dev, grp->npins * sizeof(struct at91_pmx_pin), + GFP_KERNEL); + grp->pins = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int), + GFP_KERNEL); + if (!grp->pins_conf || !grp->pins) + return -ENOMEM; + + for (i = 0, j = 0; i < size; i += 4, j++) { + pin->bank = be32_to_cpu(*list++); + pin->pin = be32_to_cpu(*list++); + grp->pins[j] = pin->bank * MAX_NB_GPIO_PER_BANK + pin->pin; + pin->mux = be32_to_cpu(*list++); + pin->conf = be32_to_cpu(*list++); + + at91_pin_dbg(info->dev, pin); + pin++; + } + + return 0; +} + +static int __devinit at91_pinctrl_parse_functions(struct device_node *np, + struct at91_pinctrl *info, u32 index) +{ + struct device_node *child; + struct at91_pmx_func *func; + struct at91_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\n"); + 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 = at91_pinctrl_parse_groups(child, grp, info, i++); + if (ret) + return ret; + } + + return 0; +} + +static struct of_device_id at91_pinctrl_of_match[] __devinitdata = { + { .compatible = "atmel,at91sam9x5-pinctrl", .data = &at91sam9x5_ops }, + { .compatible = "atmel,at91rm9200-pinctrl", .data = &at91rm9200_ops }, + { /* sentinel */ } +}; + +static int __devinit at91_pinctrl_probe_dt(struct platform_device *pdev, + struct at91_pinctrl *info) +{ + int ret = 0; + int i, j; + uint32_t *tmp; + struct device_node *np = pdev->dev.of_node; + struct device_node *child; + + if (!np) + return -ENODEV; + + info->dev = &pdev->dev; + info->ops = + of_match_device(at91_pinctrl_of_match, &pdev->dev)->data; + at91_pinctrl_child_count(info, np); + + if (info->nbanks < 1) { + dev_err(&pdev->dev, "you need to specify atleast one gpio-controller\n"); + return -EINVAL; + } + + ret = at91_pinctrl_mux_mask(info, np); + if (ret) + return ret; + + dev_dbg(&pdev->dev, "nmux = %d\n", info->nmux); + + dev_dbg(&pdev->dev, "mux-mask\n"); + tmp = info->mux_mask; + for (i = 0; i < info->nbanks; i++) { + for (j = 0; j < info->nmux; j++, tmp++) { + dev_dbg(&pdev->dev, "%d:%d\t0x%x\n", i, j, tmp[0]); + } + } + + dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions); + dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups); + info->functions = devm_kzalloc(&pdev->dev, info->nfunctions * sizeof(struct at91_pmx_func), + GFP_KERNEL); + if (!info->functions) + return -ENOMEM; + + info->groups = devm_kzalloc(&pdev->dev, info->ngroups * sizeof(struct at91_pin_group), + GFP_KERNEL); + if (!info->groups) + return -ENOMEM; + + dev_dbg(&pdev->dev, "nbanks = %d\n", info->nbanks); + dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions); + dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups); + + i = 0; + + for_each_child_of_node(np, child) { + if (of_device_is_compatible(child, gpio_compat)) + continue; + ret = at91_pinctrl_parse_functions(child, info, i++); + if (ret) { + dev_err(&pdev->dev, "failed to parse function\n"); + return ret; + } + } + + return 0; +} + +static int __devinit at91_pinctrl_probe(struct platform_device *pdev) +{ + struct at91_pinctrl *info; + struct pinctrl_pin_desc *pdesc; + int ret, i, j ,k; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + ret = at91_pinctrl_probe_dt(pdev, info); + if (ret) + return ret; + + /* + * We need all the GPIO drivers to probe FIRST, or we will not be able + * to obtain references to the struct gpio_chip * for them, and we + * need this to proceed. + */ + for (i = 0; i < info->nbanks; i++) { + if (!gpio_chips[i]) { + dev_warn(&pdev->dev, "GPIO chip %d not registered yet\n", i); + devm_kfree(&pdev->dev, info); + return -EPROBE_DEFER; + } + } + + at91_pinctrl_desc.name = dev_name(&pdev->dev); + at91_pinctrl_desc.npins = info->nbanks * MAX_NB_GPIO_PER_BANK; + at91_pinctrl_desc.pins = pdesc = + devm_kzalloc(&pdev->dev, sizeof(*pdesc) * at91_pinctrl_desc.npins, GFP_KERNEL); + + if (!at91_pinctrl_desc.pins) + return -ENOMEM; + + for (i = 0 , k = 0; i < info->nbanks; i++) { + for (j = 0; j < MAX_NB_GPIO_PER_BANK; j++, k++) { + pdesc->number = k; + pdesc->name = kasprintf(GFP_KERNEL, "pio%c%d", i + 'A', j); + pdesc++; + } + } + + platform_set_drvdata(pdev, info); + info->pctl = pinctrl_register(&at91_pinctrl_desc, &pdev->dev, info); + + if (!info->pctl) { + dev_err(&pdev->dev, "could not register AT91 pinctrl driver\n"); + ret = -EINVAL; + goto err; + } + + /* We will handle a range of GPIO pins */ + for (i = 0; i < info->nbanks; i++) + pinctrl_add_gpio_range(info->pctl, &gpio_chips[i]->range); + + dev_info(&pdev->dev, "initialized AT91 pinctrl driver\n"); + + return 0; + +err: + return ret; +} + +int __devexit at91_pinctrl_remove(struct platform_device *pdev) +{ + struct at91_pinctrl *info = platform_get_drvdata(pdev); + + pinctrl_unregister(info->pctl); + + return 0; +} + +static int at91_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + /* + * Map back to global GPIO space and request muxing, the direction + * parameter does not matter for this controller. + */ + int gpio = chip->base + offset; + int bank = chip->base / chip->ngpio; + + dev_dbg(chip->dev, "%s:%d pio%c%d(%d)\n", __func__, __LINE__, + 'A' + bank, offset, gpio); + + return pinctrl_request_gpio(gpio); +} + +static void at91_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + int gpio = chip->base + offset; + + pinctrl_free_gpio(gpio); +} + +static int at91_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << offset; + + writel_relaxed(mask, pio + PIO_ODR); + return 0; +} + +static int at91_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << offset; + u32 pdsr; + + pdsr = readl_relaxed(pio + PIO_PDSR); + return (pdsr & mask) != 0; +} + +static void at91_gpio_set(struct gpio_chip *chip, unsigned offset, + int val) +{ + struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << offset; + + writel_relaxed(mask, pio + (val ? PIO_SODR : PIO_CODR)); +} + +static int at91_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int val) +{ + struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << offset; + + writel_relaxed(mask, pio + (val ? PIO_SODR : PIO_CODR)); + writel_relaxed(mask, pio + PIO_OER); + + return 0; +} + +static int at91_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); + int virq; + + if (offset < chip->ngpio) + virq = irq_create_mapping(at91_gpio->domain, offset); + else + virq = -ENXIO; + + dev_dbg(chip->dev, "%s: request IRQ for GPIO %d, return %d\n", + chip->label, offset + chip->base, virq); + return virq; +} + +#ifdef CONFIG_DEBUG_FS +static void at91_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + enum at91_mux mode; + int i; + struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); + void __iomem *pio = at91_gpio->regbase; + + for (i = 0; i < chip->ngpio; i++) { + unsigned pin = chip->base + i; + unsigned mask = pin_to_mask(pin); + const char *gpio_label; + u32 pdsr; + + gpio_label = gpiochip_is_requested(chip, i); + if (!gpio_label) + continue; + mode = at91_gpio->ops->get_periph(pio, mask); + seq_printf(s, "[%s] GPIO%s%d: ", + gpio_label, chip->label, i); + if (mode == AT91_MUX_GPIO) { + pdsr = readl_relaxed(pio + PIO_PDSR); + + seq_printf(s, "[gpio] %s\n", + pdsr & mask ? + "set" : "clear"); + } else { + seq_printf(s, "[periph %c]\n", + mode + 'A' - 1); + } + } +} +#else +#define at91_gpio_dbg_show NULL +#endif + +/* Several AIC controller irqs are dispatched through this GPIO handler. + * To use any AT91_PIN_* as an externally triggered IRQ, first call + * at91_set_gpio_input() then maybe enable its glitch filter. + * Then just request_irq() with the pin ID; it works like any ARM IRQ + * handler. + * First implementation always triggers on rising and falling edges + * whereas the newer PIO3 can be additionally configured to trigger on + * level, edge with any polarity. + * + * Alternatively, certain pins may be used directly as IRQ0..IRQ6 after + * configuring them with at91_set_a_periph() or at91_set_b_periph(). + * IRQ0..IRQ6 should be configurable, e.g. level vs edge triggering. + */ + +static void gpio_irq_mask(struct irq_data *d) +{ + struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << d->hwirq; + + if (pio) + writel_relaxed(mask, pio + PIO_IDR); +} + +static void gpio_irq_unmask(struct irq_data *d) +{ + struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << d->hwirq; + + if (pio) + writel_relaxed(mask, pio + PIO_IER); +} + +static int gpio_irq_type(struct irq_data *d, unsigned type) +{ + switch (type) { + case IRQ_TYPE_NONE: + case IRQ_TYPE_EDGE_BOTH: + return 0; + default: + return -EINVAL; + } +} + +/* Alternate irq type for PIO3 support */ +static int alt_gpio_irq_type(struct irq_data *d, unsigned type) +{ + struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << d->hwirq; + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + writel_relaxed(mask, pio + PIO_ESR); + writel_relaxed(mask, pio + PIO_REHLSR); + break; + case IRQ_TYPE_EDGE_FALLING: + writel_relaxed(mask, pio + PIO_ESR); + writel_relaxed(mask, pio + PIO_FELLSR); + break; + case IRQ_TYPE_LEVEL_LOW: + writel_relaxed(mask, pio + PIO_LSR); + writel_relaxed(mask, pio + PIO_FELLSR); + break; + case IRQ_TYPE_LEVEL_HIGH: + writel_relaxed(mask, pio + PIO_LSR); + writel_relaxed(mask, pio + PIO_REHLSR); + break; + case IRQ_TYPE_EDGE_BOTH: + /* + * disable additional interrupt modes: + * fall back to default behavior + */ + writel_relaxed(mask, pio + PIO_AIMDR); + return 0; + case IRQ_TYPE_NONE: + default: + pr_warn("AT91: No type for irq %d\n", gpio_to_irq(d->irq)); + return -EINVAL; + } + + /* enable additional interrupt modes */ + writel_relaxed(mask, pio + PIO_AIMER); + + return 0; +} + +#ifdef CONFIG_PM +static int gpio_irq_set_wake(struct irq_data *d, unsigned state) +{ + struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); + unsigned bank = at91_gpio->pioc_idx; + + if (unlikely(bank >= MAX_GPIO_BANKS)) + return -EINVAL; + + irq_set_irq_wake(at91_gpio->pioc_virq, state); + + return 0; +} +#else +#define gpio_irq_set_wake NULL +#endif + +static struct irq_chip gpio_irqchip = { + .name = "GPIO", + .irq_disable = gpio_irq_mask, + .irq_mask = gpio_irq_mask, + .irq_unmask = gpio_irq_unmask, + /* .irq_set_type is set dynamically */ + .irq_set_wake = gpio_irq_set_wake, +}; + +static void gpio_irq_handler(unsigned irq, struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct irq_data *idata = irq_desc_get_irq_data(desc); + struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(idata); + void __iomem *pio = at91_gpio->regbase; + unsigned long isr; + int n; + + chained_irq_enter(chip, desc); + for (;;) { + /* Reading ISR acks pending (edge triggered) GPIO interrupts. + * When there none are pending, we're finished unless we need + * to process multiple banks (like ID_PIOCDE on sam9263). + */ + isr = readl_relaxed(pio + PIO_ISR) & readl_relaxed(pio + PIO_IMR); + if (!isr) { + if (!at91_gpio->next) + break; + at91_gpio = at91_gpio->next; + pio = at91_gpio->regbase; + continue; + } + + n = find_first_bit(&isr, BITS_PER_LONG); + while (n < BITS_PER_LONG) { + generic_handle_irq(irq_find_mapping(at91_gpio->domain, n)); + n = find_next_bit(&isr, BITS_PER_LONG, n + 1); + } + } + chained_irq_exit(chip, desc); + /* now it may re-trigger */ +} + +/* + * This lock class tells lockdep that GPIO irqs are in a different + * category than their parents, so it won't report false recursion. + */ +static struct lock_class_key gpio_lock_class; + +static int at91_gpio_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct at91_gpio_chip *at91_gpio = h->host_data; + + irq_set_lockdep_class(virq, &gpio_lock_class); + + /* + * Can use the "simple" and not "edge" handler since it's + * shorter, and the AIC handles interrupts sanely. + */ + irq_set_chip_and_handler(virq, &gpio_irqchip, + handle_simple_irq); + set_irq_flags(virq, IRQF_VALID); + irq_set_chip_data(virq, at91_gpio); + + return 0; +} + +static struct irq_domain_ops at91_gpio_ops = { + .map = at91_gpio_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +static int at91_gpio_of_irq_setup(struct device_node *node, + struct at91_gpio_chip *at91_gpio) +{ + struct at91_gpio_chip *prev = NULL; + struct irq_data *d = irq_get_irq_data(at91_gpio->pioc_virq); + + at91_gpio->pioc_hwirq = irqd_to_hwirq(d); + + /* Setup proper .irq_set_type function */ + gpio_irqchip.irq_set_type = at91_gpio->ops->irq_type; + + /* Disable irqs of this PIO controller */ + writel_relaxed(~0, at91_gpio->regbase + PIO_IDR); + + /* Setup irq domain */ + at91_gpio->domain = irq_domain_add_linear(node, at91_gpio->chip.ngpio, + &at91_gpio_ops, at91_gpio); + if (!at91_gpio->domain) + panic("at91_gpio.%d: couldn't allocate irq domain (DT).\n", + at91_gpio->pioc_idx); + + /* Setup chained handler */ + if (at91_gpio->pioc_idx) + prev = gpio_chips[at91_gpio->pioc_idx - 1]; + + /* The toplevel handler handles one bank of GPIOs, except + * on some SoC it can handles up to three... + * We only set up the handler for the first of the list. + */ + if (prev && prev->next == at91_gpio) + return 0; + + irq_set_chip_data(at91_gpio->pioc_virq, at91_gpio); + irq_set_chained_handler(at91_gpio->pioc_virq, gpio_irq_handler); + + return 0; +} + +/* This structure is replicated for each GPIO block allocated at probe time */ +static struct gpio_chip at91_gpio_template = { + .request = at91_gpio_request, + .free = at91_gpio_free, + .direction_input = at91_gpio_direction_input, + .get = at91_gpio_get, + .direction_output = at91_gpio_direction_output, + .set = at91_gpio_set, + .to_irq = at91_gpio_to_irq, + .dbg_show = at91_gpio_dbg_show, + .can_sleep = 0, + .ngpio = MAX_NB_GPIO_PER_BANK, +}; + +static void __devinit at91_gpio_probe_fixup(void) +{ + unsigned i; + struct at91_gpio_chip *at91_gpio, *last = NULL; + + for (i = 0; i < gpio_banks; i++) { + at91_gpio = gpio_chips[i]; + + /* + * GPIO controller are grouped on some SoC: + * PIOC, PIOD and PIOE can share the same IRQ line + */ + if (last && last->pioc_virq == at91_gpio->pioc_virq) + last->next = at91_gpio; + last = at91_gpio; + } +} + +static struct of_device_id at91_gpio_of_match[] __devinitdata = { + { .compatible = "atmel,at91sam9x5-gpio", .data = &at91sam9x5_ops, }, + { .compatible = "atmel,at91rm9200-gpio", .data = &at91rm9200_ops }, + { /* sentinel */ } +}; + +static int __devinit at91_gpio_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct resource *res; + struct at91_gpio_chip *at91_chip = NULL; + struct gpio_chip *chip; + struct pinctrl_gpio_range *range; + int ret = 0; + int irq; + int alias_idx = of_alias_get_id(np, "gpio"); + uint32_t ngpio; + + BUG_ON(alias_idx >= ARRAY_SIZE(gpio_chips)); + if (gpio_chips[alias_idx]) { + ret = -EBUSY; + goto err; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENOENT; + goto err; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + goto err; + } + + at91_chip = devm_kzalloc(&pdev->dev, sizeof(*at91_chip), GFP_KERNEL); + if (!at91_chip) { + ret = -ENOMEM; + goto err; + } + + at91_chip->regbase = devm_request_and_ioremap(&pdev->dev, res); + if (!at91_chip->regbase) { + dev_err(&pdev->dev, "failed to map registers, ignoring.\n"); + ret = -EBUSY; + goto err; + } + + at91_chip->ops = + of_match_device(at91_gpio_of_match, &pdev->dev)->data; + at91_chip->pioc_virq = irq; + at91_chip->pioc_idx = alias_idx; + + at91_chip->clock = clk_get(&pdev->dev, NULL); + if (IS_ERR(at91_chip->clock)) { + dev_err(&pdev->dev, "failed to get clock, ignoring.\n"); + goto err; + } + + if (clk_prepare(at91_chip->clock)) + goto clk_prep_err; + + /* enable PIO controller's clock */ + if (clk_enable(at91_chip->clock)) { + dev_err(&pdev->dev, "failed to enable clock, ignoring.\n"); + goto clk_err; + } + + at91_chip->chip = at91_gpio_template; + + chip = &at91_chip->chip; + chip->of_node = np; + chip->label = dev_name(&pdev->dev); + chip->dev = &pdev->dev; + chip->owner = THIS_MODULE; + chip->base = alias_idx * MAX_NB_GPIO_PER_BANK; + + if (!of_property_read_u32(np, "#gpio-lines", &ngpio)) { + if (ngpio >= MAX_NB_GPIO_PER_BANK) + pr_err("at91_gpio.%d, gpio-nb >= %d failback to %d\n", + alias_idx, MAX_NB_GPIO_PER_BANK, MAX_NB_GPIO_PER_BANK); + else + chip->ngpio = ngpio; + } + + range = &at91_chip->range; + range->name = chip->label; + range->id = alias_idx; + range->pin_base = range->base = range->id * MAX_NB_GPIO_PER_BANK; + + range->npins = chip->ngpio; + range->gc = chip; + + ret = gpiochip_add(chip); + if (ret) + goto clk_err; + + gpio_chips[alias_idx] = at91_chip; + gpio_banks = max(gpio_banks, alias_idx + 1); + + at91_gpio_probe_fixup(); + + at91_gpio_of_irq_setup(np, at91_chip); + + dev_info(&pdev->dev, "at address %p\n", at91_chip->regbase); + + return 0; + +clk_err: + clk_unprepare(at91_chip->clock); +clk_prep_err: + clk_put(at91_chip->clock); +err: + dev_err(&pdev->dev, "Failure %i for GPIO %i\n", ret, alias_idx); + + return ret; +} + +static struct platform_driver at91_gpio_driver = { + .driver = { + .name = "gpio-at91", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(at91_gpio_of_match), + }, + .probe = at91_gpio_probe, +}; + +static struct platform_driver at91_pinctrl_driver = { + .driver = { + .name = "pinctrl-at91", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(at91_pinctrl_of_match), + }, + .probe = at91_pinctrl_probe, + .remove = __devexit_p(at91_pinctrl_remove), +}; + +static int __init at91_pinctrl_init(void) +{ + int ret; + + ret = platform_driver_register(&at91_gpio_driver); + if (ret) + return ret; + return platform_driver_register(&at91_pinctrl_driver); +} +arch_initcall(at91_pinctrl_init); + +static void __exit at91_pinctrl_exit(void) +{ + platform_driver_unregister(&at91_pinctrl_driver); +} + +module_exit(at91_pinctrl_exit); +MODULE_AUTHOR("Jean-Christophe PLAGNIOL-VILLARD "); +MODULE_DESCRIPTION("Atmel AT91 pinctrl driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From ac5dbea0d483bc0f6281f55261fab1dee7e6ac96 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sun, 7 Oct 2012 17:51:24 +0200 Subject: ASoC: ak4104: add DT bindings Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/ak4104.txt | 17 +++++++++++++++++ sound/soc/codecs/ak4104.c | 7 +++++++ 2 files changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/ak4104.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/ak4104.txt b/Documentation/devicetree/bindings/sound/ak4104.txt new file mode 100644 index 000000000000..1f8e5066d443 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ak4104.txt @@ -0,0 +1,17 @@ +AK4104 S/PDIF transmitter + +This device supports SPI mode only. + +Required properties: + + - compatible : "asahi-kasei,ak4104" + + - reg : The chip select number on the SPI bus + +Example: + +spdif: ak4104@0 { + compatible = "asahi-kasei,ak4104"; + reg = <0>; + spi-max-frequency = <5000000>; +}; diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index d14163f57224..d4d485808894 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -258,10 +258,17 @@ static int __devexit ak4104_spi_remove(struct spi_device *spi) return 0; } +static const struct of_device_id ak4104_of_match[] = { + { .compatible = "asahi-kasei,ak4104", }, + { } +}; +MODULE_DEVICE_TABLE(of, ak4104_of_match); + static struct spi_driver ak4104_spi_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .of_match_table = ak4104_of_match, }, .probe = ak4104_spi_probe, .remove = __devexit_p(ak4104_spi_remove), -- cgit v1.2.3 From 293750f9bcb3368d5298ed1a091b5402de359d3b Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 4 Oct 2012 14:03:23 +0200 Subject: ASoC: cs4271: add support for AMUTEB=BMUTEC feature The CS4271 has a feature to sync its analog mute flags, so one mute circuitry can be used for both channels. Give users access to this feature with a new DT property and a flag in the platform data. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/cs4271.txt | 2 ++ include/sound/cs4271.h | 1 + sound/soc/codecs/cs4271.c | 21 ++++++++++++++++++--- 3 files changed, 21 insertions(+), 3 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/cs4271.txt b/Documentation/devicetree/bindings/sound/cs4271.txt index c81b5fd5a5bc..a850fb9c88ea 100644 --- a/Documentation/devicetree/bindings/sound/cs4271.txt +++ b/Documentation/devicetree/bindings/sound/cs4271.txt @@ -18,6 +18,8 @@ Optional properties: - reset-gpio: a GPIO spec to define which pin is connected to the chip's !RESET pin + - cirrus,amuteb-eq-bmutec: When given, the Codec's AMUTEB=BMUTEC flag + is enabled. Examples: diff --git a/include/sound/cs4271.h b/include/sound/cs4271.h index 50a059e7d116..6d9e15ed1dcf 100644 --- a/include/sound/cs4271.h +++ b/include/sound/cs4271.h @@ -19,6 +19,7 @@ struct cs4271_platform_data { int gpio_nreset; /* GPIO driving Reset pin, if any */ + int amutec_eq_bmutec:1; /* flag to enable AMUTEC=BMUTEC */ }; #endif /* __CS4271_H */ diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index f994af34f552..2ac5fe61a96c 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -474,15 +474,25 @@ static int cs4271_probe(struct snd_soc_codec *codec) struct cs4271_platform_data *cs4271plat = codec->dev->platform_data; int ret; int gpio_nreset = -EINVAL; + int amutec_eq_bmutec = 0; #ifdef CONFIG_OF - if (of_match_device(cs4271_dt_ids, codec->dev)) + if (of_match_device(cs4271_dt_ids, codec->dev)) { gpio_nreset = of_get_named_gpio(codec->dev->of_node, "reset-gpio", 0); + + if (!of_get_property(codec->dev->of_node, + "cirrus,amutec-eq-bmutec", NULL)) + amutec_eq_bmutec = 1; + } #endif - if (cs4271plat && gpio_is_valid(cs4271plat->gpio_nreset)) - gpio_nreset = cs4271plat->gpio_nreset; + if (cs4271plat) { + if (gpio_is_valid(cs4271plat->gpio_nreset)) + gpio_nreset = cs4271plat->gpio_nreset; + + amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec; + } if (gpio_nreset >= 0) if (gpio_request(gpio_nreset, "CS4271 Reset")) @@ -528,6 +538,11 @@ static int cs4271_probe(struct snd_soc_codec *codec) /* Power-up sequence requires 85 uS */ udelay(85); + if (amutec_eq_bmutec) + snd_soc_update_bits(codec, CS4271_MODE2, + CS4271_MODE2_MUTECAEQUB, + CS4271_MODE2_MUTECAEQUB); + return snd_soc_add_codec_controls(codec, cs4271_snd_controls, ARRAY_SIZE(cs4271_snd_controls)); } -- cgit v1.2.3 From 5bdfba29f18f0a36df8e28328315213ea47eb529 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 14 Sep 2012 15:19:00 +0800 Subject: i2c: imx: remove cpu_is_xxx by using platform_device_id This is some amount of work left/forgot from device tree conversion. Instead of checking cpu_is_xxx to determine the controller type, the driver should use platform_device_id, which should match the device tree compatible string. The patch changes the driver to use platform_device_id rather than cpu_is_xxx to determine the controller type/version. It also updates the platform code and device tree source accordingly. As the result, mach/hardware.h inclusion gets removed from the driver. Signed-off-by: Shawn Guo Acked-by: Sascha Hauer Acked-by: Arnd Bergmann Cc: Wolfram Sang Cc: linux-i2c@vger.kernel.org --- .../devicetree/bindings/i2c/fsl-imx-i2c.txt | 4 +-- arch/arm/boot/dts/imx27.dtsi | 4 +-- arch/arm/boot/dts/imx51.dtsi | 4 +-- arch/arm/boot/dts/imx53.dtsi | 6 ++-- arch/arm/boot/dts/imx6q.dtsi | 6 ++-- arch/arm/mach-imx/clk-imx1.c | 2 +- arch/arm/mach-imx/clk-imx21.c | 2 +- arch/arm/mach-imx/clk-imx25.c | 6 ++-- arch/arm/mach-imx/clk-imx27.c | 4 +-- arch/arm/mach-imx/clk-imx31.c | 6 ++-- arch/arm/mach-imx/clk-imx35.c | 6 ++-- arch/arm/mach-imx/clk-imx51-imx53.c | 8 ++--- arch/arm/mach-imx/devices/devices-common.h | 1 + arch/arm/mach-imx/devices/platform-imx-i2c.c | 28 ++++++++------- arch/arm/mach-imx/imx27-dt.c | 4 +-- arch/arm/mach-imx/imx51-dt.c | 4 +-- arch/arm/mach-imx/mach-imx53.c | 6 ++-- drivers/i2c/busses/i2c-imx.c | 40 +++++++++++++++++++--- 18 files changed, 87 insertions(+), 54 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/i2c/fsl-imx-i2c.txt b/Documentation/devicetree/bindings/i2c/fsl-imx-i2c.txt index f3cf43b66f7e..3614242e7732 100644 --- a/Documentation/devicetree/bindings/i2c/fsl-imx-i2c.txt +++ b/Documentation/devicetree/bindings/i2c/fsl-imx-i2c.txt @@ -12,13 +12,13 @@ Optional properties: Examples: i2c@83fc4000 { /* I2C2 on i.MX51 */ - compatible = "fsl,imx51-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx51-i2c", "fsl,imx21-i2c"; reg = <0x83fc4000 0x4000>; interrupts = <63>; }; i2c@70038000 { /* HS-I2C on i.MX51 */ - compatible = "fsl,imx51-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx51-i2c", "fsl,imx21-i2c"; reg = <0x70038000 0x4000>; interrupts = <64>; clock-frequency = <400000>; diff --git a/arch/arm/boot/dts/imx27.dtsi b/arch/arm/boot/dts/imx27.dtsi index 3e54f1498841..67d672792b0d 100644 --- a/arch/arm/boot/dts/imx27.dtsi +++ b/arch/arm/boot/dts/imx27.dtsi @@ -113,7 +113,7 @@ i2c1: i2c@10012000 { #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,imx27-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx27-i2c", "fsl,imx21-i2c"; reg = <0x10012000 0x1000>; interrupts = <12>; status = "disabled"; @@ -205,7 +205,7 @@ i2c2: i2c@1001d000 { #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,imx27-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx27-i2c", "fsl,imx21-i2c"; reg = <0x1001d000 0x1000>; interrupts = <1>; status = "disabled"; diff --git a/arch/arm/boot/dts/imx51.dtsi b/arch/arm/boot/dts/imx51.dtsi index 75d069fcf897..54aea74769a1 100644 --- a/arch/arm/boot/dts/imx51.dtsi +++ b/arch/arm/boot/dts/imx51.dtsi @@ -377,7 +377,7 @@ i2c@83fc4000 { /* I2C2 */ #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,imx51-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx51-i2c", "fsl,imx21-i2c"; reg = <0x83fc4000 0x4000>; interrupts = <63>; status = "disabled"; @@ -386,7 +386,7 @@ i2c@83fc8000 { /* I2C1 */ #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,imx51-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx51-i2c", "fsl,imx21-i2c"; reg = <0x83fc8000 0x4000>; interrupts = <62>; status = "disabled"; diff --git a/arch/arm/boot/dts/imx53.dtsi b/arch/arm/boot/dts/imx53.dtsi index 76ebb1ad2675..caf09ff73f10 100644 --- a/arch/arm/boot/dts/imx53.dtsi +++ b/arch/arm/boot/dts/imx53.dtsi @@ -432,7 +432,7 @@ i2c@53fec000 { /* I2C3 */ #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,imx53-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx53-i2c", "fsl,imx21-i2c"; reg = <0x53fec000 0x4000>; interrupts = <64>; status = "disabled"; @@ -488,7 +488,7 @@ i2c@63fc4000 { /* I2C2 */ #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,imx53-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx53-i2c", "fsl,imx21-i2c"; reg = <0x63fc4000 0x4000>; interrupts = <63>; status = "disabled"; @@ -497,7 +497,7 @@ i2c@63fc8000 { /* I2C1 */ #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,imx53-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx53-i2c", "fsl,imx21-i2c"; reg = <0x63fc8000 0x4000>; interrupts = <62>; status = "disabled"; diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi index f3990b04fecf..f604a44a5c66 100644 --- a/arch/arm/boot/dts/imx6q.dtsi +++ b/arch/arm/boot/dts/imx6q.dtsi @@ -882,7 +882,7 @@ i2c@021a0000 { /* I2C1 */ #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,imx6q-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c"; reg = <0x021a0000 0x4000>; interrupts = <0 36 0x04>; clocks = <&clks 125>; @@ -892,7 +892,7 @@ i2c@021a4000 { /* I2C2 */ #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,imx6q-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c"; reg = <0x021a4000 0x4000>; interrupts = <0 37 0x04>; clocks = <&clks 126>; @@ -902,7 +902,7 @@ i2c@021a8000 { /* I2C3 */ #address-cells = <1>; #size-cells = <0>; - compatible = "fsl,imx6q-i2c", "fsl,imx1-i2c"; + compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c"; reg = <0x021a8000 0x4000>; interrupts = <0 38 0x04>; clocks = <&clks 127>; diff --git a/arch/arm/mach-imx/clk-imx1.c b/arch/arm/mach-imx/clk-imx1.c index b5f90cc9e371..198238859446 100644 --- a/arch/arm/mach-imx/clk-imx1.c +++ b/arch/arm/mach-imx/clk-imx1.c @@ -95,7 +95,7 @@ int __init mx1_clocks_init(unsigned long fref) clk_register_clkdev(clk[hclk], "ipg", "imx1-uart.1"); clk_register_clkdev(clk[per1], "per", "imx1-uart.2"); clk_register_clkdev(clk[hclk], "ipg", "imx1-uart.2"); - clk_register_clkdev(clk[hclk], NULL, "imx-i2c.0"); + clk_register_clkdev(clk[hclk], NULL, "imx1-i2c.0"); clk_register_clkdev(clk[per2], "per", "imx1-cspi.0"); clk_register_clkdev(clk[dummy], "ipg", "imx1-cspi.0"); clk_register_clkdev(clk[per2], "per", "imx1-cspi.1"); diff --git a/arch/arm/mach-imx/clk-imx21.c b/arch/arm/mach-imx/clk-imx21.c index fbee6a4b1de1..09fc31c58474 100644 --- a/arch/arm/mach-imx/clk-imx21.c +++ b/arch/arm/mach-imx/clk-imx21.c @@ -166,7 +166,7 @@ int __init mx21_clocks_init(unsigned long lref, unsigned long href) clk_register_clkdev(clk[dma_hclk_gate], "ahb", "imx-dma"); clk_register_clkdev(clk[dma_gate], "ipg", "imx-dma"); clk_register_clkdev(clk[wdog_gate], NULL, "imx2-wdt.0"); - clk_register_clkdev(clk[i2c_gate], NULL, "imx-i2c.0"); + clk_register_clkdev(clk[i2c_gate], NULL, "imx21-i2c.0"); clk_register_clkdev(clk[kpp_gate], NULL, "mxc-keypad"); clk_register_clkdev(clk[owire_gate], NULL, "mxc_w1.0"); clk_register_clkdev(clk[brom_gate], "brom", NULL); diff --git a/arch/arm/mach-imx/clk-imx25.c b/arch/arm/mach-imx/clk-imx25.c index 08a143a19e63..f1d75e72e002 100644 --- a/arch/arm/mach-imx/clk-imx25.c +++ b/arch/arm/mach-imx/clk-imx25.c @@ -213,9 +213,9 @@ int __init mx25_clocks_init(void) clk_register_clkdev(clk[per10], "per", "mxc_pwm.3"); clk_register_clkdev(clk[kpp_ipg], NULL, "imx-keypad"); clk_register_clkdev(clk[tsc_ipg], NULL, "mx25-adc"); - clk_register_clkdev(clk[i2c_ipg_per], NULL, "imx-i2c.0"); - clk_register_clkdev(clk[i2c_ipg_per], NULL, "imx-i2c.1"); - clk_register_clkdev(clk[i2c_ipg_per], NULL, "imx-i2c.2"); + clk_register_clkdev(clk[i2c_ipg_per], NULL, "imx21-i2c.0"); + clk_register_clkdev(clk[i2c_ipg_per], NULL, "imx21-i2c.1"); + clk_register_clkdev(clk[i2c_ipg_per], NULL, "imx21-i2c.2"); clk_register_clkdev(clk[fec_ipg], "ipg", "imx25-fec.0"); clk_register_clkdev(clk[fec_ahb], "ahb", "imx25-fec.0"); clk_register_clkdev(clk[dryice_ipg], NULL, "imxdi_rtc.0"); diff --git a/arch/arm/mach-imx/clk-imx27.c b/arch/arm/mach-imx/clk-imx27.c index 31010cb93c6d..c2f111f42ddb 100644 --- a/arch/arm/mach-imx/clk-imx27.c +++ b/arch/arm/mach-imx/clk-imx27.c @@ -247,8 +247,8 @@ int __init mx27_clocks_init(unsigned long fref) clk_register_clkdev(clk[fec_ipg_gate], "ipg", "imx27-fec.0"); clk_register_clkdev(clk[fec_ahb_gate], "ahb", "imx27-fec.0"); clk_register_clkdev(clk[wdog_ipg_gate], NULL, "imx2-wdt.0"); - clk_register_clkdev(clk[i2c1_ipg_gate], NULL, "imx-i2c.0"); - clk_register_clkdev(clk[i2c2_ipg_gate], NULL, "imx-i2c.1"); + clk_register_clkdev(clk[i2c1_ipg_gate], NULL, "imx21-i2c.0"); + clk_register_clkdev(clk[i2c2_ipg_gate], NULL, "imx21-i2c.1"); clk_register_clkdev(clk[owire_ipg_gate], NULL, "mxc_w1.0"); clk_register_clkdev(clk[kpp_ipg_gate], NULL, "imx-keypad"); clk_register_clkdev(clk[emma_ahb_gate], "emma-ahb", "mx2-camera.0"); diff --git a/arch/arm/mach-imx/clk-imx31.c b/arch/arm/mach-imx/clk-imx31.c index a4c298acd04e..76e6462cf9ae 100644 --- a/arch/arm/mach-imx/clk-imx31.c +++ b/arch/arm/mach-imx/clk-imx31.c @@ -155,9 +155,9 @@ int __init mx31_clocks_init(unsigned long fref) clk_register_clkdev(clk[ipg], "ipg", "imx21-uart.3"); clk_register_clkdev(clk[uart5_gate], "per", "imx21-uart.4"); clk_register_clkdev(clk[ipg], "ipg", "imx21-uart.4"); - clk_register_clkdev(clk[i2c1_gate], NULL, "imx-i2c.0"); - clk_register_clkdev(clk[i2c2_gate], NULL, "imx-i2c.1"); - clk_register_clkdev(clk[i2c3_gate], NULL, "imx-i2c.2"); + clk_register_clkdev(clk[i2c1_gate], NULL, "imx21-i2c.0"); + clk_register_clkdev(clk[i2c2_gate], NULL, "imx21-i2c.1"); + clk_register_clkdev(clk[i2c3_gate], NULL, "imx21-i2c.2"); clk_register_clkdev(clk[owire_gate], NULL, "mxc_w1.0"); clk_register_clkdev(clk[sdhc1_gate], NULL, "mxc-mmc.0"); clk_register_clkdev(clk[sdhc2_gate], NULL, "mxc-mmc.1"); diff --git a/arch/arm/mach-imx/clk-imx35.c b/arch/arm/mach-imx/clk-imx35.c index ba26bf3ee933..ee2581254fbc 100644 --- a/arch/arm/mach-imx/clk-imx35.c +++ b/arch/arm/mach-imx/clk-imx35.c @@ -226,9 +226,9 @@ int __init mx35_clocks_init() clk_register_clkdev(clk[fec_gate], NULL, "imx27-fec.0"); clk_register_clkdev(clk[gpt_gate], "per", "imx-gpt.0"); clk_register_clkdev(clk[ipg], "ipg", "imx-gpt.0"); - clk_register_clkdev(clk[i2c1_gate], NULL, "imx-i2c.0"); - clk_register_clkdev(clk[i2c2_gate], NULL, "imx-i2c.1"); - clk_register_clkdev(clk[i2c3_gate], NULL, "imx-i2c.2"); + clk_register_clkdev(clk[i2c1_gate], NULL, "imx21-i2c.0"); + clk_register_clkdev(clk[i2c2_gate], NULL, "imx21-i2c.1"); + clk_register_clkdev(clk[i2c3_gate], NULL, "imx21-i2c.2"); clk_register_clkdev(clk[ipu_gate], NULL, "ipu-core"); clk_register_clkdev(clk[ipu_gate], NULL, "mx3_sdc_fb"); clk_register_clkdev(clk[kpp_gate], NULL, "imx-keypad"); diff --git a/arch/arm/mach-imx/clk-imx51-imx53.c b/arch/arm/mach-imx/clk-imx51-imx53.c index 024587cee620..2449d8ff4a18 100644 --- a/arch/arm/mach-imx/clk-imx51-imx53.c +++ b/arch/arm/mach-imx/clk-imx51-imx53.c @@ -258,8 +258,8 @@ static void __init mx5_clocks_common_init(unsigned long rate_ckil, clk_register_clkdev(clk[cspi_ipg_gate], NULL, "imx35-cspi.2"); clk_register_clkdev(clk[pwm1_ipg_gate], "pwm", "mxc_pwm.0"); clk_register_clkdev(clk[pwm2_ipg_gate], "pwm", "mxc_pwm.1"); - clk_register_clkdev(clk[i2c1_gate], NULL, "imx-i2c.0"); - clk_register_clkdev(clk[i2c2_gate], NULL, "imx-i2c.1"); + clk_register_clkdev(clk[i2c1_gate], NULL, "imx21-i2c.0"); + clk_register_clkdev(clk[i2c2_gate], NULL, "imx21-i2c.1"); clk_register_clkdev(clk[usboh3_per_gate], "per", "mxc-ehci.0"); clk_register_clkdev(clk[usboh3_gate], "ipg", "mxc-ehci.0"); clk_register_clkdev(clk[usboh3_gate], "ahb", "mxc-ehci.0"); @@ -345,7 +345,7 @@ int __init mx51_clocks_init(unsigned long rate_ckil, unsigned long rate_osc, mx5_clocks_common_init(rate_ckil, rate_osc, rate_ckih1, rate_ckih2); - clk_register_clkdev(clk[hsi2c_gate], NULL, "imx-i2c.2"); + clk_register_clkdev(clk[hsi2c_gate], NULL, "imx21-i2c.2"); clk_register_clkdev(clk[mx51_mipi], "mipi_hsp", NULL); clk_register_clkdev(clk[vpu_gate], NULL, "imx51-vpu.0"); clk_register_clkdev(clk[fec_gate], NULL, "imx27-fec.0"); @@ -440,7 +440,7 @@ int __init mx53_clocks_init(unsigned long rate_ckil, unsigned long rate_osc, mx5_clocks_common_init(rate_ckil, rate_osc, rate_ckih1, rate_ckih2); clk_register_clkdev(clk[vpu_gate], NULL, "imx53-vpu.0"); - clk_register_clkdev(clk[i2c3_gate], NULL, "imx-i2c.2"); + clk_register_clkdev(clk[i2c3_gate], NULL, "imx21-i2c.2"); clk_register_clkdev(clk[fec_gate], NULL, "imx25-fec.0"); clk_register_clkdev(clk[ipu_gate], "bus", "imx53-ipu"); clk_register_clkdev(clk[ipu_di0_gate], "di0", "imx53-ipu"); diff --git a/arch/arm/mach-imx/devices/devices-common.h b/arch/arm/mach-imx/devices/devices-common.h index eaf79d220c9a..86bf34cd4865 100644 --- a/arch/arm/mach-imx/devices/devices-common.h +++ b/arch/arm/mach-imx/devices/devices-common.h @@ -118,6 +118,7 @@ struct platform_device *__init imx_add_imx_fb( #include struct imx_imx_i2c_data { + const char *devid; int id; resource_size_t iobase; resource_size_t iosize; diff --git a/arch/arm/mach-imx/devices/platform-imx-i2c.c b/arch/arm/mach-imx/devices/platform-imx-i2c.c index e0c7d6291bed..424ce246fe43 100644 --- a/arch/arm/mach-imx/devices/platform-imx-i2c.c +++ b/arch/arm/mach-imx/devices/platform-imx-i2c.c @@ -9,31 +9,32 @@ #include #include "devices-common.h" -#define imx_imx_i2c_data_entry_single(soc, _id, _hwid, _size) \ +#define imx_imx_i2c_data_entry_single(soc, _devid, _id, _hwid, _size) \ { \ + .devid = _devid, \ .id = _id, \ .iobase = soc ## _I2C ## _hwid ## _BASE_ADDR, \ .iosize = _size, \ .irq = soc ## _INT_I2C ## _hwid, \ } -#define imx_imx_i2c_data_entry(soc, _id, _hwid, _size) \ - [_id] = imx_imx_i2c_data_entry_single(soc, _id, _hwid, _size) +#define imx_imx_i2c_data_entry(soc, _devid, _id, _hwid, _size) \ + [_id] = imx_imx_i2c_data_entry_single(soc, _devid, _id, _hwid, _size) #ifdef CONFIG_SOC_IMX1 const struct imx_imx_i2c_data imx1_imx_i2c_data __initconst = - imx_imx_i2c_data_entry_single(MX1, 0, , SZ_4K); + imx_imx_i2c_data_entry_single(MX1, "imx1-i2c", 0, , SZ_4K); #endif /* ifdef CONFIG_SOC_IMX1 */ #ifdef CONFIG_SOC_IMX21 const struct imx_imx_i2c_data imx21_imx_i2c_data __initconst = - imx_imx_i2c_data_entry_single(MX21, 0, , SZ_4K); + imx_imx_i2c_data_entry_single(MX21, "imx21-i2c", 0, , SZ_4K); #endif /* ifdef CONFIG_SOC_IMX21 */ #ifdef CONFIG_SOC_IMX25 const struct imx_imx_i2c_data imx25_imx_i2c_data[] __initconst = { #define imx25_imx_i2c_data_entry(_id, _hwid) \ - imx_imx_i2c_data_entry(MX25, _id, _hwid, SZ_16K) + imx_imx_i2c_data_entry(MX25, "imx21-i2c", _id, _hwid, SZ_16K) imx25_imx_i2c_data_entry(0, 1), imx25_imx_i2c_data_entry(1, 2), imx25_imx_i2c_data_entry(2, 3), @@ -43,7 +44,7 @@ const struct imx_imx_i2c_data imx25_imx_i2c_data[] __initconst = { #ifdef CONFIG_SOC_IMX27 const struct imx_imx_i2c_data imx27_imx_i2c_data[] __initconst = { #define imx27_imx_i2c_data_entry(_id, _hwid) \ - imx_imx_i2c_data_entry(MX27, _id, _hwid, SZ_4K) + imx_imx_i2c_data_entry(MX27, "imx21-i2c", _id, _hwid, SZ_4K) imx27_imx_i2c_data_entry(0, 1), imx27_imx_i2c_data_entry(1, 2), }; @@ -52,7 +53,7 @@ const struct imx_imx_i2c_data imx27_imx_i2c_data[] __initconst = { #ifdef CONFIG_SOC_IMX31 const struct imx_imx_i2c_data imx31_imx_i2c_data[] __initconst = { #define imx31_imx_i2c_data_entry(_id, _hwid) \ - imx_imx_i2c_data_entry(MX31, _id, _hwid, SZ_4K) + imx_imx_i2c_data_entry(MX31, "imx21-i2c", _id, _hwid, SZ_4K) imx31_imx_i2c_data_entry(0, 1), imx31_imx_i2c_data_entry(1, 2), imx31_imx_i2c_data_entry(2, 3), @@ -62,7 +63,7 @@ const struct imx_imx_i2c_data imx31_imx_i2c_data[] __initconst = { #ifdef CONFIG_SOC_IMX35 const struct imx_imx_i2c_data imx35_imx_i2c_data[] __initconst = { #define imx35_imx_i2c_data_entry(_id, _hwid) \ - imx_imx_i2c_data_entry(MX35, _id, _hwid, SZ_4K) + imx_imx_i2c_data_entry(MX35, "imx21-i2c", _id, _hwid, SZ_4K) imx35_imx_i2c_data_entry(0, 1), imx35_imx_i2c_data_entry(1, 2), imx35_imx_i2c_data_entry(2, 3), @@ -72,7 +73,7 @@ const struct imx_imx_i2c_data imx35_imx_i2c_data[] __initconst = { #ifdef CONFIG_SOC_IMX50 const struct imx_imx_i2c_data imx50_imx_i2c_data[] __initconst = { #define imx50_imx_i2c_data_entry(_id, _hwid) \ - imx_imx_i2c_data_entry(MX50, _id, _hwid, SZ_4K) + imx_imx_i2c_data_entry(MX50, "imx21-i2c", _id, _hwid, SZ_4K) imx50_imx_i2c_data_entry(0, 1), imx50_imx_i2c_data_entry(1, 2), imx50_imx_i2c_data_entry(2, 3), @@ -82,10 +83,11 @@ const struct imx_imx_i2c_data imx50_imx_i2c_data[] __initconst = { #ifdef CONFIG_SOC_IMX51 const struct imx_imx_i2c_data imx51_imx_i2c_data[] __initconst = { #define imx51_imx_i2c_data_entry(_id, _hwid) \ - imx_imx_i2c_data_entry(MX51, _id, _hwid, SZ_4K) + imx_imx_i2c_data_entry(MX51, "imx21-i2c", _id, _hwid, SZ_4K) imx51_imx_i2c_data_entry(0, 1), imx51_imx_i2c_data_entry(1, 2), { + .devid = "imx21-i2c", .id = 2, .iobase = MX51_HSI2C_DMA_BASE_ADDR, .iosize = SZ_16K, @@ -97,7 +99,7 @@ const struct imx_imx_i2c_data imx51_imx_i2c_data[] __initconst = { #ifdef CONFIG_SOC_IMX53 const struct imx_imx_i2c_data imx53_imx_i2c_data[] __initconst = { #define imx53_imx_i2c_data_entry(_id, _hwid) \ - imx_imx_i2c_data_entry(MX53, _id, _hwid, SZ_4K) + imx_imx_i2c_data_entry(MX53, "imx21-i2c", _id, _hwid, SZ_4K) imx53_imx_i2c_data_entry(0, 1), imx53_imx_i2c_data_entry(1, 2), imx53_imx_i2c_data_entry(2, 3), @@ -120,7 +122,7 @@ struct platform_device *__init imx_add_imx_i2c( }, }; - return imx_add_platform_device("imx-i2c", data->id, + return imx_add_platform_device(data->devid, data->id, res, ARRAY_SIZE(res), pdata, sizeof(*pdata)); } diff --git a/arch/arm/mach-imx/imx27-dt.c b/arch/arm/mach-imx/imx27-dt.c index 83c56fb46aa2..43fbf8640109 100644 --- a/arch/arm/mach-imx/imx27-dt.c +++ b/arch/arm/mach-imx/imx27-dt.c @@ -23,8 +23,8 @@ static const struct of_dev_auxdata imx27_auxdata_lookup[] __initconst = { OF_DEV_AUXDATA("fsl,imx27-uart", MX27_UART2_BASE_ADDR, "imx21-uart.1", NULL), OF_DEV_AUXDATA("fsl,imx27-uart", MX27_UART3_BASE_ADDR, "imx21-uart.2", NULL), OF_DEV_AUXDATA("fsl,imx27-fec", MX27_FEC_BASE_ADDR, "imx27-fec.0", NULL), - OF_DEV_AUXDATA("fsl,imx27-i2c", MX27_I2C1_BASE_ADDR, "imx-i2c.0", NULL), - OF_DEV_AUXDATA("fsl,imx27-i2c", MX27_I2C2_BASE_ADDR, "imx-i2c.1", NULL), + OF_DEV_AUXDATA("fsl,imx27-i2c", MX27_I2C1_BASE_ADDR, "imx21-i2c.0", NULL), + OF_DEV_AUXDATA("fsl,imx27-i2c", MX27_I2C2_BASE_ADDR, "imx21-i2c.1", NULL), OF_DEV_AUXDATA("fsl,imx27-cspi", MX27_CSPI1_BASE_ADDR, "imx27-cspi.0", NULL), OF_DEV_AUXDATA("fsl,imx27-cspi", MX27_CSPI2_BASE_ADDR, "imx27-cspi.1", NULL), OF_DEV_AUXDATA("fsl,imx27-cspi", MX27_CSPI3_BASE_ADDR, "imx27-cspi.2", NULL), diff --git a/arch/arm/mach-imx/imx51-dt.c b/arch/arm/mach-imx/imx51-dt.c index e105f12f1e6f..a0391a14ad22 100644 --- a/arch/arm/mach-imx/imx51-dt.c +++ b/arch/arm/mach-imx/imx51-dt.c @@ -37,8 +37,8 @@ static const struct of_dev_auxdata imx51_auxdata_lookup[] __initconst = { OF_DEV_AUXDATA("fsl,imx51-ecspi", MX51_ECSPI1_BASE_ADDR, "imx51-ecspi.0", NULL), OF_DEV_AUXDATA("fsl,imx51-ecspi", MX51_ECSPI2_BASE_ADDR, "imx51-ecspi.1", NULL), OF_DEV_AUXDATA("fsl,imx51-cspi", MX51_CSPI_BASE_ADDR, "imx35-cspi.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-i2c", MX51_I2C1_BASE_ADDR, "imx-i2c.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-i2c", MX51_I2C2_BASE_ADDR, "imx-i2c.1", NULL), + OF_DEV_AUXDATA("fsl,imx51-i2c", MX51_I2C1_BASE_ADDR, "imx21-i2c.0", NULL), + OF_DEV_AUXDATA("fsl,imx51-i2c", MX51_I2C2_BASE_ADDR, "imx21-i2c.1", NULL), OF_DEV_AUXDATA("fsl,imx51-sdma", MX51_SDMA_BASE_ADDR, "imx35-sdma", NULL), OF_DEV_AUXDATA("fsl,imx51-wdt", MX51_WDOG1_BASE_ADDR, "imx2-wdt.0", NULL), { /* sentinel */ } diff --git a/arch/arm/mach-imx/mach-imx53.c b/arch/arm/mach-imx/mach-imx53.c index aaa90a781b35..0e0aadfb625e 100644 --- a/arch/arm/mach-imx/mach-imx53.c +++ b/arch/arm/mach-imx/mach-imx53.c @@ -43,9 +43,9 @@ static const struct of_dev_auxdata imx53_auxdata_lookup[] __initconst = { OF_DEV_AUXDATA("fsl,imx53-ecspi", MX53_ECSPI1_BASE_ADDR, "imx51-ecspi.0", NULL), OF_DEV_AUXDATA("fsl,imx53-ecspi", MX53_ECSPI2_BASE_ADDR, "imx51-ecspi.1", NULL), OF_DEV_AUXDATA("fsl,imx53-cspi", MX53_CSPI_BASE_ADDR, "imx35-cspi.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C1_BASE_ADDR, "imx-i2c.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C2_BASE_ADDR, "imx-i2c.1", NULL), - OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C3_BASE_ADDR, "imx-i2c.2", NULL), + OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C1_BASE_ADDR, "imx21-i2c.0", NULL), + OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C2_BASE_ADDR, "imx21-i2c.1", NULL), + OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C3_BASE_ADDR, "imx21-i2c.2", NULL), OF_DEV_AUXDATA("fsl,imx53-sdma", MX53_SDMA_BASE_ADDR, "imx35-sdma", NULL), OF_DEV_AUXDATA("fsl,imx53-wdt", MX53_WDOG1_BASE_ADDR, "imx2-wdt.0", NULL), { /* sentinel */ } diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 2ef162d148cb..b9734747d610 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -52,8 +52,6 @@ #include #include #include - -#include #include /** Defines ******************************************************************** @@ -115,6 +113,11 @@ static u16 __initdata i2c_clk_div[50][2] = { { 3072, 0x1E }, { 3840, 0x1F } }; +enum imx_i2c_type { + IMX1_I2C, + IMX21_I2C, +}; + struct imx_i2c_struct { struct i2c_adapter adapter; struct clk *clk; @@ -124,13 +127,33 @@ struct imx_i2c_struct { unsigned int disable_delay; int stopped; unsigned int ifdr; /* IMX_I2C_IFDR */ + enum imx_i2c_type devtype; +}; + +static struct platform_device_id imx_i2c_devtype[] = { + { + .name = "imx1-i2c", + .driver_data = IMX1_I2C, + }, { + .name = "imx21-i2c", + .driver_data = IMX21_I2C, + }, { + /* sentinel */ + } }; +MODULE_DEVICE_TABLE(platform, imx_i2c_devtype); static const struct of_device_id i2c_imx_dt_ids[] = { - { .compatible = "fsl,imx1-i2c", }, + { .compatible = "fsl,imx1-i2c", .data = &imx_i2c_devtype[IMX1_I2C], }, + { .compatible = "fsl,imx21-i2c", .data = &imx_i2c_devtype[IMX21_I2C], }, { /* sentinel */ } }; +static inline int is_imx1_i2c(struct imx_i2c_struct *i2c_imx) +{ + return i2c_imx->devtype == IMX1_I2C; +} + /** Functions for IMX I2C adapter driver *************************************** *******************************************************************************/ @@ -223,7 +246,7 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx) temp &= ~(I2CR_MSTA | I2CR_MTX); writeb(temp, i2c_imx->base + IMX_I2C_I2CR); } - if (cpu_is_mx1()) { + if (is_imx1_i2c(i2c_imx)) { /* * This delay caused by an i.MXL hardware bug. * If no (or too short) delay, no "STOP" bit will be generated. @@ -465,6 +488,8 @@ static struct i2c_algorithm i2c_imx_algo = { static int __init i2c_imx_probe(struct platform_device *pdev) { + const struct of_device_id *of_id = of_match_device(i2c_imx_dt_ids, + &pdev->dev); struct imx_i2c_struct *i2c_imx; struct resource *res; struct imxi2c_platform_data *pdata = pdev->dev.platform_data; @@ -497,6 +522,10 @@ static int __init i2c_imx_probe(struct platform_device *pdev) return -ENOMEM; } + if (of_id) + pdev->id_entry = of_id->data; + i2c_imx->devtype = pdev->id_entry->driver_data; + /* Setup i2c_imx driver structure */ strlcpy(i2c_imx->adapter.name, pdev->name, sizeof(i2c_imx->adapter.name)); i2c_imx->adapter.owner = THIS_MODULE; @@ -593,7 +622,8 @@ static struct platform_driver i2c_imx_driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = i2c_imx_dt_ids, - } + }, + .id_table = imx_i2c_devtype, }; static int __init i2c_adap_imx_init(void) -- cgit v1.2.3 From 31e54086dd7bb86ad40f1d76a9063f2a95866b87 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Mon, 24 Sep 2012 18:56:54 +0100 Subject: regulator: Versatile Express regulator driver Implementation of the regulator framework driver for the Versatile Express voltage control. Devices without voltage constraints (ie. "regulator-[min|max]-microvolt" properties in the DT node) are treated as fixed (or rather read-only) regulators. Signed-off-by: Pawel Moll Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/vexpress.txt | 32 +++++ drivers/regulator/Kconfig | 7 + drivers/regulator/Makefile | 1 + drivers/regulator/vexpress.c | 146 +++++++++++++++++++++ 4 files changed, 186 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/vexpress.txt create mode 100644 drivers/regulator/vexpress.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/regulator/vexpress.txt b/Documentation/devicetree/bindings/regulator/vexpress.txt new file mode 100644 index 000000000000..d775f72487aa --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/vexpress.txt @@ -0,0 +1,32 @@ +Versatile Express voltage regulators +------------------------------------ + +Requires node properties: +- "compatible" value: "arm,vexpress-volt" +- "arm,vexpress-sysreg,func" when controlled via vexpress-sysreg + (see Documentation/devicetree/bindings/arm/vexpress-sysreg.txt + for more details) + +Required regulator properties: +- "regulator-name" +- "regulator-always-on" + +Optional regulator properties: +- "regulator-min-microvolt" +- "regulator-max-microvolt" + +See Documentation/devicetree/bindings/regulator/regulator.txt +for more details about the regulator properties. + +When no "regulator-[min|max]-microvolt" properties are defined, +the device is treated as fixed (or rather "read-only") regulator. + +Example: + volt@0 { + compatible = "arm,vexpress-volt"; + arm,vexpress-sysreg,func = <2 0>; + regulator-name = "Cores"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1050000>; + regulator-always-on; + }; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 67d47b59a66d..b44b019b9433 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -422,6 +422,13 @@ config REGULATOR_TWL4030 This driver supports the voltage regulators provided by this family of companion chips. +config REGULATOR_VEXPRESS + tristate "Versatile Express regulators" + depends on VEXPRESS_CONFIG + help + This driver provides support for voltage regulators available + on the ARM Ltd's Versatile Express platform. + config REGULATOR_WM831X tristate "Wolfson Microelectronics WM831x PMIC regulators" depends on MFD_WM831X diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index e431eed8a878..9fa7a7bc42d8 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o +obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o diff --git a/drivers/regulator/vexpress.c b/drivers/regulator/vexpress.c new file mode 100644 index 000000000000..1702945a93a9 --- /dev/null +++ b/drivers/regulator/vexpress.c @@ -0,0 +1,146 @@ +/* + * 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. + * + * Copyright (C) 2012 ARM Limited + */ + +#define DRVNAME "vexpress-regulator" +#define pr_fmt(fmt) DRVNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +struct vexpress_regulator { + struct regulator_desc desc; + struct regulator_dev *regdev; + struct vexpress_config_func *func; +}; + +static int vexpress_regulator_get_voltage(struct regulator_dev *regdev) +{ + struct vexpress_regulator *reg = rdev_get_drvdata(regdev); + u32 uV; + int err = vexpress_config_read(reg->func, 0, &uV); + + return err ? err : uV; +} + +static int vexpress_regulator_set_voltage(struct regulator_dev *regdev, + int min_uV, int max_uV, unsigned *selector) +{ + struct vexpress_regulator *reg = rdev_get_drvdata(regdev); + + return vexpress_config_write(reg->func, 0, min_uV); +} + +static struct regulator_ops vexpress_regulator_ops_ro = { + .get_voltage = vexpress_regulator_get_voltage, +}; + +static struct regulator_ops vexpress_regulator_ops = { + .get_voltage = vexpress_regulator_get_voltage, + .set_voltage = vexpress_regulator_set_voltage, +}; + +static int vexpress_regulator_probe(struct platform_device *pdev) +{ + int err; + struct vexpress_regulator *reg; + struct regulator_init_data *init_data; + struct regulator_config config = { }; + + reg = devm_kzalloc(&pdev->dev, sizeof(*reg), GFP_KERNEL); + if (!reg) { + err = -ENOMEM; + goto error_kzalloc; + } + + reg->func = vexpress_config_func_get_by_dev(&pdev->dev); + if (!reg->func) { + err = -ENXIO; + goto error_get_func; + } + + reg->desc.name = dev_name(&pdev->dev); + reg->desc.type = REGULATOR_VOLTAGE; + reg->desc.owner = THIS_MODULE; + reg->desc.continuous_voltage_range = true; + + init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node); + if (!init_data) { + err = -EINVAL; + goto error_get_regulator_init_data; + } + + init_data->constraints.apply_uV = 0; + if (init_data->constraints.min_uV && init_data->constraints.max_uV) + reg->desc.ops = &vexpress_regulator_ops; + else + reg->desc.ops = &vexpress_regulator_ops_ro; + + config.dev = &pdev->dev; + config.init_data = init_data; + config.driver_data = reg; + config.of_node = pdev->dev.of_node; + + reg->regdev = regulator_register(®->desc, &config); + if (IS_ERR(reg->regdev)) { + err = PTR_ERR(reg->regdev); + goto error_regulator_register; + } + + platform_set_drvdata(pdev, reg); + + return 0; + +error_regulator_register: +error_get_regulator_init_data: + vexpress_config_func_put(reg->func); +error_get_func: +error_kzalloc: + return err; +} + +static int __devexit vexpress_regulator_remove(struct platform_device *pdev) +{ + struct vexpress_regulator *reg = platform_get_drvdata(pdev); + + vexpress_config_func_put(reg->func); + regulator_unregister(reg->regdev); + + return 0; +} + +static struct of_device_id vexpress_regulator_of_match[] = { + { .compatible = "arm,vexpress-volt", }, +}; + +static struct platform_driver vexpress_regulator_driver = { + .probe = vexpress_regulator_probe, + .remove = __devexit_p(vexpress_regulator_remove), + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + .of_match_table = vexpress_regulator_of_match, + }, +}; + +module_platform_driver(vexpress_regulator_driver); + +MODULE_AUTHOR("Pawel Moll "); +MODULE_DESCRIPTION("Versatile Express regulator"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:vexpress-regulator"); -- cgit v1.2.3 From b33ef91f4b799a5de5904c4b361df97a0c3ec7dd Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 11 Oct 2012 10:11:21 +0200 Subject: Documentation: Update samsung-pinctrl device tree bindings documentation Signed-off-by: Tomasz Figa Reviewed-by: Kyungmin Park Acked-by: Thomas Abraham Signed-off-by: Linus Walleij --- .../bindings/pinctrl/samsung-pinctrl.txt | 118 ++++++++++++++++----- 1 file changed, 93 insertions(+), 25 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt index 03dee50532f5..63806e2d49c2 100644 --- a/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt @@ -13,8 +13,14 @@ Required Properties: - reg: Base address of the pin controller hardware module and length of the address space it occupies. -- interrupts: interrupt specifier for the controller. The format and value of - the interrupt specifier depends on the interrupt parent for the controller. +- Pin banks as child nodes: Pin banks of the controller are represented by child + nodes of the controller node. Bank name is taken from name of the node. Each + bank node must contain following properties: + + - 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. - Pin mux/config groups as child nodes: The pin mux (selecting pin function mode) and pin config (pull up/down, driver strength) settings are represented @@ -72,16 +78,24 @@ used as system wakeup events. A. External GPIO Interrupts: For supporting external gpio interrupts, the following properties should be specified in the pin-controller device node. -- interrupt-controller: identifies the controller node as interrupt-parent. -- #interrupt-cells: the value of this property should be 2. - - First Cell: represents the external gpio interrupt number local to the - external gpio interrupt space of the controller. - - Second Cell: flags to identify the type of the interrupt - - 1 = rising edge triggered - - 2 = falling edge triggered - - 3 = rising and falling edge triggered - - 4 = high level triggered - - 8 = low level triggered + - interrupt-parent: phandle of the interrupt parent to which the external + GPIO interrupts are forwarded to. + - interrupts: interrupt specifier for the controller. The format and value of + the interrupt specifier depends on the interrupt parent for the controller. + + In addition, following properties must be present in node of every bank + of pins supporting GPIO interrupts: + + - interrupt-controller: identifies the controller node as interrupt-parent. + - #interrupt-cells: the value of this property should be 2. + - First Cell: represents the external gpio interrupt number local to the + external gpio interrupt space of the controller. + - Second Cell: flags to identify the type of the interrupt + - 1 = rising edge triggered + - 2 = falling edge triggered + - 3 = rising and falling edge triggered + - 4 = high level triggered + - 8 = low level triggered B. External Wakeup Interrupts: For supporting external wakeup interrupts, a child node representing the external wakeup interrupt controller should be @@ -94,6 +108,11 @@ B. External Wakeup Interrupts: For supporting external wakeup interrupts, a found on Samsung Exynos4210 SoC. - interrupt-parent: phandle of the interrupt parent to which the external wakeup interrupts are forwarded to. + - interrupts: interrupt used by multiplexed wakeup interrupts. + + In addition, following properties must be present in node of every bank + of pins supporting wake-up interrupts: + - interrupt-controller: identifies the node as interrupt-parent. - #interrupt-cells: the value of this property should be 2 - First Cell: represents the external wakeup interrupt number local to @@ -105,11 +124,63 @@ B. External Wakeup Interrupts: For supporting external wakeup interrupts, a - 4 = high level triggered - 8 = low level triggered + Node of every bank of pins supporting direct wake-up interrupts (without + multiplexing) must contain following properties: + + - interrupt-parent: phandle of the interrupt parent to which the external + wakeup interrupts are forwarded to. + - interrupts: interrupts of the interrupt parent which are used for external + wakeup interrupts from pins of the bank, must contain interrupts for all + pins of the bank. + Aliases: All the pin controller nodes should be represented in the aliases node using the following format 'pinctrl{n}' where n is a unique number for the alias. +Example: A pin-controller node with pin banks: + + pinctrl_0: pinctrl@11400000 { + compatible = "samsung,pinctrl-exynos4210"; + reg = <0x11400000 0x1000>; + interrupts = <0 47 0>; + + /* ... */ + + /* Pin bank without external interrupts */ + gpy0: gpy0 { + gpio-controller; + #gpio-cells = <2>; + }; + + /* ... */ + + /* Pin bank with external GPIO or muxed wake-up interrupts */ + gpj0: gpj0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + /* ... */ + + /* Pin bank with external direct wake-up interrupts */ + gpx0: gpx0 { + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + interrupt-parent = <&gic>; + interrupts = <0 16 0>, <0 17 0>, <0 18 0>, <0 19 0>, + <0 20 0>, <0 21 0>, <0 22 0>, <0 23 0>; + #interrupt-cells = <2>; + }; + + /* ... */ + }; + Example 1: A pin-controller node with pin groups. pinctrl_0: pinctrl@11400000 { @@ -117,6 +188,8 @@ Example 1: A pin-controller node with pin groups. reg = <0x11400000 0x1000>; interrupts = <0 47 0>; + /* ... */ + uart0_data: uart0-data { samsung,pins = "gpa0-0", "gpa0-1"; samsung,pin-function = <2>; @@ -158,20 +231,14 @@ Example 2: A pin-controller node with external wakeup interrupt controller node. pinctrl_1: pinctrl@11000000 { compatible = "samsung,pinctrl-exynos4210"; reg = <0x11000000 0x1000>; - interrupts = <0 46 0>; - interrupt-controller; - #interrupt-cells = <2>; + interrupts = <0 46 0> - wakup_eint: wakeup-interrupt-controller { + /* ... */ + + wakeup-interrupt-controller { compatible = "samsung,exynos4210-wakeup-eint"; interrupt-parent = <&gic>; - interrupt-controller; - #interrupt-cells = <2>; - interrupts = <0 16 0>, <0 17 0>, <0 18 0>, <0 19 0>, - <0 20 0>, <0 21 0>, <0 22 0>, <0 23 0>, - <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>, - <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>, - <0 32 0>; + interrupts = <0 32 0>; }; }; @@ -190,7 +257,8 @@ Example 4: Set up the default pin state for uart controller. static int s3c24xx_serial_probe(struct platform_device *pdev) { struct pinctrl *pinctrl; - ... - ... + + /* ... */ + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); } -- cgit v1.2.3 From 48ed8877244637b52aec0a114cdccd8ec26e66b1 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Mon, 17 Sep 2012 18:40:09 +0100 Subject: hwmon: Versatile Express hwmon driver hwmon framework driver for Versatile Express sensors, providing information about board level voltage (only when regulator driver is not configured), currents, temperature and power/energy usage. Labels for the values can be defined as DT properties. Signed-off-by: Pawel Moll Acked-by: Guenter Roeck --- .../devicetree/bindings/hwmon/vexpress.txt | 23 +++ Documentation/hwmon/vexpress | 34 +++ drivers/hwmon/Kconfig | 8 + drivers/hwmon/Makefile | 1 + drivers/hwmon/vexpress.c | 229 +++++++++++++++++++++ 5 files changed, 295 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/vexpress.txt create mode 100644 Documentation/hwmon/vexpress create mode 100644 drivers/hwmon/vexpress.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/hwmon/vexpress.txt b/Documentation/devicetree/bindings/hwmon/vexpress.txt new file mode 100644 index 000000000000..9c27ed694bbb --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/vexpress.txt @@ -0,0 +1,23 @@ +Versatile Express hwmon sensors +------------------------------- + +Requires node properties: +- "compatible" value : one of + "arm,vexpress-volt" + "arm,vexpress-amp" + "arm,vexpress-temp" + "arm,vexpress-power" + "arm,vexpress-energy" +- "arm,vexpress-sysreg,func" when controlled via vexpress-sysreg + (see Documentation/devicetree/bindings/arm/vexpress-sysreg.txt + for more details) + +Optional node properties: +- label : string describing the monitored value + +Example: + energy@0 { + compatible = "arm,vexpress-energy"; + arm,vexpress-sysreg,func = <13 0>; + label = "A15 Jcore"; + }; diff --git a/Documentation/hwmon/vexpress b/Documentation/hwmon/vexpress new file mode 100644 index 000000000000..557d6d5ad90d --- /dev/null +++ b/Documentation/hwmon/vexpress @@ -0,0 +1,34 @@ +Kernel driver vexpress +====================== + +Supported systems: + * ARM Ltd. Versatile Express platform + Prefix: 'vexpress' + Datasheets: + * "Hardware Description" sections of the Technical Reference Manuals + for the Versatile Express boards: + http://infocenter.arm.com/help/topic/com.arm.doc.subset.boards.express/index.html + * Section "4.4.14. System Configuration registers" of the V2M-P1 TRM: + http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0447-/index.html + +Author: Pawel Moll + +Description +----------- + +Versatile Express platform (http://www.arm.com/versatileexpress/) is a +reference & prototyping system for ARM Ltd. processors. It can be set up +from a wide range of boards, each of them containing (apart of the main +chip/FPGA) a number of microcontrollers responsible for platform +configuration and control. Theses microcontrollers can also monitor the +board and its environment by a number of internal and external sensors, +providing information about power lines voltages and currents, board +temperature and power usage. Some of them also calculate consumed energy +and provide a cumulative use counter. + +The configuration devices are _not_ memory mapped and must be accessed +via a custom interface, abstracted by the "vexpress_config" API. + +As these devices are non-discoverable, they must be described in a Device +Tree passed to the kernel. Details of the DT binding for them can be found +in Documentation/devicetree/bindings/hwmon/vexpress.txt. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index c4633de64465..db213fe958a5 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1197,6 +1197,14 @@ config SENSORS_TWL4030_MADC This driver can also be built as a module. If so it will be called twl4030-madc-hwmon. +config SENSORS_VEXPRESS + tristate "Versatile Express" + depends on VEXPRESS_CONFIG + help + This driver provides support for hardware sensors available on + the ARM Ltd's Versatile Express platform. It can provide wide + range of information like temperature, power, energy. + config SENSORS_VIA_CPUTEMP tristate "VIA CPU temperature sensor" depends on X86 diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 8d5fcb5e8e9f..aac8b7c619d6 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -121,6 +121,7 @@ obj-$(CONFIG_SENSORS_TMP102) += tmp102.o obj-$(CONFIG_SENSORS_TMP401) += tmp401.o obj-$(CONFIG_SENSORS_TMP421) += tmp421.o obj-$(CONFIG_SENSORS_TWL4030_MADC)+= twl4030-madc-hwmon.o +obj-$(CONFIG_SENSORS_VEXPRESS) += vexpress.o obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o obj-$(CONFIG_SENSORS_VIA686A) += via686a.o obj-$(CONFIG_SENSORS_VT1211) += vt1211.o diff --git a/drivers/hwmon/vexpress.c b/drivers/hwmon/vexpress.c new file mode 100644 index 000000000000..59fd1268e58a --- /dev/null +++ b/drivers/hwmon/vexpress.c @@ -0,0 +1,229 @@ +/* + * 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. + * + * Copyright (C) 2012 ARM Limited + */ + +#define DRVNAME "vexpress-hwmon" +#define pr_fmt(fmt) DRVNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +struct vexpress_hwmon_data { + struct device *hwmon_dev; + struct vexpress_config_func *func; +}; + +static ssize_t vexpress_hwmon_name_show(struct device *dev, + struct device_attribute *dev_attr, char *buffer) +{ + const char *compatible = of_get_property(dev->of_node, "compatible", + NULL); + + return sprintf(buffer, "%s\n", compatible); +} + +static ssize_t vexpress_hwmon_label_show(struct device *dev, + struct device_attribute *dev_attr, char *buffer) +{ + const char *label = of_get_property(dev->of_node, "label", NULL); + + if (!label) + return -ENOENT; + + return snprintf(buffer, PAGE_SIZE, "%s\n", label); +} + +static ssize_t vexpress_hwmon_u32_show(struct device *dev, + struct device_attribute *dev_attr, char *buffer) +{ + struct vexpress_hwmon_data *data = dev_get_drvdata(dev); + int err; + u32 value; + + err = vexpress_config_read(data->func, 0, &value); + if (err) + return err; + + return snprintf(buffer, PAGE_SIZE, "%u\n", value / + to_sensor_dev_attr(dev_attr)->index); +} + +static ssize_t vexpress_hwmon_u64_show(struct device *dev, + struct device_attribute *dev_attr, char *buffer) +{ + struct vexpress_hwmon_data *data = dev_get_drvdata(dev); + int err; + u32 value_hi, value_lo; + + err = vexpress_config_read(data->func, 0, &value_lo); + if (err) + return err; + + err = vexpress_config_read(data->func, 1, &value_hi); + if (err) + return err; + + return snprintf(buffer, PAGE_SIZE, "%llu\n", + div_u64(((u64)value_hi << 32) | value_lo, + to_sensor_dev_attr(dev_attr)->index)); +} + +static DEVICE_ATTR(name, S_IRUGO, vexpress_hwmon_name_show, NULL); + +#define VEXPRESS_HWMON_ATTRS(_name, _label_attr, _input_attr) \ +struct attribute *vexpress_hwmon_attrs_##_name[] = { \ + &dev_attr_name.attr, \ + &dev_attr_##_label_attr.attr, \ + &sensor_dev_attr_##_input_attr.dev_attr.attr, \ + NULL \ +} + +#if !defined(CONFIG_REGULATOR_VEXPRESS) +static DEVICE_ATTR(in1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, vexpress_hwmon_u32_show, + NULL, 1000); +static VEXPRESS_HWMON_ATTRS(volt, in1_label, in1_input); +static struct attribute_group vexpress_hwmon_group_volt = { + .attrs = vexpress_hwmon_attrs_volt, +}; +#endif + +static DEVICE_ATTR(curr1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, vexpress_hwmon_u32_show, + NULL, 1000); +static VEXPRESS_HWMON_ATTRS(amp, curr1_label, curr1_input); +static struct attribute_group vexpress_hwmon_group_amp = { + .attrs = vexpress_hwmon_attrs_amp, +}; + +static DEVICE_ATTR(temp1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, vexpress_hwmon_u32_show, + NULL, 1000); +static VEXPRESS_HWMON_ATTRS(temp, temp1_label, temp1_input); +static struct attribute_group vexpress_hwmon_group_temp = { + .attrs = vexpress_hwmon_attrs_temp, +}; + +static DEVICE_ATTR(power1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, vexpress_hwmon_u32_show, + NULL, 1); +static VEXPRESS_HWMON_ATTRS(power, power1_label, power1_input); +static struct attribute_group vexpress_hwmon_group_power = { + .attrs = vexpress_hwmon_attrs_power, +}; + +static DEVICE_ATTR(energy1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); +static SENSOR_DEVICE_ATTR(energy1_input, S_IRUGO, vexpress_hwmon_u64_show, + NULL, 1); +static VEXPRESS_HWMON_ATTRS(energy, energy1_label, energy1_input); +static struct attribute_group vexpress_hwmon_group_energy = { + .attrs = vexpress_hwmon_attrs_energy, +}; + +static struct of_device_id vexpress_hwmon_of_match[] = { +#if !defined(CONFIG_REGULATOR_VEXPRESS) + { + .compatible = "arm,vexpress-volt", + .data = &vexpress_hwmon_group_volt, + }, +#endif + { + .compatible = "arm,vexpress-amp", + .data = &vexpress_hwmon_group_amp, + }, { + .compatible = "arm,vexpress-temp", + .data = &vexpress_hwmon_group_temp, + }, { + .compatible = "arm,vexpress-power", + .data = &vexpress_hwmon_group_power, + }, { + .compatible = "arm,vexpress-energy", + .data = &vexpress_hwmon_group_energy, + }, + {} +}; +MODULE_DEVICE_TABLE(of, vexpress_hwmon_of_match); + +static int vexpress_hwmon_probe(struct platform_device *pdev) +{ + int err; + const struct of_device_id *match; + struct vexpress_hwmon_data *data; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + platform_set_drvdata(pdev, data); + + match = of_match_device(vexpress_hwmon_of_match, &pdev->dev); + if (!match) + return -ENODEV; + + data->func = vexpress_config_func_get_by_dev(&pdev->dev); + if (!data->func) + return -ENODEV; + + err = sysfs_create_group(&pdev->dev.kobj, match->data); + if (err) + goto error; + + data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto error; + } + + return 0; + +error: + sysfs_remove_group(&pdev->dev.kobj, match->data); + vexpress_config_func_put(data->func); + return err; +} + +static int __devexit vexpress_hwmon_remove(struct platform_device *pdev) +{ + struct vexpress_hwmon_data *data = platform_get_drvdata(pdev); + const struct of_device_id *match; + + hwmon_device_unregister(data->hwmon_dev); + + match = of_match_device(vexpress_hwmon_of_match, &pdev->dev); + sysfs_remove_group(&pdev->dev.kobj, match->data); + + vexpress_config_func_put(data->func); + + return 0; +} + +static struct platform_driver vexpress_hwmon_driver = { + .probe = vexpress_hwmon_probe, + .remove = __devexit_p(vexpress_hwmon_remove), + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + .of_match_table = vexpress_hwmon_of_match, + }, +}; + +module_platform_driver(vexpress_hwmon_driver); + +MODULE_AUTHOR("Pawel Moll "); +MODULE_DESCRIPTION("Versatile Express hwmon sensors driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:vexpress-hwmon"); -- cgit v1.2.3 From 0384e90b853357d24935c65ba0e1bdd27faa6e58 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sun, 7 Oct 2012 18:19:44 +0200 Subject: spi/mcspi: allow configuration of pin directions Allow D0 to be an input and D1 to be an output, configurable via platform data and a new DT property. Based on a patch from Matus Ujhelyi Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/omap-spi.txt | 4 +++- drivers/spi/spi-omap2-mcspi.c | 25 ++++++++++++++++------ include/linux/platform_data/spi-omap2-mcspi.h | 4 ++++ 3 files changed, 25 insertions(+), 8 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/spi/omap-spi.txt b/Documentation/devicetree/bindings/spi/omap-spi.txt index 81df374adbb9..2ef0a6b85653 100644 --- a/Documentation/devicetree/bindings/spi/omap-spi.txt +++ b/Documentation/devicetree/bindings/spi/omap-spi.txt @@ -6,7 +6,9 @@ Required properties: - "ti,omap4-spi" for OMAP4+. - ti,spi-num-cs : Number of chipselect supported by the instance. - ti,hwmods: Name of the hwmod associated to the McSPI - +- ti,pindir-d0-in-d1-out: Select the D0 pin as input and D1 as + output. The default is D0 as output and + D1 as input. Example: diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 3542fdc664b1..51046332677c 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -130,6 +130,7 @@ struct omap2_mcspi { struct omap2_mcspi_dma *dma_channels; struct device *dev; struct omap2_mcspi_regs ctx; + unsigned int pin_dir:1; }; struct omap2_mcspi_cs { @@ -765,8 +766,15 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS * REVISIT: this controller could support SPI_3WIRE mode. */ - l &= ~(OMAP2_MCSPI_CHCONF_IS|OMAP2_MCSPI_CHCONF_DPE1); - l |= OMAP2_MCSPI_CHCONF_DPE0; + if (mcspi->pin_dir == MCSPI_PINDIR_D0_OUT_D1_IN) { + l &= ~OMAP2_MCSPI_CHCONF_IS; + l &= ~OMAP2_MCSPI_CHCONF_DPE1; + l |= OMAP2_MCSPI_CHCONF_DPE0; + } else { + l |= OMAP2_MCSPI_CHCONF_IS; + l |= OMAP2_MCSPI_CHCONF_DPE1; + l &= ~OMAP2_MCSPI_CHCONF_DPE0; + } /* wordlength */ l &= ~OMAP2_MCSPI_CHCONF_WL_MASK; @@ -1167,6 +1175,11 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev) master->cleanup = omap2_mcspi_cleanup; master->dev.of_node = node; + dev_set_drvdata(&pdev->dev, master); + + mcspi = spi_master_get_devdata(master); + mcspi->master = master; + match = of_match_device(omap_mcspi_of_match, &pdev->dev); if (match) { u32 num_cs = 1; /* default number of chipselect */ @@ -1175,19 +1188,17 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev) of_property_read_u32(node, "ti,spi-num-cs", &num_cs); master->num_chipselect = num_cs; master->bus_num = bus_num++; + if (of_get_property(node, "ti,pindir-d0-in-d1-out", NULL)) + mcspi->pin_dir = MCSPI_PINDIR_D0_IN_D1_OUT; } else { pdata = pdev->dev.platform_data; master->num_chipselect = pdata->num_cs; if (pdev->id != -1) master->bus_num = pdev->id; + mcspi->pin_dir = pdata->pin_dir; } regs_offset = pdata->regs_offset; - dev_set_drvdata(&pdev->dev, master); - - mcspi = spi_master_get_devdata(master); - mcspi->master = master; - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { status = -ENODEV; diff --git a/include/linux/platform_data/spi-omap2-mcspi.h b/include/linux/platform_data/spi-omap2-mcspi.h index a357eb26bd25..ce70f7b5a8e1 100644 --- a/include/linux/platform_data/spi-omap2-mcspi.h +++ b/include/linux/platform_data/spi-omap2-mcspi.h @@ -7,9 +7,13 @@ #define OMAP4_MCSPI_REG_OFFSET 0x100 +#define MCSPI_PINDIR_D0_OUT_D1_IN 0 +#define MCSPI_PINDIR_D0_IN_D1_OUT 1 + struct omap2_mcspi_platform_config { unsigned short num_cs; unsigned int regs_offset; + unsigned int pin_dir:1; }; struct omap2_mcspi_dev_attr { -- cgit v1.2.3 From 006694d099e86e5a928fb39bdea280ab42c9d59c Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 15 Oct 2012 14:16:59 +0100 Subject: regulator: gpio-regulator: Allow use of GPIO controlled regulators though DT Here we provide the GPIO Regulator driver with Device Tree capability, so that when a platform is booting with DT instead of platform data we can still make full use of it. Signed-off-by: Lee Jones Reviewed-by: Stephen Warren Signed-off-by: Mark Brown --- .../bindings/regulator/gpio-regulator.txt | 36 +++++++++ drivers/regulator/gpio-regulator.c | 94 ++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/gpio-regulator.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/regulator/gpio-regulator.txt b/Documentation/devicetree/bindings/regulator/gpio-regulator.txt new file mode 100644 index 000000000000..3703be2ab821 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/gpio-regulator.txt @@ -0,0 +1,36 @@ +GPIO controlled regulators + +Required properties: +- compatible : Must be "regulator-gpio". + +Optional properties: +- gpio-enable : GPIO to use to enable/disable the regulator. +- gpios : GPIO group used to control voltage. +- states : Selection of available voltages and GPIO configs. +- startup-delay-us : Startup time in microseconds. +- enable-active-high : Polarity of GPIO is active high (default is low). + +Any property defined as part of the core regulator binding defined in +regulator.txt can also be used. + +Example: + + mmciv: gpio-regulator { + compatible = "regulator-gpio"; + + regulator-name = "mmci-gpio-supply"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2600000>; + regulator-boot-on; + + gpio-enable = <&gpio0 23 0x4>; + gpios = <&gpio0 24 0x4 + &gpio0 25 0x4>; + states = <1800000 0x3 + 2200000 0x2 + 2600000 0x1 + 2900000 0x0>; + + startup-delay-us = <100000>; + enable-active-high; + }; diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index 8b5944f2d7d1..e467d0ac8705 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -28,9 +28,12 @@ #include #include #include +#include #include #include #include +#include +#include struct gpio_regulator_data { struct regulator_desc desc; @@ -129,6 +132,84 @@ static struct regulator_ops gpio_regulator_voltage_ops = { .list_voltage = gpio_regulator_list_voltage, }; +struct gpio_regulator_config * +of_get_gpio_regulator_config(struct device *dev, struct device_node *np) +{ + struct gpio_regulator_config *config; + struct property *prop; + const char *regtype; + int proplen, gpio, i; + + config = devm_kzalloc(dev, + sizeof(struct gpio_regulator_config), + GFP_KERNEL); + if (!config) + return ERR_PTR(-ENOMEM); + + config->init_data = of_get_regulator_init_data(dev, np); + if (!config->init_data) + return ERR_PTR(-EINVAL); + + config->supply_name = config->init_data->constraints.name; + + if (of_property_read_bool(np, "enable-active-high")) + config->enable_high = true; + + if (of_property_read_bool(np, "enable-at-boot")) + config->enabled_at_boot = true; + + of_property_read_u32(np, "startup-delay-us", &config->startup_delay); + + config->enable_gpio = of_get_named_gpio(np, "enable-gpio", 0); + + /* Fetch GPIOs. */ + for (i = 0; ; i++) + if (of_get_named_gpio(np, "gpios", i) < 0) + break; + config->nr_gpios = i; + + config->gpios = devm_kzalloc(dev, + sizeof(struct gpio) * config->nr_gpios, + GFP_KERNEL); + if (!config->gpios) + return ERR_PTR(-ENOMEM); + + for (i = 0; config->nr_gpios; i++) { + gpio = of_get_named_gpio(np, "gpios", i); + if (gpio < 0) + break; + config->gpios[i].gpio = gpio; + } + + /* Fetch states. */ + prop = of_find_property(np, "states", NULL); + proplen = prop->length / sizeof(int); + + config->states = devm_kzalloc(dev, + sizeof(struct gpio_regulator_state) + * (proplen / 2), + GFP_KERNEL); + if (!config->states) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < proplen / 2; i++) { + config->states[i].value = + be32_to_cpup((int *)prop->value + (i * 2)); + config->states[i].gpios = + be32_to_cpup((int *)prop->value + (i * 2 + 1)); + } + config->nr_states = i; + + of_property_read_string(np, "regulator-type", ®type); + + if (!strncmp("voltage", regtype, 7)) + config->type = REGULATOR_VOLTAGE; + else if (!strncmp("current", regtype, 7)) + config->type = REGULATOR_CURRENT; + + return config; +} + static struct regulator_ops gpio_regulator_current_ops = { .get_current_limit = gpio_regulator_get_value, .set_current_limit = gpio_regulator_set_current_limit, @@ -137,10 +218,17 @@ static struct regulator_ops gpio_regulator_current_ops = { static int __devinit gpio_regulator_probe(struct platform_device *pdev) { struct gpio_regulator_config *config = pdev->dev.platform_data; + struct device_node *np = pdev->dev.of_node; struct gpio_regulator_data *drvdata; struct regulator_config cfg = { }; int ptr, ret, state; + if (np) { + config = of_get_gpio_regulator_config(&pdev->dev, np); + if (IS_ERR(config)) + return PTR_ERR(config); + } + drvdata = devm_kzalloc(&pdev->dev, sizeof(struct gpio_regulator_data), GFP_KERNEL); if (drvdata == NULL) { @@ -270,12 +358,18 @@ static int __devexit gpio_regulator_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id regulator_gpio_of_match[] __devinitconst = { + { .compatible = "regulator-gpio", }, + {}, +}; + static struct platform_driver gpio_regulator_driver = { .probe = gpio_regulator_probe, .remove = __devexit_p(gpio_regulator_remove), .driver = { .name = "gpio-regulator", .owner = THIS_MODULE, + .of_match_table = regulator_gpio_of_match, }, }; -- cgit v1.2.3 From 2eae613b95a786714bd1b5825ea3abc78d229d3f Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Tue, 23 Oct 2012 22:51:33 +0900 Subject: ARM: EXYNOS: Add MFC device tree support This patch adds device tree entry for MFC v6 in the Exynos5 SoC. Makes the required changes in the clock files and adds MFC to the DT device list. Signed-off-by: Naveen Krishna Chatradhi Signed-off-by: Arun Kumar K [kgene.kim@samsung.com: fixed section mismatches Seung-Woo Kim reported] Signed-off-by: Kukjin Kim --- .../devicetree/bindings/media/s5p-mfc.txt | 23 +++++++++++++++ arch/arm/boot/dts/exynos5250-smdk5250.dts | 5 ++++ arch/arm/boot/dts/exynos5250.dtsi | 6 ++++ arch/arm/mach-exynos/Kconfig | 1 + arch/arm/mach-exynos/clock-exynos5.c | 2 +- arch/arm/mach-exynos/mach-exynos5-dt.c | 16 ++++++++++ arch/arm/plat-samsung/devs.c | 1 + arch/arm/plat-samsung/include/plat/mfc.h | 11 +++++++ arch/arm/plat-samsung/s5p-dev-mfc.c | 34 ++++++++++++++++++++++ 9 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/media/s5p-mfc.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/media/s5p-mfc.txt b/Documentation/devicetree/bindings/media/s5p-mfc.txt new file mode 100644 index 000000000000..67ec3d4ccc7f --- /dev/null +++ b/Documentation/devicetree/bindings/media/s5p-mfc.txt @@ -0,0 +1,23 @@ +* Samsung Multi Format Codec (MFC) + +Multi Format Codec (MFC) is the IP present in Samsung SoCs which +supports high resolution decoding and encoding functionalities. +The MFC device driver is a v4l2 driver which can encode/decode +video raw/elementary streams and has support for all popular +video codecs. + +Required properties: + - compatible : value should be either one among the following + (a) "samsung,mfc-v5" for MFC v5 present in Exynos4 SoCs + (b) "samsung,mfc-v6" for MFC v6 present in Exynos5 SoCs + + - reg : Physical base address of the IP registers and length of memory + mapped region. + + - interrupts : MFC interrupt number to the CPU. + + - samsung,mfc-r : Base address of the first memory bank used by MFC + for DMA contiguous memory allocation and its size. + + - samsung,mfc-l : Base address of the second memory bank used by MFC + for DMA contiguous memory allocation and its size. diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts index a352df403b7a..21d4ccdb0a5a 100644 --- a/arch/arm/boot/dts/exynos5250-smdk5250.dts +++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts @@ -166,4 +166,9 @@ spi_2: spi@12d40000 { status = "disabled"; }; + + codec@11000000 { + samsung,mfc-r = <0x43000000 0x800000>; + samsung,mfc-l = <0x51000000 0x800000>; + }; }; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index dddfd6e444dc..49546bc43ec0 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -62,6 +62,12 @@ interrupts = <0 42 0>; }; + codec@11000000 { + compatible = "samsung,mfc-v6"; + reg = <0x11000000 0x10000>; + interrupts = <0 96 0>; + }; + rtc { compatible = "samsung,s3c6410-rtc"; reg = <0x101E0000 0x100>; diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index da55107033dd..bb3b09aa9183 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -63,6 +63,7 @@ config SOC_EXYNOS5250 depends on ARCH_EXYNOS5 select S5P_PM if PM select S5P_SLEEP if PM + select S5P_DEV_MFC select SAMSUNG_DMADEV help Enable EXYNOS5250 SoC support diff --git a/arch/arm/mach-exynos/clock-exynos5.c b/arch/arm/mach-exynos/clock-exynos5.c index c44ca1ee1b8d..8c4e3253cdb0 100644 --- a/arch/arm/mach-exynos/clock-exynos5.c +++ b/arch/arm/mach-exynos/clock-exynos5.c @@ -664,7 +664,7 @@ static struct clk exynos5_init_clocks_off[] = { .ctrlbit = (1 << 25), }, { .name = "mfc", - .devname = "s5p-mfc", + .devname = "s5p-mfc-v6", .enable = exynos5_clk_ip_mfc_ctrl, .ctrlbit = (1 << 0), }, { diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c index db1cd8eacf28..b7f1154088bd 100644 --- a/arch/arm/mach-exynos/mach-exynos5-dt.c +++ b/arch/arm/mach-exynos/mach-exynos5-dt.c @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include @@ -18,6 +20,7 @@ #include #include +#include #include "common.h" @@ -72,6 +75,7 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = { "exynos-gsc.2", NULL), OF_DEV_AUXDATA("samsung,exynos5-gsc", EXYNOS5_PA_GSC3, "exynos-gsc.3", NULL), + OF_DEV_AUXDATA("samsung,mfc-v6", 0x11000000, "s5p-mfc-v6", NULL), {}, }; @@ -92,6 +96,17 @@ static char const *exynos5250_dt_compat[] __initdata = { NULL }; +static void __init exynos5_reserve(void) +{ + struct s5p_mfc_dt_meminfo mfc_mem; + + /* Reserve memory for MFC only if it's available */ + mfc_mem.compatible = "samsung,mfc-v6"; + if (of_scan_flat_dt(s5p_fdt_find_mfc_mem, &mfc_mem)) + s5p_mfc_reserve_mem(mfc_mem.roff, mfc_mem.rsize, mfc_mem.loff, + mfc_mem.lsize); +} + DT_MACHINE_START(EXYNOS5_DT, "SAMSUNG EXYNOS5 (Flattened Device Tree)") /* Maintainer: Kukjin Kim */ .init_irq = exynos5_init_irq, @@ -103,4 +118,5 @@ DT_MACHINE_START(EXYNOS5_DT, "SAMSUNG EXYNOS5 (Flattened Device Tree)") .timer = &exynos4_timer, .dt_compat = exynos5250_dt_compat, .restart = exynos5_restart, + .reserve = exynos5_reserve, MACHINE_END diff --git a/arch/arm/plat-samsung/devs.c b/arch/arm/plat-samsung/devs.c index 03f654d55eff..52dfa8f914c7 100644 --- a/arch/arm/plat-samsung/devs.c +++ b/arch/arm/plat-samsung/devs.c @@ -933,6 +933,7 @@ struct platform_device s5p_device_mfc_r = { .coherent_dma_mask = DMA_BIT_MASK(32), }, }; + #endif /* CONFIG_S5P_DEV_MFC */ /* MIPI CSIS */ diff --git a/arch/arm/plat-samsung/include/plat/mfc.h b/arch/arm/plat-samsung/include/plat/mfc.h index ac13227272f0..e6d7c42d68b6 100644 --- a/arch/arm/plat-samsung/include/plat/mfc.h +++ b/arch/arm/plat-samsung/include/plat/mfc.h @@ -10,6 +10,14 @@ #ifndef __PLAT_SAMSUNG_MFC_H #define __PLAT_SAMSUNG_MFC_H __FILE__ +struct s5p_mfc_dt_meminfo { + unsigned long loff; + unsigned long lsize; + unsigned long roff; + unsigned long rsize; + char *compatible; +}; + /** * s5p_mfc_reserve_mem - function to early reserve memory for MFC driver * @rbase: base address for MFC 'right' memory interface @@ -24,4 +32,7 @@ void __init s5p_mfc_reserve_mem(phys_addr_t rbase, unsigned int rsize, phys_addr_t lbase, unsigned int lsize); +int __init s5p_fdt_find_mfc_mem(unsigned long node, const char *uname, + int depth, void *data); + #endif /* __PLAT_SAMSUNG_MFC_H */ diff --git a/arch/arm/plat-samsung/s5p-dev-mfc.c b/arch/arm/plat-samsung/s5p-dev-mfc.c index ad6089465e2a..5ec104b5408b 100644 --- a/arch/arm/plat-samsung/s5p-dev-mfc.c +++ b/arch/arm/plat-samsung/s5p-dev-mfc.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include @@ -69,3 +71,35 @@ static int __init s5p_mfc_memory_init(void) return 0; } device_initcall(s5p_mfc_memory_init); + +#ifdef CONFIG_OF +int __init s5p_fdt_find_mfc_mem(unsigned long node, const char *uname, + int depth, void *data) +{ + __be32 *prop; + unsigned long len; + struct s5p_mfc_dt_meminfo *mfc_mem = data; + + if (!data) + return 0; + + if (!of_flat_dt_is_compatible(node, mfc_mem->compatible)) + return 0; + + prop = of_get_flat_dt_prop(node, "samsung,mfc-l", &len); + if (!prop || (len != 2 * sizeof(unsigned long))) + return 0; + + mfc_mem->loff = be32_to_cpu(prop[0]); + mfc_mem->lsize = be32_to_cpu(prop[1]); + + prop = of_get_flat_dt_prop(node, "samsung,mfc-r", &len); + if (!prop || (len != 2 * sizeof(unsigned long))) + return 0; + + mfc_mem->roff = be32_to_cpu(prop[0]); + mfc_mem->rsize = be32_to_cpu(prop[1]); + + return 1; +} +#endif -- cgit v1.2.3 From ab72fa5523866cb93681abb9f9a401d43a93b7be Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Mon, 22 Oct 2012 11:58:02 -0400 Subject: Documentation: of-serial.txt - update for clocks phandle for clk Signed-off-by: Murali Karicheri Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/tty/serial/of-serial.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/tty/serial/of-serial.txt b/Documentation/devicetree/bindings/tty/serial/of-serial.txt index ba385f2e0ddc..1e1145ca4f3c 100644 --- a/Documentation/devicetree/bindings/tty/serial/of-serial.txt +++ b/Documentation/devicetree/bindings/tty/serial/of-serial.txt @@ -14,7 +14,10 @@ Required properties: - "serial" if the port type is unknown. - reg : offset and length of the register set for the device. - interrupts : should contain uart interrupt. -- clock-frequency : the input clock frequency for the UART. +- clock-frequency : the input clock frequency for the UART + or + clocks phandle to refer to the clk used as per Documentation/devicetree + /bindings/clock/clock-bindings.txt Optional properties: - current-speed : the current active speed of the UART. -- cgit v1.2.3 From d0f1c7ffaa32bdda2d413d2db41c51bbdd105834 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Sat, 15 Sep 2012 22:18:10 -0600 Subject: ARM: bcm2835: implement machine restart hook Implement the machine restart hook using the SoC's watchdog timer module. To support this, define a DT binding for the watchdog module, and add it to the device tree. The downstream rpi-split branch contains a full watchdog timer driver implementation, which also implements the restart hook. However, the restart function is largely separate from the watchdog driver, so for simplicity, the restart hook is implemented here directly in the main machine source file. Overall structure (separate setup/restart) functions derived from the picoxcell ARM support. Watchdog register IO sequence taken from code by Simon Arlott. Note that the watchdog module is not documented in BCM2835-ARM-Peripherals.pdf. Signed-off-by: Stephen Warren --- .../bindings/watchdog/brcm,bcm2835-pm-wdog.txt | 13 ++++++ arch/arm/boot/dts/bcm2835.dtsi | 5 +++ arch/arm/mach-bcm2835/bcm2835.c | 46 ++++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 Documentation/devicetree/bindings/watchdog/brcm,bcm2835-pm-wdog.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/watchdog/brcm,bcm2835-pm-wdog.txt b/Documentation/devicetree/bindings/watchdog/brcm,bcm2835-pm-wdog.txt new file mode 100644 index 000000000000..d209366b4a69 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/brcm,bcm2835-pm-wdog.txt @@ -0,0 +1,13 @@ +BCM2835 Watchdog timer + +Required properties: + +- compatible : should be "brcm,bcm2835-pm-wdt" +- reg : Specifies base physical address and size of the registers. + +Example: + +watchdog { + compatible = "brcm,bcm2835-pm-wdt"; + reg = <0x7e100000 0x28>; +}; diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi index 0b619398532c..5c5cbafed191 100644 --- a/arch/arm/boot/dts/bcm2835.dtsi +++ b/arch/arm/boot/dts/bcm2835.dtsi @@ -29,6 +29,11 @@ #interrupt-cells = <2>; }; + watchdog { + compatible = "brcm,bcm2835-pm-wdt"; + reg = <0x7e100000 0x28>; + }; + uart@20201000 { compatible = "brcm,bcm2835-pl011", "arm,pl011", "arm,primecell"; reg = <0x7e201000 0x1000>; diff --git a/arch/arm/mach-bcm2835/bcm2835.c b/arch/arm/mach-bcm2835/bcm2835.c index f6fea4933571..c4dd210f4db1 100644 --- a/arch/arm/mach-bcm2835/bcm2835.c +++ b/arch/arm/mach-bcm2835/bcm2835.c @@ -12,8 +12,10 @@ * GNU General Public License for more details. */ +#include #include #include +#include #include #include #include @@ -23,6 +25,48 @@ #include +#define PM_RSTC 0x1c +#define PM_WDOG 0x24 + +#define PM_PASSWORD 0x5a000000 +#define PM_RSTC_WRCFG_MASK 0x00000030 +#define PM_RSTC_WRCFG_FULL_RESET 0x00000020 + +static void __iomem *wdt_regs; + +/* + * The machine restart method can be called from an atomic context so we won't + * be able to ioremap the regs then. + */ +static void bcm2835_setup_restart(void) +{ + struct device_node *np = of_find_compatible_node(NULL, NULL, + "brcm,bcm2835-pm-wdt"); + if (WARN(!np, "unable to setup watchdog restart")) + return; + + wdt_regs = of_iomap(np, 0); + WARN(!wdt_regs, "failed to remap watchdog regs"); +} + +static void bcm2835_restart(char mode, const char *cmd) +{ + u32 val; + + if (!wdt_regs) + return; + + /* use a timeout of 10 ticks (~150us) */ + writel_relaxed(10 | PM_PASSWORD, wdt_regs + PM_WDOG); + val = readl_relaxed(wdt_regs + PM_RSTC); + val &= ~PM_RSTC_WRCFG_MASK; + val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET; + writel_relaxed(val, wdt_regs + PM_RSTC); + + /* No sleeping, possibly atomic. */ + mdelay(1); +} + static struct map_desc io_map __initdata = { .virtual = BCM2835_PERIPH_VIRT, .pfn = __phys_to_pfn(BCM2835_PERIPH_PHYS), @@ -39,6 +83,7 @@ void __init bcm2835_init(void) { int ret; + bcm2835_setup_restart(); bcm2835_init_clocks(); ret = of_platform_populate(NULL, of_default_bus_match_table, NULL, @@ -60,5 +105,6 @@ DT_MACHINE_START(BCM2835, "BCM2835") .handle_irq = bcm2835_handle_irq, .init_machine = bcm2835_init, .timer = &bcm2835_timer, + .restart = bcm2835_restart, .dt_compat = bcm2835_compat MACHINE_END -- cgit v1.2.3 From 2066306099d35430d01ed1b3d81c0536d77bf390 Mon Sep 17 00:00:00 2001 From: Josh Cartwright Date: Tue, 23 Oct 2012 19:53:06 -0500 Subject: arm: l2cc: doc: fix device tree example typo The list of attributes above details the use of the 'filter-ranges' property, but the example improperly used 'filter-latency'. Make these consistent by fixing up the example. Signed-off-by: Josh Cartwright Signed-off-by: Rob Herring --- Documentation/devicetree/bindings/arm/l2cc.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/l2cc.txt b/Documentation/devicetree/bindings/arm/l2cc.txt index 7ca52161e7ab..7c3ee3aeb7b7 100644 --- a/Documentation/devicetree/bindings/arm/l2cc.txt +++ b/Documentation/devicetree/bindings/arm/l2cc.txt @@ -37,7 +37,7 @@ L2: cache-controller { reg = <0xfff12000 0x1000>; arm,data-latency = <1 1 1>; arm,tag-latency = <2 2 2>; - arm,filter-latency = <0x80000000 0x8000000>; + arm,filter-ranges = <0x80000000 0x8000000>; cache-unified; cache-level = <2>; interrupts = <45>; -- cgit v1.2.3 From 9c4566a117a6fe404a0e49b27ac71b631945a70f Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Thu, 25 Oct 2012 10:41:39 -0600 Subject: ARM: socfpga: Enable SMP for socfpga Enable SMP for the SOCFPGA platform. Signed-off-by: Pavel Machek Signed-off-by: Dinh Nguyen Reviewed-by: Rob Herring Signed-off-by: Arnd Bergmann --- .../bindings/arm/altera/socfpga-reset.txt | 11 ++ .../bindings/arm/altera/socfpga-system.txt | 11 ++ arch/arm/boot/dts/socfpga.dtsi | 10 ++ arch/arm/configs/socfpga_defconfig | 3 +- arch/arm/mach-socfpga/Kconfig | 1 + arch/arm/mach-socfpga/Makefile | 1 + arch/arm/mach-socfpga/core.h | 34 ++++++ arch/arm/mach-socfpga/headsmp.S | 24 +++++ arch/arm/mach-socfpga/platsmp.c | 116 +++++++++++++++++++++ arch/arm/mach-socfpga/socfpga.c | 45 +++++++- 10 files changed, 254 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/altera/socfpga-reset.txt create mode 100644 Documentation/devicetree/bindings/arm/altera/socfpga-system.txt create mode 100644 arch/arm/mach-socfpga/core.h create mode 100644 arch/arm/mach-socfpga/headsmp.S create mode 100644 arch/arm/mach-socfpga/platsmp.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/altera/socfpga-reset.txt b/Documentation/devicetree/bindings/arm/altera/socfpga-reset.txt new file mode 100644 index 000000000000..ecdb57d69dbf --- /dev/null +++ b/Documentation/devicetree/bindings/arm/altera/socfpga-reset.txt @@ -0,0 +1,11 @@ +Altera SOCFPGA Reset Manager + +Required properties: +- compatible : "altr,rst-mgr" +- reg : Should contain 1 register ranges(address and length) + +Example: + rstmgr@ffd05000 { + compatible = "altr,rst-mgr"; + reg = <0xffd05000 0x1000>; + }; diff --git a/Documentation/devicetree/bindings/arm/altera/socfpga-system.txt b/Documentation/devicetree/bindings/arm/altera/socfpga-system.txt new file mode 100644 index 000000000000..07c65e3cdcbe --- /dev/null +++ b/Documentation/devicetree/bindings/arm/altera/socfpga-system.txt @@ -0,0 +1,11 @@ +Altera SOCFPGA System Manager + +Required properties: +- compatible : "altr,sys-mgr" +- reg : Should contain 1 register ranges(address and length) + +Example: + sysmgr@ffd08000 { + compatible = "altr,sys-mgr"; + reg = <0xffd08000 0x1000>; + }; diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index 0772f5739f59..19aec421bb26 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -143,5 +143,15 @@ reg-shift = <2>; reg-io-width = <4>; }; + + rstmgr@ffd05000 { + compatible = "altr,rst-mgr"; + reg = <0xffd05000 0x1000>; + }; + + sysmgr@ffd08000 { + compatible = "altr,sys-mgr"; + reg = <0xffd08000 0x4000>; + }; }; }; diff --git a/arch/arm/configs/socfpga_defconfig b/arch/arm/configs/socfpga_defconfig index 0ac1293dba10..4e1ce211d43f 100644 --- a/arch/arm/configs/socfpga_defconfig +++ b/arch/arm/configs/socfpga_defconfig @@ -18,9 +18,10 @@ CONFIG_MODULE_UNLOAD=y CONFIG_ARCH_SOCFPGA=y CONFIG_MACH_SOCFPGA_CYCLONE5=y CONFIG_ARM_THUMBEE=y +# CONFIG_ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA is not set # CONFIG_CACHE_L2X0 is not set CONFIG_HIGH_RES_TIMERS=y -CONFIG_VMSPLIT_2G=y +CONFIG_SMP=y CONFIG_NR_CPUS=2 CONFIG_AEABI=y CONFIG_ZBOOT_ROM_TEXT=0x0 diff --git a/arch/arm/mach-socfpga/Kconfig b/arch/arm/mach-socfpga/Kconfig index 803a3281feb5..566e804d4036 100644 --- a/arch/arm/mach-socfpga/Kconfig +++ b/arch/arm/mach-socfpga/Kconfig @@ -12,5 +12,6 @@ config ARCH_SOCFPGA select GENERIC_CLOCKEVENTS select GPIO_PL061 if GPIOLIB select HAVE_ARM_SCU + select HAVE_SMP select SPARSE_IRQ select USE_OF diff --git a/arch/arm/mach-socfpga/Makefile b/arch/arm/mach-socfpga/Makefile index 4fb93240971d..6dd7a93a90fe 100644 --- a/arch/arm/mach-socfpga/Makefile +++ b/arch/arm/mach-socfpga/Makefile @@ -3,3 +3,4 @@ # obj-y := socfpga.o +obj-$(CONFIG_SMP) += headsmp.o platsmp.o diff --git a/arch/arm/mach-socfpga/core.h b/arch/arm/mach-socfpga/core.h new file mode 100644 index 000000000000..9941caa94931 --- /dev/null +++ b/arch/arm/mach-socfpga/core.h @@ -0,0 +1,34 @@ +/* + * Copyright 2012 Pavel Machek + * Copyright (C) 2012 Altera Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __MACH_CORE_H +#define __MACH_CORE_H + +extern void secondary_startup(void); +extern void __iomem *socfpga_scu_base_addr; + +extern void socfpga_init_clocks(void); +extern void socfpga_sysmgr_init(void); + +extern struct smp_operations socfpga_smp_ops; +extern char secondary_trampoline, secondary_trampoline_end; + +#define SOCFPGA_SCU_VIRT_BASE 0xfffec000 + +#endif diff --git a/arch/arm/mach-socfpga/headsmp.S b/arch/arm/mach-socfpga/headsmp.S new file mode 100644 index 000000000000..17d6eaf9afff --- /dev/null +++ b/arch/arm/mach-socfpga/headsmp.S @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2003 ARM Limited + * Copyright (c) u-boot contributors + * Copyright (c) 2012 Pavel Machek + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include + + __INIT + +#define CPU1_START_ADDR 0xffd08010 + +ENTRY(secondary_trampoline) + movw r0, #:lower16:CPU1_START_ADDR + movt r0, #:upper16:CPU1_START_ADDR + + ldr r1, [r0] + bx r1 + +ENTRY(secondary_trampoline_end) diff --git a/arch/arm/mach-socfpga/platsmp.c b/arch/arm/mach-socfpga/platsmp.c new file mode 100644 index 000000000000..68dd1b69512a --- /dev/null +++ b/arch/arm/mach-socfpga/platsmp.c @@ -0,0 +1,116 @@ +/* + * Copyright 2010-2011 Calxeda, Inc. + * Copyright 2012 Pavel Machek + * Based on platsmp.c, Copyright (C) 2002 ARM Ltd. + * Copyright (C) 2012 Altera Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "core.h" + +extern void __iomem *sys_manager_base_addr; +extern void __iomem *rst_manager_base_addr; + +static void __cpuinit socfpga_secondary_init(unsigned int cpu) +{ + /* + * if any interrupts are already enabled for the primary + * core (e.g. timer irq), then they will not have been enabled + * for us: do so + */ + gic_secondary_init(0); +} + +static int __cpuinit socfpga_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + int trampoline_size = &secondary_trampoline_end - &secondary_trampoline; + + memcpy(phys_to_virt(0), &secondary_trampoline, trampoline_size); + + __raw_writel(virt_to_phys(secondary_startup), (sys_manager_base_addr+0x10)); + + flush_cache_all(); + smp_wmb(); + outer_clean_range(0, trampoline_size); + + /* This will release CPU #1 out of reset.*/ + __raw_writel(0, rst_manager_base_addr + 0x10); + + return 0; +} + +/* + * Initialise the CPU possible map early - this describes the CPUs + * which may be present or become present in the system. + */ +static void __init socfpga_smp_init_cpus(void) +{ + unsigned int i, ncores; + + ncores = scu_get_core_count(socfpga_scu_base_addr); + + for (i = 0; i < ncores; i++) + set_cpu_possible(i, true); + + /* sanity check */ + if (ncores > num_possible_cpus()) { + pr_warn("socfpga: no. of cores (%d) greater than configured" + "maximum of %d - clipping\n", ncores, num_possible_cpus()); + ncores = num_possible_cpus(); + } + + for (i = 0; i < ncores; i++) + set_cpu_possible(i, true); + + set_smp_cross_call(gic_raise_softirq); +} + +static void __init socfpga_smp_prepare_cpus(unsigned int max_cpus) +{ + scu_enable(socfpga_scu_base_addr); +} + +/* + * platform-specific code to shutdown a CPU + * + * Called with IRQs disabled + */ +static void socfpga_cpu_die(unsigned int cpu) +{ + cpu_do_idle(); + + /* We should have never returned from idle */ + panic("cpu %d unexpectedly exit from shutdown\n", cpu); +} + +struct smp_operations socfpga_smp_ops __initdata = { + .smp_init_cpus = socfpga_smp_init_cpus, + .smp_prepare_cpus = socfpga_smp_prepare_cpus, + .smp_secondary_init = socfpga_secondary_init, + .smp_boot_secondary = socfpga_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_die = socfpga_cpu_die, +#endif +}; diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index f01e1ebf5396..ab81ea91a7c4 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c @@ -15,23 +15,64 @@ * along with this program. If not, see . */ #include +#include #include #include #include #include #include +#include -extern void socfpga_init_clocks(void); +#include "core.h" + +void __iomem *socfpga_scu_base_addr = ((void __iomem *)(SOCFPGA_SCU_VIRT_BASE)); +void __iomem *sys_manager_base_addr; +void __iomem *rst_manager_base_addr; + +static struct map_desc scu_io_desc __initdata = { + .virtual = SOCFPGA_SCU_VIRT_BASE, + .pfn = 0, /* run-time */ + .length = SZ_8K, + .type = MT_DEVICE, +}; + +static void __init socfpga_scu_map_io(void) +{ + unsigned long base; + + /* Get SCU base */ + asm("mrc p15, 4, %0, c15, c0, 0" : "=r" (base)); + + scu_io_desc.pfn = __phys_to_pfn(base); + iotable_init(&scu_io_desc, 1); +} + +static void __init socfpga_map_io(void) +{ + socfpga_scu_map_io(); +} const static struct of_device_id irq_match[] = { { .compatible = "arm,cortex-a9-gic", .data = gic_of_init, }, {} }; +void __init socfpga_sysmgr_init(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "altr,sys-mgr"); + sys_manager_base_addr = of_iomap(np, 0); + + np = of_find_compatible_node(NULL, NULL, "altr,rst-mgr"); + rst_manager_base_addr = of_iomap(np, 0); +} + static void __init gic_init_irq(void) { of_irq_init(irq_match); + socfpga_sysmgr_init(); } static void socfpga_cyclone5_restart(char mode, const char *cmd) @@ -53,6 +94,8 @@ static const char *altera_dt_match[] = { }; DT_MACHINE_START(SOCFPGA, "Altera SOCFPGA") + .smp = smp_ops(socfpga_smp_ops), + .map_io = socfpga_map_io, .init_irq = gic_init_irq, .handle_irq = gic_handle_irq, .timer = &dw_apb_timer, -- cgit v1.2.3 From 689e331f3fb6e0a44155eef35fe19caf3285c6ce Mon Sep 17 00:00:00 2001 From: Sekhar Nori Date: Tue, 28 Aug 2012 15:27:52 +0530 Subject: ARM: davinci: da850: add DT boot support Add support for booting DA850 using flattened device tree to describe the hardware. At this time only the very basic bootup using a serial console is supported. Signed-off-by: Sekhar Nori --- Documentation/devicetree/bindings/arm/davinci.txt | 9 ++++ arch/arm/Kconfig | 1 + arch/arm/mach-davinci/Kconfig | 8 +++ arch/arm/mach-davinci/Makefile | 1 + arch/arm/mach-davinci/da8xx-dt.c | 64 +++++++++++++++++++++++ 5 files changed, 83 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/davinci.txt create mode 100644 arch/arm/mach-davinci/da8xx-dt.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/davinci.txt b/Documentation/devicetree/bindings/arm/davinci.txt new file mode 100644 index 000000000000..aa4e7a156dd2 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/davinci.txt @@ -0,0 +1,9 @@ +Texas Instruments DaVinci Platforms Device Tree Bindings +-------------------------------------------------------- + +Generic DaVinci Boards +---------------------- + +DA850/OMAP-L138/AM18x generic board +Required root node properties: + - compatible = "ti,da850"; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 73067efd4845..06e0adf2368e 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -924,6 +924,7 @@ config ARCH_DAVINCI select GENERIC_IRQ_CHIP select HAVE_IDE select NEED_MACH_GPIO_H + select USE_OF select ZONE_DMA help Support for TI's DaVinci platform. diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index f8eecb959413..0153950f6068 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -58,6 +58,14 @@ config ARCH_DAVINCI_TNETV107X comment "DaVinci Board Type" +config MACH_DA8XX_DT + bool "Support DA8XX platforms using device tree" + default y + depends on ARCH_DAVINCI_DA8XX + help + Say y here to include support for TI DaVinci DA850 based using + Flattened Device Tree. More information at Documentation/devicetree + config MACH_DAVINCI_EVM bool "TI DM644x EVM" default ARCH_DAVINCI_DM644x diff --git a/arch/arm/mach-davinci/Makefile b/arch/arm/mach-davinci/Makefile index 2227effcb0e9..fb5c1aa98a63 100644 --- a/arch/arm/mach-davinci/Makefile +++ b/arch/arm/mach-davinci/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_AINTC) += irq.o obj-$(CONFIG_CP_INTC) += cp_intc.o # Board specific +obj-$(CONFIG_MACH_DA8XX_DT) += da8xx-dt.o obj-$(CONFIG_MACH_DAVINCI_EVM) += board-dm644x-evm.o obj-$(CONFIG_MACH_SFFSDR) += board-sffsdr.o obj-$(CONFIG_MACH_NEUROS_OSD2) += board-neuros-osd2.o diff --git a/arch/arm/mach-davinci/da8xx-dt.c b/arch/arm/mach-davinci/da8xx-dt.c new file mode 100644 index 000000000000..df393d9b1517 --- /dev/null +++ b/arch/arm/mach-davinci/da8xx-dt.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * Modified from mach-omap/omap2/board-generic.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include + +#include + +#include +#include +#include + +#define DA8XX_NUM_UARTS 3 + +void __init da8xx_uart_clk_enable(void) +{ + int i; + for (i = 0; i < DA8XX_NUM_UARTS; i++) + davinci_serial_setup_clk(i, NULL); +} + +static struct of_device_id da8xx_irq_match[] __initdata = { + { .compatible = "ti,cp-intc", .data = cp_intc_of_init, }, + { } +}; + +static void __init da8xx_init_irq(void) +{ + of_irq_init(da8xx_irq_match); +} + +#ifdef CONFIG_ARCH_DAVINCI_DA850 + +static void __init da850_init_machine(void) +{ + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); + + da8xx_uart_clk_enable(); +} + +static const char *da850_boards_compat[] __initdata = { + "ti,da850", + NULL, +}; + +DT_MACHINE_START(DA850_DT, "Generic DA850/OMAP-L138/AM18x") + .map_io = da850_init, + .init_irq = da8xx_init_irq, + .timer = &davinci_timer, + .init_machine = da850_init_machine, + .dt_compat = da850_boards_compat, + .init_late = davinci_init_late, + .restart = da8xx_restart, +MACHINE_END + +#endif -- cgit v1.2.3 From 27e6e0d3dafa5b487dc28b219753443db300f289 Mon Sep 17 00:00:00 2001 From: Sekhar Nori Date: Wed, 29 Aug 2012 23:25:27 +0530 Subject: ARM: davinci: da850 evm: add DT data Add device tree data for DA850 EVM. At this time, only information on serial ports is added. Signed-off-by: Sekhar Nori --- Documentation/devicetree/bindings/arm/davinci.txt | 4 ++++ arch/arm/boot/dts/da850-evm.dts | 28 +++++++++++++++++++++++ arch/arm/mach-davinci/da8xx-dt.c | 1 + 3 files changed, 33 insertions(+) create mode 100644 arch/arm/boot/dts/da850-evm.dts (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/davinci.txt b/Documentation/devicetree/bindings/arm/davinci.txt index aa4e7a156dd2..2dda756414cc 100644 --- a/Documentation/devicetree/bindings/arm/davinci.txt +++ b/Documentation/devicetree/bindings/arm/davinci.txt @@ -1,6 +1,10 @@ Texas Instruments DaVinci Platforms Device Tree Bindings -------------------------------------------------------- +DA850/OMAP-L138/AM18x Evaluation Module (EVM) board +Required root node properties: + - compatible = "ti,da850-evm", "ti,da850"; + Generic DaVinci Boards ---------------------- diff --git a/arch/arm/boot/dts/da850-evm.dts b/arch/arm/boot/dts/da850-evm.dts new file mode 100644 index 000000000000..37dc5a3243b8 --- /dev/null +++ b/arch/arm/boot/dts/da850-evm.dts @@ -0,0 +1,28 @@ +/* + * Device Tree for DA850 EVM board + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, version 2. + */ +/dts-v1/; +/include/ "da850.dtsi" + +/ { + compatible = "ti,da850-evm", "ti,da850"; + model = "DA850/AM1808/OMAP-L138 EVM"; + + soc { + serial0: serial@1c42000 { + status = "okay"; + }; + serial1: serial@1d0c000 { + status = "okay"; + }; + serial2: serial@1d0d000 { + status = "okay"; + }; + }; +}; diff --git a/arch/arm/mach-davinci/da8xx-dt.c b/arch/arm/mach-davinci/da8xx-dt.c index df393d9b1517..94b09fd4cca4 100644 --- a/arch/arm/mach-davinci/da8xx-dt.c +++ b/arch/arm/mach-davinci/da8xx-dt.c @@ -47,6 +47,7 @@ static void __init da850_init_machine(void) } static const char *da850_boards_compat[] __initdata = { + "ti,da850-evm", "ti,da850", NULL, }; -- cgit v1.2.3 From 3015fb33483b80f64f82092f8acc89ce9c3f07e3 Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Wed, 30 May 2012 12:19:03 +0200 Subject: ARM: davinci: add support for am1808 based EnBW CMC board This board supports: - AM1808 based board - 64 MiB DDR ram - 2 MiB Nor flash - 128 MiB NAND flash - use internal RTC - I2C support - hwmon lm75 support - UBI/UBIFS support - MMC support - USB OTG support This patch adds the basic DTS file enabling the serial ports. Other features will be added in due course of time. Signed-off-by: Heiko Schocher Cc: linux-arm-kernel@lists.infradead.org Cc: devicetree-discuss@lists.ozlabs.org Cc: davinci-linux-open-source@linux.davincidsp.com Cc: Sekhar Nori Cc: Kevin Hilman Cc: Wolfgang Denk [nsekhar@ti.com: modified after dtsi refactoring and added binding documentation] Signed-off-by: Sekhar Nori --- Documentation/devicetree/bindings/arm/davinci.txt | 4 +++ arch/arm/boot/dts/da850-enbw-cmc.dts | 30 +++++++++++++++++++++++ arch/arm/mach-davinci/da8xx-dt.c | 1 + 3 files changed, 35 insertions(+) create mode 100644 arch/arm/boot/dts/da850-enbw-cmc.dts (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/davinci.txt b/Documentation/devicetree/bindings/arm/davinci.txt index 2dda756414cc..cfaeda4274e6 100644 --- a/Documentation/devicetree/bindings/arm/davinci.txt +++ b/Documentation/devicetree/bindings/arm/davinci.txt @@ -5,6 +5,10 @@ DA850/OMAP-L138/AM18x Evaluation Module (EVM) board Required root node properties: - compatible = "ti,da850-evm", "ti,da850"; +EnBW AM1808 based CMC board +Required root node properties: + - compatible = "enbw,cmc", "ti,da850; + Generic DaVinci Boards ---------------------- diff --git a/arch/arm/boot/dts/da850-enbw-cmc.dts b/arch/arm/boot/dts/da850-enbw-cmc.dts new file mode 100644 index 000000000000..422fdb3fcfc1 --- /dev/null +++ b/arch/arm/boot/dts/da850-enbw-cmc.dts @@ -0,0 +1,30 @@ +/* + * Device Tree for AM1808 EnBW CMC board + * + * Copyright 2012 DENX Software Engineering GmbH + * Heiko Schocher + * + * 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. + */ +/dts-v1/; +/include/ "da850.dtsi" + +/ { + compatible = "enbw,cmc", "ti,da850"; + model = "EnBW CMC"; + + soc { + serial0: serial@1c42000 { + status = "okay"; + }; + serial1: serial@1d0c000 { + status = "okay"; + }; + serial2: serial@1d0d000 { + status = "okay"; + }; + }; +}; diff --git a/arch/arm/mach-davinci/da8xx-dt.c b/arch/arm/mach-davinci/da8xx-dt.c index 94b09fd4cca4..37c27af18fa0 100644 --- a/arch/arm/mach-davinci/da8xx-dt.c +++ b/arch/arm/mach-davinci/da8xx-dt.c @@ -47,6 +47,7 @@ static void __init da850_init_machine(void) } static const char *da850_boards_compat[] __initdata = { + "enbw,cmc", "ti,da850-evm", "ti,da850", NULL, -- cgit v1.2.3 From 566cf8eef26d99415aa8a542bd6bba6a719d2031 Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Mon, 29 Oct 2012 21:48:43 +0900 Subject: ARM: dts: add device tree support for exynos5 hdmi This patch adds support for device tree based discovery for exynos5 hdmi. Hdmi node is also renamed with "exynos5-hdmi". Signed-off-by: Rahul Sharma Signed-off-by: Kukjin Kim --- .../devicetree/bindings/drm/exynos/hdmi.txt | 22 ++++++++++++++++++++++ arch/arm/boot/dts/exynos5250-smdk5250.dts | 4 ++++ arch/arm/boot/dts/exynos5250.dtsi | 6 ++++++ arch/arm/mach-exynos/mach-exynos5-dt.c | 2 ++ 4 files changed, 34 insertions(+) create mode 100644 Documentation/devicetree/bindings/drm/exynos/hdmi.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/drm/exynos/hdmi.txt b/Documentation/devicetree/bindings/drm/exynos/hdmi.txt new file mode 100644 index 000000000000..589edee37394 --- /dev/null +++ b/Documentation/devicetree/bindings/drm/exynos/hdmi.txt @@ -0,0 +1,22 @@ +Device-Tree bindings for drm hdmi driver + +Required properties: +- compatible: value should be "samsung,exynos5-hdmi". +- reg: physical base address of the hdmi and length of memory mapped + region. +- interrupts: interrupt number to the cpu. +- hpd-gpio: following information about the hotplug gpio pin. + a) phandle of the gpio controller node. + b) pin number within the gpio controller. + c) pin function mode. + d) optional flags and pull up/down. + e) drive strength. + +Example: + + hdmi { + compatible = "samsung,exynos5-hdmi"; + reg = <0x14530000 0x100000>; + interrupts = <0 95 0>; + hpd-gpio = <&gpx3 7 0xf 1 3>; + }; diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts index a352df403b7a..2a478abbd98a 100644 --- a/arch/arm/boot/dts/exynos5250-smdk5250.dts +++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts @@ -166,4 +166,8 @@ spi_2: spi@12d40000 { status = "disabled"; }; + + hdmi { + hpd-gpio = <&gpx3 7 0xf 1 3>; + }; }; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index dddfd6e444dc..b68be1cab2e0 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -520,4 +520,10 @@ reg = <0x13e30000 0x1000>; interrupts = <0 88 0>; }; + + hdmi { + compatible = "samsung,exynos5-hdmi"; + reg = <0x14530000 0x100000>; + interrupts = <0 95 0>; + }; }; diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c index db1cd8eacf28..5e38208e0d13 100644 --- a/arch/arm/mach-exynos/mach-exynos5-dt.c +++ b/arch/arm/mach-exynos/mach-exynos5-dt.c @@ -72,6 +72,8 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = { "exynos-gsc.2", NULL), OF_DEV_AUXDATA("samsung,exynos5-gsc", EXYNOS5_PA_GSC3, "exynos-gsc.3", NULL), + OF_DEV_AUXDATA("samsung,exynos5-hdmi", 0x14530000, + "exynos5-hdmi", NULL), {}, }; -- cgit v1.2.3 From 5af0d8a372811c353ab7de5d161463f58aa2173a Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Mon, 29 Oct 2012 21:51:36 +0900 Subject: ARM: dts: add device tree support for exynos5 mixer This patch adds support for device tree based discovery for exynos5 mixer. Mixer node is also renamed with "exynos5-mixer". Signed-off-by: Rahul Sharma Signed-off-by: Kukjin Kim --- Documentation/devicetree/bindings/drm/exynos/mixer.txt | 15 +++++++++++++++ arch/arm/boot/dts/exynos5250.dtsi | 6 ++++++ arch/arm/mach-exynos/mach-exynos5-dt.c | 2 ++ 3 files changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/drm/exynos/mixer.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/drm/exynos/mixer.txt b/Documentation/devicetree/bindings/drm/exynos/mixer.txt new file mode 100644 index 000000000000..9b2ea0343566 --- /dev/null +++ b/Documentation/devicetree/bindings/drm/exynos/mixer.txt @@ -0,0 +1,15 @@ +Device-Tree bindings for mixer driver + +Required properties: +- compatible: value should be "samsung,exynos5-mixer". +- reg: physical base address of the mixer and length of memory mapped + region. +- interrupts: interrupt number to the cpu. + +Example: + + mixer { + compatible = "samsung,exynos5-mixer"; + reg = <0x14450000 0x10000>; + interrupts = <0 94 0>; + }; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index b68be1cab2e0..2a203e62f745 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -526,4 +526,10 @@ reg = <0x14530000 0x100000>; interrupts = <0 95 0>; }; + + mixer { + compatible = "samsung,exynos5-mixer"; + reg = <0x14450000 0x10000>; + interrupts = <0 94 0>; + }; }; diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c index 5e38208e0d13..4fc15daf7c17 100644 --- a/arch/arm/mach-exynos/mach-exynos5-dt.c +++ b/arch/arm/mach-exynos/mach-exynos5-dt.c @@ -74,6 +74,8 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = { "exynos-gsc.3", NULL), OF_DEV_AUXDATA("samsung,exynos5-hdmi", 0x14530000, "exynos5-hdmi", NULL), + OF_DEV_AUXDATA("samsung,exynos5-mixer", 0x14450000, + "exynos5-mixer", NULL), {}, }; -- cgit v1.2.3 From 3e3e9ce4474c3c1ec6e3e05268705ed086825159 Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Mon, 29 Oct 2012 21:51:42 +0900 Subject: ARM: dts: add device tree support for exynos5 hdmiphy This patch adds support for device tree based discovery for exynos5 hdmiphy. Signed-off-by: Rahul Sharma Signed-off-by: Kukjin Kim --- Documentation/devicetree/bindings/drm/exynos/hdmiphy.txt | 12 ++++++++++++ arch/arm/boot/dts/exynos5250-smdk5250.dts | 10 ++++++++++ arch/arm/boot/dts/exynos5250.dtsi | 8 ++++++++ arch/arm/mach-exynos/mach-exynos5-dt.c | 2 ++ 4 files changed, 32 insertions(+) create mode 100644 Documentation/devicetree/bindings/drm/exynos/hdmiphy.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/drm/exynos/hdmiphy.txt b/Documentation/devicetree/bindings/drm/exynos/hdmiphy.txt new file mode 100644 index 000000000000..858f4f9b902f --- /dev/null +++ b/Documentation/devicetree/bindings/drm/exynos/hdmiphy.txt @@ -0,0 +1,12 @@ +Device-Tree bindings for hdmiphy driver + +Required properties: +- compatible: value should be "samsung,exynos5-hdmiphy". +- reg: I2C address of the hdmiphy device. + +Example: + + hdmiphy { + compatible = "samsung,exynos5-hdmiphy"; + reg = <0x38>; + }; diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts index 2a478abbd98a..7dc196f6b0a1 100644 --- a/arch/arm/boot/dts/exynos5250-smdk5250.dts +++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts @@ -79,6 +79,16 @@ status = "disabled"; }; + i2c@12CE0000 { + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <66000>; + + hdmiphy@38 { + compatible = "samsung,exynos5-hdmiphy"; + reg = <0x38>; + }; + }; + dwmmc_0: dwmmc0@12200000 { num-slots = <1>; supports-highspeed; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index 2a203e62f745..190068f66499 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -156,6 +156,14 @@ #size-cells = <0>; }; + i2c@12CE0000 { + compatible = "samsung,s3c2440-hdmiphy-i2c"; + reg = <0x12CE0000 0x1000>; + interrupts = <0 64 0>; + #address-cells = <1>; + #size-cells = <0>; + }; + spi_0: spi@12d20000 { compatible = "samsung,exynos4210-spi"; reg = <0x12d20000 0x100>; diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c index 4fc15daf7c17..0f20ba8fcf1b 100644 --- a/arch/arm/mach-exynos/mach-exynos5-dt.c +++ b/arch/arm/mach-exynos/mach-exynos5-dt.c @@ -47,6 +47,8 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = { "s3c2440-i2c.0", NULL), OF_DEV_AUXDATA("samsung,s3c2440-i2c", EXYNOS5_PA_IIC(1), "s3c2440-i2c.1", NULL), + OF_DEV_AUXDATA("samsung,s3c2440-hdmiphy-i2c", EXYNOS5_PA_IIC(8), + "s3c2440-hdmiphy-i2c", NULL), OF_DEV_AUXDATA("samsung,exynos5250-dw-mshc", EXYNOS5_PA_DWMCI0, "dw_mmc.0", NULL), OF_DEV_AUXDATA("samsung,exynos5250-dw-mshc", EXYNOS5_PA_DWMCI1, -- cgit v1.2.3 From 62a1f83f0cbbc9c2e2776b4ad91ccb2f500b6a0b Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Mon, 29 Oct 2012 21:51:47 +0900 Subject: ARM: dts: add device tree support for exynos5 hdmiddc This patch adds support for device tree based discovery for exynos5 hdmi ddc. Signed-off-by: Rahul Sharma Signed-off-by: Kukjin Kim --- Documentation/devicetree/bindings/drm/exynos/hdmiddc.txt | 12 ++++++++++++ arch/arm/boot/dts/exynos5250-smdk5250.dts | 10 +++++++++- arch/arm/mach-exynos/mach-exynos5-dt.c | 2 ++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/drm/exynos/hdmiddc.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/drm/exynos/hdmiddc.txt b/Documentation/devicetree/bindings/drm/exynos/hdmiddc.txt new file mode 100644 index 000000000000..fa166d945809 --- /dev/null +++ b/Documentation/devicetree/bindings/drm/exynos/hdmiddc.txt @@ -0,0 +1,12 @@ +Device-Tree bindings for hdmiddc driver + +Required properties: +- compatible: value should be "samsung,exynos5-hdmiddc". +- reg: I2C address of the hdmiddc device. + +Example: + + hdmiddc { + compatible = "samsung,exynos5-hdmiddc"; + reg = <0x50>; + }; diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts index 7dc196f6b0a1..7212d538086d 100644 --- a/arch/arm/boot/dts/exynos5250-smdk5250.dts +++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts @@ -56,7 +56,15 @@ }; i2c@12C80000 { - status = "disabled"; + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <66000>; + gpios = <&gpa0 6 3 3 0>, + <&gpa0 7 3 3 0>; + + hdmiddc@50 { + compatible = "samsung,exynos5-hdmiddc"; + reg = <0x50>; + }; }; i2c@12C90000 { diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c index 0f20ba8fcf1b..db035374087e 100644 --- a/arch/arm/mach-exynos/mach-exynos5-dt.c +++ b/arch/arm/mach-exynos/mach-exynos5-dt.c @@ -47,6 +47,8 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = { "s3c2440-i2c.0", NULL), OF_DEV_AUXDATA("samsung,s3c2440-i2c", EXYNOS5_PA_IIC(1), "s3c2440-i2c.1", NULL), + OF_DEV_AUXDATA("samsung,s3c2440-i2c", EXYNOS5_PA_IIC(2), + "s3c2440-i2c.2", NULL), OF_DEV_AUXDATA("samsung,s3c2440-hdmiphy-i2c", EXYNOS5_PA_IIC(8), "s3c2440-hdmiphy-i2c", NULL), OF_DEV_AUXDATA("samsung,exynos5250-dw-mshc", EXYNOS5_PA_DWMCI0, -- cgit v1.2.3 From 1c48ddbaa61aa017fefacac1bce233227933d18e Mon Sep 17 00:00:00 2001 From: AnilKumar Ch Date: Fri, 19 Oct 2012 12:28:56 +0530 Subject: Documentation: dt: i2c: Update trivial-devices list Update i2c trivial-devices list by adding the description for ti,tmp275 temperature sensor and taos,tsl2550 ambient light sensor. Signed-off-by: AnilKumar Ch Signed-off-by: Benoit Cousson --- Documentation/devicetree/bindings/i2c/trivial-devices.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt index 2f5322b119eb..446859fcdca4 100644 --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt @@ -55,5 +55,7 @@ st-micro,24c256 i2c serial eeprom (24cxx) stm,m41t00 Serial Access TIMEKEEPER stm,m41t62 Serial real-time clock (RTC) with alarm stm,m41t80 M41T80 - SERIAL ACCESS RTC WITH ALARMS +taos,tsl2550 Ambient Light Sensor with SMBUS/Two Wire Serial Interface ti,tsc2003 I2C Touch-Screen Controller ti,tmp102 Low Power Digital Temperature Sensor with SMBUS/Two Wire Serial Interface +ti,tmp275 Digital Temperature Sensor -- cgit v1.2.3 From 3ce0a99cd4c23762441f0efb18c0e0ef1e9d8bfc Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Wed, 19 Sep 2012 16:02:51 +0530 Subject: ARM: dts: OMAP4: add *reg* property for ocp2scp *reg* property for ocp2scp was previously obtained from ti,hwmods property. But that is now explicitly added to the dt node. Also updated the documentation with *reg* info. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Benoit Cousson --- Documentation/devicetree/bindings/bus/omap-ocp2scp.txt | 18 ++++++++++++++++++ arch/arm/boot/dts/omap4.dtsi | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/bus/omap-ocp2scp.txt b/Documentation/devicetree/bindings/bus/omap-ocp2scp.txt index d2fe064a828b..63dd8051521c 100644 --- a/Documentation/devicetree/bindings/bus/omap-ocp2scp.txt +++ b/Documentation/devicetree/bindings/bus/omap-ocp2scp.txt @@ -2,9 +2,27 @@ properties: - compatible : Should be "ti,omap-ocp2scp" +- reg : Address and length of the register set for the device - #address-cells, #size-cells : Must be present if the device has sub-nodes - ranges : the child address space are mapped 1:1 onto the parent address space - ti,hwmods : must be "ocp2scp_usb_phy" Sub-nodes: All the devices connected to ocp2scp are described using sub-node to ocp2scp + +ocp2scp@4a0ad000 { + compatible = "ti,omap-ocp2scp"; + reg = <0x4a0ad000 0x1f>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + ti,hwmods = "ocp2scp_usb_phy"; + + subnode1 { + ... + }; + + subnode2 { + ... + }; +}; diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi index 3883f94fdbd0..812461efdea6 100644 --- a/arch/arm/boot/dts/omap4.dtsi +++ b/arch/arm/boot/dts/omap4.dtsi @@ -431,8 +431,9 @@ hw-caps-temp-alert; }; - ocp2scp { + ocp2scp@4a0ad000 { compatible = "ti,omap-ocp2scp"; + reg = <0x4a0ad000 0x1f>; #address-cells = <1>; #size-cells = <1>; ranges; -- cgit v1.2.3 From fab8ad0b2b5f2b6d25c6020a61bf3339e53fec61 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Fri, 19 Oct 2012 09:59:00 -0500 Subject: ARM: dts: OMAP: Add timer nodes Add the 12 GP timers nodes present in OMAP2. Add the 12 GP timers nodes present in OMAP3. Add the 11 GP timers nodes present in OMAP4. Add the 7 GP timers nodes present in AM33xx. Add documentation for timer properties specific to OMAP. Thanks to Vaibhav Hiremath for creating the AM33xx timer nodes. I have modified Vaibhav's original nodes adding information on which timers support a PWM output. V5 changes: - Updated timer register sizes for OMAP2/3/4. - Modified AM335x timer register size to be 1KB instead of 4KB to align with HWMOD. Signed-off-by: Jon Hunter Acked-Reviewed-&-Tested-By: Vaibhav Hiremath Signed-off-by: Benoit Cousson --- .../devicetree/bindings/arm/omap/timer.txt | 31 +++++++ arch/arm/boot/dts/am33xx.dtsi | 54 ++++++++++++ arch/arm/boot/dts/omap2.dtsi | 85 +++++++++++++++++++ arch/arm/boot/dts/omap2420.dtsi | 8 ++ arch/arm/boot/dts/omap2430.dtsi | 8 ++ arch/arm/boot/dts/omap3.dtsi | 95 ++++++++++++++++++++++ arch/arm/boot/dts/omap4.dtsi | 86 ++++++++++++++++++++ 7 files changed, 367 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/omap/timer.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/omap/timer.txt b/Documentation/devicetree/bindings/arm/omap/timer.txt new file mode 100644 index 000000000000..8732d4d41f8b --- /dev/null +++ b/Documentation/devicetree/bindings/arm/omap/timer.txt @@ -0,0 +1,31 @@ +OMAP Timer bindings + +Required properties: +- compatible: Must be "ti,omap2-timer" for OMAP2+ controllers. +- reg: Contains timer register address range (base address and + length). +- interrupts: Contains the interrupt information for the timer. The + format is being dependent on which interrupt controller + the OMAP device uses. +- ti,hwmods: Name of the hwmod associated to the timer, "timer", + where is the instance number of the timer from the + HW spec. + +Optional properties: +- ti,timer-alwon: Indicates the timer is in an alway-on power domain. +- ti,timer-dsp: Indicates the timer can interrupt the on-chip DSP in + addition to the ARM CPU. +- ti,timer-pwm: Indicates the timer can generate a PWM output. +- ti,timer-secure: Indicates the timer is reserved on a secure OMAP device + and therefore cannot be used by the kernel. + +Example: + +timer12: timer@48304000 { + compatible = "ti,omap2-timer"; + reg = <0x48304000 0x400>; + interrupts = <95>; + ti,hwmods = "timer12" + ti,timer-alwon; + ti,timer-secure; +}; diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index 470926946ed6..70d24b848c11 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -237,5 +237,59 @@ interrupts = <55>; status = "disabled"; }; + + timer1: timer@44e31000 { + compatible = "ti,omap2-timer"; + reg = <0x44e31000 0x400>; + interrupts = <67>; + ti,hwmods = "timer1"; + ti,timer-alwon; + }; + + timer2: timer@48040000 { + compatible = "ti,omap2-timer"; + reg = <0x48040000 0x400>; + interrupts = <68>; + ti,hwmods = "timer2"; + }; + + timer3: timer@48042000 { + compatible = "ti,omap2-timer"; + reg = <0x48042000 0x400>; + interrupts = <69>; + ti,hwmods = "timer3"; + }; + + timer4: timer@48044000 { + compatible = "ti,omap2-timer"; + reg = <0x48044000 0x400>; + interrupts = <92>; + ti,hwmods = "timer4"; + ti,timer-pwm; + }; + + timer5: timer@48046000 { + compatible = "ti,omap2-timer"; + reg = <0x48046000 0x400>; + interrupts = <93>; + ti,hwmods = "timer5"; + ti,timer-pwm; + }; + + timer6: timer@48048000 { + compatible = "ti,omap2-timer"; + reg = <0x48048000 0x400>; + interrupts = <94>; + ti,hwmods = "timer6"; + ti,timer-pwm; + }; + + timer7: timer@4804a000 { + compatible = "ti,omap2-timer"; + reg = <0x4804a000 0x400>; + interrupts = <95>; + ti,hwmods = "timer7"; + ti,timer-pwm; + }; }; }; diff --git a/arch/arm/boot/dts/omap2.dtsi b/arch/arm/boot/dts/omap2.dtsi index f366482216c0..761c4b69b25b 100644 --- a/arch/arm/boot/dts/omap2.dtsi +++ b/arch/arm/boot/dts/omap2.dtsi @@ -66,5 +66,90 @@ ti,hwmods = "uart3"; clock-frequency = <48000000>; }; + + timer2: timer@4802a000 { + compatible = "ti,omap2-timer"; + reg = <0x4802a000 0x400>; + interrupts = <38>; + ti,hwmods = "timer2"; + }; + + timer3: timer@48078000 { + compatible = "ti,omap2-timer"; + reg = <0x48078000 0x400>; + interrupts = <39>; + ti,hwmods = "timer3"; + }; + + timer4: timer@4807a000 { + compatible = "ti,omap2-timer"; + reg = <0x4807a000 0x400>; + interrupts = <40>; + ti,hwmods = "timer4"; + }; + + timer5: timer@4807c000 { + compatible = "ti,omap2-timer"; + reg = <0x4807c000 0x400>; + interrupts = <41>; + ti,hwmods = "timer5"; + ti,timer-dsp; + }; + + timer6: timer@4807e000 { + compatible = "ti,omap2-timer"; + reg = <0x4807e000 0x400>; + interrupts = <42>; + ti,hwmods = "timer6"; + ti,timer-dsp; + }; + + timer7: timer@48080000 { + compatible = "ti,omap2-timer"; + reg = <0x48080000 0x400>; + interrupts = <43>; + ti,hwmods = "timer7"; + ti,timer-dsp; + }; + + timer8: timer@48082000 { + compatible = "ti,omap2-timer"; + reg = <0x48082000 0x400>; + interrupts = <44>; + ti,hwmods = "timer8"; + ti,timer-dsp; + }; + + timer9: timer@48084000 { + compatible = "ti,omap2-timer"; + reg = <0x48084000 0x400>; + interrupts = <45>; + ti,hwmods = "timer9"; + ti,timer-pwm; + }; + + timer10: timer@48086000 { + compatible = "ti,omap2-timer"; + reg = <0x48086000 0x400>; + interrupts = <46>; + ti,hwmods = "timer10"; + ti,timer-pwm; + }; + + timer11: timer@48088000 { + compatible = "ti,omap2-timer"; + reg = <0x48088000 0x400>; + interrupts = <47>; + ti,hwmods = "timer11"; + ti,timer-pwm; + }; + + timer12: timer@4808a000 { + compatible = "ti,omap2-timer"; + reg = <0x4808a000 0x400>; + interrupts = <48>; + ti,hwmods = "timer12"; + ti,timer-pwm; + }; }; }; diff --git a/arch/arm/boot/dts/omap2420.dtsi b/arch/arm/boot/dts/omap2420.dtsi index 4d5ce91416c3..af5ee262d0cf 100644 --- a/arch/arm/boot/dts/omap2420.dtsi +++ b/arch/arm/boot/dts/omap2420.dtsi @@ -42,5 +42,13 @@ interrupt-names = "tx", "rx"; ti,hwmods = "mcbsp2"; }; + + timer1: timer@48028000 { + compatible = "ti,omap2-timer"; + reg = <0x48028000 0x400>; + interrupts = <37>; + ti,hwmods = "timer1"; + ti,timer-alwon; + }; }; }; diff --git a/arch/arm/boot/dts/omap2430.dtsi b/arch/arm/boot/dts/omap2430.dtsi index fa845324b642..688729840f14 100644 --- a/arch/arm/boot/dts/omap2430.dtsi +++ b/arch/arm/boot/dts/omap2430.dtsi @@ -83,5 +83,13 @@ ti,buffer-size = <128>; ti,hwmods = "mcbsp5"; }; + + timer1: timer@49018000 { + compatible = "ti,omap2-timer"; + reg = <0x49018000 0x400>; + interrupts = <37>; + ti,hwmods = "timer1"; + ti,timer-alwon; + }; }; }; diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi index fa155418eaad..af9b1822ff84 100644 --- a/arch/arm/boot/dts/omap3.dtsi +++ b/arch/arm/boot/dts/omap3.dtsi @@ -296,5 +296,100 @@ ti,buffer-size = <128>; ti,hwmods = "mcbsp5"; }; + + timer1: timer@48318000 { + compatible = "ti,omap2-timer"; + reg = <0x48318000 0x400>; + interrupts = <37>; + ti,hwmods = "timer1"; + ti,timer-alwon; + }; + + timer2: timer@49032000 { + compatible = "ti,omap2-timer"; + reg = <0x49032000 0x400>; + interrupts = <38>; + ti,hwmods = "timer2"; + }; + + timer3: timer@49034000 { + compatible = "ti,omap2-timer"; + reg = <0x49034000 0x400>; + interrupts = <39>; + ti,hwmods = "timer3"; + }; + + timer4: timer@49036000 { + compatible = "ti,omap2-timer"; + reg = <0x49036000 0x400>; + interrupts = <40>; + ti,hwmods = "timer4"; + }; + + timer5: timer@49038000 { + compatible = "ti,omap2-timer"; + reg = <0x49038000 0x400>; + interrupts = <41>; + ti,hwmods = "timer5"; + ti,timer-dsp; + }; + + timer6: timer@4903a000 { + compatible = "ti,omap2-timer"; + reg = <0x4903a000 0x400>; + interrupts = <42>; + ti,hwmods = "timer6"; + ti,timer-dsp; + }; + + timer7: timer@4903c000 { + compatible = "ti,omap2-timer"; + reg = <0x4903c000 0x400>; + interrupts = <43>; + ti,hwmods = "timer7"; + ti,timer-dsp; + }; + + timer8: timer@4903e000 { + compatible = "ti,omap2-timer"; + reg = <0x4903e000 0x400>; + interrupts = <44>; + ti,hwmods = "timer8"; + ti,timer-pwm; + ti,timer-dsp; + }; + + timer9: timer@49040000 { + compatible = "ti,omap2-timer"; + reg = <0x49040000 0x400>; + interrupts = <45>; + ti,hwmods = "timer9"; + ti,timer-pwm; + }; + + timer10: timer@48086000 { + compatible = "ti,omap2-timer"; + reg = <0x48086000 0x400>; + interrupts = <46>; + ti,hwmods = "timer10"; + ti,timer-pwm; + }; + + timer11: timer@48088000 { + compatible = "ti,omap2-timer"; + reg = <0x48088000 0x400>; + interrupts = <47>; + ti,hwmods = "timer11"; + ti,timer-pwm; + }; + + timer12: timer@48304000 { + compatible = "ti,omap2-timer"; + reg = <0x48304000 0x400>; + interrupts = <95>; + ti,hwmods = "timer12"; + ti,timer-alwon; + ti,timer-secure; + }; }; }; diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi index 2ab6e68ccbf7..d3a82e0c3804 100644 --- a/arch/arm/boot/dts/omap4.dtsi +++ b/arch/arm/boot/dts/omap4.dtsi @@ -433,5 +433,91 @@ ranges; ti,hwmods = "ocp2scp_usb_phy"; }; + + timer1: timer@4a318000 { + compatible = "ti,omap2-timer"; + reg = <0x4a318000 0x80>; + interrupts = <0 37 0x4>; + ti,hwmods = "timer1"; + ti,timer-alwon; + }; + + timer2: timer@48032000 { + compatible = "ti,omap2-timer"; + reg = <0x48032000 0x80>; + interrupts = <0 38 0x4>; + ti,hwmods = "timer2"; + }; + + timer3: timer@48034000 { + compatible = "ti,omap2-timer"; + reg = <0x48034000 0x80>; + interrupts = <0 39 0x4>; + ti,hwmods = "timer3"; + }; + + timer4: timer@48036000 { + compatible = "ti,omap2-timer"; + reg = <0x48036000 0x80>; + interrupts = <0 40 0x4>; + ti,hwmods = "timer4"; + }; + + timer5: timer@49038000 { + compatible = "ti,omap2-timer"; + reg = <0x49038000 0x80>; + interrupts = <0 41 0x4>; + ti,hwmods = "timer5"; + ti,timer-dsp; + }; + + timer6: timer@4903a000 { + compatible = "ti,omap2-timer"; + reg = <0x4903a000 0x80>; + interrupts = <0 42 0x4>; + ti,hwmods = "timer6"; + ti,timer-dsp; + }; + + timer7: timer@4903c000 { + compatible = "ti,omap2-timer"; + reg = <0x4903c000 0x80>; + interrupts = <0 43 0x4>; + ti,hwmods = "timer7"; + ti,timer-dsp; + }; + + timer8: timer@4903e000 { + compatible = "ti,omap2-timer"; + reg = <0x4903e000 0x80>; + interrupts = <0 44 0x4>; + ti,hwmods = "timer8"; + ti,timer-pwm; + ti,timer-dsp; + }; + + timer9: timer@4803e000 { + compatible = "ti,omap2-timer"; + reg = <0x4803e000 0x80>; + interrupts = <0 45 0x4>; + ti,hwmods = "timer9"; + ti,timer-pwm; + }; + + timer10: timer@48086000 { + compatible = "ti,omap2-timer"; + reg = <0x48086000 0x80>; + interrupts = <0 46 0x4>; + ti,hwmods = "timer10"; + ti,timer-pwm; + }; + + timer11: timer@48088000 { + compatible = "ti,omap2-timer"; + reg = <0x48088000 0x80>; + interrupts = <0 47 0x4>; + ti,hwmods = "timer11"; + ti,timer-pwm; + }; }; }; -- cgit v1.2.3 From 510c0ffdd408ced2654f073d0397f0fec410a235 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Thu, 25 Oct 2012 14:24:14 -0500 Subject: ARM: dts: OMAP: Add counter-32k nodes Adds the counter-32k timers nodes present in OMAP2/3/4 devices and device-tree binding documentation for OMAP counter-32k. Signed-off-by: Jon Hunter Signed-off-by: Benoit Cousson --- Documentation/devicetree/bindings/arm/omap/counter.txt | 15 +++++++++++++++ arch/arm/boot/dts/omap2420.dtsi | 6 ++++++ arch/arm/boot/dts/omap2430.dtsi | 6 ++++++ arch/arm/boot/dts/omap3.dtsi | 6 ++++++ arch/arm/boot/dts/omap4.dtsi | 6 ++++++ 5 files changed, 39 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/omap/counter.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/omap/counter.txt b/Documentation/devicetree/bindings/arm/omap/counter.txt new file mode 100644 index 000000000000..5bd8aa091315 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/omap/counter.txt @@ -0,0 +1,15 @@ +OMAP Counter-32K bindings + +Required properties: +- compatible: Must be "ti,omap-counter32k" for OMAP controllers +- reg: Contains timer register address range (base address and length) +- ti,hwmods: Name of the hwmod associated to the counter, which is typically + "counter_32k" + +Example: + +counter32k: counter@4a304000 { + compatible = "ti,omap-counter32k"; + reg = <0x4a304000 0x20>; + ti,hwmods = "counter_32k"; +}; diff --git a/arch/arm/boot/dts/omap2420.dtsi b/arch/arm/boot/dts/omap2420.dtsi index af5ee262d0cf..af6560908905 100644 --- a/arch/arm/boot/dts/omap2420.dtsi +++ b/arch/arm/boot/dts/omap2420.dtsi @@ -14,6 +14,12 @@ compatible = "ti,omap2420", "ti,omap2"; ocp { + counter32k: counter@48004000 { + compatible = "ti,omap-counter32k"; + reg = <0x48004000 0x20>; + ti,hwmods = "counter_32k"; + }; + omap2420_pmx: pinmux@48000030 { compatible = "ti,omap2420-padconf", "pinctrl-single"; reg = <0x48000030 0x0113>; diff --git a/arch/arm/boot/dts/omap2430.dtsi b/arch/arm/boot/dts/omap2430.dtsi index 688729840f14..c3924457c9b6 100644 --- a/arch/arm/boot/dts/omap2430.dtsi +++ b/arch/arm/boot/dts/omap2430.dtsi @@ -14,6 +14,12 @@ compatible = "ti,omap2430", "ti,omap2"; ocp { + counter32k: counter@49020000 { + compatible = "ti,omap-counter32k"; + reg = <0x49020000 0x20>; + ti,hwmods = "counter_32k"; + }; + omap2430_pmx: pinmux@49002030 { compatible = "ti,omap2430-padconf", "pinctrl-single"; reg = <0x49002030 0x0154>; diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi index af9b1822ff84..1acc26148ffc 100644 --- a/arch/arm/boot/dts/omap3.dtsi +++ b/arch/arm/boot/dts/omap3.dtsi @@ -61,6 +61,12 @@ ranges; ti,hwmods = "l3_main"; + counter32k: counter@48320000 { + compatible = "ti,omap-counter32k"; + reg = <0x48320000 0x20>; + ti,hwmods = "counter_32k"; + }; + intc: interrupt-controller@48200000 { compatible = "ti,omap2-intc"; interrupt-controller; diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi index d3a82e0c3804..23ee1498c98c 100644 --- a/arch/arm/boot/dts/omap4.dtsi +++ b/arch/arm/boot/dts/omap4.dtsi @@ -95,6 +95,12 @@ ranges; ti,hwmods = "l3_main_1", "l3_main_2", "l3_main_3"; + counter32k: counter@4a304000 { + compatible = "ti,omap-counter32k"; + reg = <0x4a304000 0x20>; + ti,hwmods = "counter_32k"; + }; + omap4_pmx_core: pinmux@4a100040 { compatible = "ti,omap4-padconf", "pinctrl-single"; reg = <0x4a100040 0x0196>; -- cgit v1.2.3 From dc4dc36056392c0b0b1ca9e81bebff964b9297e0 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 30 Oct 2012 12:34:05 +0530 Subject: spi: tegra: add spi driver for SLINK controller Tegra20/Tegra30 supports the spi interface through its SLINK controller. Add spi driver for SLINK controller. Signed-off-by: Laxman Dewangan Reviewed-by: Stephen Warren Signed-off-by: Mark Brown --- .../bindings/spi/nvidia,tegra20-slink.txt | 26 + drivers/spi/Kconfig | 6 + drivers/spi/Makefile | 2 +- drivers/spi/spi-tegra20-slink.c | 1359 ++++++++++++++++++++ include/linux/spi/spi-tegra.h | 40 + 5 files changed, 1432 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/spi/nvidia,tegra20-slink.txt create mode 100644 drivers/spi/spi-tegra20-slink.c create mode 100644 include/linux/spi/spi-tegra.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/spi/nvidia,tegra20-slink.txt b/Documentation/devicetree/bindings/spi/nvidia,tegra20-slink.txt new file mode 100644 index 000000000000..f5b1ad1a1ec3 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/nvidia,tegra20-slink.txt @@ -0,0 +1,26 @@ +NVIDIA Tegra20/Tegra30 SLINK controller. + +Required properties: +- compatible : should be "nvidia,tegra20-slink", "nvidia,tegra30-slink". +- reg: Should contain SLINK registers location and length. +- interrupts: Should contain SLINK interrupts. +- nvidia,dma-request-selector : The Tegra DMA controller's phandle and + request selector for this SLINK controller. + +Recommended properties: +- spi-max-frequency: Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt + +Example: + +slink@7000d600 { + compatible = "nvidia,tegra20-slink"; + reg = <0x7000d600 0x200>; + interrupts = <0 82 0x04>; + nvidia,dma-request-selector = <&apbdma 16>; + spi-max-frequency = <25000000>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; +}; + diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 1acae359cabe..25290d9780b2 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -385,6 +385,12 @@ config SPI_MXS help SPI driver for Freescale MXS devices. +config SPI_TEGRA20_SLINK + tristate "Nvidia Tegra20/Tegra30 SLINK Controller" + depends on ARCH_TEGRA && TEGRA20_APB_DMA + help + SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface. + config SPI_TI_SSP tristate "TI Sequencer Serial Port - SPI Support" depends on MFD_TI_SSP diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c48df47e4b0f..f87c0f142e5a 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -60,10 +60,10 @@ obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o obj-$(CONFIG_SPI_SIRF) += spi-sirf.o obj-$(CONFIG_SPI_STMP3XXX) += spi-stmp.o +obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o obj-$(CONFIG_SPI_TXX9) += spi-txx9.o obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o - diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c new file mode 100644 index 000000000000..b8985be81d96 --- /dev/null +++ b/drivers/spi/spi-tegra20-slink.c @@ -0,0 +1,1359 @@ +/* + * SPI driver for Nvidia's Tegra20/Tegra30 SLINK Controller. + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SLINK_COMMAND 0x000 +#define SLINK_BIT_LENGTH(x) (((x) & 0x1f) << 0) +#define SLINK_WORD_SIZE(x) (((x) & 0x1f) << 5) +#define SLINK_BOTH_EN (1 << 10) +#define SLINK_CS_SW (1 << 11) +#define SLINK_CS_VALUE (1 << 12) +#define SLINK_CS_POLARITY (1 << 13) +#define SLINK_IDLE_SDA_DRIVE_LOW (0 << 16) +#define SLINK_IDLE_SDA_DRIVE_HIGH (1 << 16) +#define SLINK_IDLE_SDA_PULL_LOW (2 << 16) +#define SLINK_IDLE_SDA_PULL_HIGH (3 << 16) +#define SLINK_IDLE_SDA_MASK (3 << 16) +#define SLINK_CS_POLARITY1 (1 << 20) +#define SLINK_CK_SDA (1 << 21) +#define SLINK_CS_POLARITY2 (1 << 22) +#define SLINK_CS_POLARITY3 (1 << 23) +#define SLINK_IDLE_SCLK_DRIVE_LOW (0 << 24) +#define SLINK_IDLE_SCLK_DRIVE_HIGH (1 << 24) +#define SLINK_IDLE_SCLK_PULL_LOW (2 << 24) +#define SLINK_IDLE_SCLK_PULL_HIGH (3 << 24) +#define SLINK_IDLE_SCLK_MASK (3 << 24) +#define SLINK_M_S (1 << 28) +#define SLINK_WAIT (1 << 29) +#define SLINK_GO (1 << 30) +#define SLINK_ENB (1 << 31) + +#define SLINK_MODES (SLINK_IDLE_SCLK_MASK | SLINK_CK_SDA) + +#define SLINK_COMMAND2 0x004 +#define SLINK_LSBFE (1 << 0) +#define SLINK_SSOE (1 << 1) +#define SLINK_SPIE (1 << 4) +#define SLINK_BIDIROE (1 << 6) +#define SLINK_MODFEN (1 << 7) +#define SLINK_INT_SIZE(x) (((x) & 0x1f) << 8) +#define SLINK_CS_ACTIVE_BETWEEN (1 << 17) +#define SLINK_SS_EN_CS(x) (((x) & 0x3) << 18) +#define SLINK_SS_SETUP(x) (((x) & 0x3) << 20) +#define SLINK_FIFO_REFILLS_0 (0 << 22) +#define SLINK_FIFO_REFILLS_1 (1 << 22) +#define SLINK_FIFO_REFILLS_2 (2 << 22) +#define SLINK_FIFO_REFILLS_3 (3 << 22) +#define SLINK_FIFO_REFILLS_MASK (3 << 22) +#define SLINK_WAIT_PACK_INT(x) (((x) & 0x7) << 26) +#define SLINK_SPC0 (1 << 29) +#define SLINK_TXEN (1 << 30) +#define SLINK_RXEN (1 << 31) + +#define SLINK_STATUS 0x008 +#define SLINK_COUNT(val) (((val) >> 0) & 0x1f) +#define SLINK_WORD(val) (((val) >> 5) & 0x1f) +#define SLINK_BLK_CNT(val) (((val) >> 0) & 0xffff) +#define SLINK_MODF (1 << 16) +#define SLINK_RX_UNF (1 << 18) +#define SLINK_TX_OVF (1 << 19) +#define SLINK_TX_FULL (1 << 20) +#define SLINK_TX_EMPTY (1 << 21) +#define SLINK_RX_FULL (1 << 22) +#define SLINK_RX_EMPTY (1 << 23) +#define SLINK_TX_UNF (1 << 24) +#define SLINK_RX_OVF (1 << 25) +#define SLINK_TX_FLUSH (1 << 26) +#define SLINK_RX_FLUSH (1 << 27) +#define SLINK_SCLK (1 << 28) +#define SLINK_ERR (1 << 29) +#define SLINK_RDY (1 << 30) +#define SLINK_BSY (1 << 31) +#define SLINK_FIFO_ERROR (SLINK_TX_OVF | SLINK_RX_UNF | \ + SLINK_TX_UNF | SLINK_RX_OVF) + +#define SLINK_FIFO_EMPTY (SLINK_TX_EMPTY | SLINK_RX_EMPTY) + +#define SLINK_MAS_DATA 0x010 +#define SLINK_SLAVE_DATA 0x014 + +#define SLINK_DMA_CTL 0x018 +#define SLINK_DMA_BLOCK_SIZE(x) (((x) & 0xffff) << 0) +#define SLINK_TX_TRIG_1 (0 << 16) +#define SLINK_TX_TRIG_4 (1 << 16) +#define SLINK_TX_TRIG_8 (2 << 16) +#define SLINK_TX_TRIG_16 (3 << 16) +#define SLINK_TX_TRIG_MASK (3 << 16) +#define SLINK_RX_TRIG_1 (0 << 18) +#define SLINK_RX_TRIG_4 (1 << 18) +#define SLINK_RX_TRIG_8 (2 << 18) +#define SLINK_RX_TRIG_16 (3 << 18) +#define SLINK_RX_TRIG_MASK (3 << 18) +#define SLINK_PACKED (1 << 20) +#define SLINK_PACK_SIZE_4 (0 << 21) +#define SLINK_PACK_SIZE_8 (1 << 21) +#define SLINK_PACK_SIZE_16 (2 << 21) +#define SLINK_PACK_SIZE_32 (3 << 21) +#define SLINK_PACK_SIZE_MASK (3 << 21) +#define SLINK_IE_TXC (1 << 26) +#define SLINK_IE_RXC (1 << 27) +#define SLINK_DMA_EN (1 << 31) + +#define SLINK_STATUS2 0x01c +#define SLINK_TX_FIFO_EMPTY_COUNT(val) (((val) & 0x3f) >> 0) +#define SLINK_RX_FIFO_FULL_COUNT(val) (((val) & 0x3f0000) >> 16) +#define SLINK_SS_HOLD_TIME(val) (((val) & 0xF) << 6) + +#define SLINK_TX_FIFO 0x100 +#define SLINK_RX_FIFO 0x180 + +#define DATA_DIR_TX (1 << 0) +#define DATA_DIR_RX (1 << 1) + +#define SLINK_DMA_TIMEOUT (msecs_to_jiffies(1000)) + +#define DEFAULT_SPI_DMA_BUF_LEN (16*1024) +#define TX_FIFO_EMPTY_COUNT_MAX SLINK_TX_FIFO_EMPTY_COUNT(0x20) +#define RX_FIFO_FULL_COUNT_ZERO SLINK_RX_FIFO_FULL_COUNT(0) + +#define SLINK_STATUS2_RESET \ + (TX_FIFO_EMPTY_COUNT_MAX | RX_FIFO_FULL_COUNT_ZERO << 16) + +#define MAX_CHIP_SELECT 4 +#define SLINK_FIFO_DEPTH 32 + +struct tegra_slink_chip_data { + bool cs_hold_time; +}; + +struct tegra_slink_data { + struct device *dev; + struct spi_master *master; + const struct tegra_slink_chip_data *chip_data; + spinlock_t lock; + + struct clk *clk; + void __iomem *base; + phys_addr_t phys; + unsigned irq; + int dma_req_sel; + u32 spi_max_frequency; + u32 cur_speed; + + struct spi_device *cur_spi; + unsigned cur_pos; + unsigned cur_len; + unsigned words_per_32bit; + unsigned bytes_per_word; + unsigned curr_dma_words; + unsigned cur_direction; + + unsigned cur_rx_pos; + unsigned cur_tx_pos; + + unsigned dma_buf_size; + unsigned max_buf_size; + bool is_curr_dma_xfer; + bool is_hw_based_cs; + + struct completion rx_dma_complete; + struct completion tx_dma_complete; + + u32 tx_status; + u32 rx_status; + u32 status_reg; + bool is_packed; + unsigned long packed_size; + + u32 command_reg; + u32 command2_reg; + u32 dma_control_reg; + u32 def_command_reg; + u32 def_command2_reg; + + struct completion xfer_completion; + struct spi_transfer *curr_xfer; + struct dma_chan *rx_dma_chan; + u32 *rx_dma_buf; + dma_addr_t rx_dma_phys; + struct dma_async_tx_descriptor *rx_dma_desc; + + struct dma_chan *tx_dma_chan; + u32 *tx_dma_buf; + dma_addr_t tx_dma_phys; + struct dma_async_tx_descriptor *tx_dma_desc; +}; + +static int tegra_slink_runtime_suspend(struct device *dev); +static int tegra_slink_runtime_resume(struct device *dev); + +static inline unsigned long tegra_slink_readl(struct tegra_slink_data *tspi, + unsigned long reg) +{ + return readl(tspi->base + reg); +} + +static inline void tegra_slink_writel(struct tegra_slink_data *tspi, + unsigned long val, unsigned long reg) +{ + writel(val, tspi->base + reg); + + /* Read back register to make sure that register writes completed */ + if (reg != SLINK_TX_FIFO) + readl(tspi->base + SLINK_MAS_DATA); +} + +static void tegra_slink_clear_status(struct tegra_slink_data *tspi) +{ + unsigned long val; + unsigned long val_write = 0; + + val = tegra_slink_readl(tspi, SLINK_STATUS); + + /* Write 1 to clear status register */ + val_write = SLINK_RDY | SLINK_FIFO_ERROR; + tegra_slink_writel(tspi, val_write, SLINK_STATUS); +} + +static unsigned long tegra_slink_get_packed_size(struct tegra_slink_data *tspi, + struct spi_transfer *t) +{ + unsigned long val; + + switch (tspi->bytes_per_word) { + case 0: + val = SLINK_PACK_SIZE_4; + break; + case 1: + val = SLINK_PACK_SIZE_8; + break; + case 2: + val = SLINK_PACK_SIZE_16; + break; + case 4: + val = SLINK_PACK_SIZE_32; + break; + default: + val = 0; + } + return val; +} + +static unsigned tegra_slink_calculate_curr_xfer_param( + struct spi_device *spi, struct tegra_slink_data *tspi, + struct spi_transfer *t) +{ + unsigned remain_len = t->len - tspi->cur_pos; + unsigned max_word; + unsigned bits_per_word ; + unsigned max_len; + unsigned total_fifo_words; + + bits_per_word = t->bits_per_word ? t->bits_per_word : + spi->bits_per_word; + tspi->bytes_per_word = (bits_per_word - 1) / 8 + 1; + + if (bits_per_word == 8 || bits_per_word == 16) { + tspi->is_packed = 1; + tspi->words_per_32bit = 32/bits_per_word; + } else { + tspi->is_packed = 0; + tspi->words_per_32bit = 1; + } + tspi->packed_size = tegra_slink_get_packed_size(tspi, t); + + if (tspi->is_packed) { + max_len = min(remain_len, tspi->max_buf_size); + tspi->curr_dma_words = max_len/tspi->bytes_per_word; + total_fifo_words = max_len/4; + } else { + max_word = (remain_len - 1) / tspi->bytes_per_word + 1; + max_word = min(max_word, tspi->max_buf_size/4); + tspi->curr_dma_words = max_word; + total_fifo_words = max_word; + } + return total_fifo_words; +} + +static unsigned tegra_slink_fill_tx_fifo_from_client_txbuf( + struct tegra_slink_data *tspi, struct spi_transfer *t) +{ + unsigned nbytes; + unsigned tx_empty_count; + unsigned long fifo_status; + unsigned max_n_32bit; + unsigned i, count; + unsigned long x; + unsigned int written_words; + unsigned fifo_words_left; + u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_tx_pos; + + fifo_status = tegra_slink_readl(tspi, SLINK_STATUS2); + tx_empty_count = SLINK_TX_FIFO_EMPTY_COUNT(fifo_status); + + if (tspi->is_packed) { + fifo_words_left = tx_empty_count * tspi->words_per_32bit; + written_words = min(fifo_words_left, tspi->curr_dma_words); + nbytes = written_words * tspi->bytes_per_word; + max_n_32bit = DIV_ROUND_UP(nbytes, 4); + for (count = 0; count < max_n_32bit; count++) { + x = 0; + for (i = 0; (i < 4) && nbytes; i++, nbytes--) + x |= (*tx_buf++) << (i*8); + tegra_slink_writel(tspi, x, SLINK_TX_FIFO); + } + } else { + max_n_32bit = min(tspi->curr_dma_words, tx_empty_count); + written_words = max_n_32bit; + nbytes = written_words * tspi->bytes_per_word; + for (count = 0; count < max_n_32bit; count++) { + x = 0; + for (i = 0; nbytes && (i < tspi->bytes_per_word); + i++, nbytes--) + x |= ((*tx_buf++) << i*8); + tegra_slink_writel(tspi, x, SLINK_TX_FIFO); + } + } + tspi->cur_tx_pos += written_words * tspi->bytes_per_word; + return written_words; +} + +static unsigned int tegra_slink_read_rx_fifo_to_client_rxbuf( + struct tegra_slink_data *tspi, struct spi_transfer *t) +{ + unsigned rx_full_count; + unsigned long fifo_status; + unsigned i, count; + unsigned long x; + unsigned int read_words = 0; + unsigned len; + u8 *rx_buf = (u8 *)t->rx_buf + tspi->cur_rx_pos; + + fifo_status = tegra_slink_readl(tspi, SLINK_STATUS2); + rx_full_count = SLINK_RX_FIFO_FULL_COUNT(fifo_status); + if (tspi->is_packed) { + len = tspi->curr_dma_words * tspi->bytes_per_word; + for (count = 0; count < rx_full_count; count++) { + x = tegra_slink_readl(tspi, SLINK_RX_FIFO); + for (i = 0; len && (i < 4); i++, len--) + *rx_buf++ = (x >> i*8) & 0xFF; + } + tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word; + read_words += tspi->curr_dma_words; + } else { + unsigned int bits_per_word; + + bits_per_word = t->bits_per_word ? t->bits_per_word : + tspi->cur_spi->bits_per_word; + for (count = 0; count < rx_full_count; count++) { + x = tegra_slink_readl(tspi, SLINK_RX_FIFO); + for (i = 0; (i < tspi->bytes_per_word); i++) + *rx_buf++ = (x >> (i*8)) & 0xFF; + } + tspi->cur_rx_pos += rx_full_count * tspi->bytes_per_word; + read_words += rx_full_count; + } + return read_words; +} + +static void tegra_slink_copy_client_txbuf_to_spi_txbuf( + struct tegra_slink_data *tspi, struct spi_transfer *t) +{ + unsigned len; + + /* Make the dma buffer to read by cpu */ + dma_sync_single_for_cpu(tspi->dev, tspi->tx_dma_phys, + tspi->dma_buf_size, DMA_TO_DEVICE); + + if (tspi->is_packed) { + len = tspi->curr_dma_words * tspi->bytes_per_word; + memcpy(tspi->tx_dma_buf, t->tx_buf + tspi->cur_pos, len); + } else { + unsigned int i; + unsigned int count; + u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_tx_pos; + unsigned consume = tspi->curr_dma_words * tspi->bytes_per_word; + unsigned int x; + + for (count = 0; count < tspi->curr_dma_words; count++) { + x = 0; + for (i = 0; consume && (i < tspi->bytes_per_word); + i++, consume--) + x |= ((*tx_buf++) << i * 8); + tspi->tx_dma_buf[count] = x; + } + } + tspi->cur_tx_pos += tspi->curr_dma_words * tspi->bytes_per_word; + + /* Make the dma buffer to read by dma */ + dma_sync_single_for_device(tspi->dev, tspi->tx_dma_phys, + tspi->dma_buf_size, DMA_TO_DEVICE); +} + +static void tegra_slink_copy_spi_rxbuf_to_client_rxbuf( + struct tegra_slink_data *tspi, struct spi_transfer *t) +{ + unsigned len; + + /* Make the dma buffer to read by cpu */ + dma_sync_single_for_cpu(tspi->dev, tspi->rx_dma_phys, + tspi->dma_buf_size, DMA_FROM_DEVICE); + + if (tspi->is_packed) { + len = tspi->curr_dma_words * tspi->bytes_per_word; + memcpy(t->rx_buf + tspi->cur_rx_pos, tspi->rx_dma_buf, len); + } else { + unsigned int i; + unsigned int count; + unsigned char *rx_buf = t->rx_buf + tspi->cur_rx_pos; + unsigned int x; + unsigned int rx_mask, bits_per_word; + + bits_per_word = t->bits_per_word ? t->bits_per_word : + tspi->cur_spi->bits_per_word; + rx_mask = (1 << bits_per_word) - 1; + for (count = 0; count < tspi->curr_dma_words; count++) { + x = tspi->rx_dma_buf[count]; + x &= rx_mask; + for (i = 0; (i < tspi->bytes_per_word); i++) + *rx_buf++ = (x >> (i*8)) & 0xFF; + } + } + tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word; + + /* Make the dma buffer to read by dma */ + dma_sync_single_for_device(tspi->dev, tspi->rx_dma_phys, + tspi->dma_buf_size, DMA_FROM_DEVICE); +} + +static void tegra_slink_dma_complete(void *args) +{ + struct completion *dma_complete = args; + + complete(dma_complete); +} + +static int tegra_slink_start_tx_dma(struct tegra_slink_data *tspi, int len) +{ + INIT_COMPLETION(tspi->tx_dma_complete); + tspi->tx_dma_desc = dmaengine_prep_slave_single(tspi->tx_dma_chan, + tspi->tx_dma_phys, len, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tspi->tx_dma_desc) { + dev_err(tspi->dev, "Not able to get desc for Tx\n"); + return -EIO; + } + + tspi->tx_dma_desc->callback = tegra_slink_dma_complete; + tspi->tx_dma_desc->callback_param = &tspi->tx_dma_complete; + + dmaengine_submit(tspi->tx_dma_desc); + dma_async_issue_pending(tspi->tx_dma_chan); + return 0; +} + +static int tegra_slink_start_rx_dma(struct tegra_slink_data *tspi, int len) +{ + INIT_COMPLETION(tspi->rx_dma_complete); + tspi->rx_dma_desc = dmaengine_prep_slave_single(tspi->rx_dma_chan, + tspi->rx_dma_phys, len, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tspi->rx_dma_desc) { + dev_err(tspi->dev, "Not able to get desc for Rx\n"); + return -EIO; + } + + tspi->rx_dma_desc->callback = tegra_slink_dma_complete; + tspi->rx_dma_desc->callback_param = &tspi->rx_dma_complete; + + dmaengine_submit(tspi->rx_dma_desc); + dma_async_issue_pending(tspi->rx_dma_chan); + return 0; +} + +static int tegra_slink_start_dma_based_transfer( + struct tegra_slink_data *tspi, struct spi_transfer *t) +{ + unsigned long val; + unsigned long test_val; + unsigned int len; + int ret = 0; + unsigned long status; + + /* Make sure that Rx and Tx fifo are empty */ + status = tegra_slink_readl(tspi, SLINK_STATUS); + if ((status & SLINK_FIFO_EMPTY) != SLINK_FIFO_EMPTY) { + dev_err(tspi->dev, + "Rx/Tx fifo are not empty status 0x%08lx\n", status); + return -EIO; + } + + val = SLINK_DMA_BLOCK_SIZE(tspi->curr_dma_words - 1); + val |= tspi->packed_size; + if (tspi->is_packed) + len = DIV_ROUND_UP(tspi->curr_dma_words * tspi->bytes_per_word, + 4) * 4; + else + len = tspi->curr_dma_words * 4; + + /* Set attention level based on length of transfer */ + if (len & 0xF) + val |= SLINK_TX_TRIG_1 | SLINK_RX_TRIG_1; + else if (((len) >> 4) & 0x1) + val |= SLINK_TX_TRIG_4 | SLINK_RX_TRIG_4; + else + val |= SLINK_TX_TRIG_8 | SLINK_RX_TRIG_8; + + if (tspi->cur_direction & DATA_DIR_TX) + val |= SLINK_IE_TXC; + + if (tspi->cur_direction & DATA_DIR_RX) + val |= SLINK_IE_RXC; + + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + tspi->dma_control_reg = val; + + if (tspi->cur_direction & DATA_DIR_TX) { + tegra_slink_copy_client_txbuf_to_spi_txbuf(tspi, t); + wmb(); + ret = tegra_slink_start_tx_dma(tspi, len); + if (ret < 0) { + dev_err(tspi->dev, + "Starting tx dma failed, err %d\n", ret); + return ret; + } + + /* Wait for tx fifo to be fill before starting slink */ + test_val = tegra_slink_readl(tspi, SLINK_STATUS); + while (!(test_val & SLINK_TX_FULL)) + test_val = tegra_slink_readl(tspi, SLINK_STATUS); + } + + if (tspi->cur_direction & DATA_DIR_RX) { + /* Make the dma buffer to read by dma */ + dma_sync_single_for_device(tspi->dev, tspi->rx_dma_phys, + tspi->dma_buf_size, DMA_FROM_DEVICE); + + ret = tegra_slink_start_rx_dma(tspi, len); + if (ret < 0) { + dev_err(tspi->dev, + "Starting rx dma failed, err %d\n", ret); + if (tspi->cur_direction & DATA_DIR_TX) + dmaengine_terminate_all(tspi->tx_dma_chan); + return ret; + } + } + tspi->is_curr_dma_xfer = true; + if (tspi->is_packed) { + val |= SLINK_PACKED; + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + /* HW need small delay after settign Packed mode */ + udelay(1); + } + tspi->dma_control_reg = val; + + val |= SLINK_DMA_EN; + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + return ret; +} + +static int tegra_slink_start_cpu_based_transfer( + struct tegra_slink_data *tspi, struct spi_transfer *t) +{ + unsigned long val; + unsigned cur_words; + + val = tspi->packed_size; + if (tspi->cur_direction & DATA_DIR_TX) + val |= SLINK_IE_TXC; + + if (tspi->cur_direction & DATA_DIR_RX) + val |= SLINK_IE_RXC; + + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + tspi->dma_control_reg = val; + + if (tspi->cur_direction & DATA_DIR_TX) + cur_words = tegra_slink_fill_tx_fifo_from_client_txbuf(tspi, t); + else + cur_words = tspi->curr_dma_words; + val |= SLINK_DMA_BLOCK_SIZE(cur_words - 1); + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + tspi->dma_control_reg = val; + + tspi->is_curr_dma_xfer = false; + if (tspi->is_packed) { + val |= SLINK_PACKED; + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + udelay(1); + wmb(); + } + tspi->dma_control_reg = val; + val |= SLINK_DMA_EN; + tegra_slink_writel(tspi, val, SLINK_DMA_CTL); + return 0; +} + +static int tegra_slink_init_dma_param(struct tegra_slink_data *tspi, + bool dma_to_memory) +{ + struct dma_chan *dma_chan; + u32 *dma_buf; + dma_addr_t dma_phys; + int ret; + struct dma_slave_config dma_sconfig; + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_chan = dma_request_channel(mask, NULL, NULL); + if (!dma_chan) { + dev_err(tspi->dev, + "Dma channel is not available, will try later\n"); + return -EPROBE_DEFER; + } + + dma_buf = dma_alloc_coherent(tspi->dev, tspi->dma_buf_size, + &dma_phys, GFP_KERNEL); + if (!dma_buf) { + dev_err(tspi->dev, " Not able to allocate the dma buffer\n"); + dma_release_channel(dma_chan); + return -ENOMEM; + } + + dma_sconfig.slave_id = tspi->dma_req_sel; + if (dma_to_memory) { + dma_sconfig.src_addr = tspi->phys + SLINK_RX_FIFO; + dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_sconfig.src_maxburst = 0; + } else { + dma_sconfig.dst_addr = tspi->phys + SLINK_TX_FIFO; + dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_sconfig.dst_maxburst = 0; + } + + ret = dmaengine_slave_config(dma_chan, &dma_sconfig); + if (ret) + goto scrub; + if (dma_to_memory) { + tspi->rx_dma_chan = dma_chan; + tspi->rx_dma_buf = dma_buf; + tspi->rx_dma_phys = dma_phys; + } else { + tspi->tx_dma_chan = dma_chan; + tspi->tx_dma_buf = dma_buf; + tspi->tx_dma_phys = dma_phys; + } + return 0; + +scrub: + dma_free_coherent(tspi->dev, tspi->dma_buf_size, dma_buf, dma_phys); + dma_release_channel(dma_chan); + return ret; +} + +static void tegra_slink_deinit_dma_param(struct tegra_slink_data *tspi, + bool dma_to_memory) +{ + u32 *dma_buf; + dma_addr_t dma_phys; + struct dma_chan *dma_chan; + + if (dma_to_memory) { + dma_buf = tspi->rx_dma_buf; + dma_chan = tspi->rx_dma_chan; + dma_phys = tspi->rx_dma_phys; + tspi->rx_dma_chan = NULL; + tspi->rx_dma_buf = NULL; + } else { + dma_buf = tspi->tx_dma_buf; + dma_chan = tspi->tx_dma_chan; + dma_phys = tspi->tx_dma_phys; + tspi->tx_dma_buf = NULL; + tspi->tx_dma_chan = NULL; + } + if (!dma_chan) + return; + + dma_free_coherent(tspi->dev, tspi->dma_buf_size, dma_buf, dma_phys); + dma_release_channel(dma_chan); +} + +static int tegra_slink_start_transfer_one(struct spi_device *spi, + struct spi_transfer *t, bool is_first_of_msg, + bool is_single_xfer) +{ + struct tegra_slink_data *tspi = spi_master_get_devdata(spi->master); + u32 speed; + u8 bits_per_word; + unsigned total_fifo_words; + int ret; + struct tegra_spi_device_controller_data *cdata = spi->controller_data; + unsigned long command; + unsigned long command2; + + bits_per_word = t->bits_per_word ? t->bits_per_word : + spi->bits_per_word; + speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz; + if (!speed) + speed = tspi->spi_max_frequency; + if (speed != tspi->cur_speed) { + clk_set_rate(tspi->clk, speed * 4); + tspi->cur_speed = speed; + } + + tspi->cur_spi = spi; + tspi->cur_pos = 0; + tspi->cur_rx_pos = 0; + tspi->cur_tx_pos = 0; + tspi->curr_xfer = t; + total_fifo_words = tegra_slink_calculate_curr_xfer_param(spi, tspi, t); + + if (is_first_of_msg) { + tegra_slink_clear_status(tspi); + + command = tspi->def_command_reg; + command |= SLINK_BIT_LENGTH(bits_per_word - 1); + + command2 = tspi->def_command2_reg; + command2 |= SLINK_SS_EN_CS(spi->chip_select); + + /* possibly use the hw based chip select */ + tspi->is_hw_based_cs = false; + if (cdata && cdata->is_hw_based_cs && is_single_xfer && + ((tspi->curr_dma_words * tspi->bytes_per_word) == + (t->len - tspi->cur_pos))) { + int setup_count; + int sts2; + + setup_count = cdata->cs_setup_clk_count >> 1; + setup_count = max(setup_count, 3); + command2 |= SLINK_SS_SETUP(setup_count); + if (tspi->chip_data->cs_hold_time) { + int hold_count; + + hold_count = cdata->cs_hold_clk_count; + hold_count = max(hold_count, 0xF); + sts2 = tegra_slink_readl(tspi, SLINK_STATUS2); + sts2 &= ~SLINK_SS_HOLD_TIME(0xF); + sts2 |= SLINK_SS_HOLD_TIME(hold_count); + tegra_slink_writel(tspi, sts2, SLINK_STATUS2); + } + tspi->is_hw_based_cs = true; + } + + if (tspi->is_hw_based_cs) + command &= ~SLINK_CS_SW; + else + command |= SLINK_CS_SW | SLINK_CS_VALUE; + + command &= ~SLINK_MODES; + if (spi->mode & SPI_CPHA) + command |= SLINK_CK_SDA; + + if (spi->mode & SPI_CPOL) + command |= SLINK_IDLE_SCLK_DRIVE_HIGH; + else + command |= SLINK_IDLE_SCLK_DRIVE_LOW; + } else { + command = tspi->command_reg; + command &= ~SLINK_BIT_LENGTH(~0); + command |= SLINK_BIT_LENGTH(bits_per_word - 1); + + command2 = tspi->command2_reg; + command2 &= ~(SLINK_RXEN | SLINK_TXEN); + } + + tegra_slink_writel(tspi, command, SLINK_COMMAND); + tspi->command_reg = command; + + tspi->cur_direction = 0; + if (t->rx_buf) { + command2 |= SLINK_RXEN; + tspi->cur_direction |= DATA_DIR_RX; + } + if (t->tx_buf) { + command2 |= SLINK_TXEN; + tspi->cur_direction |= DATA_DIR_TX; + } + tegra_slink_writel(tspi, command2, SLINK_COMMAND2); + tspi->command2_reg = command2; + + if (total_fifo_words > SLINK_FIFO_DEPTH) + ret = tegra_slink_start_dma_based_transfer(tspi, t); + else + ret = tegra_slink_start_cpu_based_transfer(tspi, t); + return ret; +} + +static int tegra_slink_setup(struct spi_device *spi) +{ + struct tegra_slink_data *tspi = spi_master_get_devdata(spi->master); + unsigned long val; + unsigned long flags; + int ret; + unsigned int cs_pol_bit[MAX_CHIP_SELECT] = { + SLINK_CS_POLARITY, + SLINK_CS_POLARITY1, + SLINK_CS_POLARITY2, + SLINK_CS_POLARITY3, + }; + + dev_dbg(&spi->dev, "setup %d bpw, %scpol, %scpha, %dHz\n", + spi->bits_per_word, + spi->mode & SPI_CPOL ? "" : "~", + spi->mode & SPI_CPHA ? "" : "~", + spi->max_speed_hz); + + BUG_ON(spi->chip_select >= MAX_CHIP_SELECT); + + ret = pm_runtime_get_sync(tspi->dev); + if (ret < 0) { + dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret); + return ret; + } + + spin_lock_irqsave(&tspi->lock, flags); + val = tspi->def_command_reg; + if (spi->mode & SPI_CS_HIGH) + val |= cs_pol_bit[spi->chip_select]; + else + val &= ~cs_pol_bit[spi->chip_select]; + tspi->def_command_reg = val; + tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND); + spin_unlock_irqrestore(&tspi->lock, flags); + + pm_runtime_put(tspi->dev); + return 0; +} + +static int tegra_slink_prepare_transfer(struct spi_master *master) +{ + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + + return pm_runtime_get_sync(tspi->dev); +} + +static int tegra_slink_unprepare_transfer(struct spi_master *master) +{ + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + + pm_runtime_put(tspi->dev); + return 0; +} + +static int tegra_slink_transfer_one_message(struct spi_master *master, + struct spi_message *msg) +{ + bool is_first_msg = true; + int single_xfer; + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + struct spi_transfer *xfer; + struct spi_device *spi = msg->spi; + int ret; + + msg->status = 0; + msg->actual_length = 0; + single_xfer = list_is_singular(&msg->transfers); + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + INIT_COMPLETION(tspi->xfer_completion); + ret = tegra_slink_start_transfer_one(spi, xfer, + is_first_msg, single_xfer); + if (ret < 0) { + dev_err(tspi->dev, + "spi can not start transfer, err %d\n", ret); + goto exit; + } + is_first_msg = false; + ret = wait_for_completion_timeout(&tspi->xfer_completion, + SLINK_DMA_TIMEOUT); + if (WARN_ON(ret == 0)) { + dev_err(tspi->dev, + "spi trasfer timeout, err %d\n", ret); + ret = -EIO; + goto exit; + } + + if (tspi->tx_status || tspi->rx_status) { + dev_err(tspi->dev, "Error in Transfer\n"); + ret = -EIO; + goto exit; + } + msg->actual_length += xfer->len; + if (xfer->cs_change && xfer->delay_usecs) { + tegra_slink_writel(tspi, tspi->def_command_reg, + SLINK_COMMAND); + udelay(xfer->delay_usecs); + } + } + ret = 0; +exit: + tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND); + tegra_slink_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2); + msg->status = ret; + spi_finalize_current_message(master); + return ret; +} + +static irqreturn_t handle_cpu_based_xfer(struct tegra_slink_data *tspi) +{ + struct spi_transfer *t = tspi->curr_xfer; + unsigned long flags; + + spin_lock_irqsave(&tspi->lock, flags); + if (tspi->tx_status || tspi->rx_status || + (tspi->status_reg & SLINK_BSY)) { + dev_err(tspi->dev, + "CpuXfer ERROR bit set 0x%x\n", tspi->status_reg); + dev_err(tspi->dev, + "CpuXfer 0x%08x:0x%08x:0x%08x\n", tspi->command_reg, + tspi->command2_reg, tspi->dma_control_reg); + tegra_periph_reset_assert(tspi->clk); + udelay(2); + tegra_periph_reset_deassert(tspi->clk); + complete(&tspi->xfer_completion); + goto exit; + } + + if (tspi->cur_direction & DATA_DIR_RX) + tegra_slink_read_rx_fifo_to_client_rxbuf(tspi, t); + + if (tspi->cur_direction & DATA_DIR_TX) + tspi->cur_pos = tspi->cur_tx_pos; + else + tspi->cur_pos = tspi->cur_rx_pos; + + if (tspi->cur_pos == t->len) { + complete(&tspi->xfer_completion); + goto exit; + } + + tegra_slink_calculate_curr_xfer_param(tspi->cur_spi, tspi, t); + tegra_slink_start_cpu_based_transfer(tspi, t); +exit: + spin_unlock_irqrestore(&tspi->lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t handle_dma_based_xfer(struct tegra_slink_data *tspi) +{ + struct spi_transfer *t = tspi->curr_xfer; + long wait_status; + int err = 0; + unsigned total_fifo_words; + unsigned long flags; + + /* Abort dmas if any error */ + if (tspi->cur_direction & DATA_DIR_TX) { + if (tspi->tx_status) { + dmaengine_terminate_all(tspi->tx_dma_chan); + err += 1; + } else { + wait_status = wait_for_completion_interruptible_timeout( + &tspi->tx_dma_complete, SLINK_DMA_TIMEOUT); + if (wait_status <= 0) { + dmaengine_terminate_all(tspi->tx_dma_chan); + dev_err(tspi->dev, "TxDma Xfer failed\n"); + err += 1; + } + } + } + + if (tspi->cur_direction & DATA_DIR_RX) { + if (tspi->rx_status) { + dmaengine_terminate_all(tspi->rx_dma_chan); + err += 2; + } else { + wait_status = wait_for_completion_interruptible_timeout( + &tspi->rx_dma_complete, SLINK_DMA_TIMEOUT); + if (wait_status <= 0) { + dmaengine_terminate_all(tspi->rx_dma_chan); + dev_err(tspi->dev, "RxDma Xfer failed\n"); + err += 2; + } + } + } + + spin_lock_irqsave(&tspi->lock, flags); + if (err) { + dev_err(tspi->dev, + "DmaXfer: ERROR bit set 0x%x\n", tspi->status_reg); + dev_err(tspi->dev, + "DmaXfer 0x%08x:0x%08x:0x%08x\n", tspi->command_reg, + tspi->command2_reg, tspi->dma_control_reg); + tegra_periph_reset_assert(tspi->clk); + udelay(2); + tegra_periph_reset_deassert(tspi->clk); + complete(&tspi->xfer_completion); + spin_unlock_irqrestore(&tspi->lock, flags); + return IRQ_HANDLED; + } + + if (tspi->cur_direction & DATA_DIR_RX) + tegra_slink_copy_spi_rxbuf_to_client_rxbuf(tspi, t); + + if (tspi->cur_direction & DATA_DIR_TX) + tspi->cur_pos = tspi->cur_tx_pos; + else + tspi->cur_pos = tspi->cur_rx_pos; + + if (tspi->cur_pos == t->len) { + complete(&tspi->xfer_completion); + goto exit; + } + + /* Continue transfer in current message */ + total_fifo_words = tegra_slink_calculate_curr_xfer_param(tspi->cur_spi, + tspi, t); + if (total_fifo_words > SLINK_FIFO_DEPTH) + err = tegra_slink_start_dma_based_transfer(tspi, t); + else + err = tegra_slink_start_cpu_based_transfer(tspi, t); + +exit: + spin_unlock_irqrestore(&tspi->lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t tegra_slink_isr_thread(int irq, void *context_data) +{ + struct tegra_slink_data *tspi = context_data; + + if (!tspi->is_curr_dma_xfer) + return handle_cpu_based_xfer(tspi); + return handle_dma_based_xfer(tspi); +} + +static irqreturn_t tegra_slink_isr(int irq, void *context_data) +{ + struct tegra_slink_data *tspi = context_data; + + tspi->status_reg = tegra_slink_readl(tspi, SLINK_STATUS); + if (tspi->cur_direction & DATA_DIR_TX) + tspi->tx_status = tspi->status_reg & + (SLINK_TX_OVF | SLINK_TX_UNF); + + if (tspi->cur_direction & DATA_DIR_RX) + tspi->rx_status = tspi->status_reg & + (SLINK_RX_OVF | SLINK_RX_UNF); + tegra_slink_clear_status(tspi); + + return IRQ_WAKE_THREAD; +} + +static struct tegra_spi_platform_data *tegra_slink_parse_dt( + struct platform_device *pdev) +{ + struct tegra_spi_platform_data *pdata; + const unsigned int *prop; + struct device_node *np = pdev->dev.of_node; + u32 of_dma[2]; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(&pdev->dev, "Memory alloc for pdata failed\n"); + return NULL; + } + + if (of_property_read_u32_array(np, "nvidia,dma-request-selector", + of_dma, 2) >= 0) + pdata->dma_req_sel = of_dma[1]; + + prop = of_get_property(np, "spi-max-frequency", NULL); + if (prop) + pdata->spi_max_frequency = be32_to_cpup(prop); + + return pdata; +} + +const struct tegra_slink_chip_data tegra30_spi_cdata = { + .cs_hold_time = true, +}; + +const struct tegra_slink_chip_data tegra20_spi_cdata = { + .cs_hold_time = false, +}; + +static struct of_device_id tegra_slink_of_match[] __devinitconst = { + { .compatible = "nvidia,tegra20-slink", .data = &tegra20_spi_cdata, }, + { .compatible = "nvidia,tegra30-slink", .data = &tegra30_spi_cdata, }, + {} +}; +MODULE_DEVICE_TABLE(of, tegra_slink_of_match); + +static int __devinit tegra_slink_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct tegra_slink_data *tspi; + struct resource *r; + struct tegra_spi_platform_data *pdata = pdev->dev.platform_data; + int ret, spi_irq; + const struct tegra_slink_chip_data *cdata = NULL; + const struct of_device_id *match; + + match = of_match_device(of_match_ptr(tegra_slink_of_match), &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + cdata = match->data; + if (!pdata && pdev->dev.of_node) + pdata = tegra_slink_parse_dt(pdev); + + if (!pdata) { + dev_err(&pdev->dev, "No platform data, exiting\n"); + return -ENODEV; + } + + if (!pdata->spi_max_frequency) + pdata->spi_max_frequency = 25000000; /* 25MHz */ + + master = spi_alloc_master(&pdev->dev, sizeof(*tspi)); + if (!master) { + dev_err(&pdev->dev, "master allocation failed\n"); + return -ENOMEM; + } + + /* the spi->mode bits understood by this driver: */ + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->setup = tegra_slink_setup; + master->prepare_transfer_hardware = tegra_slink_prepare_transfer; + master->transfer_one_message = tegra_slink_transfer_one_message; + master->unprepare_transfer_hardware = tegra_slink_unprepare_transfer; + master->num_chipselect = MAX_CHIP_SELECT; + master->bus_num = -1; + + dev_set_drvdata(&pdev->dev, master); + tspi = spi_master_get_devdata(master); + tspi->master = master; + tspi->dma_req_sel = pdata->dma_req_sel; + tspi->dev = &pdev->dev; + tspi->chip_data = cdata; + spin_lock_init(&tspi->lock); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "No IO memory resource\n"); + ret = -ENODEV; + goto exit_free_master; + } + tspi->phys = r->start; + tspi->base = devm_request_and_ioremap(&pdev->dev, r); + if (!tspi->base) { + dev_err(&pdev->dev, + "Cannot request memregion/iomap dma address\n"); + ret = -EADDRNOTAVAIL; + goto exit_free_master; + } + + spi_irq = platform_get_irq(pdev, 0); + tspi->irq = spi_irq; + ret = request_threaded_irq(tspi->irq, tegra_slink_isr, + tegra_slink_isr_thread, IRQF_ONESHOT, + dev_name(&pdev->dev), tspi); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n", + tspi->irq); + goto exit_free_master; + } + + tspi->clk = devm_clk_get(&pdev->dev, "slink"); + if (IS_ERR(tspi->clk)) { + dev_err(&pdev->dev, "can not get clock\n"); + ret = PTR_ERR(tspi->clk); + goto exit_free_irq; + } + + tspi->max_buf_size = SLINK_FIFO_DEPTH << 2; + tspi->dma_buf_size = DEFAULT_SPI_DMA_BUF_LEN; + tspi->spi_max_frequency = pdata->spi_max_frequency; + + if (pdata->dma_req_sel) { + ret = tegra_slink_init_dma_param(tspi, true); + if (ret < 0) { + dev_err(&pdev->dev, "RxDma Init failed, err %d\n", ret); + goto exit_free_irq; + } + + ret = tegra_slink_init_dma_param(tspi, false); + if (ret < 0) { + dev_err(&pdev->dev, "TxDma Init failed, err %d\n", ret); + goto exit_rx_dma_free; + } + tspi->max_buf_size = tspi->dma_buf_size; + init_completion(&tspi->tx_dma_complete); + init_completion(&tspi->rx_dma_complete); + } + + init_completion(&tspi->xfer_completion); + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = tegra_slink_runtime_resume(&pdev->dev); + if (ret) + goto exit_pm_disable; + } + + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret); + goto exit_pm_disable; + } + tspi->def_command_reg = SLINK_M_S; + tspi->def_command2_reg = SLINK_CS_ACTIVE_BETWEEN; + tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND); + tegra_slink_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2); + pm_runtime_put(&pdev->dev); + + master->dev.of_node = pdev->dev.of_node; + ret = spi_register_master(master); + if (ret < 0) { + dev_err(&pdev->dev, "can not register to master err %d\n", ret); + goto exit_pm_disable; + } + return ret; + +exit_pm_disable: + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra_slink_runtime_suspend(&pdev->dev); + tegra_slink_deinit_dma_param(tspi, false); +exit_rx_dma_free: + tegra_slink_deinit_dma_param(tspi, true); +exit_free_irq: + free_irq(spi_irq, tspi); +exit_free_master: + spi_master_put(master); + return ret; +} + +static int __devexit tegra_slink_remove(struct platform_device *pdev) +{ + struct spi_master *master = dev_get_drvdata(&pdev->dev); + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + + free_irq(tspi->irq, tspi); + spi_unregister_master(master); + + if (tspi->tx_dma_chan) + tegra_slink_deinit_dma_param(tspi, false); + + if (tspi->rx_dma_chan) + tegra_slink_deinit_dma_param(tspi, true); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra_slink_runtime_suspend(&pdev->dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tegra_slink_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + + return spi_master_suspend(master); +} + +static int tegra_slink_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "pm runtime failed, e = %d\n", ret); + return ret; + } + tegra_slink_writel(tspi, tspi->command_reg, SLINK_COMMAND); + tegra_slink_writel(tspi, tspi->command2_reg, SLINK_COMMAND2); + pm_runtime_put(dev); + + return spi_master_resume(master); +} +#endif + +static int tegra_slink_runtime_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + + /* Flush all write which are in PPSB queue by reading back */ + tegra_slink_readl(tspi, SLINK_MAS_DATA); + + clk_disable_unprepare(tspi->clk); + return 0; +} + +static int tegra_slink_runtime_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct tegra_slink_data *tspi = spi_master_get_devdata(master); + int ret; + + ret = clk_prepare_enable(tspi->clk); + if (ret < 0) { + dev_err(tspi->dev, "clk_prepare failed: %d\n", ret); + return ret; + } + return 0; +} + +static const struct dev_pm_ops slink_pm_ops = { + SET_RUNTIME_PM_OPS(tegra_slink_runtime_suspend, + tegra_slink_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(tegra_slink_suspend, tegra_slink_resume) +}; +static struct platform_driver tegra_slink_driver = { + .driver = { + .name = "spi-tegra-slink", + .owner = THIS_MODULE, + .pm = &slink_pm_ops, + .of_match_table = of_match_ptr(tegra_slink_of_match), + }, + .probe = tegra_slink_probe, + .remove = __devexit_p(tegra_slink_remove), +}; +module_platform_driver(tegra_slink_driver); + +MODULE_ALIAS("platform:spi-tegra-slink"); +MODULE_DESCRIPTION("NVIDIA Tegra20/Tegra30 SLINK Controller Driver"); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/spi/spi-tegra.h b/include/linux/spi/spi-tegra.h new file mode 100644 index 000000000000..786932c62edb --- /dev/null +++ b/include/linux/spi/spi-tegra.h @@ -0,0 +1,40 @@ +/* + * spi-tegra.h: SPI interface for Nvidia Tegra20 SLINK controller. + * + * Copyright (C) 2011 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _LINUX_SPI_TEGRA_H +#define _LINUX_SPI_TEGRA_H + +struct tegra_spi_platform_data { + int dma_req_sel; + unsigned int spi_max_frequency; +}; + +/* + * Controller data from device to pass some info like + * hw based chip select can be used or not and if yes + * then CS hold and setup time. + */ +struct tegra_spi_device_controller_data { + bool is_hw_based_cs; + int cs_setup_clk_count; + int cs_hold_clk_count; +}; + +#endif /* _LINUX_SPI_TEGRA_H */ -- cgit v1.2.3 From 7d6ab9b8620bbca6718b36242113f4f069840641 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 25 Oct 2012 11:59:09 -0500 Subject: ARM: dts: Add Calxeda ECX-2000 support Separate out common dts pieces from highbank dts and add support for Calxeda ECX-2000 (Midway) SOC. Signed-off-by: Rob Herring Acked-by: Arnd Bergmann --- Documentation/devicetree/bindings/arm/calxeda.txt | 13 +- arch/arm/boot/dts/Makefile | 3 +- arch/arm/boot/dts/ecx-2000.dts | 104 ++++++++++ arch/arm/boot/dts/ecx-common.dtsi | 237 ++++++++++++++++++++++ arch/arm/boot/dts/highbank.dts | 219 +------------------- 5 files changed, 356 insertions(+), 220 deletions(-) create mode 100644 arch/arm/boot/dts/ecx-2000.dts create mode 100644 arch/arm/boot/dts/ecx-common.dtsi (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/calxeda.txt b/Documentation/devicetree/bindings/arm/calxeda.txt index 4755caaccba6..25fcf96795ca 100644 --- a/Documentation/devicetree/bindings/arm/calxeda.txt +++ b/Documentation/devicetree/bindings/arm/calxeda.txt @@ -1,8 +1,15 @@ -Calxeda Highbank Platforms Device Tree Bindings +Calxeda Platforms Device Tree Bindings ----------------------------------------------- -Boards with Calxeda Cortex-A9 based Highbank SOC shall have the following -properties. +Boards with Calxeda Cortex-A9 based ECX-1000 (Highbank) SOC shall have the +following properties. Required root node properties: - compatible = "calxeda,highbank"; + + +Boards with Calxeda Cortex-A15 based ECX-2000 SOC shall have the following +properties. + +Required root node properties: + - compatible = "calxeda,ecx-2000"; diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index f37cf9fa5fa0..5cc95667fa3b 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -24,7 +24,8 @@ dtb-$(CONFIG_ARCH_EXYNOS) += exynos4210-origen.dtb \ exynos4210-smdkv310.dtb \ exynos4210-trats.dtb \ exynos5250-smdk5250.dtb -dtb-$(CONFIG_ARCH_HIGHBANK) += highbank.dtb +dtb-$(CONFIG_ARCH_HIGHBANK) += highbank.dtb \ + ecx-2000.dtb dtb-$(CONFIG_ARCH_INTEGRATOR) += integratorap.dtb \ integratorcp.dtb dtb-$(CONFIG_ARCH_LPC32XX) += ea3250.dtb phy3250.dtb diff --git a/arch/arm/boot/dts/ecx-2000.dts b/arch/arm/boot/dts/ecx-2000.dts new file mode 100644 index 000000000000..46477ac1de99 --- /dev/null +++ b/arch/arm/boot/dts/ecx-2000.dts @@ -0,0 +1,104 @@ +/* + * Copyright 2011-2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/dts-v1/; + +/* First 4KB has pen for secondary cores. */ +/memreserve/ 0x00000000 0x0001000; + +/ { + model = "Calxeda ECX-2000"; + compatible = "calxeda,ecx-2000"; + #address-cells = <2>; + #size-cells = <2>; + clock-ranges; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "arm,cortex-a15"; + reg = <0>; + clocks = <&a9pll>; + clock-names = "cpu"; + }; + + cpu@1 { + compatible = "arm,cortex-a15"; + reg = <1>; + clocks = <&a9pll>; + clock-names = "cpu"; + }; + + cpu@2 { + compatible = "arm,cortex-a15"; + reg = <2>; + clocks = <&a9pll>; + clock-names = "cpu"; + }; + + cpu@3 { + compatible = "arm,cortex-a15"; + reg = <3>; + clocks = <&a9pll>; + clock-names = "cpu"; + }; + }; + + memory@0 { + name = "memory"; + device_type = "memory"; + reg = <0x00000000 0x00000000 0x00000000 0xff800000>; + }; + + memory@200000000 { + name = "memory"; + device_type = "memory"; + reg = <0x00000002 0x00000000 0x00000003 0x00000000>; + }; + + soc { + ranges = <0x00000000 0x00000000 0x00000000 0xffffffff>; + + timer { + compatible = "arm,cortex-a15-timer", "arm,armv7-timer"; interrupts = <1 13 0xf08>, + <1 14 0xf08>, + <1 11 0xf08>, + <1 10 0xf08>; + }; + + intc: interrupt-controller@fff11000 { + compatible = "arm,cortex-a15-gic"; + #interrupt-cells = <3>; + #size-cells = <0>; + #address-cells = <1>; + interrupt-controller; + interrupts = <1 9 0xf04>; + reg = <0xfff11000 0x1000>, + <0xfff12000 0x1000>, + <0xfff14000 0x2000>, + <0xfff16000 0x2000>; + }; + + pmu { + compatible = "arm,cortex-a9-pmu"; + interrupts = <0 76 4 0 75 4 0 74 4 0 73 4>; + }; + }; +}; + +/include/ "ecx-common.dtsi" diff --git a/arch/arm/boot/dts/ecx-common.dtsi b/arch/arm/boot/dts/ecx-common.dtsi new file mode 100644 index 000000000000..d61b535f682a --- /dev/null +++ b/arch/arm/boot/dts/ecx-common.dtsi @@ -0,0 +1,237 @@ +/* + * Copyright 2011-2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/ { + chosen { + bootargs = "console=ttyAMA0"; + }; + + soc { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + interrupt-parent = <&intc>; + + sata@ffe08000 { + compatible = "calxeda,hb-ahci"; + reg = <0xffe08000 0x10000>; + interrupts = <0 83 4>; + dma-coherent; + calxeda,port-phys = <&combophy5 0 &combophy0 0 + &combophy0 1 &combophy0 2 + &combophy0 3>; + }; + + sdhci@ffe0e000 { + compatible = "calxeda,hb-sdhci"; + reg = <0xffe0e000 0x1000>; + interrupts = <0 90 4>; + clocks = <&eclk>; + status = "disabled"; + }; + + memory-controller@fff00000 { + compatible = "calxeda,hb-ddr-ctrl"; + reg = <0xfff00000 0x1000>; + interrupts = <0 91 4>; + }; + + ipc@fff20000 { + compatible = "arm,pl320", "arm,primecell"; + reg = <0xfff20000 0x1000>; + interrupts = <0 7 4>; + clocks = <&pclk>; + clock-names = "apb_pclk"; + }; + + gpioe: gpio@fff30000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0xfff30000 0x1000>; + interrupts = <0 14 4>; + clocks = <&pclk>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + gpiof: gpio@fff31000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0xfff31000 0x1000>; + interrupts = <0 15 4>; + clocks = <&pclk>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + gpiog: gpio@fff32000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0xfff32000 0x1000>; + interrupts = <0 16 4>; + clocks = <&pclk>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + gpioh: gpio@fff33000 { + #gpio-cells = <2>; + compatible = "arm,pl061", "arm,primecell"; + gpio-controller; + reg = <0xfff33000 0x1000>; + interrupts = <0 17 4>; + clocks = <&pclk>; + clock-names = "apb_pclk"; + status = "disabled"; + }; + + timer@fff34000 { + compatible = "arm,sp804", "arm,primecell"; + reg = <0xfff34000 0x1000>; + interrupts = <0 18 4>; + clocks = <&pclk>; + clock-names = "apb_pclk"; + }; + + rtc@fff35000 { + compatible = "arm,pl031", "arm,primecell"; + reg = <0xfff35000 0x1000>; + interrupts = <0 19 4>; + clocks = <&pclk>; + clock-names = "apb_pclk"; + }; + + serial@fff36000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0xfff36000 0x1000>; + interrupts = <0 20 4>; + clocks = <&pclk>; + clock-names = "apb_pclk"; + }; + + smic@fff3a000 { + compatible = "ipmi-smic"; + device_type = "ipmi"; + reg = <0xfff3a000 0x1000>; + interrupts = <0 24 4>; + reg-size = <4>; + reg-spacing = <4>; + }; + + sregs@fff3c000 { + compatible = "calxeda,hb-sregs"; + reg = <0xfff3c000 0x1000>; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + osc: oscillator { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <33333000>; + }; + + ddrpll: ddrpll { + #clock-cells = <0>; + compatible = "calxeda,hb-pll-clock"; + clocks = <&osc>; + reg = <0x108>; + }; + + a9pll: a9pll { + #clock-cells = <0>; + compatible = "calxeda,hb-pll-clock"; + clocks = <&osc>; + reg = <0x100>; + }; + + a9periphclk: a9periphclk { + #clock-cells = <0>; + compatible = "calxeda,hb-a9periph-clock"; + clocks = <&a9pll>; + reg = <0x104>; + }; + + a9bclk: a9bclk { + #clock-cells = <0>; + compatible = "calxeda,hb-a9bus-clock"; + clocks = <&a9pll>; + reg = <0x104>; + }; + + emmcpll: emmcpll { + #clock-cells = <0>; + compatible = "calxeda,hb-pll-clock"; + clocks = <&osc>; + reg = <0x10C>; + }; + + eclk: eclk { + #clock-cells = <0>; + compatible = "calxeda,hb-emmc-clock"; + clocks = <&emmcpll>; + reg = <0x114>; + }; + + pclk: pclk { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <150000000>; + }; + }; + }; + + dma@fff3d000 { + compatible = "arm,pl330", "arm,primecell"; + reg = <0xfff3d000 0x1000>; + interrupts = <0 92 4>; + clocks = <&pclk>; + clock-names = "apb_pclk"; + }; + + ethernet@fff50000 { + compatible = "calxeda,hb-xgmac"; + reg = <0xfff50000 0x1000>; + interrupts = <0 77 4 0 78 4 0 79 4>; + dma-coherent; + }; + + ethernet@fff51000 { + compatible = "calxeda,hb-xgmac"; + reg = <0xfff51000 0x1000>; + interrupts = <0 80 4 0 81 4 0 82 4>; + dma-coherent; + }; + + combophy0: combo-phy@fff58000 { + compatible = "calxeda,hb-combophy"; + #phy-cells = <1>; + reg = <0xfff58000 0x1000>; + phydev = <5>; + }; + + combophy5: combo-phy@fff5d000 { + compatible = "calxeda,hb-combophy"; + #phy-cells = <1>; + reg = <0xfff5d000 0x1000>; + phydev = <31>; + }; + }; +}; diff --git a/arch/arm/boot/dts/highbank.dts b/arch/arm/boot/dts/highbank.dts index e39a79a61e0c..a9ae5d32e80d 100644 --- a/arch/arm/boot/dts/highbank.dts +++ b/arch/arm/boot/dts/highbank.dts @@ -69,16 +69,8 @@ reg = <0x00000000 0xff900000>; }; - chosen { - bootargs = "console=ttyAMA0"; - }; - soc { - #address-cells = <1>; - #size-cells = <1>; - compatible = "simple-bus"; - interrupt-parent = <&intc>; - ranges; + ranges = <0x00000000 0x00000000 0xffffffff>; timer@fff10600 { compatible = "arm,cortex-a9-twd-timer"; @@ -117,178 +109,6 @@ interrupts = <0 76 4 0 75 4 0 74 4 0 73 4>; }; - sata@ffe08000 { - compatible = "calxeda,hb-ahci"; - reg = <0xffe08000 0x10000>; - interrupts = <0 83 4>; - calxeda,port-phys = <&combophy5 0 &combophy0 0 - &combophy0 1 &combophy0 2 - &combophy0 3>; - dma-coherent; - }; - - sdhci@ffe0e000 { - compatible = "calxeda,hb-sdhci"; - reg = <0xffe0e000 0x1000>; - interrupts = <0 90 4>; - clocks = <&eclk>; - status = "disabled"; - }; - - memory-controller@fff00000 { - compatible = "calxeda,hb-ddr-ctrl"; - reg = <0xfff00000 0x1000>; - interrupts = <0 91 4>; - }; - - ipc@fff20000 { - compatible = "arm,pl320", "arm,primecell"; - reg = <0xfff20000 0x1000>; - interrupts = <0 7 4>; - clocks = <&pclk>; - clock-names = "apb_pclk"; - }; - - gpioe: gpio@fff30000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0xfff30000 0x1000>; - interrupts = <0 14 4>; - clocks = <&pclk>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - - gpiof: gpio@fff31000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0xfff31000 0x1000>; - interrupts = <0 15 4>; - clocks = <&pclk>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - - gpiog: gpio@fff32000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0xfff32000 0x1000>; - interrupts = <0 16 4>; - clocks = <&pclk>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - - gpioh: gpio@fff33000 { - #gpio-cells = <2>; - compatible = "arm,pl061", "arm,primecell"; - gpio-controller; - reg = <0xfff33000 0x1000>; - interrupts = <0 17 4>; - clocks = <&pclk>; - clock-names = "apb_pclk"; - status = "disabled"; - }; - - timer { - compatible = "arm,sp804", "arm,primecell"; - reg = <0xfff34000 0x1000>; - interrupts = <0 18 4>; - clocks = <&pclk>; - clock-names = "apb_pclk"; - }; - - rtc@fff35000 { - compatible = "arm,pl031", "arm,primecell"; - reg = <0xfff35000 0x1000>; - interrupts = <0 19 4>; - clocks = <&pclk>; - clock-names = "apb_pclk"; - }; - - serial@fff36000 { - compatible = "arm,pl011", "arm,primecell"; - reg = <0xfff36000 0x1000>; - interrupts = <0 20 4>; - clocks = <&pclk>; - clock-names = "apb_pclk"; - }; - - smic@fff3a000 { - compatible = "ipmi-smic"; - device_type = "ipmi"; - reg = <0xfff3a000 0x1000>; - interrupts = <0 24 4>; - reg-size = <4>; - reg-spacing = <4>; - }; - - sregs@fff3c000 { - compatible = "calxeda,hb-sregs"; - reg = <0xfff3c000 0x1000>; - - clocks { - #address-cells = <1>; - #size-cells = <0>; - - osc: oscillator { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <33333000>; - }; - - ddrpll: ddrpll { - #clock-cells = <0>; - compatible = "calxeda,hb-pll-clock"; - clocks = <&osc>; - reg = <0x108>; - }; - - a9pll: a9pll { - #clock-cells = <0>; - compatible = "calxeda,hb-pll-clock"; - clocks = <&osc>; - reg = <0x100>; - }; - - a9periphclk: a9periphclk { - #clock-cells = <0>; - compatible = "calxeda,hb-a9periph-clock"; - clocks = <&a9pll>; - reg = <0x104>; - }; - - a9bclk: a9bclk { - #clock-cells = <0>; - compatible = "calxeda,hb-a9bus-clock"; - clocks = <&a9pll>; - reg = <0x104>; - }; - - emmcpll: emmcpll { - #clock-cells = <0>; - compatible = "calxeda,hb-pll-clock"; - clocks = <&osc>; - reg = <0x10C>; - }; - - eclk: eclk { - #clock-cells = <0>; - compatible = "calxeda,hb-emmc-clock"; - clocks = <&emmcpll>; - reg = <0x114>; - }; - - pclk: pclk { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <150000000>; - }; - }; - }; sregs@fff3c200 { compatible = "calxeda,hb-sregs-l2-ecc"; @@ -296,40 +116,7 @@ interrupts = <0 71 4 0 72 4>; }; - dma@fff3d000 { - compatible = "arm,pl330", "arm,primecell"; - reg = <0xfff3d000 0x1000>; - interrupts = <0 92 4>; - clocks = <&pclk>; - clock-names = "apb_pclk"; - }; - - ethernet@fff50000 { - compatible = "calxeda,hb-xgmac"; - reg = <0xfff50000 0x1000>; - interrupts = <0 77 4 0 78 4 0 79 4>; - dma-coherent; - }; - - ethernet@fff51000 { - compatible = "calxeda,hb-xgmac"; - reg = <0xfff51000 0x1000>; - interrupts = <0 80 4 0 81 4 0 82 4>; - dma-coherent; - }; - - combophy0: combo-phy@fff58000 { - compatible = "calxeda,hb-combophy"; - #phy-cells = <1>; - reg = <0xfff58000 0x1000>; - phydev = <5>; - }; - - combophy5: combo-phy@fff5d000 { - compatible = "calxeda,hb-combophy"; - #phy-cells = <1>; - reg = <0xfff5d000 0x1000>; - phydev = <31>; - }; }; }; + +/include/ "ecx-common.dtsi" -- cgit v1.2.3 From fb647002e4b6393a1f552b2fe48ba88c50d1d6e4 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 28 Sep 2012 15:24:40 +0100 Subject: Documentation: Detail permitted DT properties for the BU21013 Touch Screen Here we apply required documentation for the Rohm BU21013 Touch Screen driver which describe available properties and how to use them. Cc: Dmitry Torokhov Cc: linux-input@vger.kernel.org Cc: devicetree-discuss@lists.ozlabs.org Acked-by: Arnd Bergmann Acked-by: Linus Walleij Signed-off-by: Lee Jones --- .../bindings/input/touchscreen/bu21013.txt | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/bu21013.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/input/touchscreen/bu21013.txt b/Documentation/devicetree/bindings/input/touchscreen/bu21013.txt new file mode 100644 index 000000000000..ca5a2c86480c --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/bu21013.txt @@ -0,0 +1,28 @@ +* Rohm BU21013 Touch Screen + +Required properties: + - compatible : "rohm,bu21013_tp" + - reg : I2C device address + +Optional properties: + - touch-gpio : GPIO pin registering a touch event + - -supply : Phandle to a regulator supply + - rohm,touch-max-x : Maximum outward permitted limit in the X axis + - rohm,touch-max-y : Maximum outward permitted limit in the Y axis + - rohm,flip-x : Flip touch coordinates on the X axis + - rohm,flip-y : Flip touch coordinates on the Y axis + +Example: + + i2c@80110000 { + bu21013_tp@0x5c { + compatible = "rohm,bu21013_tp"; + reg = <0x5c>; + touch-gpio = <&gpio2 20 0x4>; + avdd-supply = <&ab8500_ldo_aux1_reg>; + + rohm,touch-max-x = <384>; + rohm,touch-max-y = <704>; + rohm,flip-y; + }; + }; -- cgit v1.2.3 From 63e1ed2364050073770c085021377d7764969b85 Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Sat, 27 Oct 2012 14:49:57 +1300 Subject: pwm: vt8500: Update vt8500 PWM driver support This patch updates pwm-vt8500.c to support devicetree probing and make use of the common clock subsystem. A binding document describing the PWM controller found on arch-vt8500 is also included. Signed-off-by: Tony Prisk Acked-by: Arnd Bergmann Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/vt8500-pwm.txt | 17 +++++ drivers/pwm/pwm-vt8500.c | 86 ++++++++++++++++------ 2 files changed, 80 insertions(+), 23 deletions(-) create mode 100644 Documentation/devicetree/bindings/pwm/vt8500-pwm.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt b/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt new file mode 100644 index 000000000000..bcc63678a9a5 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt @@ -0,0 +1,17 @@ +VIA/Wondermedia VT8500/WM8xxx series SoC PWM controller + +Required properties: +- compatible: should be "via,vt8500-pwm" +- reg: physical base address and length of the controller's registers +- #pwm-cells: should be 2. The first cell specifies the per-chip index + of the PWM to use and the second cell is the period in nanoseconds. +- clocks: phandle to the PWM source clock + +Example: + +pwm1: pwm@d8220000 { + #pwm-cells = <2>; + compatible = "via,vt8500-pwm"; + reg = <0xd8220000 0x1000>; + clocks = <&clkpwm>; +}; diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index ad14389b7144..970b0c6e268f 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -1,7 +1,8 @@ /* * drivers/pwm/pwm-vt8500.c * - * Copyright (C) 2010 Alexey Charkov + * Copyright (C) 2012 Tony Prisk + * Copyright (C) 2010 Alexey Charkov * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -21,14 +22,24 @@ #include #include #include +#include #include -#define VT8500_NR_PWMS 4 +#include +#include +#include + +/* + * SoC architecture allocates register space for 4 PWMs but only + * 2 are currently implemented. + */ +#define VT8500_NR_PWMS 2 struct vt8500_chip { struct pwm_chip chip; void __iomem *base; + struct clk *clk; }; #define to_vt8500_chip(chip) container_of(chip, struct vt8500_chip, chip) @@ -52,7 +63,7 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, unsigned long long c; unsigned long period_cycles, prescale, pv, dc; - c = 25000000/2; /* wild guess --- need to implement clocks */ + c = clk_get_rate(vt8500->clk); c = c * period_ns; do_div(c, 1000000000); period_cycles = c; @@ -85,8 +96,15 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, static int vt8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { + int err; struct vt8500_chip *vt8500 = to_vt8500_chip(chip); + err = clk_enable(vt8500->clk); + if (err < 0) + dev_err(chip->dev, "failed to enable clock\n"); + return err; + }; + pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0)); writel(5, vt8500->base + (pwm->hwpwm << 4)); return 0; @@ -98,6 +116,8 @@ static void vt8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0)); writel(0, vt8500->base + (pwm->hwpwm << 4)); + + clk_disable(vt8500->clk); } static struct pwm_ops vt8500_pwm_ops = { @@ -107,12 +127,24 @@ static struct pwm_ops vt8500_pwm_ops = { .owner = THIS_MODULE, }; -static int __devinit pwm_probe(struct platform_device *pdev) +static const struct of_device_id vt8500_pwm_dt_ids[] = { + { .compatible = "via,vt8500-pwm", }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, vt8500_pwm_dt_ids); + +static int vt8500_pwm_probe(struct platform_device *pdev) { struct vt8500_chip *chip; struct resource *r; + struct device_node *np = pdev->dev.of_node; int ret; + if (!np) { + dev_err(&pdev->dev, "invalid devicetree node\n"); + return -EINVAL; + } + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); if (chip == NULL) { dev_err(&pdev->dev, "failed to allocate memory\n"); @@ -124,6 +156,12 @@ static int __devinit pwm_probe(struct platform_device *pdev) chip->chip.base = -1; chip->chip.npwm = VT8500_NR_PWMS; + chip->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(chip->clk)) { + dev_err(&pdev->dev, "clock source not specified\n"); + return PTR_ERR(chip->clk); + } + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { dev_err(&pdev->dev, "no memory resource defined\n"); @@ -131,18 +169,26 @@ static int __devinit pwm_probe(struct platform_device *pdev) } chip->base = devm_request_and_ioremap(&pdev->dev, r); - if (chip->base == NULL) + if (!chip->base) return -EADDRNOTAVAIL; + ret = clk_prepare(chip->clk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to prepare clock\n"); + return ret; + } + ret = pwmchip_add(&chip->chip); - if (ret < 0) + if (ret < 0) { + dev_err(&pdev->dev, "failed to add PWM chip\n"); return ret; + } platform_set_drvdata(pdev, chip); return ret; } -static int __devexit pwm_remove(struct platform_device *pdev) +static int vt8500_pwm_remove(struct platform_device *pdev) { struct vt8500_chip *chip; @@ -150,28 +196,22 @@ static int __devexit pwm_remove(struct platform_device *pdev) if (chip == NULL) return -ENODEV; + clk_unprepare(chip->clk); + return pwmchip_remove(&chip->chip); } -static struct platform_driver pwm_driver = { +static struct platform_driver vt8500_pwm_driver = { + .probe = vt8500_pwm_probe, + .remove = vt8500_pwm_remove, .driver = { .name = "vt8500-pwm", .owner = THIS_MODULE, + .of_match_table = vt8500_pwm_dt_ids, }, - .probe = pwm_probe, - .remove = __devexit_p(pwm_remove), }; +module_platform_driver(vt8500_pwm_driver); -static int __init pwm_init(void) -{ - return platform_driver_register(&pwm_driver); -} -arch_initcall(pwm_init); - -static void __exit pwm_exit(void) -{ - platform_driver_unregister(&pwm_driver); -} -module_exit(pwm_exit); - -MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("VT8500 PWM Driver"); +MODULE_AUTHOR("Tony Prisk "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 9750a3ade7b635a18f04371b4fddad0de0b4e6d8 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Mon, 29 Oct 2012 08:45:15 +0000 Subject: cpsw: support both silicon versions This patch fixes the cpsw driver to operate correctly with both the dm814x and the am335x versions of the switch hardware. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/cpsw.txt | 8 +- drivers/net/ethernet/ti/cpsw.c | 106 +++++++++++++++++++++---- 2 files changed, 94 insertions(+), 20 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt index dcaabe9fe869..3af47b78caea 100644 --- a/Documentation/devicetree/bindings/net/cpsw.txt +++ b/Documentation/devicetree/bindings/net/cpsw.txt @@ -59,14 +59,14 @@ Examples: mac_control = <0x20>; slaves = <2>; cpsw_emac0: slave@0 { - slave_reg_ofs = <0x208>; + slave_reg_ofs = <0x200>; sliver_reg_ofs = <0xd80>; phy_id = "davinci_mdio.16:00"; /* Filled in by U-Boot */ mac-address = [ 00 00 00 00 00 00 ]; }; cpsw_emac1: slave@1 { - slave_reg_ofs = <0x308>; + slave_reg_ofs = <0x300>; sliver_reg_ofs = <0xdc0>; phy_id = "davinci_mdio.16:01"; /* Filled in by U-Boot */ @@ -93,14 +93,14 @@ Examples: mac_control = <0x20>; slaves = <2>; cpsw_emac0: slave@0 { - slave_reg_ofs = <0x208>; + slave_reg_ofs = <0x200>; sliver_reg_ofs = <0xd80>; phy_id = "davinci_mdio.16:00"; /* Filled in by U-Boot */ mac-address = [ 00 00 00 00 00 00 ]; }; cpsw_emac1: slave@1 { - slave_reg_ofs = <0x308>; + slave_reg_ofs = <0x300>; sliver_reg_ofs = <0xdc0>; phy_id = "davinci_mdio.16:01"; /* Filled in by U-Boot */ diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 5c427cf1ef87..588f5c340490 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -160,18 +160,74 @@ struct cpsw_ss_regs { u32 dlr_ltype; }; -struct cpsw_slave_regs { - u32 max_blks; - u32 blk_cnt; - u32 flow_thresh; - u32 port_vlan; - u32 tx_pri_map; - u32 ts_ctl; - u32 ts_seq_ltype; - u32 ts_vlan; - u32 sa_lo; - u32 sa_hi; -}; +/* CPSW_PORT_V1 */ +#define CPSW1_MAX_BLKS 0x00 /* Maximum FIFO Blocks */ +#define CPSW1_BLK_CNT 0x04 /* FIFO Block Usage Count (Read Only) */ +#define CPSW1_TX_IN_CTL 0x08 /* Transmit FIFO Control */ +#define CPSW1_PORT_VLAN 0x0c /* VLAN Register */ +#define CPSW1_TX_PRI_MAP 0x10 /* Tx Header Priority to Switch Pri Mapping */ +#define CPSW1_TS_CTL 0x14 /* Time Sync Control */ +#define CPSW1_TS_SEQ_LTYPE 0x18 /* Time Sync Sequence ID Offset and Msg Type */ +#define CPSW1_TS_VLAN 0x1c /* Time Sync VLAN1 and VLAN2 */ + +/* CPSW_PORT_V2 */ +#define CPSW2_CONTROL 0x00 /* Control Register */ +#define CPSW2_MAX_BLKS 0x08 /* Maximum FIFO Blocks */ +#define CPSW2_BLK_CNT 0x0c /* FIFO Block Usage Count (Read Only) */ +#define CPSW2_TX_IN_CTL 0x10 /* Transmit FIFO Control */ +#define CPSW2_PORT_VLAN 0x14 /* VLAN Register */ +#define CPSW2_TX_PRI_MAP 0x18 /* Tx Header Priority to Switch Pri Mapping */ +#define CPSW2_TS_SEQ_MTYPE 0x1c /* Time Sync Sequence ID Offset and Msg Type */ + +/* CPSW_PORT_V1 and V2 */ +#define SA_LO 0x20 /* CPGMAC_SL Source Address Low */ +#define SA_HI 0x24 /* CPGMAC_SL Source Address High */ +#define SEND_PERCENT 0x28 /* Transmit Queue Send Percentages */ + +/* CPSW_PORT_V2 only */ +#define RX_DSCP_PRI_MAP0 0x30 /* Rx DSCP Priority to Rx Packet Mapping */ +#define RX_DSCP_PRI_MAP1 0x34 /* Rx DSCP Priority to Rx Packet Mapping */ +#define RX_DSCP_PRI_MAP2 0x38 /* Rx DSCP Priority to Rx Packet Mapping */ +#define RX_DSCP_PRI_MAP3 0x3c /* Rx DSCP Priority to Rx Packet Mapping */ +#define RX_DSCP_PRI_MAP4 0x40 /* Rx DSCP Priority to Rx Packet Mapping */ +#define RX_DSCP_PRI_MAP5 0x44 /* Rx DSCP Priority to Rx Packet Mapping */ +#define RX_DSCP_PRI_MAP6 0x48 /* Rx DSCP Priority to Rx Packet Mapping */ +#define RX_DSCP_PRI_MAP7 0x4c /* Rx DSCP Priority to Rx Packet Mapping */ + +/* Bit definitions for the CPSW2_CONTROL register */ +#define PASS_PRI_TAGGED (1<<24) /* Pass Priority Tagged */ +#define VLAN_LTYPE2_EN (1<<21) /* VLAN LTYPE 2 enable */ +#define VLAN_LTYPE1_EN (1<<20) /* VLAN LTYPE 1 enable */ +#define DSCP_PRI_EN (1<<16) /* DSCP Priority Enable */ +#define TS_320 (1<<14) /* Time Sync Dest Port 320 enable */ +#define TS_319 (1<<13) /* Time Sync Dest Port 319 enable */ +#define TS_132 (1<<12) /* Time Sync Dest IP Addr 132 enable */ +#define TS_131 (1<<11) /* Time Sync Dest IP Addr 131 enable */ +#define TS_130 (1<<10) /* Time Sync Dest IP Addr 130 enable */ +#define TS_129 (1<<9) /* Time Sync Dest IP Addr 129 enable */ +#define TS_BIT8 (1<<8) /* ts_ttl_nonzero? */ +#define TS_ANNEX_D_EN (1<<4) /* Time Sync Annex D enable */ +#define TS_LTYPE2_EN (1<<3) /* Time Sync LTYPE 2 enable */ +#define TS_LTYPE1_EN (1<<2) /* Time Sync LTYPE 1 enable */ +#define TS_TX_EN (1<<1) /* Time Sync Transmit Enable */ +#define TS_RX_EN (1<<0) /* Time Sync Receive Enable */ + +#define CTRL_TS_BITS \ + (TS_320 | TS_319 | TS_132 | TS_131 | TS_130 | TS_129 | TS_BIT8 | \ + TS_ANNEX_D_EN | TS_LTYPE1_EN) + +#define CTRL_ALL_TS_MASK (CTRL_TS_BITS | TS_TX_EN | TS_RX_EN) +#define CTRL_TX_TS_BITS (CTRL_TS_BITS | TS_TX_EN) +#define CTRL_RX_TS_BITS (CTRL_TS_BITS | TS_RX_EN) + +/* Bit definitions for the CPSW2_TS_SEQ_MTYPE register */ +#define TS_SEQ_ID_OFFSET_SHIFT (16) /* Time Sync Sequence ID Offset */ +#define TS_SEQ_ID_OFFSET_MASK (0x3f) +#define TS_MSG_TYPE_EN_SHIFT (0) /* Time Sync Message Type Enable */ +#define TS_MSG_TYPE_EN_MASK (0xffff) + +/* The PTP event messages - Sync, Delay_Req, Pdelay_Req, and Pdelay_Resp. */ +#define EVENT_MSG_BITS ((1<<0) | (1<<1) | (1<<2) | (1<<3)) struct cpsw_host_regs { u32 max_blks; @@ -197,7 +253,7 @@ struct cpsw_sliver_regs { }; struct cpsw_slave { - struct cpsw_slave_regs __iomem *regs; + void __iomem *regs; struct cpsw_sliver_regs __iomem *sliver; int slave_num; u32 mac_control; @@ -205,6 +261,16 @@ struct cpsw_slave { struct phy_device *phy; }; +static inline u32 slave_read(struct cpsw_slave *slave, u32 offset) +{ + return __raw_readl(slave->regs + offset); +} + +static inline void slave_write(struct cpsw_slave *slave, u32 val, u32 offset) +{ + __raw_writel(val, slave->regs + offset); +} + struct cpsw_priv { spinlock_t lock; struct platform_device *pdev; @@ -396,8 +462,8 @@ static inline void soft_reset(const char *module, void __iomem *reg) static void cpsw_set_slave_mac(struct cpsw_slave *slave, struct cpsw_priv *priv) { - __raw_writel(mac_hi(priv->mac_addr), &slave->regs->sa_hi); - __raw_writel(mac_lo(priv->mac_addr), &slave->regs->sa_lo); + slave_write(slave, mac_hi(priv->mac_addr), SA_HI); + slave_write(slave, mac_lo(priv->mac_addr), SA_LO); } static void _cpsw_adjust_link(struct cpsw_slave *slave, @@ -483,7 +549,15 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) /* setup priority mapping */ __raw_writel(RX_PRIORITY_MAPPING, &slave->sliver->rx_pri_map); - __raw_writel(TX_PRIORITY_MAPPING, &slave->regs->tx_pri_map); + + switch (priv->version) { + case CPSW_VERSION_1: + slave_write(slave, TX_PRIORITY_MAPPING, CPSW1_TX_PRI_MAP); + break; + case CPSW_VERSION_2: + slave_write(slave, TX_PRIORITY_MAPPING, CPSW2_TX_PRI_MAP); + break; + } /* setup max packet size, and mac address */ __raw_writel(priv->rx_packet_max, &slave->sliver->rx_maxlen); -- cgit v1.2.3 From 6b60393e08f9263c7b129d54eeb261e8f970175c Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Mon, 29 Oct 2012 08:45:17 +0000 Subject: cpsw: add a DT field for the cpts offset Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/cpsw.txt | 3 +++ drivers/net/ethernet/ti/cpsw.c | 7 +++++++ include/linux/platform_data/cpsw.h | 1 + 3 files changed, 11 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt index 3af47b78caea..dba014fc5e7b 100644 --- a/Documentation/devicetree/bindings/net/cpsw.txt +++ b/Documentation/devicetree/bindings/net/cpsw.txt @@ -16,6 +16,7 @@ Required properties: - ale_entries : Specifies No of entries ALE can hold - host_port_reg_ofs : Specifies host port register offset - hw_stats_reg_ofs : Specifies hardware statistics register offset +- cpts_reg_ofs : Specifies the offset of the CPTS registers - bd_ram_ofs : Specifies internal desciptor RAM offset - bd_ram_size : Specifies internal descriptor RAM size - rx_descs : Specifies number of Rx descriptors @@ -52,6 +53,7 @@ Examples: ale_entries = <1024>; host_port_reg_ofs = <0x108>; hw_stats_reg_ofs = <0x900>; + cpts_reg_ofs = <0xc00>; bd_ram_ofs = <0x2000>; bd_ram_size = <0x2000>; no_bd_ram = <0>; @@ -86,6 +88,7 @@ Examples: ale_entries = <1024>; host_port_reg_ofs = <0x108>; hw_stats_reg_ofs = <0x900>; + cpts_reg_ofs = <0xc00>; bd_ram_ofs = <0x2000>; bd_ram_size = <0x2000>; no_bd_ram = <0>; diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 588f5c340490..f1af5e08cabb 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -912,6 +912,13 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->hw_stats_reg_ofs = prop; + if (of_property_read_u32(node, "cpts_reg_ofs", &prop)) { + pr_err("Missing cpts_reg_ofs property in the DT.\n"); + ret = -EINVAL; + goto error_ret; + } + data->cpts_reg_ofs = prop; + if (of_property_read_u32(node, "bd_ram_ofs", &prop)) { pr_err("Missing bd_ram_ofs property in the DT.\n"); ret = -EINVAL; diff --git a/include/linux/platform_data/cpsw.h b/include/linux/platform_data/cpsw.h index c4e23d029498..a052b1dca957 100644 --- a/include/linux/platform_data/cpsw.h +++ b/include/linux/platform_data/cpsw.h @@ -41,6 +41,7 @@ struct cpsw_platform_data { u32 host_port_num; /* The port number for the host port */ u32 hw_stats_reg_ofs; /* cpsw hardware statistics counters */ + u32 cpts_reg_ofs; /* cpts registers */ u32 bd_ram_ofs; /* embedded buffer descriptor RAM offset*/ u32 bd_ram_size; /*buffer descriptor ram size */ -- cgit v1.2.3 From 78ca0b287314ad6b7b06161b3ff9b13e8a8bcce0 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Mon, 29 Oct 2012 08:45:18 +0000 Subject: cpsw: add a DT field for the active time stamping port Because time stamping on both external ports of the switch simultaneously is positively useless from the application's point of view, this patch provides a DT configuration method to choose the active port. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/cpsw.txt | 3 +++ drivers/net/ethernet/ti/cpsw.c | 7 +++++++ include/linux/platform_data/cpsw.h | 1 + 3 files changed, 11 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt index dba014fc5e7b..9f61f2b51681 100644 --- a/Documentation/devicetree/bindings/net/cpsw.txt +++ b/Documentation/devicetree/bindings/net/cpsw.txt @@ -23,6 +23,7 @@ Required properties: - mac_control : Specifies Default MAC control register content for the specific platform - slaves : Specifies number for slaves +- cpts_active_slave : Specifies the slave to use for time stamping - slave_reg_ofs : Specifies slave register offset - sliver_reg_ofs : Specifies slave sliver register offset - phy_id : Specifies slave phy id @@ -60,6 +61,7 @@ Examples: rx_descs = <64>; mac_control = <0x20>; slaves = <2>; + cpts_active_slave = <0>; cpsw_emac0: slave@0 { slave_reg_ofs = <0x200>; sliver_reg_ofs = <0xd80>; @@ -95,6 +97,7 @@ Examples: rx_descs = <64>; mac_control = <0x20>; slaves = <2>; + cpts_active_slave = <0>; cpsw_emac0: slave@0 { slave_reg_ofs = <0x200>; sliver_reg_ofs = <0xd80>; diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index f1af5e08cabb..f16579123c1b 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -847,6 +847,13 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->slaves = prop; + if (of_property_read_u32(node, "cpts_active_slave", &prop)) { + pr_err("Missing cpts_active_slave property in the DT.\n"); + ret = -EINVAL; + goto error_ret; + } + data->cpts_active_slave = prop; + data->slave_data = kzalloc(sizeof(struct cpsw_slave_data) * data->slaves, GFP_KERNEL); if (!data->slave_data) { diff --git a/include/linux/platform_data/cpsw.h b/include/linux/platform_data/cpsw.h index a052b1dca957..15a077eb0689 100644 --- a/include/linux/platform_data/cpsw.h +++ b/include/linux/platform_data/cpsw.h @@ -33,6 +33,7 @@ struct cpsw_platform_data { u32 slaves; /* number of slave cpgmac ports */ struct cpsw_slave_data *slave_data; + u32 cpts_active_slave; /* time stamping slave */ u32 ale_reg_ofs; /* address lookup engine reg offset */ u32 ale_entries; /* ale table size */ -- cgit v1.2.3 From 00ab94eeaf6c1ad38ad7368c5148fed31403c8a2 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Mon, 29 Oct 2012 08:45:19 +0000 Subject: cpts: specify the input clock frequency via DT This patch adds a way to configure the CPTS input clock scaling factors via the device tree. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/cpsw.txt | 6 ++++++ drivers/net/ethernet/ti/cpsw.c | 14 ++++++++++++++ include/linux/platform_data/cpsw.h | 2 ++ 3 files changed, 22 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt index 9f61f2b51681..221460714c56 100644 --- a/Documentation/devicetree/bindings/net/cpsw.txt +++ b/Documentation/devicetree/bindings/net/cpsw.txt @@ -24,6 +24,8 @@ Required properties: for the specific platform - slaves : Specifies number for slaves - cpts_active_slave : Specifies the slave to use for time stamping +- cpts_clock_mult : Numerator to convert input clock ticks into nanoseconds +- cpts_clock_shift : Denominator to convert input clock ticks into nanoseconds - slave_reg_ofs : Specifies slave register offset - sliver_reg_ofs : Specifies slave sliver register offset - phy_id : Specifies slave phy id @@ -62,6 +64,8 @@ Examples: mac_control = <0x20>; slaves = <2>; cpts_active_slave = <0>; + cpts_clock_mult = <0x80000000>; + cpts_clock_shift = <29>; cpsw_emac0: slave@0 { slave_reg_ofs = <0x200>; sliver_reg_ofs = <0xd80>; @@ -98,6 +102,8 @@ Examples: mac_control = <0x20>; slaves = <2>; cpts_active_slave = <0>; + cpts_clock_mult = <0x80000000>; + cpts_clock_shift = <29>; cpsw_emac0: slave@0 { slave_reg_ofs = <0x200>; sliver_reg_ofs = <0xd80>; diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index f16579123c1b..c04627cd60dd 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -854,6 +854,20 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->cpts_active_slave = prop; + if (of_property_read_u32(node, "cpts_clock_mult", &prop)) { + pr_err("Missing cpts_clock_mult property in the DT.\n"); + ret = -EINVAL; + goto error_ret; + } + data->cpts_clock_mult = prop; + + if (of_property_read_u32(node, "cpts_clock_shift", &prop)) { + pr_err("Missing cpts_clock_shift property in the DT.\n"); + ret = -EINVAL; + goto error_ret; + } + data->cpts_clock_shift = prop; + data->slave_data = kzalloc(sizeof(struct cpsw_slave_data) * data->slaves, GFP_KERNEL); if (!data->slave_data) { diff --git a/include/linux/platform_data/cpsw.h b/include/linux/platform_data/cpsw.h index 15a077eb0689..b5c16c3df458 100644 --- a/include/linux/platform_data/cpsw.h +++ b/include/linux/platform_data/cpsw.h @@ -34,6 +34,8 @@ struct cpsw_platform_data { u32 slaves; /* number of slave cpgmac ports */ struct cpsw_slave_data *slave_data; u32 cpts_active_slave; /* time stamping slave */ + u32 cpts_clock_mult; /* convert input clock ticks to nanoseconds */ + u32 cpts_clock_shift; /* convert input clock ticks to nanoseconds */ u32 ale_reg_ofs; /* address lookup engine reg offset */ u32 ale_entries; /* ale table size */ -- cgit v1.2.3 From 3ecbf05be159a95e1d23ba9b3b21c5bc2941ba6b Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Mon, 24 Sep 2012 14:55:40 +0100 Subject: mfd: Versatile Express config infrastructure Versatile Express platform has an elaborated configuration system, consisting of microcontrollers residing on the mother- and daughterboards known as Motherboard/Daughterboard Configuration Controller (MCC and DCC). The controllers are responsible for the platform initialization (reset generation, flash programming, FPGA bitfiles loading etc.) but also control clock generators, voltage regulators, gather environmental data like temperature, power consumption etc. Even the video output switch (FPGA) is controlled that way. Those devices are _not_ visible in the main address space and the usual communication channel uses some kind of a bridge in the peripheral block sending commands (requests) to the controllers and receiving responses. It can take up to 500 microseconds for a transaction to be completed, therefore it is important to provide a non-blocking interface to it. This patch adds an abstraction of this infrastructure. Bridge drivers can register themselves with the framework. Then, a driver of a device can request an abstract "function" - the request will be redirected to a bridge referred by thedd "arm,vexpress,config-bridge" property of the device tree node. Signed-off-by: Pawel Moll --- Documentation/devicetree/bindings/arm/vexpress.txt | 68 ++++- drivers/mfd/Kconfig | 6 + drivers/mfd/Makefile | 1 + drivers/mfd/vexpress-config.c | 277 +++++++++++++++++++++ include/linux/vexpress.h | 85 +++++++ 5 files changed, 436 insertions(+), 1 deletion(-) create mode 100644 drivers/mfd/vexpress-config.c create mode 100644 include/linux/vexpress.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/vexpress.txt b/Documentation/devicetree/bindings/arm/vexpress.txt index ec8b50cbb2e8..5d9996b9eabf 100644 --- a/Documentation/devicetree/bindings/arm/vexpress.txt +++ b/Documentation/devicetree/bindings/arm/vexpress.txt @@ -11,6 +11,10 @@ the motherboard file using a /include/ directive. As the motherboard can be initialized in one of two different configurations ("memory maps"), care must be taken to include the correct one. + +Root node +--------- + Required properties in the root node: - compatible value: compatible = "arm,vexpress,", "arm,vexpress"; @@ -45,6 +49,10 @@ Optional properties in the root node: - Coretile Express A9x4 (V2P-CA9) HBI-0225: arm,hbi = <0x225>; + +CPU nodes +--------- + Top-level standard "cpus" node is required. It must contain a node with device_type = "cpu" property for every available core, eg.: @@ -59,6 +67,52 @@ with device_type = "cpu" property for every available core, eg.: }; }; + +Configuration infrastructure +---------------------------- + +The platform has an elaborated configuration system, consisting of +microcontrollers residing on the mother- and daughterboards known +as Motherboard/Daughterboard Configuration Controller (MCC and DCC). +The controllers are responsible for the platform initialization +(reset generation, flash programming, FPGA bitfiles loading etc.) +but also control clock generators, voltage regulators, gather +environmental data like temperature, power consumption etc. Even +the video output switch (FPGA) is controlled that way. + +Nodes describing devices controlled by this infrastructure should +point at the bridge device node: +- bridge phandle: + arm,vexpress,config-bridge = ; +This property can be also defined in a parent node (eg. for a DCC) +and is effective for all children. + + +Platform topology +----------------- + +As Versatile Express can be configured in number of physically +different setups, the device tree should describe platform topology. +Root node and main motherboard node must define the following +property, describing physical location of the children nodes: +- site number: + arm,vexpress,site = ; + where 0 means motherboard, 1 or 2 are daugtherboard sites, + 0xf means "master" site (site containing main CPU tile) +- when daughterboards are stacked on one site, their position + in the stack be be described with: + arm,vexpress,position = ; +- when describing tiles consisting more than one DCC, its number + can be described with: + arm,vexpress,dcc = ; + +Any of the numbers above defaults to zero if not defined in +the node or any of its parent. + + +Motherboard +----------- + The motherboard description file provides a single "motherboard" node using 2 address cells corresponding to the Static Memory Bus used between the motherboard and the tile. The first cell defines the Chip @@ -96,13 +150,16 @@ The tile description must define "ranges", "interrupt-map-mask" and "interrupt-map" properties to translate the motherboard's address and interrupt space into one used by the tile's processor. -Abbreviated example: + +Example of a VE tile description (simplified) +--------------------------------------------- /dts-v1/; / { model = "V2P-CA5s"; arm,hbi = <0x225>; + arm,vexpress,site = <0xf>; compatible = "arm,vexpress-v2p-ca5s", "arm,vexpress"; interrupt-parent = <&gic>; #address-cells = <1>; @@ -134,6 +191,15 @@ Abbreviated example: <0x2c000100 0x100>; }; + dcc { + compatible = "simple-bus"; + arm,vexpress,config-bridge = <&v2m_sysreg>; + + osc@0 { + compatible = "arm,vexpress-osc"; + }; + }; + motherboard { /* CS0 is visible at 0x08000000 */ ranges = <0 0 0x08000000 0x04000000>; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index acab3ef8a310..637bcdf8ce77 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1070,3 +1070,9 @@ config MCP_UCB1200_TS depends on MCP_UCB1200 && INPUT endmenu + +config VEXPRESS_CONFIG + bool + help + Platform configuration infrastructure for the ARM Ltd. + Versatile Express. diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index d8ccb630ddb0..e807164f68da 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -138,3 +138,4 @@ obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o obj-$(CONFIG_MFD_SYSCON) += syscon.o obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o +obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o diff --git a/drivers/mfd/vexpress-config.c b/drivers/mfd/vexpress-config.c new file mode 100644 index 000000000000..fae15d880758 --- /dev/null +++ b/drivers/mfd/vexpress-config.c @@ -0,0 +1,277 @@ +/* + * 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. + * + * Copyright (C) 2012 ARM Limited + */ + +#define pr_fmt(fmt) "vexpress-config: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define VEXPRESS_CONFIG_MAX_BRIDGES 2 + +struct vexpress_config_bridge { + struct device_node *node; + struct vexpress_config_bridge_info *info; + struct list_head transactions; + spinlock_t transactions_lock; +} vexpress_config_bridges[VEXPRESS_CONFIG_MAX_BRIDGES]; + +static DECLARE_BITMAP(vexpress_config_bridges_map, + ARRAY_SIZE(vexpress_config_bridges)); +static DEFINE_MUTEX(vexpress_config_bridges_mutex); + +struct vexpress_config_bridge *vexpress_config_bridge_register( + struct device_node *node, + struct vexpress_config_bridge_info *info) +{ + struct vexpress_config_bridge *bridge; + int i; + + pr_debug("Registering bridge '%s'\n", info->name); + + mutex_lock(&vexpress_config_bridges_mutex); + i = find_first_zero_bit(vexpress_config_bridges_map, + ARRAY_SIZE(vexpress_config_bridges)); + if (i >= ARRAY_SIZE(vexpress_config_bridges)) { + pr_err("Can't register more bridges!\n"); + mutex_unlock(&vexpress_config_bridges_mutex); + return NULL; + } + __set_bit(i, vexpress_config_bridges_map); + bridge = &vexpress_config_bridges[i]; + + bridge->node = node; + bridge->info = info; + INIT_LIST_HEAD(&bridge->transactions); + spin_lock_init(&bridge->transactions_lock); + + mutex_unlock(&vexpress_config_bridges_mutex); + + return bridge; +} + +void vexpress_config_bridge_unregister(struct vexpress_config_bridge *bridge) +{ + struct vexpress_config_bridge __bridge = *bridge; + int i; + + mutex_lock(&vexpress_config_bridges_mutex); + for (i = 0; i < ARRAY_SIZE(vexpress_config_bridges); i++) + if (&vexpress_config_bridges[i] == bridge) + __clear_bit(i, vexpress_config_bridges_map); + mutex_unlock(&vexpress_config_bridges_mutex); + + WARN_ON(!list_empty(&__bridge.transactions)); + while (!list_empty(&__bridge.transactions)) + cpu_relax(); +} + + +struct vexpress_config_func { + struct vexpress_config_bridge *bridge; + void *func; +}; + +struct vexpress_config_func *__vexpress_config_func_get(struct device *dev, + struct device_node *node) +{ + struct device_node *bridge_node; + struct vexpress_config_func *func; + int i; + + if (WARN_ON(dev && node && dev->of_node != node)) + return NULL; + if (dev && !node) + node = dev->of_node; + + func = kzalloc(sizeof(*func), GFP_KERNEL); + if (!func) + return NULL; + + bridge_node = of_node_get(node); + while (bridge_node) { + const __be32 *prop = of_get_property(bridge_node, + "arm,vexpress,config-bridge", NULL); + + if (prop) { + bridge_node = of_find_node_by_phandle( + be32_to_cpup(prop)); + break; + } + + bridge_node = of_get_next_parent(bridge_node); + } + + mutex_lock(&vexpress_config_bridges_mutex); + for (i = 0; i < ARRAY_SIZE(vexpress_config_bridges); i++) { + struct vexpress_config_bridge *bridge = + &vexpress_config_bridges[i]; + + if (test_bit(i, vexpress_config_bridges_map) && + bridge->node == bridge_node) { + func->bridge = bridge; + func->func = bridge->info->func_get(dev, node); + break; + } + } + mutex_unlock(&vexpress_config_bridges_mutex); + + if (!func->func) { + of_node_put(node); + kfree(func); + return NULL; + } + + return func; +} + +void vexpress_config_func_put(struct vexpress_config_func *func) +{ + func->bridge->info->func_put(func->func); + of_node_put(func->bridge->node); + kfree(func); +} + + +struct vexpress_config_trans { + struct vexpress_config_func *func; + int offset; + bool write; + u32 *data; + int status; + struct completion completion; + struct list_head list; +}; + +static void vexpress_config_dump_trans(const char *what, + struct vexpress_config_trans *trans) +{ + pr_debug("%s %s trans %p func 0x%p offset %d data 0x%x status %d\n", + what, trans->write ? "write" : "read", trans, + trans->func->func, trans->offset, + trans->data ? *trans->data : 0, trans->status); +} + +static int vexpress_config_schedule(struct vexpress_config_trans *trans) +{ + int status; + struct vexpress_config_bridge *bridge = trans->func->bridge; + unsigned long flags; + + init_completion(&trans->completion); + trans->status = -EFAULT; + + spin_lock_irqsave(&bridge->transactions_lock, flags); + + vexpress_config_dump_trans("Executing", trans); + + if (list_empty(&bridge->transactions)) + status = bridge->info->func_exec(trans->func->func, + trans->offset, trans->write, trans->data); + else + status = VEXPRESS_CONFIG_STATUS_WAIT; + + switch (status) { + case VEXPRESS_CONFIG_STATUS_DONE: + vexpress_config_dump_trans("Finished", trans); + trans->status = status; + break; + case VEXPRESS_CONFIG_STATUS_WAIT: + list_add_tail(&trans->list, &bridge->transactions); + break; + } + + spin_unlock_irqrestore(&bridge->transactions_lock, flags); + + return status; +} + +void vexpress_config_complete(struct vexpress_config_bridge *bridge, + int status) +{ + struct vexpress_config_trans *trans; + unsigned long flags; + + spin_lock_irqsave(&bridge->transactions_lock, flags); + + trans = list_first_entry(&bridge->transactions, + struct vexpress_config_trans, list); + vexpress_config_dump_trans("Completed", trans); + + trans->status = status; + list_del(&trans->list); + + if (!list_empty(&bridge->transactions)) { + vexpress_config_dump_trans("Pending", trans); + + bridge->info->func_exec(trans->func->func, trans->offset, + trans->write, trans->data); + } + spin_unlock_irqrestore(&bridge->transactions_lock, flags); + + complete(&trans->completion); +} + +int vexpress_config_wait(struct vexpress_config_trans *trans) +{ + wait_for_completion(&trans->completion); + + return trans->status; +} + + +int vexpress_config_read(struct vexpress_config_func *func, int offset, + u32 *data) +{ + struct vexpress_config_trans trans = { + .func = func, + .offset = offset, + .write = false, + .data = data, + .status = 0, + }; + int status = vexpress_config_schedule(&trans); + + if (status == VEXPRESS_CONFIG_STATUS_WAIT) + status = vexpress_config_wait(&trans); + + return status; +} +EXPORT_SYMBOL(vexpress_config_read); + +int vexpress_config_write(struct vexpress_config_func *func, int offset, + u32 data) +{ + struct vexpress_config_trans trans = { + .func = func, + .offset = offset, + .write = true, + .data = &data, + .status = 0, + }; + int status = vexpress_config_schedule(&trans); + + if (status == VEXPRESS_CONFIG_STATUS_WAIT) + status = vexpress_config_wait(&trans); + + return status; +} +EXPORT_SYMBOL(vexpress_config_write); diff --git a/include/linux/vexpress.h b/include/linux/vexpress.h new file mode 100644 index 000000000000..c2d877a7b691 --- /dev/null +++ b/include/linux/vexpress.h @@ -0,0 +1,85 @@ +/* + * 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. + * + * Copyright (C) 2012 ARM Limited + */ + +#ifndef _LINUX_VEXPRESS_H +#define _LINUX_VEXPRESS_H + +#include + +#define VEXPRESS_SITE_MB 0 +#define VEXPRESS_SITE_DB1 1 +#define VEXPRESS_SITE_DB2 2 +#define VEXPRESS_SITE_MASTER 0xf + +#define VEXPRESS_CONFIG_STATUS_DONE 0 +#define VEXPRESS_CONFIG_STATUS_WAIT 1 + +/* Config bridge API */ + +/** + * struct vexpress_config_bridge_info - description of the platform + * configuration infrastructure bridge. + * + * @name: Bridge name + * + * @func_get: Obtains pointer to a configuration function for a given + * device or a Device Tree node, to be used with @func_put + * and @func_exec. The node pointer should take precedence + * over device pointer when both are passed. + * + * @func_put: Tells the bridge that the function will not be used any + * more, so all allocated resources can be released. + * + * @func_exec: Executes a configuration function read or write operation. + * The offset selects a 32 bit word of the value accessed. + * Must return VEXPRESS_CONFIG_STATUS_DONE when operation + * is finished immediately, VEXPRESS_CONFIG_STATUS_WAIT when + * will be completed in some time or negative value in case + * of error. + */ +struct vexpress_config_bridge_info { + const char *name; + void *(*func_get)(struct device *dev, struct device_node *node); + void (*func_put)(void *func); + int (*func_exec)(void *func, int offset, bool write, u32 *data); +}; + +struct vexpress_config_bridge; + +struct vexpress_config_bridge *vexpress_config_bridge_register( + struct device_node *node, + struct vexpress_config_bridge_info *info); +void vexpress_config_bridge_unregister(struct vexpress_config_bridge *bridge); + +void vexpress_config_complete(struct vexpress_config_bridge *bridge, + int status); + +/* Config function API */ + +struct vexpress_config_func; + +struct vexpress_config_func *__vexpress_config_func_get(struct device *dev, + struct device_node *node); +#define vexpress_config_func_get_by_dev(dev) \ + __vexpress_config_func_get(dev, NULL) +#define vexpress_config_func_get_by_node(node) \ + __vexpress_config_func_get(NULL, node) +void vexpress_config_func_put(struct vexpress_config_func *func); + +/* Both may sleep! */ +int vexpress_config_read(struct vexpress_config_func *func, int offset, + u32 *data); +int vexpress_config_write(struct vexpress_config_func *func, int offset, + u32 data); + +#endif -- cgit v1.2.3 From 88e0abcd7a8171ca7af3402373e7bd81fe9b6754 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Tue, 18 Sep 2012 12:24:57 +0100 Subject: mfd: Versatile Express system registers driver This is a platform driver for Versatile Express' "system register" block. It's a random collection of registers providing the following functionality: - low level platform functions like board ID access; in order to use those, the driver must be initialized early, either statically or based on the DT - config bus bridge via "system control" interface; as the response from the controller does not generate interrupt (yet), the status register is periodically polled using a timer - pseudo GPIO lines providing MMC card status and Flash WP# signal control - LED interface for a set of 8 LEDs on the motherboard, with "heartbeat", "mmc0" and "cpu0" to "cpu5" as default triggers Signed-off-by: Pawel Moll --- .../devicetree/bindings/arm/vexpress-sysreg.txt | 50 ++ drivers/mfd/Makefile | 2 +- drivers/mfd/vexpress-sysreg.c | 552 +++++++++++++++++++++ include/linux/vexpress.h | 25 + 4 files changed, 628 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/arm/vexpress-sysreg.txt create mode 100644 drivers/mfd/vexpress-sysreg.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/vexpress-sysreg.txt b/Documentation/devicetree/bindings/arm/vexpress-sysreg.txt new file mode 100644 index 000000000000..9cf3f25544c7 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/vexpress-sysreg.txt @@ -0,0 +1,50 @@ +ARM Versatile Express system registers +-------------------------------------- + +This is a system control registers block, providing multiple low level +platform functions like board detection and identification, software +interrupt generation, MMC and NOR Flash control etc. + +Required node properties: +- compatible value : = "arm,vexpress,sysreg"; +- reg : physical base address and the size of the registers window +- gpio-controller : specifies that the node is a GPIO controller +- #gpio-cells : size of the GPIO specifier, should be 2: + - first cell is the pseudo-GPIO line number: + 0 - MMC CARDIN + 1 - MMC WPROT + 2 - NOR FLASH WPn + - second cell can take standard GPIO flags (currently ignored). + +Example: + v2m_sysreg: sysreg@10000000 { + compatible = "arm,vexpress-sysreg"; + reg = <0x10000000 0x1000>; + gpio-controller; + #gpio-cells = <2>; + }; + +This block also can also act a bridge to the platform's configuration +bus via "system control" interface, addressing devices with site number, +position in the board stack, config controller, function and device +numbers - see motherboard's TRM for more details. + +The node describing a config device must refer to the sysreg node via +"arm,vexpress,config-bridge" phandle (can be also defined in the node's +parent) and relies on the board topology properties - see main vexpress +node documentation for more details. It must must also define the +following property: +- arm,vexpress-sysreg,func : must contain two cells: + - first cell defines function number (eg. 1 for clock generator, + 2 for voltage regulators etc.) + - device number (eg. osc 0, osc 1 etc.) + +Example: + mcc { + arm,vexpress,config-bridge = <&v2m_sysreg>; + + osc@0 { + compatible = "arm,vexpress-osc"; + arm,vexpress-sysreg,func = <1 0>; + }; + }; diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index e807164f68da..296817c6c06f 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -138,4 +138,4 @@ obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o obj-$(CONFIG_MFD_SYSCON) += syscon.o obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o -obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o +obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o vexpress-sysreg.o diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c new file mode 100644 index 000000000000..059d6b17b14a --- /dev/null +++ b/drivers/mfd/vexpress-sysreg.c @@ -0,0 +1,552 @@ +/* + * 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. + * + * Copyright (C) 2012 ARM Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SYS_ID 0x000 +#define SYS_SW 0x004 +#define SYS_LED 0x008 +#define SYS_100HZ 0x024 +#define SYS_FLAGS 0x030 +#define SYS_FLAGSSET 0x030 +#define SYS_FLAGSCLR 0x034 +#define SYS_NVFLAGS 0x038 +#define SYS_NVFLAGSSET 0x038 +#define SYS_NVFLAGSCLR 0x03c +#define SYS_MCI 0x048 +#define SYS_FLASH 0x04c +#define SYS_CFGSW 0x058 +#define SYS_24MHZ 0x05c +#define SYS_MISC 0x060 +#define SYS_DMA 0x064 +#define SYS_PROCID0 0x084 +#define SYS_PROCID1 0x088 +#define SYS_CFGDATA 0x0a0 +#define SYS_CFGCTRL 0x0a4 +#define SYS_CFGSTAT 0x0a8 + +#define SYS_HBI_MASK 0xfff +#define SYS_ID_HBI_SHIFT 16 +#define SYS_PROCIDx_HBI_SHIFT 0 + +#define SYS_MCI_CARDIN (1 << 0) +#define SYS_MCI_WPROT (1 << 1) + +#define SYS_FLASH_WPn (1 << 0) + +#define SYS_MISC_MASTERSITE (1 << 14) + +#define SYS_CFGCTRL_START (1 << 31) +#define SYS_CFGCTRL_WRITE (1 << 30) +#define SYS_CFGCTRL_DCC(n) (((n) & 0xf) << 26) +#define SYS_CFGCTRL_FUNC(n) (((n) & 0x3f) << 20) +#define SYS_CFGCTRL_SITE(n) (((n) & 0x3) << 16) +#define SYS_CFGCTRL_POSITION(n) (((n) & 0xf) << 12) +#define SYS_CFGCTRL_DEVICE(n) (((n) & 0xfff) << 0) + +#define SYS_CFGSTAT_ERR (1 << 1) +#define SYS_CFGSTAT_COMPLETE (1 << 0) + + +static void __iomem *vexpress_sysreg_base; +static struct device *vexpress_sysreg_dev; +static int vexpress_master_site; + + +void vexpress_flags_set(u32 data) +{ + writel(~0, vexpress_sysreg_base + SYS_FLAGSCLR); + writel(data, vexpress_sysreg_base + SYS_FLAGSSET); +} + +u32 vexpress_get_procid(int site) +{ + if (site == VEXPRESS_SITE_MASTER) + site = vexpress_master_site; + + return readl(vexpress_sysreg_base + (site == VEXPRESS_SITE_DB1 ? + SYS_PROCID0 : SYS_PROCID1)); +} + +u32 vexpress_get_hbi(int site) +{ + u32 id; + + switch (site) { + case VEXPRESS_SITE_MB: + id = readl(vexpress_sysreg_base + SYS_ID); + return (id >> SYS_ID_HBI_SHIFT) & SYS_HBI_MASK; + case VEXPRESS_SITE_MASTER: + case VEXPRESS_SITE_DB1: + case VEXPRESS_SITE_DB2: + id = vexpress_get_procid(site); + return (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK; + } + + return ~0; +} + +void __iomem *vexpress_get_24mhz_clock_base(void) +{ + return vexpress_sysreg_base + SYS_24MHZ; +} + + +static void vexpress_sysreg_find_prop(struct device_node *node, + const char *name, u32 *val) +{ + of_node_get(node); + while (node) { + if (of_property_read_u32(node, name, val) == 0) { + of_node_put(node); + return; + } + node = of_get_next_parent(node); + } +} + +unsigned __vexpress_get_site(struct device *dev, struct device_node *node) +{ + u32 site = 0; + + WARN_ON(dev && node && dev->of_node != node); + if (dev && !node) + node = dev->of_node; + + if (node) { + vexpress_sysreg_find_prop(node, "arm,vexpress,site", &site); + } else if (dev && dev->bus == &platform_bus_type) { + struct platform_device *pdev = to_platform_device(dev); + + if (pdev->num_resources == 1 && + pdev->resource[0].flags == IORESOURCE_BUS) + site = pdev->resource[0].start; + } else if (dev && strncmp(dev_name(dev), "ct:", 3) == 0) { + site = VEXPRESS_SITE_MASTER; + } + + if (site == VEXPRESS_SITE_MASTER) + site = vexpress_master_site; + + return site; +} + + +struct vexpress_sysreg_config_func { + u32 template; + u32 device; +}; + +static struct vexpress_config_bridge *vexpress_sysreg_config_bridge; +static struct timer_list vexpress_sysreg_config_timer; +static u32 *vexpress_sysreg_config_data; +static int vexpress_sysreg_config_tries; + +static void *vexpress_sysreg_config_func_get(struct device *dev, + struct device_node *node) +{ + struct vexpress_sysreg_config_func *config_func; + u32 site; + u32 position = 0; + u32 dcc = 0; + u32 func_device[2]; + int err = -EFAULT; + + if (node) { + of_node_get(node); + vexpress_sysreg_find_prop(node, "arm,vexpress,site", &site); + vexpress_sysreg_find_prop(node, "arm,vexpress,position", + &position); + vexpress_sysreg_find_prop(node, "arm,vexpress,dcc", &dcc); + err = of_property_read_u32_array(node, + "arm,vexpress-sysreg,func", func_device, + ARRAY_SIZE(func_device)); + of_node_put(node); + } else if (dev && dev->bus == &platform_bus_type) { + struct platform_device *pdev = to_platform_device(dev); + + if (pdev->num_resources == 1 && + pdev->resource[0].flags == IORESOURCE_BUS) { + site = pdev->resource[0].start; + func_device[0] = pdev->resource[0].end; + func_device[1] = pdev->id; + err = 0; + } + } + if (err) + return NULL; + + config_func = kzalloc(sizeof(*config_func), GFP_KERNEL); + if (!config_func) + return NULL; + + config_func->template = SYS_CFGCTRL_DCC(dcc); + config_func->template |= SYS_CFGCTRL_FUNC(func_device[0]); + config_func->template |= SYS_CFGCTRL_SITE(site == VEXPRESS_SITE_MASTER ? + vexpress_master_site : site); + config_func->template |= SYS_CFGCTRL_POSITION(position); + config_func->device |= func_device[1]; + + dev_dbg(vexpress_sysreg_dev, "func 0x%p = 0x%x, %d\n", config_func, + config_func->template, config_func->device); + + return config_func; +} + +static void vexpress_sysreg_config_func_put(void *func) +{ + kfree(func); +} + +static int vexpress_sysreg_config_func_exec(void *func, int offset, + bool write, u32 *data) +{ + int status; + struct vexpress_sysreg_config_func *config_func = func; + u32 command; + + if (WARN_ON(!vexpress_sysreg_base)) + return -ENOENT; + + command = readl(vexpress_sysreg_base + SYS_CFGCTRL); + if (WARN_ON(command & SYS_CFGCTRL_START)) + return -EBUSY; + + command = SYS_CFGCTRL_START; + command |= write ? SYS_CFGCTRL_WRITE : 0; + command |= config_func->template; + command |= SYS_CFGCTRL_DEVICE(config_func->device + offset); + + /* Use a canary for reads */ + if (!write) + *data = 0xdeadbeef; + + dev_dbg(vexpress_sysreg_dev, "command %x, data %x\n", + command, *data); + writel(*data, vexpress_sysreg_base + SYS_CFGDATA); + writel(0, vexpress_sysreg_base + SYS_CFGSTAT); + writel(command, vexpress_sysreg_base + SYS_CFGCTRL); + mb(); + + if (vexpress_sysreg_dev) { + /* Schedule completion check */ + if (!write) + vexpress_sysreg_config_data = data; + vexpress_sysreg_config_tries = 100; + mod_timer(&vexpress_sysreg_config_timer, + jiffies + usecs_to_jiffies(100)); + status = VEXPRESS_CONFIG_STATUS_WAIT; + } else { + /* Early execution, no timer available, have to spin */ + u32 cfgstat; + + do { + cpu_relax(); + cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT); + } while (!cfgstat); + + if (!write && (cfgstat & SYS_CFGSTAT_COMPLETE)) + *data = readl(vexpress_sysreg_base + SYS_CFGDATA); + status = VEXPRESS_CONFIG_STATUS_DONE; + + if (cfgstat & SYS_CFGSTAT_ERR) + status = -EINVAL; + } + + return status; +} + +struct vexpress_config_bridge_info vexpress_sysreg_config_bridge_info = { + .name = "vexpress-sysreg", + .func_get = vexpress_sysreg_config_func_get, + .func_put = vexpress_sysreg_config_func_put, + .func_exec = vexpress_sysreg_config_func_exec, +}; + +static void vexpress_sysreg_config_complete(unsigned long data) +{ + int status = VEXPRESS_CONFIG_STATUS_DONE; + u32 cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT); + + if (cfgstat & SYS_CFGSTAT_ERR) + status = -EINVAL; + if (!vexpress_sysreg_config_tries--) + status = -ETIMEDOUT; + + if (status < 0) { + dev_err(vexpress_sysreg_dev, "error %d\n", status); + } else if (!(cfgstat & SYS_CFGSTAT_COMPLETE)) { + mod_timer(&vexpress_sysreg_config_timer, + jiffies + usecs_to_jiffies(50)); + return; + } + + if (vexpress_sysreg_config_data) { + *vexpress_sysreg_config_data = readl(vexpress_sysreg_base + + SYS_CFGDATA); + dev_dbg(vexpress_sysreg_dev, "read data %x\n", + *vexpress_sysreg_config_data); + vexpress_sysreg_config_data = NULL; + } + + vexpress_config_complete(vexpress_sysreg_config_bridge, status); +} + + +void __init vexpress_sysreg_early_init(void __iomem *base) +{ + struct device_node *node = of_find_compatible_node(NULL, NULL, + "arm,vexpress-sysreg"); + + if (node) + base = of_iomap(node, 0); + + if (WARN_ON(!base)) + return; + + vexpress_sysreg_base = base; + + if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE) + vexpress_master_site = VEXPRESS_SITE_DB2; + else + vexpress_master_site = VEXPRESS_SITE_DB1; + + vexpress_sysreg_config_bridge = vexpress_config_bridge_register( + node, &vexpress_sysreg_config_bridge_info); + WARN_ON(!vexpress_sysreg_config_bridge); +} + +void __init vexpress_sysreg_of_early_init(void) +{ + vexpress_sysreg_early_init(NULL); +} + + +static struct vexpress_sysreg_gpio { + unsigned long reg; + u32 value; +} vexpress_sysreg_gpios[] = { + [VEXPRESS_GPIO_MMC_CARDIN] = { + .reg = SYS_MCI, + .value = SYS_MCI_CARDIN, + }, + [VEXPRESS_GPIO_MMC_WPROT] = { + .reg = SYS_MCI, + .value = SYS_MCI_WPROT, + }, + [VEXPRESS_GPIO_FLASH_WPn] = { + .reg = SYS_FLASH, + .value = SYS_FLASH_WPn, + }, +}; + +static int vexpress_sysreg_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + return 0; +} + +static int vexpress_sysreg_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + return 0; +} + +static int vexpress_sysreg_gpio_get(struct gpio_chip *chip, + unsigned offset) +{ + struct vexpress_sysreg_gpio *gpio = &vexpress_sysreg_gpios[offset]; + u32 reg_value = readl(vexpress_sysreg_base + gpio->reg); + + return !!(reg_value & gpio->value); +} + +static void vexpress_sysreg_gpio_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct vexpress_sysreg_gpio *gpio = &vexpress_sysreg_gpios[offset]; + u32 reg_value = readl(vexpress_sysreg_base + gpio->reg); + + if (value) + reg_value |= gpio->value; + else + reg_value &= ~gpio->value; + + writel(reg_value, vexpress_sysreg_base + gpio->reg); +} + +static struct gpio_chip vexpress_sysreg_gpio_chip = { + .label = "vexpress-sysreg", + .direction_input = vexpress_sysreg_gpio_direction_input, + .direction_output = vexpress_sysreg_gpio_direction_output, + .get = vexpress_sysreg_gpio_get, + .set = vexpress_sysreg_gpio_set, + .ngpio = ARRAY_SIZE(vexpress_sysreg_gpios), + .base = 0, +}; + + +static ssize_t vexpress_sysreg_sys_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "0x%08x\n", readl(vexpress_sysreg_base + SYS_ID)); +} + +DEVICE_ATTR(sys_id, S_IRUGO, vexpress_sysreg_sys_id_show, NULL); + +static int __devinit vexpress_sysreg_probe(struct platform_device *pdev) +{ + int err; + struct resource *res = platform_get_resource(pdev, + IORESOURCE_MEM, 0); + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "Failed to request memory region!\n"); + return -EBUSY; + } + + if (!vexpress_sysreg_base) + vexpress_sysreg_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + + if (!vexpress_sysreg_base) { + dev_err(&pdev->dev, "Failed to obtain base address!\n"); + return -EFAULT; + } + + setup_timer(&vexpress_sysreg_config_timer, + vexpress_sysreg_config_complete, 0); + + vexpress_sysreg_gpio_chip.dev = &pdev->dev; + err = gpiochip_add(&vexpress_sysreg_gpio_chip); + if (err) { + vexpress_config_bridge_unregister( + vexpress_sysreg_config_bridge); + dev_err(&pdev->dev, "Failed to register GPIO chip! (%d)\n", + err); + return err; + } + + vexpress_sysreg_dev = &pdev->dev; + + device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id); + + return 0; +} + +static const struct of_device_id vexpress_sysreg_match[] = { + { .compatible = "arm,vexpress-sysreg", }, + {}, +}; + +static struct platform_driver vexpress_sysreg_driver = { + .driver = { + .name = "vexpress-sysreg", + .of_match_table = vexpress_sysreg_match, + }, + .probe = vexpress_sysreg_probe, +}; + +static int __init vexpress_sysreg_init(void) +{ + return platform_driver_register(&vexpress_sysreg_driver); +} +core_initcall(vexpress_sysreg_init); + + +#if defined(CONFIG_LEDS_CLASS) + +struct vexpress_sysreg_led { + u32 mask; + struct led_classdev cdev; +} vexpress_sysreg_leds[] = { + { .mask = 1 << 0, .cdev.name = "v2m:green:user1", + .cdev.default_trigger = "heartbeat", }, + { .mask = 1 << 1, .cdev.name = "v2m:green:user2", + .cdev.default_trigger = "mmc0", }, + { .mask = 1 << 2, .cdev.name = "v2m:green:user3", + .cdev.default_trigger = "cpu0", }, + { .mask = 1 << 3, .cdev.name = "v2m:green:user4", + .cdev.default_trigger = "cpu1", }, + { .mask = 1 << 4, .cdev.name = "v2m:green:user5", + .cdev.default_trigger = "cpu2", }, + { .mask = 1 << 5, .cdev.name = "v2m:green:user6", + .cdev.default_trigger = "cpu3", }, + { .mask = 1 << 6, .cdev.name = "v2m:green:user7", + .cdev.default_trigger = "cpu4", }, + { .mask = 1 << 7, .cdev.name = "v2m:green:user8", + .cdev.default_trigger = "cpu5", }, +}; + +static DEFINE_SPINLOCK(vexpress_sysreg_leds_lock); + +static void vexpress_sysreg_led_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct vexpress_sysreg_led *led = container_of(cdev, + struct vexpress_sysreg_led, cdev); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&vexpress_sysreg_leds_lock, flags); + + val = readl(vexpress_sysreg_base + SYS_LED); + if (brightness == LED_OFF) + val &= ~led->mask; + else + val |= led->mask; + writel(val, vexpress_sysreg_base + SYS_LED); + + spin_unlock_irqrestore(&vexpress_sysreg_leds_lock, flags); +} + +static int __init vexpress_sysreg_init_leds(void) +{ + struct vexpress_sysreg_led *led; + int i; + + /* Clear all user LEDs */ + writel(0, vexpress_sysreg_base + SYS_LED); + + for (i = 0, led = vexpress_sysreg_leds; + i < ARRAY_SIZE(vexpress_sysreg_leds); i++, led++) { + int err; + + led->cdev.brightness_set = vexpress_sysreg_led_brightness_set; + err = led_classdev_register(vexpress_sysreg_dev, &led->cdev); + if (err) { + dev_err(vexpress_sysreg_dev, + "Failed to register LED %d! (%d)\n", + i, err); + while (led--, i--) + led_classdev_unregister(&led->cdev); + return err; + } + } + + return 0; +} +device_initcall(vexpress_sysreg_init_leds); + +#endif diff --git a/include/linux/vexpress.h b/include/linux/vexpress.h index c2d877a7b691..09c81d7a22de 100644 --- a/include/linux/vexpress.h +++ b/include/linux/vexpress.h @@ -24,6 +24,17 @@ #define VEXPRESS_CONFIG_STATUS_DONE 0 #define VEXPRESS_CONFIG_STATUS_WAIT 1 +#define VEXPRESS_GPIO_MMC_CARDIN 0 +#define VEXPRESS_GPIO_MMC_WPROT 1 +#define VEXPRESS_GPIO_FLASH_WPn 2 + +#define VEXPRESS_RES_FUNC(_site, _func) \ +{ \ + .start = (_site), \ + .end = (_func), \ + .flags = IORESOURCE_BUS, \ +} + /* Config bridge API */ /** @@ -82,4 +93,18 @@ int vexpress_config_read(struct vexpress_config_func *func, int offset, int vexpress_config_write(struct vexpress_config_func *func, int offset, u32 data); +/* Platform control */ + +u32 vexpress_get_procid(int site); +u32 vexpress_get_hbi(int site); +void *vexpress_get_24mhz_clock_base(void); +void vexpress_flags_set(u32 data); + +#define vexpress_get_site_by_node(node) __vexpress_get_site(NULL, node) +#define vexpress_get_site_by_dev(dev) __vexpress_get_site(dev, NULL) +unsigned __vexpress_get_site(struct device *dev, struct device_node *node); + +void vexpress_sysreg_early_init(void __iomem *base); +void vexpress_sysreg_of_early_init(void); + #endif -- cgit v1.2.3 From 433683a66401adb0150792e725cc4f631c94de46 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Tue, 16 Oct 2012 15:27:12 +0100 Subject: ARM: vexpress: Remove motherboard dependencies in the DTS files The way the VE motherboard Device Trees were constructed enforced naming and structure of daughterboard files. This patch makes it possible to simply include the motherboard description anywhere in the main Device Tree and retires the "arm,v2m-timer" alias - any of the motherboard SP804 timers will be used instead. Signed-off-by: Pawel Moll --- Documentation/devicetree/bindings/arm/vexpress.txt | 32 +++++++++++++++------- arch/arm/boot/dts/vexpress-v2m-rs1.dtsi | 11 +++----- arch/arm/boot/dts/vexpress-v2m.dtsi | 11 +++----- arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts | 11 ++++++-- arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts | 11 ++++++-- arch/arm/boot/dts/vexpress-v2p-ca5s.dts | 11 ++++++-- arch/arm/boot/dts/vexpress-v2p-ca9.dts | 11 ++++++-- arch/arm/mach-vexpress/v2m.c | 19 +++++++------ 8 files changed, 73 insertions(+), 44 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/vexpress.txt b/Documentation/devicetree/bindings/arm/vexpress.txt index 5d9996b9eabf..ae49161e478a 100644 --- a/Documentation/devicetree/bindings/arm/vexpress.txt +++ b/Documentation/devicetree/bindings/arm/vexpress.txt @@ -141,14 +141,19 @@ can be used to obtain required phandle in the tile's "aliases" node: - SP804 timers: v2m_timer01 and v2m_timer23 -Current Linux implementation requires a "arm,v2m_timer" alias -pointing at one of the motherboard's SP804 timers, if it is to be -used as the system timer. This alias should be defined in the -motherboard files. - -The tile description must define "ranges", "interrupt-map-mask" and -"interrupt-map" properties to translate the motherboard's address -and interrupt space into one used by the tile's processor. +The tile description should define a "smb" node, describing the +Static Memory Bus between the tile and motherboard. It must define +the following properties: +- "simple-bus" compatible value (to ensure creation of the children) + compatible = "simple-bus"; +- mapping of the SMB CS/offset addresses into main address space: + #address-cells = <2>; + #size-cells = <1>; + ranges = <...>; +- interrupts mapping: + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 63>; + interrupt-map = <...>; Example of a VE tile description (simplified) @@ -200,13 +205,20 @@ Example of a VE tile description (simplified) }; }; - motherboard { + smb { + compatible = "simple-bus"; + + #address-cells = <2>; + #size-cells = <1>; /* CS0 is visible at 0x08000000 */ ranges = <0 0 0x08000000 0x04000000>; + + #interrupt-cells = <1>; interrupt-map-mask = <0 0 63>; /* Active high IRQ 0 is connected to GIC's SPI0 */ interrupt-map = <0 0 0 &gic 0 0 4>; + + /include/ "vexpress-v2m-rs1.dtsi" }; }; -/include/ "vexpress-v2m-rs1.dtsi" diff --git a/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi b/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi index 9a7b6922f83b..ac870fb3fa0d 100644 --- a/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi +++ b/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi @@ -17,18 +17,16 @@ * CHANGES TO vexpress-v2m.dtsi! */ -/ { - aliases { - arm,v2m_timer = &v2m_timer01; - }; - motherboard { - compatible = "simple-bus"; + model = "V2M-P1"; + arm,hbi = <0x190>; arm,vexpress,site = <0>; arm,v2m-memory-map = "rs1"; + compatible = "arm,vexpress,v2m-p1", "simple-bus"; #address-cells = <2>; /* SMB chipselect number and offset */ #size-cells = <1>; #interrupt-cells = <1>; + ranges; flash@0,00000000 { compatible = "arm,vexpress-flash", "cfi-flash"; @@ -340,4 +338,3 @@ }; }; }; -}; diff --git a/arch/arm/boot/dts/vexpress-v2m.dtsi b/arch/arm/boot/dts/vexpress-v2m.dtsi index 4d321a832dbd..f1420368355b 100644 --- a/arch/arm/boot/dts/vexpress-v2m.dtsi +++ b/arch/arm/boot/dts/vexpress-v2m.dtsi @@ -17,17 +17,15 @@ * CHANGES TO vexpress-v2m-rs1.dtsi! */ -/ { - aliases { - arm,v2m_timer = &v2m_timer01; - }; - motherboard { - compatible = "simple-bus"; + model = "V2M-P1"; + arm,hbi = <0x190>; arm,vexpress,site = <0>; + compatible = "arm,vexpress,v2m-p1", "simple-bus"; #address-cells = <2>; /* SMB chipselect number and offset */ #size-cells = <1>; #interrupt-cells = <1>; + ranges; flash@0,00000000 { compatible = "arm,vexpress-flash", "cfi-flash"; @@ -339,4 +337,3 @@ }; }; }; -}; diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts b/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts index 4bbed10de45b..a3d37ec2655d 100644 --- a/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts +++ b/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts @@ -220,7 +220,11 @@ }; }; - motherboard { + smb { + compatible = "simple-bus"; + + #address-cells = <2>; + #size-cells = <1>; ranges = <0 0 0 0x08000000 0x04000000>, <1 0 0 0x14000000 0x04000000>, <2 0 0 0x18000000 0x04000000>, @@ -228,6 +232,7 @@ <4 0 0 0x0c000000 0x04000000>, <5 0 0 0x10000000 0x04000000>; + #interrupt-cells = <1>; interrupt-map-mask = <0 0 63>; interrupt-map = <0 0 0 &gic 0 0 4>, <0 0 1 &gic 0 1 4>, @@ -272,7 +277,7 @@ <0 0 40 &gic 0 40 4>, <0 0 41 &gic 0 41 4>, <0 0 42 &gic 0 42 4>; + + /include/ "vexpress-v2m-rs1.dtsi" }; }; - -/include/ "vexpress-v2m-rs1.dtsi" diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts index 3f4e1d00f4be..1fc405a9ecfb 100644 --- a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts +++ b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts @@ -305,7 +305,11 @@ }; }; - motherboard { + smb { + compatible = "simple-bus"; + + #address-cells = <2>; + #size-cells = <1>; ranges = <0 0 0 0x08000000 0x04000000>, <1 0 0 0x14000000 0x04000000>, <2 0 0 0x18000000 0x04000000>, @@ -313,6 +317,7 @@ <4 0 0 0x0c000000 0x04000000>, <5 0 0 0x10000000 0x04000000>; + #interrupt-cells = <1>; interrupt-map-mask = <0 0 63>; interrupt-map = <0 0 0 &gic 0 0 4>, <0 0 1 &gic 0 1 4>, @@ -357,7 +362,7 @@ <0 0 40 &gic 0 40 4>, <0 0 41 &gic 0 41 4>, <0 0 42 &gic 0 42 4>; + + /include/ "vexpress-v2m-rs1.dtsi" }; }; - -/include/ "vexpress-v2m-rs1.dtsi" diff --git a/arch/arm/boot/dts/vexpress-v2p-ca5s.dts b/arch/arm/boot/dts/vexpress-v2p-ca5s.dts index f3c1f2a44072..6328cbc71d30 100644 --- a/arch/arm/boot/dts/vexpress-v2p-ca5s.dts +++ b/arch/arm/boot/dts/vexpress-v2p-ca5s.dts @@ -182,7 +182,11 @@ }; }; - motherboard { + smb { + compatible = "simple-bus"; + + #address-cells = <2>; + #size-cells = <1>; ranges = <0 0 0x08000000 0x04000000>, <1 0 0x14000000 0x04000000>, <2 0 0x18000000 0x04000000>, @@ -190,6 +194,7 @@ <4 0 0x0c000000 0x04000000>, <5 0 0x10000000 0x04000000>; + #interrupt-cells = <1>; interrupt-map-mask = <0 0 63>; interrupt-map = <0 0 0 &gic 0 0 4>, <0 0 1 &gic 0 1 4>, @@ -234,7 +239,7 @@ <0 0 40 &gic 0 40 4>, <0 0 41 &gic 0 41 4>, <0 0 42 &gic 0 42 4>; + + /include/ "vexpress-v2m-rs1.dtsi" }; }; - -/include/ "vexpress-v2m-rs1.dtsi" diff --git a/arch/arm/boot/dts/vexpress-v2p-ca9.dts b/arch/arm/boot/dts/vexpress-v2p-ca9.dts index 005259db541d..1420bb14d95c 100644 --- a/arch/arm/boot/dts/vexpress-v2p-ca9.dts +++ b/arch/arm/boot/dts/vexpress-v2p-ca9.dts @@ -265,13 +265,18 @@ }; }; - motherboard { + smb { + compatible = "simple-bus"; + + #address-cells = <2>; + #size-cells = <1>; ranges = <0 0 0x40000000 0x04000000>, <1 0 0x44000000 0x04000000>, <2 0 0x48000000 0x04000000>, <3 0 0x4c000000 0x04000000>, <7 0 0x10000000 0x00020000>; + #interrupt-cells = <1>; interrupt-map-mask = <0 0 63>; interrupt-map = <0 0 0 &gic 0 0 4>, <0 0 1 &gic 0 1 4>, @@ -316,7 +321,7 @@ <0 0 40 &gic 0 40 4>, <0 0 41 &gic 0 41 4>, <0 0 42 &gic 0 42 4>; + + /include/ "vexpress-v2m.dtsi" }; }; - -/include/ "vexpress-v2m.dtsi" diff --git a/arch/arm/mach-vexpress/v2m.c b/arch/arm/mach-vexpress/v2m.c index 99d4172816be..4e168e81fb42 100644 --- a/arch/arm/mach-vexpress/v2m.c +++ b/arch/arm/mach-vexpress/v2m.c @@ -446,17 +446,20 @@ static void __init v2m_dt_init_irq(void) static void __init v2m_dt_timer_init(void) { - struct device_node *node; - const char *path; - int err; + struct device_node *node = NULL; vexpress_clk_of_init(); - err = of_property_read_string(of_aliases, "arm,v2m_timer", &path); - if (WARN_ON(err)) - return; - node = of_find_node_by_path(path); - v2m_sp804_init(of_iomap(node, 0), irq_of_parse_and_map(node, 0)); + do { + node = of_find_compatible_node(node, NULL, "arm,sp804"); + } while (node && vexpress_get_site_by_node(node) != VEXPRESS_SITE_MB); + if (node) { + pr_info("Using SP804 '%s' as a clock & events source\n", + node->full_name); + v2m_sp804_init(of_iomap(node, 0), + irq_of_parse_and_map(node, 0)); + } + if (arch_timer_of_register() != 0) twd_local_timer_of_register(); -- cgit v1.2.3 From 96f6360c5a0c20fd3e006c3030aac0d1addbe3d9 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Sun, 4 Nov 2012 21:34:51 +0000 Subject: net: at91_ether: add dt support Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Tested-by: Joachim Eastwood Cc: Nicolas Ferre Cc: netdev@vger.kernel.org Signed-off-by: David S. Miller --- .../devicetree/bindings/net/cdns-emac.txt | 23 +++++++ drivers/net/ethernet/cadence/at91_ether.c | 73 +++++++++++++++++++--- 2 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/cdns-emac.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/net/cdns-emac.txt b/Documentation/devicetree/bindings/net/cdns-emac.txt new file mode 100644 index 000000000000..09055c2495f0 --- /dev/null +++ b/Documentation/devicetree/bindings/net/cdns-emac.txt @@ -0,0 +1,23 @@ +* Cadence EMAC Ethernet controller + +Required properties: +- compatible: Should be "cdns,[-]{emac}" + Use "cdns,at91rm9200-emac" Atmel at91rm9200 SoC. + or the generic form: "cdns,emac". +- reg: Address and length of the register set for the device +- interrupts: Should contain macb interrupt +- phy-mode: String, operation mode of the PHY interface. + Supported values are: "mii", "rmii". + +Optional properties: +- local-mac-address: 6 bytes, mac address + +Examples: + + macb0: ethernet@fffc4000 { + compatible = "cdns,at91rm9200-emac"; + reg = <0xfffc4000 0x4000>; + interrupts = <21>; + phy-mode = "rmii"; + local-mac-address = [3a 0e 03 04 05 06]; + }; diff --git a/drivers/net/ethernet/cadence/at91_ether.c b/drivers/net/ethernet/cadence/at91_ether.c index 0d6392d24ff7..039e7ef6c50e 100644 --- a/drivers/net/ethernet/cadence/at91_ether.c +++ b/drivers/net/ethernet/cadence/at91_ether.c @@ -31,6 +31,9 @@ #include #include #include +#include +#include +#include #include "macb.h" @@ -443,6 +446,50 @@ static const struct net_device_ops at91ether_netdev_ops = { #endif }; +#if defined(CONFIG_OF) +static const struct of_device_id at91ether_dt_ids[] = { + { .compatible = "cdns,at91rm9200-emac" }, + { .compatible = "cdns,emac" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, at91ether_dt_ids); + +static int at91ether_get_phy_mode_dt(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + + if (np) + return of_get_phy_mode(np); + + return -ENODEV; +} + +static int at91ether_get_hwaddr_dt(struct macb *bp) +{ + struct device_node *np = bp->pdev->dev.of_node; + + if (np) { + const char *mac = of_get_mac_address(np); + if (mac) { + memcpy(bp->dev->dev_addr, mac, ETH_ALEN); + return 0; + } + } + + return -ENODEV; +} +#else +static int at91ether_get_phy_mode_dt(struct platform_device *pdev) +{ + return -ENODEV; +} +static int at91ether_get_hwaddr_dt(struct macb *bp) +{ + return -ENODEV; +} +#endif + /* * Detect MAC & PHY and perform ethernet interface initialization */ @@ -466,7 +513,8 @@ static int __init at91ether_probe(struct platform_device *pdev) lp = netdev_priv(dev); lp->pdev = pdev; lp->dev = dev; - lp->board_data = *board_data; + if (board_data) + lp->board_data = *board_data; spin_lock_init(&lp->lock); dev->base_addr = regs->start; /* physical base address */ @@ -496,18 +544,28 @@ static int __init at91ether_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dev); SET_NETDEV_DEV(dev, &pdev->dev); - get_mac_address(dev); /* Get ethernet address and store it in dev->dev_addr */ + res = at91ether_get_hwaddr_dt(lp); + if (res < 0) + get_mac_address(dev); /* Get ethernet address and store it in dev->dev_addr */ + update_mac_address(dev); /* Program ethernet address into MAC */ + res = at91ether_get_phy_mode_dt(pdev); + if (res < 0) { + if (board_data && board_data->is_rmii) + lp->phy_interface = PHY_INTERFACE_MODE_RMII; + else + lp->phy_interface = PHY_INTERFACE_MODE_MII; + } else { + lp->phy_interface = res; + } + macb_writel(lp, NCR, 0); - if (board_data->is_rmii) { + if (lp->phy_interface == PHY_INTERFACE_MODE_RMII) macb_writel(lp, NCFGR, MACB_BF(CLK, MACB_CLK_DIV32) | MACB_BIT(BIG) | MACB_BIT(RM9200_RMII)); - lp->phy_interface = PHY_INTERFACE_MODE_RMII; - } else { + else macb_writel(lp, NCFGR, MACB_BF(CLK, MACB_CLK_DIV32) | MACB_BIT(BIG)); - lp->phy_interface = PHY_INTERFACE_MODE_MII; - } /* Register the network interface */ res = register_netdev(dev); @@ -602,6 +660,7 @@ static struct platform_driver at91ether_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(at91ether_dt_ids), }, }; -- cgit v1.2.3 From 099343c64e1615ae83b0994197027be363ca4899 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Wed, 7 Nov 2012 11:41:41 +0800 Subject: ARM: at91: atmel-ssc: add device tree support Add atmel-ssc for device tree support Match "atmel,at91rm9200-ssc" for using pdc for data transfer Match "atmel,at91sam9g45-ssc" for using dma for data transfer Signed-off-by: Bo Shen Acked-by: Nicolas Ferre Signed-off-by: Mark Brown --- .../devicetree/bindings/misc/atmel-ssc.txt | 15 +++++++ arch/arm/boot/dts/at91sam9260.dtsi | 8 ++++ arch/arm/boot/dts/at91sam9263.dtsi | 16 +++++++ arch/arm/boot/dts/at91sam9g45.dtsi | 16 +++++++ arch/arm/boot/dts/at91sam9x5.dtsi | 8 ++++ arch/arm/mach-at91/at91rm9200.c | 3 ++ arch/arm/mach-at91/at91sam9260.c | 1 + arch/arm/mach-at91/at91sam9261.c | 3 ++ arch/arm/mach-at91/at91sam9263.c | 2 + arch/arm/mach-at91/at91sam9g45.c | 2 + arch/arm/mach-at91/at91sam9rl.c | 2 + arch/arm/mach-at91/at91sam9x5.c | 1 + drivers/misc/atmel-ssc.c | 49 ++++++++++++++++++++-- 13 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/misc/atmel-ssc.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/misc/atmel-ssc.txt b/Documentation/devicetree/bindings/misc/atmel-ssc.txt new file mode 100644 index 000000000000..38e51ad2e07e --- /dev/null +++ b/Documentation/devicetree/bindings/misc/atmel-ssc.txt @@ -0,0 +1,15 @@ +* Atmel SSC driver. + +Required properties: +- compatible: "atmel,at91rm9200-ssc" or "atmel,at91sam9g45-ssc" + - atmel,at91rm9200-ssc: support pdc transfer + - atmel,at91sam9g45-ssc: support dma transfer +- reg: Should contain SSC registers location and length +- interrupts: Should contain SSC interrupt + +Example: +ssc0: ssc@fffbc000 { + compatible = "atmel,at91rm9200-ssc"; + reg = <0xfffbc000 0x4000>; + interrupts = <14 4 5>; +}; diff --git a/arch/arm/boot/dts/at91sam9260.dtsi b/arch/arm/boot/dts/at91sam9260.dtsi index d410581a5a85..aaa42d8d4f88 100644 --- a/arch/arm/boot/dts/at91sam9260.dtsi +++ b/arch/arm/boot/dts/at91sam9260.dtsi @@ -29,6 +29,7 @@ tcb0 = &tcb0; tcb1 = &tcb1; i2c0 = &i2c0; + ssc0 = &ssc0; }; cpus { cpu@0 { @@ -212,6 +213,13 @@ status = "disabled"; }; + ssc0: ssc@fffbc000 { + compatible = "atmel,at91rm9200-ssc"; + reg = <0xfffbc000 0x4000>; + interrupts = <14 4 5>; + status = "disable"; + }; + adc0: adc@fffe0000 { compatible = "atmel,at91sam9260-adc"; reg = <0xfffe0000 0x100>; diff --git a/arch/arm/boot/dts/at91sam9263.dtsi b/arch/arm/boot/dts/at91sam9263.dtsi index 3e6e5c1abbf3..3b721ee59b10 100644 --- a/arch/arm/boot/dts/at91sam9263.dtsi +++ b/arch/arm/boot/dts/at91sam9263.dtsi @@ -25,6 +25,8 @@ gpio4 = &pioE; tcb0 = &tcb0; i2c0 = &i2c0; + ssc0 = &ssc0; + ssc1 = &ssc1; }; cpus { cpu@0 { @@ -173,6 +175,20 @@ status = "disabled"; }; + ssc0: ssc@fff98000 { + compatible = "atmel,at91rm9200-ssc"; + reg = <0xfff98000 0x4000>; + interrupts = <16 4 5>; + status = "disable"; + }; + + ssc1: ssc@fff9c000 { + compatible = "atmel,at91rm9200-ssc"; + reg = <0xfff9c000 0x4000>; + interrupts = <17 4 5>; + status = "disable"; + }; + macb0: ethernet@fffbc000 { compatible = "cdns,at32ap7000-macb", "cdns,macb"; reg = <0xfffbc000 0x100>; diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi index 3add030d61f8..acfa207162ff 100644 --- a/arch/arm/boot/dts/at91sam9g45.dtsi +++ b/arch/arm/boot/dts/at91sam9g45.dtsi @@ -31,6 +31,8 @@ tcb1 = &tcb1; i2c0 = &i2c0; i2c1 = &i2c1; + ssc0 = &ssc0; + ssc1 = &ssc1; }; cpus { cpu@0 { @@ -226,6 +228,20 @@ status = "disabled"; }; + ssc0: ssc@fff9c000 { + compatible = "atmel,at91sam9g45-ssc"; + reg = <0xfff9c000 0x4000>; + interrupts = <16 4 5>; + status = "disable"; + }; + + ssc1: ssc@fffa0000 { + compatible = "atmel,at91sam9g45-ssc"; + reg = <0xfffa0000 0x4000>; + interrupts = <17 4 5>; + status = "disable"; + }; + adc0: adc@fffb0000 { compatible = "atmel,at91sam9260-adc"; reg = <0xfffb0000 0x100>; diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi index 03fc136421c5..69667d0ac347 100644 --- a/arch/arm/boot/dts/at91sam9x5.dtsi +++ b/arch/arm/boot/dts/at91sam9x5.dtsi @@ -30,6 +30,7 @@ i2c0 = &i2c0; i2c1 = &i2c1; i2c2 = &i2c2; + ssc0 = &ssc0; }; cpus { cpu@0 { @@ -87,6 +88,13 @@ interrupts = <1 4 7>; }; + ssc0: ssc@f0010000 { + compatible = "atmel,at91sam9g45-ssc"; + reg = <0xf0010000 0x4000>; + interrupts = <28 4 5>; + status = "disable"; + }; + tcb0: timer@f8008000 { compatible = "atmel,at91sam9x5-tcb"; reg = <0xf8008000 0x100>; diff --git a/arch/arm/mach-at91/at91rm9200.c b/arch/arm/mach-at91/at91rm9200.c index 400d1a34683c..af47c75db513 100644 --- a/arch/arm/mach-at91/at91rm9200.c +++ b/arch/arm/mach-at91/at91rm9200.c @@ -187,6 +187,9 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.2", &ssc2_clk), + CLKDEV_CON_DEV_ID("pclk", "fffd0000.ssc", &ssc0_clk), + CLKDEV_CON_DEV_ID("pclk", "fffd4000.ssc", &ssc1_clk), + CLKDEV_CON_DEV_ID("pclk", "fffd8000.ssc", &ssc2_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91rm9200.0", &twi_clk), /* fake hclk clock */ CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk), diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c index d14ab6aea250..a41eb3d23f68 100644 --- a/arch/arm/mach-at91/at91sam9260.c +++ b/arch/arm/mach-at91/at91sam9260.c @@ -211,6 +211,7 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.1", &tc4_clk), CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.1", &tc5_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc_clk), + CLKDEV_CON_DEV_ID("pclk", "fffbc000.ssc", &ssc_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9260.0", &twi_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi_clk), /* more usart lookup table for DT entries */ diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c index c7b605796dd4..7fcbe0583342 100644 --- a/arch/arm/mach-at91/at91sam9261.c +++ b/arch/arm/mach-at91/at91sam9261.c @@ -177,6 +177,9 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.2", &ssc2_clk), + CLKDEV_CON_DEV_ID("pclk", "fffbc000.ssc", &ssc0_clk), + CLKDEV_CON_DEV_ID("pclk", "fffc0000.ssc", &ssc1_clk), + CLKDEV_CON_DEV_ID("pclk", "fffc4000.ssc", &ssc2_clk), CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &hck0), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9261.0", &twi_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.0", &twi_clk), diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c index b87be18f08ae..c0f4c8c1f4ed 100644 --- a/arch/arm/mach-at91/at91sam9263.c +++ b/arch/arm/mach-at91/at91sam9263.c @@ -188,6 +188,8 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_ID("hclk", &macb_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk), + CLKDEV_CON_DEV_ID("pclk", "fff98000.ssc", &ssc0_clk), + CLKDEV_CON_DEV_ID("pclk", "fff9c000.ssc", &ssc1_clk), CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.0", &mmc0_clk), CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.1", &mmc1_clk), CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk), diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c index f4f96a61e4b8..a4282d3742bf 100644 --- a/arch/arm/mach-at91/at91sam9g45.c +++ b/arch/arm/mach-at91/at91sam9g45.c @@ -241,6 +241,8 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.1", &twi1_clk), CLKDEV_CON_DEV_ID("pclk", "at91sam9g45_ssc.0", &ssc0_clk), CLKDEV_CON_DEV_ID("pclk", "at91sam9g45_ssc.1", &ssc1_clk), + CLKDEV_CON_DEV_ID("pclk", "fff9c000.ssc", &ssc0_clk), + CLKDEV_CON_DEV_ID("pclk", "fffa0000.ssc", &ssc1_clk), CLKDEV_CON_DEV_ID(NULL, "atmel-trng", &trng_clk), CLKDEV_CON_DEV_ID(NULL, "atmel_sha", &aestdessha_clk), CLKDEV_CON_DEV_ID(NULL, "atmel_tdes", &aestdessha_clk), diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c index 4110b5480d49..b683fdc699f1 100644 --- a/arch/arm/mach-at91/at91sam9rl.c +++ b/arch/arm/mach-at91/at91sam9rl.c @@ -186,6 +186,8 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.0", &tc2_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk), + CLKDEV_CON_DEV_ID("pclk", "fffc0000.ssc", &ssc0_clk), + CLKDEV_CON_DEV_ID("pclk", "fffc4000.ssc", &ssc1_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi0_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.1", &twi1_clk), CLKDEV_CON_ID("pioA", &pioA_clk), diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c index e5035380dcbc..18fbbb27f97f 100644 --- a/arch/arm/mach-at91/at91sam9x5.c +++ b/arch/arm/mach-at91/at91sam9x5.c @@ -231,6 +231,7 @@ static struct clk_lookup periph_clocks_lookups[] = { CLKDEV_CON_DEV_ID("t0_clk", "f800c000.timer", &tcb0_clk), CLKDEV_CON_DEV_ID("dma_clk", "ffffec00.dma-controller", &dma0_clk), CLKDEV_CON_DEV_ID("dma_clk", "ffffee00.dma-controller", &dma1_clk), + CLKDEV_CON_DEV_ID("pclk", "f0010000.ssc", &ssc_clk), CLKDEV_CON_DEV_ID(NULL, "f8010000.i2c", &twi0_clk), CLKDEV_CON_DEV_ID(NULL, "f8014000.i2c", &twi1_clk), CLKDEV_CON_DEV_ID(NULL, "f8018000.i2c", &twi2_clk), diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index f40abd8a6695..a769719e36bf 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -18,6 +18,8 @@ #include #include +#include + /* Serialize access to ssc_list and user count */ static DEFINE_SPINLOCK(user_lock); static LIST_HEAD(ssc_list); @@ -29,7 +31,13 @@ struct ssc_device *ssc_request(unsigned int ssc_num) spin_lock(&user_lock); list_for_each_entry(ssc, &ssc_list, list) { - if (ssc->pdev->id == ssc_num) { + if (ssc->pdev->dev.of_node) { + if (of_alias_get_id(ssc->pdev->dev.of_node, "ssc") + == ssc_num) { + ssc_valid = 1; + break; + } + } else if (ssc->pdev->id == ssc_num) { ssc_valid = 1; break; } @@ -88,10 +96,41 @@ static const struct platform_device_id atmel_ssc_devtypes[] = { } }; +#ifdef CONFIG_OF +static const struct of_device_id atmel_ssc_dt_ids[] = { + { + .compatible = "atmel,at91rm9200-ssc", + .data = &at91rm9200_config, + }, { + .compatible = "atmel,at91sam9g45-ssc", + .data = &at91sam9g45_config, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, atmel_ssc_dt_ids); +#endif + +static inline const struct atmel_ssc_platform_data * __init + atmel_ssc_get_driver_data(struct platform_device *pdev) +{ + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(atmel_ssc_dt_ids, pdev->dev.of_node); + if (match == NULL) + return NULL; + return match->data; + } + + return (struct atmel_ssc_platform_data *) + platform_get_device_id(pdev)->driver_data; +} + static int ssc_probe(struct platform_device *pdev) { struct resource *regs; struct ssc_device *ssc; + const struct atmel_ssc_platform_data *plat_dat; ssc = devm_kzalloc(&pdev->dev, sizeof(struct ssc_device), GFP_KERNEL); if (!ssc) { @@ -100,8 +139,11 @@ static int ssc_probe(struct platform_device *pdev) } ssc->pdev = pdev; - ssc->pdata = (struct atmel_ssc_platform_data *) - platform_get_device_id(pdev)->driver_data; + + plat_dat = atmel_ssc_get_driver_data(pdev); + if (!plat_dat) + return -ENODEV; + ssc->pdata = (struct atmel_ssc_platform_data *)plat_dat; regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!regs) { @@ -160,6 +202,7 @@ static struct platform_driver ssc_driver = { .driver = { .name = "ssc", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(atmel_ssc_dt_ids), }, .id_table = atmel_ssc_devtypes, .probe = ssc_probe, -- cgit v1.2.3 From 5a2bc4c6607c709aa69f508502e33aebbf32f1f5 Mon Sep 17 00:00:00 2001 From: Afzal Mohammed Date: Tue, 6 Nov 2012 19:26:06 +0530 Subject: usb: musb: dsps: dt binding - add resources, example OMAP2+ family of devices are now obtaining resources via DT, earlier it was obtained from hwmod. Update binding document accrodingly, while at it add example. Signed-off-by: Afzal Mohammed Reviewed-by: Felipe Balbi Signed-off-by: Benoit Cousson --- .../devicetree/bindings/usb/am33xx-usb.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/usb/am33xx-usb.txt b/Documentation/devicetree/bindings/usb/am33xx-usb.txt index ca8fa56e9f03..707c1a2dae06 100644 --- a/Documentation/devicetree/bindings/usb/am33xx-usb.txt +++ b/Documentation/devicetree/bindings/usb/am33xx-usb.txt @@ -1,5 +1,7 @@ AM33XX MUSB GLUE - compatible : Should be "ti,musb-am33xx" + - reg : offset and length of register sets, first usbss, then for musb instances + - interrupts : usbss, musb instance interrupts in order - ti,hwmods : must be "usb_otg_hs" - multipoint : Should be "1" indicating the musb controller supports multipoint. This is a MUSB configuration-specific setting. @@ -12,3 +14,22 @@ AM33XX MUSB GLUE represents PERIPHERAL. - power : Should be "250". This signifies the controller can supply upto 500mA when operating in host mode. + +Example: + +usb@47400000 { + compatible = "ti,musb-am33xx"; + reg = <0x47400000 0x1000 /* usbss */ + 0x47401000 0x800 /* musb instance 0 */ + 0x47401800 0x800>; /* musb instance 1 */ + interrupts = <17 /* usbss */ + 18 /* musb instance 0 */ + 19>; /* musb instance 1 */ + multipoint = <1>; + num-eps = <16>; + ram-bits = <12>; + port0-mode = <3>; + port1-mode = <3>; + power = <250>; + ti,hwmods = "usb_otg_hs"; +}; -- cgit v1.2.3 From 45b6f8e8fc014fe404d155a657a04b25b861001d Mon Sep 17 00:00:00 2001 From: Qing Xu Date: Wed, 7 Nov 2012 18:09:53 +0800 Subject: regulator: max8925: support dt for regulator Signed-off-by: Qing Xu Signed-off-by: Mark Brown --- .../bindings/regulator/max8925-regulator.txt | 29 +++++++++ drivers/regulator/max8925-regulator.c | 72 +++++++++++++++++++++- 2 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/regulator/max8925-regulator.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/regulator/max8925-regulator.txt b/Documentation/devicetree/bindings/regulator/max8925-regulator.txt new file mode 100644 index 000000000000..073b41d90f2c --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/max8925-regulator.txt @@ -0,0 +1,29 @@ +Max8925 Voltage regulators + +max8925 regulator device register is still handled by mfd_add_devices, not by +of_xxx, so, it is not necessary to add compatible name. Also, those reg +offset and id info is stored in mfd_cell(see max8925-core.c), as a result +there is not private properties in dts. + +node's name should match with the definition in max8925_regulator_matches +(see max8925-regulator.c) + + +Optional properties: +- Any optional property defined in bindings/regulator/regulator.txt + + +Example: + + + regulators { + SDV1 { + regulator-min-microvolt = <637500>; + regulator-max-microvolt = <1425000>; + regulator-boot-on; + regulator-always-on; + }; + + ... + ... + } diff --git a/drivers/regulator/max8925-regulator.c b/drivers/regulator/max8925-regulator.c index 9bb0be37495f..2b549791c975 100644 --- a/drivers/regulator/max8925-regulator.c +++ b/drivers/regulator/max8925-regulator.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #define SD1_DVM_VMIN 850000 #define SD1_DVM_VMAX 1000000 @@ -187,6 +189,34 @@ static struct regulator_ops max8925_regulator_ldo_ops = { .enable_reg = MAX8925_LDOCTL##_id, \ } +#ifdef CONFIG_OF +static struct of_regulator_match max8925_regulator_matches[] = { + { .name = "SDV1",}, + { .name = "SDV2",}, + { .name = "SDV3",}, + { .name = "LDO1",}, + { .name = "LDO2",}, + { .name = "LDO3",}, + { .name = "LDO4",}, + { .name = "LDO5",}, + { .name = "LDO6",}, + { .name = "LDO7",}, + { .name = "LDO8",}, + { .name = "LDO9",}, + { .name = "LDO10",}, + { .name = "LDO11",}, + { .name = "LDO12",}, + { .name = "LDO13",}, + { .name = "LDO14",}, + { .name = "LDO15",}, + { .name = "LDO16",}, + { .name = "LDO17",}, + { .name = "LDO18",}, + { .name = "LDO19",}, + { .name = "LDO20",}, +}; +#endif + static struct max8925_regulator_info max8925_regulator_info[] = { MAX8925_SDV(1, 637.5, 1425, 12.5), MAX8925_SDV(2, 650, 2225, 25), @@ -214,6 +244,36 @@ static struct max8925_regulator_info max8925_regulator_info[] = { MAX8925_LDO(20, 750, 3900, 50), }; +#ifdef CONFIG_OF +static int max8925_regulator_dt_init(struct platform_device *pdev, + struct max8925_regulator_info *info, + struct regulator_config *config, + int ridx) +{ + struct device_node *nproot, *np; + int rcount; + nproot = pdev->dev.parent->of_node; + if (!nproot) + return -ENODEV; + np = of_find_node_by_name(nproot, "regulators"); + if (!np) { + dev_err(&pdev->dev, "failed to find regulators node\n"); + return -ENODEV; + } + + rcount = of_regulator_match(&pdev->dev, np, + &max8925_regulator_matches[ridx], 1); + if (rcount < 0) + return -ENODEV; + config->init_data = max8925_regulator_matches[ridx].init_data; + config->of_node = max8925_regulator_matches[ridx].of_node; + + return 0; +} +#else +#define max8925_regulator_dt_init(w, x, y, z) (-1) +#endif + static int __devinit max8925_regulator_probe(struct platform_device *pdev) { struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); @@ -222,7 +282,7 @@ static int __devinit max8925_regulator_probe(struct platform_device *pdev) struct max8925_regulator_info *ri; struct resource *res; struct regulator_dev *rdev; - int i; + int i, regulator_idx; res = platform_get_resource(pdev, IORESOURCE_REG, 0); if (!res) { @@ -231,9 +291,12 @@ static int __devinit max8925_regulator_probe(struct platform_device *pdev) } for (i = 0; i < ARRAY_SIZE(max8925_regulator_info); i++) { ri = &max8925_regulator_info[i]; - if (ri->vol_reg == res->start) + if (ri->vol_reg == res->start) { + regulator_idx = i; break; + } } + if (i == ARRAY_SIZE(max8925_regulator_info)) { dev_err(&pdev->dev, "Failed to find regulator %llu\n", (unsigned long long)res->start); @@ -243,9 +306,12 @@ static int __devinit max8925_regulator_probe(struct platform_device *pdev) ri->chip = chip; config.dev = &pdev->dev; - config.init_data = pdata; config.driver_data = ri; + if (max8925_regulator_dt_init(pdev, ri, &config, regulator_idx)) + if (pdata) + config.init_data = pdata; + rdev = regulator_register(&ri->desc, &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register regulator %s\n", -- cgit v1.2.3 From d928cd2ef8f7f4194e479d4a66452901ec82ccda Mon Sep 17 00:00:00 2001 From: Afzal Mohammed Date: Tue, 6 Nov 2012 20:47:35 +0530 Subject: usb: musb: dsps: document dt bindings properly DT bindings normally use '-' (hyphens) instead of '_' (underscore), driver has it the proper way, but binding documentation does not reflect it, fix it. Signed-off-by: Afzal Mohammed Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/am33xx-usb.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/usb/am33xx-usb.txt b/Documentation/devicetree/bindings/usb/am33xx-usb.txt index ca8fa56e9f03..a92250512a4e 100644 --- a/Documentation/devicetree/bindings/usb/am33xx-usb.txt +++ b/Documentation/devicetree/bindings/usb/am33xx-usb.txt @@ -3,12 +3,12 @@ AM33XX MUSB GLUE - ti,hwmods : must be "usb_otg_hs" - multipoint : Should be "1" indicating the musb controller supports multipoint. This is a MUSB configuration-specific setting. - - num_eps : Specifies the number of endpoints. This is also a + - num-eps : Specifies the number of endpoints. This is also a MUSB configuration-specific setting. Should be set to "16" - - ram_bits : Specifies the ram address size. Should be set to "12" - - port0_mode : Should be "3" to represent OTG. "1" signifies HOST and "2" + - ram-bits : Specifies the ram address size. Should be set to "12" + - port0-mode : Should be "3" to represent OTG. "1" signifies HOST and "2" represents PERIPHERAL. - - port1_mode : Should be "1" to represent HOST. "3" signifies OTG and "2" + - port1-mode : Should be "1" to represent HOST. "3" signifies OTG and "2" represents PERIPHERAL. - power : Should be "250". This signifies the controller can supply upto 500mA when operating in host mode. -- cgit v1.2.3 From 560615ef8b95bb9aa4ea77ccaf3b2114dc93206f Mon Sep 17 00:00:00 2001 From: Qing Xu Date: Thu, 8 Nov 2012 10:31:32 +0800 Subject: regulator: max8925: update dt regulator binding doc remove linux specific references, enumerates all supported regulators Signed-off-by: Qing Xu Signed-off-by: Mark Brown --- .../bindings/regulator/max8925-regulator.txt | 51 +++++++++++++--------- 1 file changed, 31 insertions(+), 20 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/regulator/max8925-regulator.txt b/Documentation/devicetree/bindings/regulator/max8925-regulator.txt index 073b41d90f2c..0057695aae8f 100644 --- a/Documentation/devicetree/bindings/regulator/max8925-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/max8925-regulator.txt @@ -1,29 +1,40 @@ Max8925 Voltage regulators -max8925 regulator device register is still handled by mfd_add_devices, not by -of_xxx, so, it is not necessary to add compatible name. Also, those reg -offset and id info is stored in mfd_cell(see max8925-core.c), as a result -there is not private properties in dts. - -node's name should match with the definition in max8925_regulator_matches -(see max8925-regulator.c) - +Required nodes: +-nodes: + - SDV1 for SDV SDV1 + - SDV2 for SDV SDV2 + - SDV3 for SDV SDV3 + - LDO1 for LDO LDO1 + - LDO2 for LDO LDO2 + - LDO3 for LDO LDO3 + - LDO4 for LDO LDO4 + - LDO5 for LDO LDO5 + - LDO6 for LDO LDO6 + - LDO7 for LDO LDO7 + - LDO8 for LDO LDO8 + - LDO9 for LDO LDO9 + - LDO10 for LDO LDO10 + - LDO11 for LDO LDO11 + - LDO12 for LDO LDO12 + - LDO13 for LDO LDO13 + - LDO14 for LDO LDO14 + - LDO15 for LDO LDO15 + - LDO16 for LDO LDO16 + - LDO17 for LDO LDO17 + - LDO18 for LDO LDO18 + - LDO19 for LDO LDO19 + - LDO20 for LDO LDO20 Optional properties: - Any optional property defined in bindings/regulator/regulator.txt - Example: + SDV1 { + regulator-min-microvolt = <637500>; + regulator-max-microvolt = <1425000>; + regulator-boot-on; + regulator-always-on; + }; - regulators { - SDV1 { - regulator-min-microvolt = <637500>; - regulator-max-microvolt = <1425000>; - regulator-boot-on; - regulator-always-on; - }; - - ... - ... - } -- cgit v1.2.3 From 55020c8056a83ef2181e4cddd39cb2b9f4b88c5a Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 9 Nov 2012 14:07:01 +0100 Subject: of: Add vendor prefix for ON Semiconductor Corp. The used vendor prefix corresponds to the stock symbol (ONNN) for ON Semiconductor Corp. Signed-off-by: Thierry Reding Signed-off-by: Rob Herring --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 9de2b9ff9d6e..ff41e4019010 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -34,6 +34,7 @@ national National Semiconductor nintendo Nintendo nvidia NVIDIA nxp NXP Semiconductors +onnn ON Semiconductor Corp. picochip Picochip Ltd powervr Imagination Technologies qcom Qualcomm, Inc. -- cgit v1.2.3 From f23f1516b6757c326cc638bed8c402c77e2a596e Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Sat, 27 Oct 2012 15:21:36 +0530 Subject: gpiolib: provide provision to register pin ranges pinctrl subsystem needs gpio chip base to prepare set of gpio pin ranges, which a given pinctrl driver can handle. This is important to handle pinctrl gpio request calls in order to program a given pin properly for gpio operation. As gpio base is allocated dynamically during gpiochip registration, presently there exists no clean way to pass this information to the pinctrl subsystem. After few discussions from [1], it was concluded that may be gpio controller reporting the pin range it supports, is a better way than pinctrl subsystem directly registering it. [1] http://comments.gmane.org/gmane.linux.ports.arm.kernel/184816 Cc: Grant Likely Signed-off-by: Viresh Kumar Signed-off-by: Shiraz Hashim [Edited documentation a bit] Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/gpio/gpio.txt | 36 ++++++++++++++++ Documentation/gpio.txt | 42 +++++++++++++++++++ Documentation/pinctrl.txt | 3 ++ drivers/gpio/gpiolib-of.c | 56 +++++++++++++++++++++++++ drivers/gpio/gpiolib.c | 43 +++++++++++++++++++ drivers/pinctrl/core.c | 13 ++++++ drivers/pinctrl/devicetree.c | 13 ++++++ include/asm-generic/gpio.h | 25 +++++++++++ include/linux/gpio.h | 3 ++ include/linux/pinctrl/pinctrl.h | 17 ++++++++ 10 files changed, 251 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt index 4e16ba4feab0..a33628759d36 100644 --- a/Documentation/devicetree/bindings/gpio/gpio.txt +++ b/Documentation/devicetree/bindings/gpio/gpio.txt @@ -75,4 +75,40 @@ Example of two SOC GPIO banks defined as gpio-controller nodes: gpio-controller; }; +2.1) gpio-controller and pinctrl subsystem +------------------------------------------ +gpio-controller on a SOC might be tightly coupled with the pinctrl +subsystem, in the sense that the pins can be used by other functions +together with optional gpio feature. + +While the pin allocation is totally managed by the pin ctrl subsystem, +gpio (under gpiolib) is still maintained by gpio drivers. It may happen +that different pin ranges in a SoC is managed by different gpio drivers. + +This makes it logical to let gpio drivers announce their pin ranges to +the pin ctrl subsystem and call 'pinctrl_request_gpio' in order to +request the corresponding pin before any gpio usage. + +For this, the gpio controller can use a pinctrl phandle and pins to +announce the pinrange to the pin ctrl subsystem. For example, + + qe_pio_e: gpio-controller@1460 { + #gpio-cells = <2>; + compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank"; + reg = <0x1460 0x18>; + gpio-controller; + gpio-ranges = <&pinctrl1 20 10>, <&pinctrl2 50 20>; + + } + +where, + &pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node. + + Next values specify the base pin and number of pins for the range + handled by 'qe_pio_e' gpio. In the given example from base pin 20 to + pin 29 under pinctrl1 and pin 50 to pin 69 under pinctrl2 is handled + by this gpio controller. + +The pinctrl node must have "#gpio-range-cells" property to show number of +arguments to pass with phandle from gpio controllers node. diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt index e08a883de36e..77a1d11af723 100644 --- a/Documentation/gpio.txt +++ b/Documentation/gpio.txt @@ -439,6 +439,48 @@ slower clock delays the rising edge of SCK, and the I2C master adjusts its signaling rate accordingly. +GPIO controllers and the pinctrl subsystem +------------------------------------------ + +A GPIO controller on a SOC might be tightly coupled with the pinctrl +subsystem, in the sense that the pins can be used by other functions +together with an optional gpio feature. We have already covered the +case where e.g. a GPIO controller need to reserve a pin or set the +direction of a pin by calling any of: + +pinctrl_request_gpio() +pinctrl_free_gpio() +pinctrl_gpio_direction_input() +pinctrl_gpio_direction_output() + +But how does the pin control subsystem cross-correlate the GPIO +numbers (which are a global business) to a certain pin on a certain +pin controller? + +This is done by registering "ranges" of pins, which are essentially +cross-reference tables. These are described in +Documentation/pinctrl.txt + +While the pin allocation is totally managed by the pinctrl subsystem, +gpio (under gpiolib) is still maintained by gpio drivers. It may happen +that different pin ranges in a SoC is managed by different gpio drivers. + +This makes it logical to let gpio drivers announce their pin ranges to +the pin ctrl subsystem before it will call 'pinctrl_request_gpio' in order +to request the corresponding pin to be prepared by the pinctrl subsystem +before any gpio usage. + +For this, the gpio controller can register its pin range with pinctrl +subsystem. There are two ways of doing it currently: with or without DT. + +For with DT support refer to Documentation/devicetree/bindings/gpio/gpio.txt. + +For non-DT support, user can call gpiochip_add_pin_range() with appropriate +parameters to register a range of gpio pins with a pinctrl driver. For this +exact name string of pinctrl device has to be passed as one of the +argument to this routine. + + What do these conventions omit? =============================== One of the biggest things these conventions omit is pin multiplexing, since diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index a1cd2f9428d7..da40efbef6ec 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -364,6 +364,9 @@ will get an pin number into its handled number range. Further it is also passed the range ID value, so that the pin controller knows which range it should deal with. +Calling pinctrl_add_gpio_range from pinctrl driver is DEPRECATED. Please see +section 2.1 of Documentation/devicetree/bindings/gpio/gpio.txt on how to bind +pinctrl and gpio drivers. PINMUX interfaces ================= diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index f1a45997aea8..a5b90c8e9844 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -19,6 +19,7 @@ #include #include #include +#include #include /* Private data structure for of_gpiochip_find_and_xlate */ @@ -216,6 +217,58 @@ err0: } EXPORT_SYMBOL(of_mm_gpiochip_add); +#ifdef CONFIG_PINCTRL +void of_gpiochip_add_pin_range(struct gpio_chip *chip) +{ + struct device_node *np = chip->of_node; + struct gpio_pin_range *pin_range; + struct of_phandle_args pinspec; + int index = 0, ret; + + if (!np) + return; + + do { + ret = of_parse_phandle_with_args(np, "gpio-ranges", + "#gpio-range-cells", index, &pinspec); + if (ret) + break; + + pin_range = devm_kzalloc(chip->dev, sizeof(*pin_range), + GFP_KERNEL); + if (!pin_range) { + pr_err("%s: GPIO chip: failed to allocate pin ranges\n", + chip->label); + break; + } + + pin_range->range.name = chip->label; + pin_range->range.base = chip->base; + pin_range->range.pin_base = pinspec.args[0]; + pin_range->range.npins = pinspec.args[1]; + pin_range->pctldev = of_pinctrl_add_gpio_range(pinspec.np, + &pin_range->range); + + list_add_tail(&pin_range->node, &chip->pin_ranges); + + } while (index++); +} + +void of_gpiochip_remove_pin_range(struct gpio_chip *chip) +{ + struct gpio_pin_range *pin_range, *tmp; + + list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) { + list_del(&pin_range->node); + pinctrl_remove_gpio_range(pin_range->pctldev, + &pin_range->range); + } +} +#else +void of_gpiochip_add_pin_range(struct gpio_chip *chip) {} +void of_gpiochip_remove_pin_range(struct gpio_chip *chip) {} +#endif + void of_gpiochip_add(struct gpio_chip *chip) { if ((!chip->of_node) && (chip->dev)) @@ -229,11 +282,14 @@ void of_gpiochip_add(struct gpio_chip *chip) chip->of_xlate = of_gpio_simple_xlate; } + of_gpiochip_add_pin_range(chip); of_node_get(chip->of_node); } void of_gpiochip_remove(struct gpio_chip *chip) { + of_gpiochip_remove_pin_range(chip); + if (chip->of_node) of_node_put(chip->of_node); } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1c8d9e3380e1..f0b07bbfcc9a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1083,6 +1083,10 @@ int gpiochip_add(struct gpio_chip *chip) } } +#ifdef CONFIG_PINCTRL + INIT_LIST_HEAD(&chip->pin_ranges); +#endif + of_gpiochip_add(chip); unlock: @@ -1180,6 +1184,45 @@ struct gpio_chip *gpiochip_find(void *data, } EXPORT_SYMBOL_GPL(gpiochip_find); +#ifdef CONFIG_PINCTRL +void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins) +{ + struct gpio_pin_range *pin_range; + + pin_range = devm_kzalloc(chip->dev, sizeof(*pin_range), GFP_KERNEL); + if (!pin_range) { + pr_err("%s: GPIO chip: failed to allocate pin ranges\n", + chip->label); + return; + } + + pin_range->range.name = chip->label; + pin_range->range.base = chip->base; + pin_range->range.pin_base = pin_base; + pin_range->range.npins = npins; + pin_range->pctldev = find_pinctrl_and_add_gpio_range(pinctl_name, + &pin_range->range); + + list_add_tail(&pin_range->node, &chip->pin_ranges); +} + +void gpiochip_remove_pin_ranges(struct gpio_chip *chip) +{ + struct gpio_pin_range *pin_range, *tmp; + + list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) { + list_del(&pin_range->node); + pinctrl_remove_gpio_range(pin_range->pctldev, + &pin_range->range); + } +} +#else +void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins) {} +void gpiochip_remove_pin_ranges(struct gpio_chip *chip) {} +#endif + /* These "optional" allocation calls help prevent drivers from stomping * on each other, and help provide better diagnostics in debugfs. * They're called even less than the "set direction" calls. diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index b1086dcde15d..71db586b2afd 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -345,6 +345,19 @@ void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev, } EXPORT_SYMBOL_GPL(pinctrl_add_gpio_ranges); +struct pinctrl_dev *find_pinctrl_and_add_gpio_range(const char *devname, + struct pinctrl_gpio_range *range) +{ + struct pinctrl_dev *pctldev = get_pinctrl_dev_from_devname(devname); + + if (!pctldev) + return NULL; + + pinctrl_add_gpio_range(pctldev, range); + return pctldev; +} +EXPORT_SYMBOL_GPL(find_pinctrl_and_add_gpio_range); + /** * pinctrl_remove_gpio_range() - remove a range of GPIOs fro a pin controller * @pctldev: pin controller device to remove the range from diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c index fcb1de45473c..6728ec71cb65 100644 --- a/drivers/pinctrl/devicetree.c +++ b/drivers/pinctrl/devicetree.c @@ -106,6 +106,19 @@ static struct pinctrl_dev *find_pinctrl_by_of_node(struct device_node *np) return NULL; } +struct pinctrl_dev *of_pinctrl_add_gpio_range(struct device_node *np, + struct pinctrl_gpio_range *range) +{ + struct pinctrl_dev *pctldev; + + pctldev = find_pinctrl_by_of_node(np); + if (!pctldev) + return NULL; + + pinctrl_add_gpio_range(pctldev, range); + return pctldev; +} + static int dt_to_map_one_config(struct pinctrl *p, const char *statename, struct device_node *np_config) { diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index a9432fc6b8ba..92e5c432421c 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -5,6 +5,7 @@ #include #include #include +#include #ifdef CONFIG_GPIOLIB @@ -47,6 +48,21 @@ struct seq_file; struct module; struct device_node; +#ifdef CONFIG_PINCTRL +/** + * struct gpio_pin_range - pin range controlled by a gpio chip + * @head: list for maintaining set of pin ranges, used internally + * @pctldev: pinctrl device which handles corresponding pins + * @range: actual range of pins controlled by a gpio controller + */ + +struct gpio_pin_range { + struct list_head node; + struct pinctrl_dev *pctldev; + struct pinctrl_gpio_range range; +}; +#endif + /** * struct gpio_chip - abstract a GPIO controller * @label: for diagnostics @@ -134,6 +150,15 @@ struct gpio_chip { int (*of_xlate)(struct gpio_chip *gc, const struct of_phandle_args *gpiospec, u32 *flags); #endif +#ifdef CONFIG_PINCTRL + /* + * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally + * describe the actual pin range which they serve in an SoC. This + * information would be used by pinctrl subsystem to configure + * corresponding pins for gpio usage. + */ + struct list_head pin_ranges; +#endif }; extern const char *gpiochip_is_requested(struct gpio_chip *chip, diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 2e31e8b3a190..a28445992b7f 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -231,6 +231,9 @@ static inline int irq_to_gpio(unsigned irq) return -EINVAL; } +void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins); +void gpiochip_remove_pin_ranges(struct gpio_chip *chip); #endif #endif /* __LINUX_GPIO_H */ diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index eda04674633d..434e5a94e131 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -136,6 +136,23 @@ extern void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev, unsigned nranges); extern void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range); + +extern struct pinctrl_dev *find_pinctrl_and_add_gpio_range(const char *devname, + struct pinctrl_gpio_range *range); + +#ifdef CONFIG_OF +extern struct pinctrl_dev *of_pinctrl_add_gpio_range(struct device_node *np, + struct pinctrl_gpio_range *range); +#else +static inline +struct pinctrl_dev *of_pinctrl_add_gpio_range(struct device_node *np, + struct pinctrl_gpio_range *range) +{ + return NULL; +} + +#endif /* CONFIG_OF */ + extern const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev); extern void *pinctrl_dev_get_drvdata(struct pinctrl_dev *pctldev); #else -- cgit v1.2.3 From 84e6de813b2d1bdb127943d3c8edf1c1afaa90da Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 5 Nov 2012 16:10:34 +0100 Subject: Documentation: Describe bindings for STMPE Multi-Functional Device driver Here we add the required documentation for the STMPE Multi-Functional Device (MFD) Device Tree bindings. It describes all of the bindings currently supported by the driver. Acked-by: Arnd Bergmann Acked-by: Linus Walleij Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- Documentation/devicetree/bindings/mfd/stmpe.txt | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/stmpe.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mfd/stmpe.txt b/Documentation/devicetree/bindings/mfd/stmpe.txt new file mode 100644 index 000000000000..8f0aeda2e326 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/stmpe.txt @@ -0,0 +1,25 @@ +* STMPE Multi-Functional Device + +Required properties: + - compatible : "st,stmpe[811|1601|2401|2403]" + - reg : I2C address of the device + +Optional properties: + - interrupts : The interrupt outputs from the controller + - interrupt-controller : Marks the device node as an interrupt controller + - interrupt-parent : Specifies which IRQ controller we're connected to + - i2c-client-wake : Marks the input device as wakable + - st,autosleep-timeout : Valid entries (ms); 4, 16, 32, 64, 128, 256, 512 and 1024 + +Example: + + stmpe1601: stmpe1601@40 { + compatible = "st,stmpe1601"; + reg = <0x40>; + interrupts = <26 0x4>; + interrupt-parent = <&gpio6>; + interrupt-controller; + + i2c-client-wake; + st,autosleep-timeout = <1024>; + }; -- cgit v1.2.3 From 5658a68fb578df8c9b9dd3071b2a56bf92c15324 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 20 Sep 2012 15:04:33 +0200 Subject: ARM i.MX25: Add devicetree This adds a i.MX25 dtsi file along with the i.MX25 clock tree documentation. The devicetree should be fairly complete for: - uart - fec - i2c - spi - pwm - nand - gpio - wdog - esdhc - flexcan The more exotic devices currently miss clock bindings. Signed-off-by: Sascha Hauer Acked-by: Shawn Guo --- .../devicetree/bindings/clock/imx25-clock.txt | 162 +++++++ arch/arm/boot/dts/imx25.dtsi | 515 +++++++++++++++++++++ 2 files changed, 677 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/imx25-clock.txt create mode 100644 arch/arm/boot/dts/imx25.dtsi (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/clock/imx25-clock.txt b/Documentation/devicetree/bindings/clock/imx25-clock.txt new file mode 100644 index 000000000000..c2a3525ecb4e --- /dev/null +++ b/Documentation/devicetree/bindings/clock/imx25-clock.txt @@ -0,0 +1,162 @@ +* Clock bindings for Freescale i.MX25 + +Required properties: +- compatible: Should be "fsl,imx25-ccm" +- reg: Address and length of the register set +- interrupts: Should contain CCM interrupt +- #clock-cells: Should be <1> + +The clock consumer should specify the desired clock by having the clock +ID in its "clocks" phandle cell. The following is a full list of i.MX25 +clocks and IDs. + + Clock ID + --------------------------- + dummy 0 + osc 1 + mpll 2 + upll 3 + mpll_cpu_3_4 4 + cpu_sel 5 + cpu 6 + ahb 7 + usb_div 8 + ipg 9 + per0_sel 10 + per1_sel 11 + per2_sel 12 + per3_sel 13 + per4_sel 14 + per5_sel 15 + per6_sel 16 + per7_sel 17 + per8_sel 18 + per9_sel 19 + per10_sel 20 + per11_sel 21 + per12_sel 22 + per13_sel 23 + per14_sel 24 + per15_sel 25 + per0 26 + per1 27 + per2 28 + per3 29 + per4 30 + per5 31 + per6 32 + per7 33 + per8 34 + per9 35 + per10 36 + per11 37 + per12 38 + per13 39 + per14 40 + per15 41 + csi_ipg_per 42 + epit_ipg_per 43 + esai_ipg_per 44 + esdhc1_ipg_per 45 + esdhc2_ipg_per 46 + gpt_ipg_per 47 + i2c_ipg_per 48 + lcdc_ipg_per 49 + nfc_ipg_per 50 + owire_ipg_per 51 + pwm_ipg_per 52 + sim1_ipg_per 53 + sim2_ipg_per 54 + ssi1_ipg_per 55 + ssi2_ipg_per 56 + uart_ipg_per 57 + ata_ahb 58 + reserved 59 + csi_ahb 60 + emi_ahb 61 + esai_ahb 62 + esdhc1_ahb 63 + esdhc2_ahb 64 + fec_ahb 65 + lcdc_ahb 66 + rtic_ahb 67 + sdma_ahb 68 + slcdc_ahb 69 + usbotg_ahb 70 + reserved 71 + reserved 72 + reserved 73 + reserved 74 + can1_ipg 75 + can2_ipg 76 + csi_ipg 77 + cspi1_ipg 78 + cspi2_ipg 79 + cspi3_ipg 80 + dryice_ipg 81 + ect_ipg 82 + epit1_ipg 83 + epit2_ipg 84 + reserved 85 + esdhc1_ipg 86 + esdhc2_ipg 87 + fec_ipg 88 + reserved 89 + reserved 90 + reserved 91 + gpt1_ipg 92 + gpt2_ipg 93 + gpt3_ipg 94 + gpt4_ipg 95 + reserved 96 + reserved 97 + reserved 98 + iim_ipg 99 + reserved 100 + reserved 101 + kpp_ipg 102 + lcdc_ipg 103 + reserved 104 + pwm1_ipg 105 + pwm2_ipg 106 + pwm3_ipg 107 + pwm4_ipg 108 + rngb_ipg 109 + reserved 110 + scc_ipg 111 + sdma_ipg 112 + sim1_ipg 113 + sim2_ipg 114 + slcdc_ipg 115 + spba_ipg 116 + ssi1_ipg 117 + ssi2_ipg 118 + tsc_ipg 119 + uart1_ipg 120 + uart2_ipg 121 + uart3_ipg 122 + uart4_ipg 123 + uart5_ipg 124 + reserved 125 + wdt_ipg 126 + +Examples: + +clks: ccm@53f80000 { + compatible = "fsl,imx25-ccm"; + reg = <0x53f80000 0x4000>; + interrupts = <31>; + clock-output-names = ... + "uart_ipg", + "uart_serial", + ...; +}; + +uart1: serial@43f90000 { + compatible = "fsl,imx25-uart", "fsl,imx21-uart"; + reg = <0x43f90000 0x4000>; + interrupts = <45>; + clocks = <&clks 79>, <&clks 50>; + clock-names = "ipg", "per"; + status = "disabled"; +}; diff --git a/arch/arm/boot/dts/imx25.dtsi b/arch/arm/boot/dts/imx25.dtsi new file mode 100644 index 000000000000..e1b13ebc96d6 --- /dev/null +++ b/arch/arm/boot/dts/imx25.dtsi @@ -0,0 +1,515 @@ +/* + * Copyright 2012 Sascha Hauer, Pengutronix + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/include/ "skeleton.dtsi" + +/ { + aliases { + serial0 = &uart1; + serial1 = &uart2; + serial2 = &uart3; + serial3 = &uart4; + serial4 = &uart5; + gpio0 = &gpio1; + gpio1 = &gpio2; + gpio2 = &gpio3; + gpio3 = &gpio4; + usb0 = &usbotg; + usb1 = &usbhost1; + }; + + asic: asic-interrupt-controller@68000000 { + compatible = "fsl,imx25-asic", "fsl,avic"; + interrupt-controller; + #interrupt-cells = <1>; + reg = <0x68000000 0x8000000>; + }; + + clocks { + #address-cells = <1>; + #size-cells = <0>; + + osc { + compatible = "fsl,imx-osc", "fixed-clock"; + clock-frequency = <24000000>; + }; + }; + + soc { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + interrupt-parent = <&asic>; + ranges; + + aips@43f00000 { /* AIPS1 */ + compatible = "fsl,aips-bus", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x43f00000 0x100000>; + ranges; + + i2c1: i2c@43f80000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx25-i2c", "fsl,imx21-i2c"; + reg = <0x43f80000 0x4000>; + clocks = <&clks 48>; + clock-names = ""; + interrupts = <3>; + status = "disabled"; + }; + + i2c3: i2c@43f84000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx25-i2c", "fsl,imx21-i2c"; + reg = <0x43f84000 0x4000>; + clocks = <&clks 48>; + clock-names = ""; + interrupts = <10>; + status = "disabled"; + }; + + can1: can@43f88000 { + compatible = "fsl,imx25-flexcan", "fsl,p1010-flexcan"; + reg = <0x43f88000 0x4000>; + interrupts = <43>; + clocks = <&clks 75>, <&clks 75>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + can2: can@43f8c000 { + compatible = "fsl,imx25-flexcan", "fsl,p1010-flexcan"; + reg = <0x43f8c000 0x4000>; + interrupts = <44>; + clocks = <&clks 76>, <&clks 76>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + uart1: serial@43f90000 { + compatible = "fsl,imx25-uart", "fsl,imx21-uart"; + reg = <0x43f90000 0x4000>; + interrupts = <45>; + clocks = <&clks 120>, <&clks 57>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + uart2: serial@43f94000 { + compatible = "fsl,imx25-uart", "fsl,imx21-uart"; + reg = <0x43f94000 0x4000>; + interrupts = <32>; + clocks = <&clks 121>, <&clks 57>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + i2c2: i2c@43f98000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx25-i2c", "fsl,imx21-i2c"; + reg = <0x43f98000 0x4000>; + clocks = <&clks 48>; + clock-names = ""; + interrupts = <4>; + status = "disabled"; + }; + + owire@43f9c000 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x43f9c000 0x4000>; + clocks = <&clks 51>; + clock-names = ""; + interrupts = <2>; + status = "disabled"; + }; + + spi1: cspi@43fa4000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx25-cspi", "fsl,imx35-cspi"; + reg = <0x43fa4000 0x4000>; + clocks = <&clks 62>; + clock-names = "ipg"; + interrupts = <14>; + status = "disabled"; + }; + + kpp@43fa8000 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x43fa8000 0x4000>; + clocks = <&clks 102>; + clock-names = ""; + interrupts = <24>; + status = "disabled"; + }; + + iomuxc@43fac000{ + compatible = "fsl,imx25-iomuxc"; + reg = <0x43fac000 0x4000>; + }; + + audmux@43fb0000 { + compatible = "fsl,imx25-audmux", "fsl,imx31-audmux"; + reg = <0x43fb0000 0x4000>; + status = "disabled"; + }; + }; + + spba@50000000 { + compatible = "fsl,spba-bus", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x50000000 0x40000>; + ranges; + + spi3: cspi@50004000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx25-cspi", "fsl,imx35-cspi"; + reg = <0x50004000 0x4000>; + interrupts = <0>; + clocks = <&clks 80>; + clock-names = "ipg"; + status = "disabled"; + }; + + uart4: serial@50008000 { + compatible = "fsl,imx25-uart", "fsl,imx21-uart"; + reg = <0x50008000 0x4000>; + interrupts = <5>; + clocks = <&clks 123>, <&clks 57>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + uart3: serial@5000c000 { + compatible = "fsl,imx25-uart", "fsl,imx21-uart"; + reg = <0x5000c000 0x4000>; + interrupts = <18>; + clocks = <&clks 122>, <&clks 57>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + spi2: cspi@50010000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx25-cspi", "fsl,imx35-cspi"; + reg = <0x50010000 0x4000>; + clocks = <&clks 79>; + clock-names = "ipg"; + interrupts = <13>; + status = "disabled"; + }; + + ssi2: ssi@50014000 { + compatible = "fsl,imx25-ssi", "fsl,imx21-ssi"; + reg = <0x50014000 0x4000>; + interrupts = <11>; + status = "disabled"; + }; + + esai@50018000 { + reg = <0x50018000 0x4000>; + interrupts = <7>; + }; + + uart5: serial@5002c000 { + compatible = "fsl,imx25-uart", "fsl,imx21-uart"; + reg = <0x5002c000 0x4000>; + interrupts = <40>; + clocks = <&clks 124>, <&clks 57>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + tsc: tsc@50030000 { + compatible = "fsl,imx25-adc", "fsl,imx21-tsc"; + reg = <0x50030000 0x4000>; + interrupts = <46>; + clocks = <&clks 119>; + clock-names = "ipg"; + status = "disabled"; + }; + + ssi1: ssi@50034000 { + compatible = "fsl,imx25-ssi", "fsl,imx21-ssi"; + reg = <0x50034000 0x4000>; + interrupts = <12>; + status = "disabled"; + }; + + fec: ethernet@50038000 { + compatible = "fsl,imx25-fec"; + reg = <0x50038000 0x4000>; + interrupts = <57>; + clocks = <&clks 88>, <&clks 65>; + clock-names = "ipg", "ahb"; + status = "disabled"; + }; + }; + + aips@53f00000 { /* AIPS2 */ + compatible = "fsl,aips-bus", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x53f00000 0x100000>; + ranges; + + clks: ccm@53f80000 { + compatible = "fsl,imx25-ccm"; + reg = <0x53f80000 0x4000>; + interrupts = <31>; + #clock-cells = <1>; + }; + + gpt4: timer@53f84000 { + compatible = "fsl,imx25-gpt", "fsl,imx31-gpt"; + reg = <0x53f84000 0x4000>; + clocks = <&clks 9>, <&clks 45>; + clock-names = "ipg", "per"; + interrupts = <1>; + }; + + gpt3: timer@53f88000 { + compatible = "fsl,imx25-gpt", "fsl,imx31-gpt"; + reg = <0x53f88000 0x4000>; + clocks = <&clks 9>, <&clks 47>; + clock-names = "ipg", "per"; + interrupts = <29>; + }; + + gpt2: timer@53f8c000 { + compatible = "fsl,imx25-gpt", "fsl,imx31-gpt"; + reg = <0x53f8c000 0x4000>; + clocks = <&clks 9>, <&clks 47>; + clock-names = "ipg", "per"; + interrupts = <53>; + }; + + gpt1: timer@53f90000 { + compatible = "fsl,imx25-gpt", "fsl,imx31-gpt"; + reg = <0x53f90000 0x4000>; + clocks = <&clks 9>, <&clks 47>; + clock-names = "ipg", "per"; + interrupts = <54>; + }; + + epit1: timer@53f94000 { + compatible = "fsl,imx25-epit"; + reg = <0x53f94000 0x4000>; + interrupts = <28>; + }; + + epit2: timer@53f98000 { + compatible = "fsl,imx25-epit"; + reg = <0x53f98000 0x4000>; + interrupts = <27>; + }; + + gpio4: gpio@53f9c000 { + compatible = "fsl,imx25-gpio", "fsl,imx35-gpio"; + reg = <0x53f9c000 0x4000>; + interrupts = <23>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + pwm2: pwm@53fa0000 { + compatible = "fsl,imx25-pwm", "fsl,imx27-pwm"; + #pwm-cells = <2>; + reg = <0x53fa0000 0x4000>; + clocks = <&clks 106>, <&clks 36>; + clock-names = "ipg", "per"; + interrupts = <36>; + }; + + gpio3: gpio@53fa4000 { + compatible = "fsl,imx25-gpio", "fsl,imx35-gpio"; + reg = <0x53fa4000 0x4000>; + interrupts = <16>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + pwm3: pwm@53fa8000 { + compatible = "fsl,imx25-pwm", "fsl,imx27-pwm"; + #pwm-cells = <2>; + reg = <0x53fa8000 0x4000>; + clocks = <&clks 107>, <&clks 36>; + clock-names = "ipg", "per"; + interrupts = <41>; + }; + + esdhc1: esdhc@53fb4000 { + compatible = "fsl,imx25-esdhc"; + reg = <0x53fb4000 0x4000>; + interrupts = <9>; + clocks = <&clks 86>, <&clks 63>, <&clks 45>; + clock-names = "ipg", "ahb", "per"; + status = "disabled"; + }; + + esdhc2: esdhc@53fb8000 { + compatible = "fsl,imx25-esdhc"; + reg = <0x53fb8000 0x4000>; + interrupts = <8>; + clocks = <&clks 87>, <&clks 64>, <&clks 46>; + clock-names = "ipg", "ahb", "per"; + status = "disabled"; + }; + + lcdc@53fbc000 { + reg = <0x53fbc000 0x4000>; + interrupts = <39>; + clocks = <&clks 103>, <&clks 66>, <&clks 49>; + clock-names = "ipg", "ahb", "per"; + status = "disabled"; + }; + + slcdc@53fc0000 { + reg = <0x53fc0000 0x4000>; + interrupts = <38>; + status = "disabled"; + }; + + pwm4: pwm@53fc8000 { + compatible = "fsl,imx25-pwm", "fsl,imx27-pwm"; + reg = <0x53fc8000 0x4000>; + clocks = <&clks 108>, <&clks 36>; + clock-names = "ipg", "per"; + interrupts = <42>; + }; + + gpio1: gpio@53fcc000 { + compatible = "fsl,imx25-gpio", "fsl,imx35-gpio"; + reg = <0x53fcc000 0x4000>; + interrupts = <52>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + gpio2: gpio@53fd0000 { + compatible = "fsl,imx25-gpio", "fsl,imx35-gpio"; + reg = <0x53fd0000 0x4000>; + interrupts = <51>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + sdma@53fd4000 { + compatible = "fsl,imx25-sdma", "fsl,imx35-sdma"; + reg = <0x53fd4000 0x4000>; + clocks = <&clks 112>, <&clks 68>; + clock-names = "ipg", "ahb"; + interrupts = <34>; + }; + + wdog@53fdc000 { + compatible = "fsl,imx25-wdt", "fsl,imx21-wdt"; + reg = <0x53fdc000 0x4000>; + clocks = <&clks 126>; + clock-names = ""; + interrupts = <55>; + }; + + pwm1: pwm@53fe0000 { + compatible = "fsl,imx25-pwm", "fsl,imx27-pwm"; + #pwm-cells = <2>; + reg = <0x53fe0000 0x4000>; + clocks = <&clks 105>, <&clks 36>; + clock-names = "ipg", "per"; + interrupts = <26>; + }; + + usbphy1: usbphy@1 { + compatible = "nop-usbphy"; + status = "disabled"; + }; + + usbphy2: usbphy@2 { + compatible = "nop-usbphy"; + status = "disabled"; + }; + + usbotg: usb@53ff4000 { + compatible = "fsl,imx25-usb", "fsl,imx27-usb"; + reg = <0x53ff4000 0x0200>; + interrupts = <37>; + clocks = <&clks 9>, <&clks 70>, <&clks 8>; + clock-names = "ipg", "ahb", "per"; + fsl,usbmisc = <&usbmisc 0>; + status = "disabled"; + }; + + usbhost1: usb@53ff4400 { + compatible = "fsl,imx25-usb", "fsl,imx27-usb"; + reg = <0x53ff4400 0x0200>; + interrupts = <35>; + clocks = <&clks 9>, <&clks 70>, <&clks 8>; + clock-names = "ipg", "ahb", "per"; + fsl,usbmisc = <&usbmisc 1>; + status = "disabled"; + }; + + usbmisc: usbmisc@53ff4600 { + #index-cells = <1>; + compatible = "fsl,imx25-usbmisc"; + clocks = <&clks 9>, <&clks 70>, <&clks 8>; + clock-names = "ipg", "ahb", "per"; + reg = <0x53ff4600 0x00f>; + status = "disabled"; + }; + + dryice@53ffc000 { + compatible = "fsl,imx25-dryice", "fsl,imx25-rtc"; + reg = <0x53ffc000 0x4000>; + clocks = <&clks 81>; + clock-names = "ipg"; + interrupts = <25>; + }; + }; + + emi@80000000 { + compatible = "fsl,emi-bus", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x80000000 0x3b002000>; + ranges; + + nand@bb000000 { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "fsl,imx25-nand"; + reg = <0xbb000000 0x2000>; + clocks = <&clks 50>; + clock-names = ""; + interrupts = <33>; + status = "disabled"; + }; + }; + }; +}; -- cgit v1.2.3 From dea05fe66edb4540a53ed6623794c4978315256a Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 12 Nov 2012 17:58:28 +0800 Subject: regulator: gpio-regulator: doc: Fix wrong key gpio-enable Wrong description in binding document. Doc use "gpio-enable", but code use "enable-gpio" In drivers/regulator/gpio-regulator.c config->enable_gpio = of_get_named_gpio(np, "enable-gpio", 0); Signed-off-by: Frank Li Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/regulator/gpio-regulator.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/regulator/gpio-regulator.txt b/Documentation/devicetree/bindings/regulator/gpio-regulator.txt index 3703be2ab821..f71f8083c2ae 100644 --- a/Documentation/devicetree/bindings/regulator/gpio-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/gpio-regulator.txt @@ -4,7 +4,7 @@ Required properties: - compatible : Must be "regulator-gpio". Optional properties: -- gpio-enable : GPIO to use to enable/disable the regulator. +- enable-gpio : GPIO to use to enable/disable the regulator. - gpios : GPIO group used to control voltage. - states : Selection of available voltages and GPIO configs. - startup-delay-us : Startup time in microseconds. @@ -23,7 +23,7 @@ Example: regulator-max-microvolt = <2600000>; regulator-boot-on; - gpio-enable = <&gpio0 23 0x4>; + enable-gpio = <&gpio0 23 0x4>; gpios = <&gpio0 24 0x4 &gpio0 25 0x4>; states = <1800000 0x3 -- cgit v1.2.3 From 8528547bcc33622176a27963dc8a2513d116b832 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 14 Nov 2012 05:54:47 +0530 Subject: spi: tegra: add spi driver for sflash controller NVIDIA's Tegra20 have the SPI (SFLASH) controller to interface with spi flash device which is used for system boot. Add the spi driver for this controller. Signed-off-by: Laxman Dewangan Acked-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Mark Brown --- .../bindings/spi/nvidia,tegra20-sflash.txt | 26 + drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/spi-tegra20-sflash.c | 665 +++++++++++++++++++++ 4 files changed, 700 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/nvidia,tegra20-sflash.txt create mode 100644 drivers/spi/spi-tegra20-sflash.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/spi/nvidia,tegra20-sflash.txt b/Documentation/devicetree/bindings/spi/nvidia,tegra20-sflash.txt new file mode 100644 index 000000000000..8cf24f6f0a99 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/nvidia,tegra20-sflash.txt @@ -0,0 +1,26 @@ +NVIDIA Tegra20 SFLASH controller. + +Required properties: +- compatible : should be "nvidia,tegra20-sflash". +- reg: Should contain SFLASH registers location and length. +- interrupts: Should contain SFLASH interrupts. +- nvidia,dma-request-selector : The Tegra DMA controller's phandle and + request selector for this SFLASH controller. + +Recommended properties: +- spi-max-frequency: Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt + +Example: + +spi@7000d600 { + compatible = "nvidia,tegra20-sflash"; + reg = <0x7000c380 0x80>; + interrupts = <0 39 0x04>; + nvidia,dma-request-selector = <&apbdma 16>; + spi-max-frequency = <25000000>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; +}; + diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 25290d9780b2..3cbc9fbe6d10 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -385,6 +385,14 @@ config SPI_MXS help SPI driver for Freescale MXS devices. +config SPI_TEGRA20_SFLASH + tristate "Nvidia Tegra20 Serial flash Controller" + depends on ARCH_TEGRA + help + SPI driver for Nvidia Tegra20 Serial flash Controller interface. + The main usecase of this controller is to use spi flash as boot + device. + config SPI_TEGRA20_SLINK tristate "Nvidia Tegra20/Tegra30 SLINK Controller" depends on ARCH_TEGRA && TEGRA20_APB_DMA diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index f87c0f142e5a..9f6b3d84545b 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o obj-$(CONFIG_SPI_SIRF) += spi-sirf.o obj-$(CONFIG_SPI_STMP3XXX) += spi-stmp.o +obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c new file mode 100644 index 000000000000..54eb9488fa5a --- /dev/null +++ b/drivers/spi/spi-tegra20-sflash.c @@ -0,0 +1,665 @@ +/* + * SPI driver for Nvidia's Tegra20 Serial Flash Controller. + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * Author: Laxman Dewangan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPI_COMMAND 0x000 +#define SPI_GO BIT(30) +#define SPI_M_S BIT(28) +#define SPI_ACTIVE_SCLK_MASK (0x3 << 26) +#define SPI_ACTIVE_SCLK_DRIVE_LOW (0 << 26) +#define SPI_ACTIVE_SCLK_DRIVE_HIGH (1 << 26) +#define SPI_ACTIVE_SCLK_PULL_LOW (2 << 26) +#define SPI_ACTIVE_SCLK_PULL_HIGH (3 << 26) + +#define SPI_CK_SDA_FALLING (1 << 21) +#define SPI_CK_SDA_RISING (0 << 21) +#define SPI_CK_SDA_MASK (1 << 21) +#define SPI_ACTIVE_SDA (0x3 << 18) +#define SPI_ACTIVE_SDA_DRIVE_LOW (0 << 18) +#define SPI_ACTIVE_SDA_DRIVE_HIGH (1 << 18) +#define SPI_ACTIVE_SDA_PULL_LOW (2 << 18) +#define SPI_ACTIVE_SDA_PULL_HIGH (3 << 18) + +#define SPI_CS_POL_INVERT BIT(16) +#define SPI_TX_EN BIT(15) +#define SPI_RX_EN BIT(14) +#define SPI_CS_VAL_HIGH BIT(13) +#define SPI_CS_VAL_LOW 0x0 +#define SPI_CS_SW BIT(12) +#define SPI_CS_HW 0x0 +#define SPI_CS_DELAY_MASK (7 << 9) +#define SPI_CS3_EN BIT(8) +#define SPI_CS2_EN BIT(7) +#define SPI_CS1_EN BIT(6) +#define SPI_CS0_EN BIT(5) + +#define SPI_CS_MASK (SPI_CS3_EN | SPI_CS2_EN | \ + SPI_CS1_EN | SPI_CS0_EN) +#define SPI_BIT_LENGTH(x) (((x) & 0x1f) << 0) + +#define SPI_MODES (SPI_ACTIVE_SCLK_MASK | SPI_CK_SDA_MASK) + +#define SPI_STATUS 0x004 +#define SPI_BSY BIT(31) +#define SPI_RDY BIT(30) +#define SPI_TXF_FLUSH BIT(29) +#define SPI_RXF_FLUSH BIT(28) +#define SPI_RX_UNF BIT(27) +#define SPI_TX_OVF BIT(26) +#define SPI_RXF_EMPTY BIT(25) +#define SPI_RXF_FULL BIT(24) +#define SPI_TXF_EMPTY BIT(23) +#define SPI_TXF_FULL BIT(22) +#define SPI_BLK_CNT(count) (((count) & 0xffff) + 1) + +#define SPI_FIFO_ERROR (SPI_RX_UNF | SPI_TX_OVF) +#define SPI_FIFO_EMPTY (SPI_TX_EMPTY | SPI_RX_EMPTY) + +#define SPI_RX_CMP 0x8 +#define SPI_DMA_CTL 0x0C +#define SPI_DMA_EN BIT(31) +#define SPI_IE_RXC BIT(27) +#define SPI_IE_TXC BIT(26) +#define SPI_PACKED BIT(20) +#define SPI_RX_TRIG_MASK (0x3 << 18) +#define SPI_RX_TRIG_1W (0x0 << 18) +#define SPI_RX_TRIG_4W (0x1 << 18) +#define SPI_TX_TRIG_MASK (0x3 << 16) +#define SPI_TX_TRIG_1W (0x0 << 16) +#define SPI_TX_TRIG_4W (0x1 << 16) +#define SPI_DMA_BLK_COUNT(count) (((count) - 1) & 0xFFFF); + +#define SPI_TX_FIFO 0x10 +#define SPI_RX_FIFO 0x20 + +#define DATA_DIR_TX (1 << 0) +#define DATA_DIR_RX (1 << 1) + +#define MAX_CHIP_SELECT 4 +#define SPI_FIFO_DEPTH 4 +#define SPI_DMA_TIMEOUT (msecs_to_jiffies(1000)) + +struct tegra_sflash_data { + struct device *dev; + struct spi_master *master; + spinlock_t lock; + + struct clk *clk; + void __iomem *base; + unsigned irq; + u32 spi_max_frequency; + u32 cur_speed; + + struct spi_device *cur_spi; + unsigned cur_pos; + unsigned cur_len; + unsigned bytes_per_word; + unsigned cur_direction; + unsigned curr_xfer_words; + + unsigned cur_rx_pos; + unsigned cur_tx_pos; + + u32 tx_status; + u32 rx_status; + u32 status_reg; + + u32 def_command_reg; + u32 command_reg; + u32 dma_control_reg; + + struct completion xfer_completion; + struct spi_transfer *curr_xfer; +}; + +static int tegra_sflash_runtime_suspend(struct device *dev); +static int tegra_sflash_runtime_resume(struct device *dev); + +static inline unsigned long tegra_sflash_readl(struct tegra_sflash_data *tsd, + unsigned long reg) +{ + return readl(tsd->base + reg); +} + +static inline void tegra_sflash_writel(struct tegra_sflash_data *tsd, + unsigned long val, unsigned long reg) +{ + writel(val, tsd->base + reg); +} + +static void tegra_sflash_clear_status(struct tegra_sflash_data *tsd) +{ + /* Write 1 to clear status register */ + tegra_sflash_writel(tsd, SPI_RDY | SPI_FIFO_ERROR, SPI_STATUS); +} + +static unsigned tegra_sflash_calculate_curr_xfer_param( + struct spi_device *spi, struct tegra_sflash_data *tsd, + struct spi_transfer *t) +{ + unsigned remain_len = t->len - tsd->cur_pos; + unsigned max_word; + + tsd->bytes_per_word = (t->bits_per_word - 1) / 8 + 1; + max_word = remain_len / tsd->bytes_per_word; + if (max_word > SPI_FIFO_DEPTH) + max_word = SPI_FIFO_DEPTH; + tsd->curr_xfer_words = max_word; + return max_word; +} + +static unsigned tegra_sflash_fill_tx_fifo_from_client_txbuf( + struct tegra_sflash_data *tsd, struct spi_transfer *t) +{ + unsigned nbytes; + unsigned long status; + unsigned max_n_32bit = tsd->curr_xfer_words; + u8 *tx_buf = (u8 *)t->tx_buf + tsd->cur_tx_pos; + + if (max_n_32bit > SPI_FIFO_DEPTH) + max_n_32bit = SPI_FIFO_DEPTH; + nbytes = max_n_32bit * tsd->bytes_per_word; + + status = tegra_sflash_readl(tsd, SPI_STATUS); + while (!(status & SPI_TXF_FULL)) { + int i; + unsigned int x = 0; + + for (i = 0; nbytes && (i < tsd->bytes_per_word); + i++, nbytes--) + x |= ((*tx_buf++) << i*8); + tegra_sflash_writel(tsd, x, SPI_TX_FIFO); + if (!nbytes) + break; + + status = tegra_sflash_readl(tsd, SPI_STATUS); + } + tsd->cur_tx_pos += max_n_32bit * tsd->bytes_per_word; + return max_n_32bit; +} + +static int tegra_sflash_read_rx_fifo_to_client_rxbuf( + struct tegra_sflash_data *tsd, struct spi_transfer *t) +{ + unsigned long status; + unsigned int read_words = 0; + u8 *rx_buf = (u8 *)t->rx_buf + tsd->cur_rx_pos; + + status = tegra_sflash_readl(tsd, SPI_STATUS); + while (!(status & SPI_RXF_EMPTY)) { + int i; + unsigned long x; + + x = tegra_sflash_readl(tsd, SPI_RX_FIFO); + for (i = 0; (i < tsd->bytes_per_word); i++) + *rx_buf++ = (x >> (i*8)) & 0xFF; + read_words++; + status = tegra_sflash_readl(tsd, SPI_STATUS); + } + tsd->cur_rx_pos += read_words * tsd->bytes_per_word; + return 0; +} + +static int tegra_sflash_start_cpu_based_transfer( + struct tegra_sflash_data *tsd, struct spi_transfer *t) +{ + unsigned long val = 0; + unsigned cur_words; + + if (tsd->cur_direction & DATA_DIR_TX) + val |= SPI_IE_TXC; + + if (tsd->cur_direction & DATA_DIR_RX) + val |= SPI_IE_RXC; + + tegra_sflash_writel(tsd, val, SPI_DMA_CTL); + tsd->dma_control_reg = val; + + if (tsd->cur_direction & DATA_DIR_TX) + cur_words = tegra_sflash_fill_tx_fifo_from_client_txbuf(tsd, t); + else + cur_words = tsd->curr_xfer_words; + val |= SPI_DMA_BLK_COUNT(cur_words); + tegra_sflash_writel(tsd, val, SPI_DMA_CTL); + tsd->dma_control_reg = val; + val |= SPI_DMA_EN; + tegra_sflash_writel(tsd, val, SPI_DMA_CTL); + return 0; +} + +static int tegra_sflash_start_transfer_one(struct spi_device *spi, + struct spi_transfer *t, bool is_first_of_msg, + bool is_single_xfer) +{ + struct tegra_sflash_data *tsd = spi_master_get_devdata(spi->master); + u32 speed; + unsigned long command; + + speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz; + if (!speed) + speed = tsd->spi_max_frequency; + if (speed != tsd->cur_speed) { + clk_set_rate(tsd->clk, speed); + tsd->cur_speed = speed; + } + + tsd->cur_spi = spi; + tsd->cur_pos = 0; + tsd->cur_rx_pos = 0; + tsd->cur_tx_pos = 0; + tsd->curr_xfer = t; + tegra_sflash_calculate_curr_xfer_param(spi, tsd, t); + if (is_first_of_msg) { + command = tsd->def_command_reg; + command |= SPI_BIT_LENGTH(t->bits_per_word - 1); + command |= SPI_CS_VAL_HIGH; + + command &= ~SPI_MODES; + if (spi->mode & SPI_CPHA) + command |= SPI_CK_SDA_FALLING; + + if (spi->mode & SPI_CPOL) + command |= SPI_ACTIVE_SCLK_DRIVE_HIGH; + else + command |= SPI_ACTIVE_SCLK_DRIVE_LOW; + command |= SPI_CS0_EN << spi->chip_select; + } else { + command = tsd->command_reg; + command &= ~SPI_BIT_LENGTH(~0); + command |= SPI_BIT_LENGTH(t->bits_per_word - 1); + command &= ~(SPI_RX_EN | SPI_TX_EN); + } + + tsd->cur_direction = 0; + if (t->rx_buf) { + command |= SPI_RX_EN; + tsd->cur_direction |= DATA_DIR_RX; + } + if (t->tx_buf) { + command |= SPI_TX_EN; + tsd->cur_direction |= DATA_DIR_TX; + } + tegra_sflash_writel(tsd, command, SPI_COMMAND); + tsd->command_reg = command; + + return tegra_sflash_start_cpu_based_transfer(tsd, t); +} + +static int tegra_sflash_transfer_one_message(struct spi_master *master, + struct spi_message *msg) +{ + bool is_first_msg = true; + int single_xfer; + struct tegra_sflash_data *tsd = spi_master_get_devdata(master); + struct spi_transfer *xfer; + struct spi_device *spi = msg->spi; + int ret; + + ret = pm_runtime_get_sync(tsd->dev); + if (ret < 0) { + dev_err(tsd->dev, "pm_runtime_get() failed, err = %d\n", ret); + return ret; + } + + msg->status = 0; + msg->actual_length = 0; + single_xfer = list_is_singular(&msg->transfers); + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + INIT_COMPLETION(tsd->xfer_completion); + ret = tegra_sflash_start_transfer_one(spi, xfer, + is_first_msg, single_xfer); + if (ret < 0) { + dev_err(tsd->dev, + "spi can not start transfer, err %d\n", ret); + goto exit; + } + is_first_msg = false; + ret = wait_for_completion_timeout(&tsd->xfer_completion, + SPI_DMA_TIMEOUT); + if (WARN_ON(ret == 0)) { + dev_err(tsd->dev, + "spi trasfer timeout, err %d\n", ret); + ret = -EIO; + goto exit; + } + + if (tsd->tx_status || tsd->rx_status) { + dev_err(tsd->dev, "Error in Transfer\n"); + ret = -EIO; + goto exit; + } + msg->actual_length += xfer->len; + if (xfer->cs_change && xfer->delay_usecs) { + tegra_sflash_writel(tsd, tsd->def_command_reg, + SPI_COMMAND); + udelay(xfer->delay_usecs); + } + } + ret = 0; +exit: + tegra_sflash_writel(tsd, tsd->def_command_reg, SPI_COMMAND); + msg->status = ret; + spi_finalize_current_message(master); + pm_runtime_put(tsd->dev); + return ret; +} + +static irqreturn_t handle_cpu_based_xfer(struct tegra_sflash_data *tsd) +{ + struct spi_transfer *t = tsd->curr_xfer; + unsigned long flags; + + spin_lock_irqsave(&tsd->lock, flags); + if (tsd->tx_status || tsd->rx_status || (tsd->status_reg & SPI_BSY)) { + dev_err(tsd->dev, + "CpuXfer ERROR bit set 0x%x\n", tsd->status_reg); + dev_err(tsd->dev, + "CpuXfer 0x%08x:0x%08x\n", tsd->command_reg, + tsd->dma_control_reg); + tegra_periph_reset_assert(tsd->clk); + udelay(2); + tegra_periph_reset_deassert(tsd->clk); + complete(&tsd->xfer_completion); + goto exit; + } + + if (tsd->cur_direction & DATA_DIR_RX) + tegra_sflash_read_rx_fifo_to_client_rxbuf(tsd, t); + + if (tsd->cur_direction & DATA_DIR_TX) + tsd->cur_pos = tsd->cur_tx_pos; + else + tsd->cur_pos = tsd->cur_rx_pos; + + if (tsd->cur_pos == t->len) { + complete(&tsd->xfer_completion); + goto exit; + } + + tegra_sflash_calculate_curr_xfer_param(tsd->cur_spi, tsd, t); + tegra_sflash_start_cpu_based_transfer(tsd, t); +exit: + spin_unlock_irqrestore(&tsd->lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t tegra_sflash_isr(int irq, void *context_data) +{ + struct tegra_sflash_data *tsd = context_data; + + tsd->status_reg = tegra_sflash_readl(tsd, SPI_STATUS); + if (tsd->cur_direction & DATA_DIR_TX) + tsd->tx_status = tsd->status_reg & SPI_TX_OVF; + + if (tsd->cur_direction & DATA_DIR_RX) + tsd->rx_status = tsd->status_reg & SPI_RX_UNF; + tegra_sflash_clear_status(tsd); + + return handle_cpu_based_xfer(tsd); +} + +static struct tegra_spi_platform_data *tegra_sflash_parse_dt( + struct platform_device *pdev) +{ + struct tegra_spi_platform_data *pdata; + struct device_node *np = pdev->dev.of_node; + u32 max_freq; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(&pdev->dev, "Memory alloc for pdata failed\n"); + return NULL; + } + + if (!of_property_read_u32(np, "spi-max-frequency", &max_freq)) + pdata->spi_max_frequency = max_freq; + + return pdata; +} + +static struct of_device_id tegra_sflash_of_match[] __devinitconst = { + { .compatible = "nvidia,tegra20-sflash", }, + {} +}; +MODULE_DEVICE_TABLE(of, tegra_sflash_of_match); + +static int __devinit tegra_sflash_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct tegra_sflash_data *tsd; + struct resource *r; + struct tegra_spi_platform_data *pdata = pdev->dev.platform_data; + int ret; + const struct of_device_id *match; + + match = of_match_device(of_match_ptr(tegra_sflash_of_match), + &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + + if (!pdata && pdev->dev.of_node) + pdata = tegra_sflash_parse_dt(pdev); + + if (!pdata) { + dev_err(&pdev->dev, "No platform data, exiting\n"); + return -ENODEV; + } + + if (!pdata->spi_max_frequency) + pdata->spi_max_frequency = 25000000; /* 25MHz */ + + master = spi_alloc_master(&pdev->dev, sizeof(*tsd)); + if (!master) { + dev_err(&pdev->dev, "master allocation failed\n"); + return -ENOMEM; + } + + /* the spi->mode bits understood by this driver: */ + master->mode_bits = SPI_CPOL | SPI_CPHA; + master->transfer_one_message = tegra_sflash_transfer_one_message; + master->num_chipselect = MAX_CHIP_SELECT; + master->bus_num = -1; + + dev_set_drvdata(&pdev->dev, master); + tsd = spi_master_get_devdata(master); + tsd->master = master; + tsd->dev = &pdev->dev; + spin_lock_init(&tsd->lock); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "No IO memory resource\n"); + ret = -ENODEV; + goto exit_free_master; + } + tsd->base = devm_request_and_ioremap(&pdev->dev, r); + if (!tsd->base) { + dev_err(&pdev->dev, + "Cannot request memregion/iomap dma address\n"); + ret = -EADDRNOTAVAIL; + goto exit_free_master; + } + + tsd->irq = platform_get_irq(pdev, 0); + ret = request_irq(tsd->irq, tegra_sflash_isr, 0, + dev_name(&pdev->dev), tsd); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n", + tsd->irq); + goto exit_free_master; + } + + tsd->clk = devm_clk_get(&pdev->dev, "spi"); + if (IS_ERR(tsd->clk)) { + dev_err(&pdev->dev, "can not get clock\n"); + ret = PTR_ERR(tsd->clk); + goto exit_free_irq; + } + + tsd->spi_max_frequency = pdata->spi_max_frequency; + init_completion(&tsd->xfer_completion); + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = tegra_sflash_runtime_resume(&pdev->dev); + if (ret) + goto exit_pm_disable; + } + + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret); + goto exit_pm_disable; + } + + /* Reset controller */ + tegra_periph_reset_assert(tsd->clk); + udelay(2); + tegra_periph_reset_deassert(tsd->clk); + + tsd->def_command_reg = SPI_M_S | SPI_CS_SW; + tegra_sflash_writel(tsd, tsd->def_command_reg, SPI_COMMAND); + pm_runtime_put(&pdev->dev); + + master->dev.of_node = pdev->dev.of_node; + ret = spi_register_master(master); + if (ret < 0) { + dev_err(&pdev->dev, "can not register to master err %d\n", ret); + goto exit_pm_disable; + } + return ret; + +exit_pm_disable: + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra_sflash_runtime_suspend(&pdev->dev); +exit_free_irq: + free_irq(tsd->irq, tsd); +exit_free_master: + spi_master_put(master); + return ret; +} + +static int __devexit tegra_sflash_remove(struct platform_device *pdev) +{ + struct spi_master *master = dev_get_drvdata(&pdev->dev); + struct tegra_sflash_data *tsd = spi_master_get_devdata(master); + + free_irq(tsd->irq, tsd); + spi_unregister_master(master); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra_sflash_runtime_suspend(&pdev->dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tegra_sflash_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + + return spi_master_suspend(master); +} + +static int tegra_sflash_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct tegra_sflash_data *tsd = spi_master_get_devdata(master); + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "pm runtime failed, e = %d\n", ret); + return ret; + } + tegra_sflash_writel(tsd, tsd->command_reg, SPI_COMMAND); + pm_runtime_put(dev); + + return spi_master_resume(master); +} +#endif + +static int tegra_sflash_runtime_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct tegra_sflash_data *tsd = spi_master_get_devdata(master); + + /* Flush all write which are in PPSB queue by reading back */ + tegra_sflash_readl(tsd, SPI_COMMAND); + + clk_disable_unprepare(tsd->clk); + return 0; +} + +static int tegra_sflash_runtime_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct tegra_sflash_data *tsd = spi_master_get_devdata(master); + int ret; + + ret = clk_prepare_enable(tsd->clk); + if (ret < 0) { + dev_err(tsd->dev, "clk_prepare failed: %d\n", ret); + return ret; + } + return 0; +} + +static const struct dev_pm_ops slink_pm_ops = { + SET_RUNTIME_PM_OPS(tegra_sflash_runtime_suspend, + tegra_sflash_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(tegra_sflash_suspend, tegra_sflash_resume) +}; +static struct platform_driver tegra_sflash_driver = { + .driver = { + .name = "spi-tegra-sflash", + .owner = THIS_MODULE, + .pm = &slink_pm_ops, + .of_match_table = of_match_ptr(tegra_sflash_of_match), + }, + .probe = tegra_sflash_probe, + .remove = __devexit_p(tegra_sflash_remove), +}; +module_platform_driver(tegra_sflash_driver); + +MODULE_ALIAS("platform:spi-tegra-sflash"); +MODULE_DESCRIPTION("NVIDIA Tegra20 Serial Flash Controller Driver"); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 2cd451792db174382aaca96c75bc3bf47a6065fe Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 14 Nov 2012 11:14:26 +0800 Subject: spi/omap: fix D0/D1 direction confusion 0384e90b8 ("spi/mcspi: allow configuration of pin directions") did what it claimed to do the wrong way around. D0/D1 is configured as output by *clearing* the bits in the conf registers, hence also breaking the former default behaviour. Fix this before that change is merged to mainline. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/omap-spi.txt | 6 +++--- drivers/spi/spi-omap2-mcspi.c | 6 +++--- include/linux/platform_data/spi-omap2-mcspi.h | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/spi/omap-spi.txt b/Documentation/devicetree/bindings/spi/omap-spi.txt index 2ef0a6b85653..938809c6829b 100644 --- a/Documentation/devicetree/bindings/spi/omap-spi.txt +++ b/Documentation/devicetree/bindings/spi/omap-spi.txt @@ -6,9 +6,9 @@ Required properties: - "ti,omap4-spi" for OMAP4+. - ti,spi-num-cs : Number of chipselect supported by the instance. - ti,hwmods: Name of the hwmod associated to the McSPI -- ti,pindir-d0-in-d1-out: Select the D0 pin as input and D1 as - output. The default is D0 as output and - D1 as input. +- ti,pindir-d0-out-d1-in: Select the D0 pin as output and D1 as + input. The default is D0 as input and + D1 as output. Example: diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 51046332677c..89f73c425c3d 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -766,7 +766,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS * REVISIT: this controller could support SPI_3WIRE mode. */ - if (mcspi->pin_dir == MCSPI_PINDIR_D0_OUT_D1_IN) { + if (mcspi->pin_dir == MCSPI_PINDIR_D0_IN_D1_OUT) { l &= ~OMAP2_MCSPI_CHCONF_IS; l &= ~OMAP2_MCSPI_CHCONF_DPE1; l |= OMAP2_MCSPI_CHCONF_DPE0; @@ -1188,8 +1188,8 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev) of_property_read_u32(node, "ti,spi-num-cs", &num_cs); master->num_chipselect = num_cs; master->bus_num = bus_num++; - if (of_get_property(node, "ti,pindir-d0-in-d1-out", NULL)) - mcspi->pin_dir = MCSPI_PINDIR_D0_IN_D1_OUT; + if (of_get_property(node, "ti,pindir-d0-out-d1-in", NULL)) + mcspi->pin_dir = MCSPI_PINDIR_D0_OUT_D1_IN; } else { pdata = pdev->dev.platform_data; master->num_chipselect = pdata->num_cs; diff --git a/include/linux/platform_data/spi-omap2-mcspi.h b/include/linux/platform_data/spi-omap2-mcspi.h index ce70f7b5a8e1..a65572d53211 100644 --- a/include/linux/platform_data/spi-omap2-mcspi.h +++ b/include/linux/platform_data/spi-omap2-mcspi.h @@ -7,8 +7,8 @@ #define OMAP4_MCSPI_REG_OFFSET 0x100 -#define MCSPI_PINDIR_D0_OUT_D1_IN 0 -#define MCSPI_PINDIR_D0_IN_D1_OUT 1 +#define MCSPI_PINDIR_D0_IN_D1_OUT 0 +#define MCSPI_PINDIR_D0_OUT_D1_IN 1 struct omap2_mcspi_platform_config { unsigned short num_cs; -- cgit v1.2.3 From 216f2b9c95ac6e4f00b08df807bc4454434a9afb Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 14 Nov 2012 11:51:36 +0000 Subject: regulator: gpio-regulator: Catch 'no states property' misuse A selection of voltage or current values (AKA states) should always be specified when using a GPIO regulator. If there are no switchable states then the fixed regulators should be used instead. Signed-off-by: Lee Jones Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/regulator/gpio-regulator.txt | 3 ++- drivers/regulator/gpio-regulator.c | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/regulator/gpio-regulator.txt b/Documentation/devicetree/bindings/regulator/gpio-regulator.txt index f71f8083c2ae..63c659800c03 100644 --- a/Documentation/devicetree/bindings/regulator/gpio-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/gpio-regulator.txt @@ -2,11 +2,12 @@ GPIO controlled regulators Required properties: - compatible : Must be "regulator-gpio". +- states : Selection of available voltages and GPIO configs. + if there are no states, then use a fixed regulator Optional properties: - enable-gpio : GPIO to use to enable/disable the regulator. - gpios : GPIO group used to control voltage. -- states : Selection of available voltages and GPIO configs. - startup-delay-us : Startup time in microseconds. - enable-active-high : Polarity of GPIO is active high (default is low). diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index faa2f712eb0d..dc9260545b80 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -183,6 +183,11 @@ of_get_gpio_regulator_config(struct device *dev, struct device_node *np) /* Fetch states. */ prop = of_find_property(np, "states", NULL); + if (!prop) { + dev_err(dev, "No 'states' property found\n"); + return ERR_PTR(-EINVAL); + } + proplen = prop->length / sizeof(int); config->states = devm_kzalloc(dev, -- cgit v1.2.3 From 45aa2c27ada4829bc91bd80455bc67059ac06bc5 Mon Sep 17 00:00:00 2001 From: Josh Cartwright Date: Tue, 13 Nov 2012 17:26:48 -0600 Subject: clk: Add support for fundamental zynq clks Provide simplified models for the necessary clocks on the zynq-7000 platform. Currently, the PLLs, the CPU clock network, and the basic peripheral clock networks (for SDIO, SMC, SPI, QSPI, UART) are modelled. OF bindings are also provided and documented. Signed-off-by: Josh Cartwright Signed-off-by: Soren Brinkmann Acked-by: Mike Turquette Acked-by: Michal Simek --- .../devicetree/bindings/clock/zynq-7000.txt | 55 +++ drivers/clk/clk-zynq.c | 383 +++++++++++++++++++++ include/linux/clk/zynq.h | 24 ++ 3 files changed, 462 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/zynq-7000.txt create mode 100644 drivers/clk/clk-zynq.c create mode 100644 include/linux/clk/zynq.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/clock/zynq-7000.txt b/Documentation/devicetree/bindings/clock/zynq-7000.txt new file mode 100644 index 000000000000..23ae1db1bc13 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/zynq-7000.txt @@ -0,0 +1,55 @@ +Device Tree Clock bindings for the Zynq 7000 EPP + +The Zynq EPP has several different clk providers, each with there own bindings. +The purpose of this document is to document their usage. + +See clock_bindings.txt for more information on the generic clock bindings. +See Chapter 25 of Zynq TRM for more information about Zynq clocks. + +== PLLs == + +Used to describe the ARM_PLL, DDR_PLL, and IO_PLL. + +Required properties: +- #clock-cells : shall be 0 (only one clock is output from this node) +- compatible : "xlnx,zynq-pll" +- reg : pair of u32 values, which are the address offsets within the SLCR + of the relevant PLL_CTRL register and PLL_CFG register respectively +- clocks : phandle for parent clock. should be the phandle for ps_clk + +Optional properties: +- clock-output-names : name of the output clock + +Example: + armpll: armpll { + #clock-cells = <0>; + compatible = "xlnx,zynq-pll"; + clocks = <&ps_clk>; + reg = <0x100 0x110>; + clock-output-names = "armpll"; + }; + +== Peripheral clocks == + +Describes clock node for the SDIO, SMC, SPI, QSPI, and UART clocks. + +Required properties: +- #clock-cells : shall be 1 +- compatible : "xlnx,zynq-periph-clock" +- reg : a single u32 value, describing the offset within the SLCR where + the CLK_CTRL register is found for this peripheral +- clocks : phandle for parent clocks. should hold phandles for + the IO_PLL, ARM_PLL, and DDR_PLL in order +- clock-output-names : names of the output clock(s). For peripherals that have + two output clocks (for example, the UART), two clocks + should be listed. + +Example: + uart_clk: uart_clk { + #clock-cells = <1>; + compatible = "xlnx,zynq-periph-clock"; + clocks = <&iopll &armpll &ddrpll>; + reg = <0x154>; + clock-output-names = "uart0_ref_clk", + "uart1_ref_clk"; + }; diff --git a/drivers/clk/clk-zynq.c b/drivers/clk/clk-zynq.c new file mode 100644 index 000000000000..37a30514fd66 --- /dev/null +++ b/drivers/clk/clk-zynq.c @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2012 National Instruments + * + * Josh Cartwright + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +#include +#include +#include +#include +#include + +static void __iomem *slcr_base; + +struct zynq_pll_clk { + struct clk_hw hw; + void __iomem *pll_ctrl; + void __iomem *pll_cfg; +}; + +#define to_zynq_pll_clk(hw) container_of(hw, struct zynq_pll_clk, hw) + +#define CTRL_PLL_FDIV(x) ((x) >> 12) + +static unsigned long zynq_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct zynq_pll_clk *pll = to_zynq_pll_clk(hw); + return parent_rate * CTRL_PLL_FDIV(ioread32(pll->pll_ctrl)); +} + +static const struct clk_ops zynq_pll_clk_ops = { + .recalc_rate = zynq_pll_recalc_rate, +}; + +static void __init zynq_pll_clk_setup(struct device_node *np) +{ + struct clk_init_data init; + struct zynq_pll_clk *pll; + const char *parent_name; + struct clk *clk; + u32 regs[2]; + int ret; + + ret = of_property_read_u32_array(np, "reg", regs, ARRAY_SIZE(regs)); + if (WARN_ON(ret)) + return; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (WARN_ON(!pll)) + return; + + pll->pll_ctrl = slcr_base + regs[0]; + pll->pll_cfg = slcr_base + regs[1]; + + of_property_read_string(np, "clock-output-names", &init.name); + + init.ops = &zynq_pll_clk_ops; + parent_name = of_clk_get_parent_name(np, 0); + init.parent_names = &parent_name; + init.num_parents = 1; + + pll->hw.init = &init; + + clk = clk_register(NULL, &pll->hw); + if (WARN_ON(IS_ERR(clk))) + return; + + ret = of_clk_add_provider(np, of_clk_src_simple_get, clk); + if (WARN_ON(ret)) + return; +} + +struct zynq_periph_clk { + struct clk_hw hw; + struct clk_onecell_data onecell_data; + struct clk *gates[2]; + void __iomem *clk_ctrl; + spinlock_t clkact_lock; +}; + +#define to_zynq_periph_clk(hw) container_of(hw, struct zynq_periph_clk, hw) + +static const u8 periph_clk_parent_map[] = { + 0, 0, 1, 2 +}; +#define PERIPH_CLK_CTRL_SRC(x) (periph_clk_parent_map[((x) & 0x30) >> 4]) +#define PERIPH_CLK_CTRL_DIV(x) (((x) & 0x3F00) >> 8) + +static unsigned long zynq_periph_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct zynq_periph_clk *periph = to_zynq_periph_clk(hw); + return parent_rate / PERIPH_CLK_CTRL_DIV(ioread32(periph->clk_ctrl)); +} + +static u8 zynq_periph_get_parent(struct clk_hw *hw) +{ + struct zynq_periph_clk *periph = to_zynq_periph_clk(hw); + return PERIPH_CLK_CTRL_SRC(ioread32(periph->clk_ctrl)); +} + +static const struct clk_ops zynq_periph_clk_ops = { + .recalc_rate = zynq_periph_recalc_rate, + .get_parent = zynq_periph_get_parent, +}; + +static void __init zynq_periph_clk_setup(struct device_node *np) +{ + struct zynq_periph_clk *periph; + const char *parent_names[3]; + struct clk_init_data init; + int clk_num = 0, err; + const char *name; + struct clk *clk; + u32 reg; + int i; + + err = of_property_read_u32(np, "reg", ®); + if (WARN_ON(err)) + return; + + periph = kzalloc(sizeof(*periph), GFP_KERNEL); + if (WARN_ON(!periph)) + return; + + periph->clk_ctrl = slcr_base + reg; + spin_lock_init(&periph->clkact_lock); + + init.name = np->name; + init.ops = &zynq_periph_clk_ops; + for (i = 0; i < ARRAY_SIZE(parent_names); i++) + parent_names[i] = of_clk_get_parent_name(np, i); + init.parent_names = parent_names; + init.num_parents = ARRAY_SIZE(parent_names); + + periph->hw.init = &init; + + clk = clk_register(NULL, &periph->hw); + if (WARN_ON(IS_ERR(clk))) + return; + + err = of_clk_add_provider(np, of_clk_src_simple_get, clk); + if (WARN_ON(err)) + return; + + err = of_property_read_string_index(np, "clock-output-names", 0, + &name); + if (WARN_ON(err)) + return; + + periph->gates[0] = clk_register_gate(NULL, name, np->name, 0, + periph->clk_ctrl, 0, 0, + &periph->clkact_lock); + if (WARN_ON(IS_ERR(periph->gates[0]))) + return; + clk_num++; + + /* some periph clks have 2 downstream gates */ + err = of_property_read_string_index(np, "clock-output-names", 1, + &name); + if (err != -ENODATA) { + periph->gates[1] = clk_register_gate(NULL, name, np->name, 0, + periph->clk_ctrl, 1, 0, + &periph->clkact_lock); + if (WARN_ON(IS_ERR(periph->gates[1]))) + return; + clk_num++; + } + + periph->onecell_data.clks = periph->gates; + periph->onecell_data.clk_num = clk_num; + + err = of_clk_add_provider(np, of_clk_src_onecell_get, + &periph->onecell_data); + if (WARN_ON(err)) + return; +} + +/* CPU Clock domain is modelled as a mux with 4 children subclks, whose + * derivative rates depend on CLK_621_TRUE + */ + +struct zynq_cpu_clk { + struct clk_hw hw; + struct clk_onecell_data onecell_data; + struct clk *subclks[4]; + void __iomem *clk_ctrl; + spinlock_t clkact_lock; +}; + +#define to_zynq_cpu_clk(hw) container_of(hw, struct zynq_cpu_clk, hw) + +static const u8 zynq_cpu_clk_parent_map[] = { + 1, 1, 2, 0 +}; +#define CPU_CLK_SRCSEL(x) (zynq_cpu_clk_parent_map[(((x) & 0x30) >> 4)]) +#define CPU_CLK_CTRL_DIV(x) (((x) & 0x3F00) >> 8) + +static u8 zynq_cpu_clk_get_parent(struct clk_hw *hw) +{ + struct zynq_cpu_clk *cpuclk = to_zynq_cpu_clk(hw); + return CPU_CLK_SRCSEL(ioread32(cpuclk->clk_ctrl)); +} + +static unsigned long zynq_cpu_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct zynq_cpu_clk *cpuclk = to_zynq_cpu_clk(hw); + return parent_rate / CPU_CLK_CTRL_DIV(ioread32(cpuclk->clk_ctrl)); +} + +static const struct clk_ops zynq_cpu_clk_ops = { + .get_parent = zynq_cpu_clk_get_parent, + .recalc_rate = zynq_cpu_clk_recalc_rate, +}; + +struct zynq_cpu_subclk { + struct clk_hw hw; + void __iomem *clk_621; + enum { + CPU_SUBCLK_6X4X, + CPU_SUBCLK_3X2X, + CPU_SUBCLK_2X, + CPU_SUBCLK_1X, + } which; +}; + +#define CLK_621_TRUE(x) ((x) & 1) + +#define to_zynq_cpu_subclk(hw) container_of(hw, struct zynq_cpu_subclk, hw); + +static unsigned long zynq_cpu_subclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned long uninitialized_var(rate); + struct zynq_cpu_subclk *subclk; + bool is_621; + + subclk = to_zynq_cpu_subclk(hw) + is_621 = CLK_621_TRUE(ioread32(subclk->clk_621)); + + switch (subclk->which) { + case CPU_SUBCLK_6X4X: + rate = parent_rate; + break; + case CPU_SUBCLK_3X2X: + rate = parent_rate / 2; + break; + case CPU_SUBCLK_2X: + rate = parent_rate / (is_621 ? 3 : 2); + break; + case CPU_SUBCLK_1X: + rate = parent_rate / (is_621 ? 6 : 4); + break; + }; + + return rate; +} + +static const struct clk_ops zynq_cpu_subclk_ops = { + .recalc_rate = zynq_cpu_subclk_recalc_rate, +}; + +static struct clk *zynq_cpu_subclk_setup(struct device_node *np, u8 which, + void __iomem *clk_621) +{ + struct zynq_cpu_subclk *subclk; + struct clk_init_data init; + struct clk *clk; + int err; + + err = of_property_read_string_index(np, "clock-output-names", + which, &init.name); + if (WARN_ON(err)) + goto err_read_output_name; + + subclk = kzalloc(sizeof(*subclk), GFP_KERNEL); + if (!subclk) + goto err_subclk_alloc; + + subclk->clk_621 = clk_621; + subclk->which = which; + + init.ops = &zynq_cpu_subclk_ops; + init.parent_names = &np->name; + init.num_parents = 1; + + subclk->hw.init = &init; + + clk = clk_register(NULL, &subclk->hw); + if (WARN_ON(IS_ERR(clk))) + goto err_clk_register; + + return clk; + +err_clk_register: + kfree(subclk); +err_subclk_alloc: +err_read_output_name: + return ERR_PTR(-EINVAL); +} + +static void __init zynq_cpu_clk_setup(struct device_node *np) +{ + struct zynq_cpu_clk *cpuclk; + const char *parent_names[3]; + struct clk_init_data init; + void __iomem *clk_621; + struct clk *clk; + u32 reg[2]; + int err; + int i; + + err = of_property_read_u32_array(np, "reg", reg, ARRAY_SIZE(reg)); + if (WARN_ON(err)) + return; + + cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL); + if (WARN_ON(!cpuclk)) + return; + + cpuclk->clk_ctrl = slcr_base + reg[0]; + clk_621 = slcr_base + reg[1]; + spin_lock_init(&cpuclk->clkact_lock); + + init.name = np->name; + init.ops = &zynq_cpu_clk_ops; + for (i = 0; i < ARRAY_SIZE(parent_names); i++) + parent_names[i] = of_clk_get_parent_name(np, i); + init.parent_names = parent_names; + init.num_parents = ARRAY_SIZE(parent_names); + + cpuclk->hw.init = &init; + + clk = clk_register(NULL, &cpuclk->hw); + if (WARN_ON(IS_ERR(clk))) + return; + + err = of_clk_add_provider(np, of_clk_src_simple_get, clk); + if (WARN_ON(err)) + return; + + for (i = 0; i < 4; i++) { + cpuclk->subclks[i] = zynq_cpu_subclk_setup(np, i, clk_621); + if (WARN_ON(IS_ERR(cpuclk->subclks[i]))) + return; + } + + cpuclk->onecell_data.clks = cpuclk->subclks; + cpuclk->onecell_data.clk_num = i; + + err = of_clk_add_provider(np, of_clk_src_onecell_get, + &cpuclk->onecell_data); + if (WARN_ON(err)) + return; +} + +static const __initconst struct of_device_id zynq_clk_match[] = { + { .compatible = "fixed-clock", .data = of_fixed_clk_setup, }, + { .compatible = "xlnx,zynq-pll", .data = zynq_pll_clk_setup, }, + { .compatible = "xlnx,zynq-periph-clock", + .data = zynq_periph_clk_setup, }, + { .compatible = "xlnx,zynq-cpu-clock", .data = zynq_cpu_clk_setup, }, + {} +}; + +void __init xilinx_zynq_clocks_init(void __iomem *slcr) +{ + slcr_base = slcr; + of_clk_init(zynq_clk_match); +} diff --git a/include/linux/clk/zynq.h b/include/linux/clk/zynq.h new file mode 100644 index 000000000000..56be7cd9aa8b --- /dev/null +++ b/include/linux/clk/zynq.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2012 National Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __LINUX_CLK_ZYNQ_H_ +#define __LINUX_CLK_ZYNQ_H_ + +void __init xilinx_zynq_clocks_init(void __iomem *slcr); + +#endif -- cgit v1.2.3 From 549985ee9c723fea8fd7759b5046fb8249896d50 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Wed, 14 Nov 2012 09:07:56 +0000 Subject: cpsw: simplify the setup of the register pointers Instead of having a host of different register offsets in the device tree, this patch simplifies the CPSW code by letting the driver set the proper register offsets automatically, based on the CPSW version. Signed-off-by: Richard Cochran Signed-off-by: Mugunthan V N Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/cpsw.txt | 42 +---- drivers/net/ethernet/ti/cpsw.c | 242 +++++++++++-------------- include/linux/platform_data/cpsw.h | 21 +-- 3 files changed, 108 insertions(+), 197 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt index 221460714c56..6ddd0286a9b7 100644 --- a/Documentation/devicetree/bindings/net/cpsw.txt +++ b/Documentation/devicetree/bindings/net/cpsw.txt @@ -9,15 +9,7 @@ Required properties: number - interrupt-parent : The parent interrupt controller - cpdma_channels : Specifies number of channels in CPDMA -- host_port_no : Specifies host port shift -- cpdma_reg_ofs : Specifies CPDMA submodule register offset -- cpdma_sram_ofs : Specifies CPDMA SRAM offset -- ale_reg_ofs : Specifies ALE submodule register offset - ale_entries : Specifies No of entries ALE can hold -- host_port_reg_ofs : Specifies host port register offset -- hw_stats_reg_ofs : Specifies hardware statistics register offset -- cpts_reg_ofs : Specifies the offset of the CPTS registers -- bd_ram_ofs : Specifies internal desciptor RAM offset - bd_ram_size : Specifies internal descriptor RAM size - rx_descs : Specifies number of Rx descriptors - mac_control : Specifies Default MAC control register content @@ -26,8 +18,6 @@ Required properties: - cpts_active_slave : Specifies the slave to use for time stamping - cpts_clock_mult : Numerator to convert input clock ticks into nanoseconds - cpts_clock_shift : Denominator to convert input clock ticks into nanoseconds -- slave_reg_ofs : Specifies slave register offset -- sliver_reg_ofs : Specifies slave sliver register offset - phy_id : Specifies slave phy id - mac-address : Specifies slave MAC address @@ -49,15 +39,7 @@ Examples: interrupts = <55 0x4>; interrupt-parent = <&intc>; cpdma_channels = <8>; - host_port_no = <0>; - cpdma_reg_ofs = <0x800>; - cpdma_sram_ofs = <0xa00>; - ale_reg_ofs = <0xd00>; ale_entries = <1024>; - host_port_reg_ofs = <0x108>; - hw_stats_reg_ofs = <0x900>; - cpts_reg_ofs = <0xc00>; - bd_ram_ofs = <0x2000>; bd_ram_size = <0x2000>; no_bd_ram = <0>; rx_descs = <64>; @@ -67,16 +49,12 @@ Examples: cpts_clock_mult = <0x80000000>; cpts_clock_shift = <29>; cpsw_emac0: slave@0 { - slave_reg_ofs = <0x200>; - sliver_reg_ofs = <0xd80>; - phy_id = "davinci_mdio.16:00"; + phy_id = <&davinci_mdio>, <0>; /* Filled in by U-Boot */ mac-address = [ 00 00 00 00 00 00 ]; }; cpsw_emac1: slave@1 { - slave_reg_ofs = <0x300>; - sliver_reg_ofs = <0xdc0>; - phy_id = "davinci_mdio.16:01"; + phy_id = <&davinci_mdio>, <1>; /* Filled in by U-Boot */ mac-address = [ 00 00 00 00 00 00 ]; }; @@ -87,15 +65,7 @@ Examples: compatible = "ti,cpsw"; ti,hwmods = "cpgmac0"; cpdma_channels = <8>; - host_port_no = <0>; - cpdma_reg_ofs = <0x800>; - cpdma_sram_ofs = <0xa00>; - ale_reg_ofs = <0xd00>; ale_entries = <1024>; - host_port_reg_ofs = <0x108>; - hw_stats_reg_ofs = <0x900>; - cpts_reg_ofs = <0xc00>; - bd_ram_ofs = <0x2000>; bd_ram_size = <0x2000>; no_bd_ram = <0>; rx_descs = <64>; @@ -105,16 +75,12 @@ Examples: cpts_clock_mult = <0x80000000>; cpts_clock_shift = <29>; cpsw_emac0: slave@0 { - slave_reg_ofs = <0x200>; - sliver_reg_ofs = <0xd80>; - phy_id = "davinci_mdio.16:00"; + phy_id = <&davinci_mdio>, <0>; /* Filled in by U-Boot */ mac-address = [ 00 00 00 00 00 00 ]; }; cpsw_emac1: slave@1 { - slave_reg_ofs = <0x300>; - sliver_reg_ofs = <0xdc0>; - phy_id = "davinci_mdio.16:01"; + phy_id = <&davinci_mdio>, <1>; /* Filled in by U-Boot */ mac-address = [ 00 00 00 00 00 00 ]; }; diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 7007abaf8c82..0da9c753c567 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -80,6 +80,29 @@ do { \ #define CPSW_VERSION_1 0x19010a #define CPSW_VERSION_2 0x19010c + +#define HOST_PORT_NUM 0 +#define SLIVER_SIZE 0x40 + +#define CPSW1_HOST_PORT_OFFSET 0x028 +#define CPSW1_SLAVE_OFFSET 0x050 +#define CPSW1_SLAVE_SIZE 0x040 +#define CPSW1_CPDMA_OFFSET 0x100 +#define CPSW1_STATERAM_OFFSET 0x200 +#define CPSW1_CPTS_OFFSET 0x500 +#define CPSW1_ALE_OFFSET 0x600 +#define CPSW1_SLIVER_OFFSET 0x700 + +#define CPSW2_HOST_PORT_OFFSET 0x108 +#define CPSW2_SLAVE_OFFSET 0x200 +#define CPSW2_SLAVE_SIZE 0x100 +#define CPSW2_CPDMA_OFFSET 0x800 +#define CPSW2_STATERAM_OFFSET 0xa00 +#define CPSW2_CPTS_OFFSET 0xc00 +#define CPSW2_ALE_OFFSET 0xd00 +#define CPSW2_SLIVER_OFFSET 0xd80 +#define CPSW2_BD_OFFSET 0x2000 + #define CPDMA_RXTHRESH 0x0c0 #define CPDMA_RXFREE 0x0e0 #define CPDMA_TXHDP 0x00 @@ -87,21 +110,6 @@ do { \ #define CPDMA_TXCP 0x40 #define CPDMA_RXCP 0x60 -#define cpsw_dma_regs(base, offset) \ - (void __iomem *)((base) + (offset)) -#define cpsw_dma_rxthresh(base, offset) \ - (void __iomem *)((base) + (offset) + CPDMA_RXTHRESH) -#define cpsw_dma_rxfree(base, offset) \ - (void __iomem *)((base) + (offset) + CPDMA_RXFREE) -#define cpsw_dma_txhdp(base, offset) \ - (void __iomem *)((base) + (offset) + CPDMA_TXHDP) -#define cpsw_dma_rxhdp(base, offset) \ - (void __iomem *)((base) + (offset) + CPDMA_RXHDP) -#define cpsw_dma_txcp(base, offset) \ - (void __iomem *)((base) + (offset) + CPDMA_TXCP) -#define cpsw_dma_rxcp(base, offset) \ - (void __iomem *)((base) + (offset) + CPDMA_RXCP) - #define CPSW_POLL_WEIGHT 64 #define CPSW_MIN_PACKET_SIZE 60 #define CPSW_MAX_PACKET_SIZE (1500 + 14 + 4 + 4) @@ -629,8 +637,7 @@ static int cpsw_ndo_open(struct net_device *ndev) pm_runtime_get_sync(&priv->pdev->dev); - reg = __raw_readl(&priv->regs->id_ver); - priv->version = reg; + reg = priv->version; dev_info(priv->dev, "initializing cpsw version %d.%d (%d)\n", CPSW_MAJOR_VERSION(reg), CPSW_MINOR_VERSION(reg), @@ -995,15 +1002,16 @@ static const struct ethtool_ops cpsw_ethtool_ops = { .get_ts_info = cpsw_get_ts_info, }; -static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv) +static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv, + u32 slave_reg_ofs, u32 sliver_reg_ofs) { void __iomem *regs = priv->regs; int slave_num = slave->slave_num; struct cpsw_slave_data *data = priv->data.slave_data + slave_num; slave->data = data; - slave->regs = regs + data->slave_reg_ofs; - slave->sliver = regs + data->sliver_reg_ofs; + slave->regs = regs + slave_reg_ofs; + slave->sliver = regs + sliver_reg_ofs; } static int cpsw_probe_dt(struct cpsw_platform_data *data, @@ -1051,8 +1059,6 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, return -EINVAL; } - data->no_bd_ram = of_property_read_bool(node, "no_bd_ram"); - if (of_property_read_u32(node, "cpdma_channels", &prop)) { pr_err("Missing cpdma_channels property in the DT.\n"); ret = -EINVAL; @@ -1060,34 +1066,6 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->channels = prop; - if (of_property_read_u32(node, "host_port_no", &prop)) { - pr_err("Missing host_port_no property in the DT.\n"); - ret = -EINVAL; - goto error_ret; - } - data->host_port_num = prop; - - if (of_property_read_u32(node, "cpdma_reg_ofs", &prop)) { - pr_err("Missing cpdma_reg_ofs property in the DT.\n"); - ret = -EINVAL; - goto error_ret; - } - data->cpdma_reg_ofs = prop; - - if (of_property_read_u32(node, "cpdma_sram_ofs", &prop)) { - pr_err("Missing cpdma_sram_ofs property in the DT.\n"); - ret = -EINVAL; - goto error_ret; - } - data->cpdma_sram_ofs = prop; - - if (of_property_read_u32(node, "ale_reg_ofs", &prop)) { - pr_err("Missing ale_reg_ofs property in the DT.\n"); - ret = -EINVAL; - goto error_ret; - } - data->ale_reg_ofs = prop; - if (of_property_read_u32(node, "ale_entries", &prop)) { pr_err("Missing ale_entries property in the DT.\n"); ret = -EINVAL; @@ -1095,34 +1073,6 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->ale_entries = prop; - if (of_property_read_u32(node, "host_port_reg_ofs", &prop)) { - pr_err("Missing host_port_reg_ofs property in the DT.\n"); - ret = -EINVAL; - goto error_ret; - } - data->host_port_reg_ofs = prop; - - if (of_property_read_u32(node, "hw_stats_reg_ofs", &prop)) { - pr_err("Missing hw_stats_reg_ofs property in the DT.\n"); - ret = -EINVAL; - goto error_ret; - } - data->hw_stats_reg_ofs = prop; - - if (of_property_read_u32(node, "cpts_reg_ofs", &prop)) { - pr_err("Missing cpts_reg_ofs property in the DT.\n"); - ret = -EINVAL; - goto error_ret; - } - data->cpts_reg_ofs = prop; - - if (of_property_read_u32(node, "bd_ram_ofs", &prop)) { - pr_err("Missing bd_ram_ofs property in the DT.\n"); - ret = -EINVAL; - goto error_ret; - } - data->bd_ram_ofs = prop; - if (of_property_read_u32(node, "bd_ram_size", &prop)) { pr_err("Missing bd_ram_size property in the DT.\n"); ret = -EINVAL; @@ -1144,33 +1094,34 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } data->mac_control = prop; + /* + * Populate all the child nodes here... + */ + ret = of_platform_populate(node, NULL, NULL, &pdev->dev); + /* We do not want to force this, as in some cases may not have child */ + if (ret) + pr_warn("Doesn't have any child node\n"); + for_each_node_by_name(slave_node, "slave") { struct cpsw_slave_data *slave_data = data->slave_data + i; - const char *phy_id = NULL; const void *mac_addr = NULL; - - if (of_property_read_string(slave_node, "phy_id", &phy_id)) { + u32 phyid; + int lenp; + const __be32 *parp; + struct device_node *mdio_node; + struct platform_device *mdio; + + parp = of_get_property(slave_node, "phy_id", &lenp); + if ((parp == NULL) && (lenp != (sizeof(void *) * 2))) { pr_err("Missing slave[%d] phy_id property\n", i); ret = -EINVAL; goto error_ret; } - slave_data->phy_id = phy_id; - - if (of_property_read_u32(slave_node, "slave_reg_ofs", &prop)) { - pr_err("Missing slave[%d] slave_reg_ofs property\n", i); - ret = -EINVAL; - goto error_ret; - } - slave_data->slave_reg_ofs = prop; - - if (of_property_read_u32(slave_node, "sliver_reg_ofs", - &prop)) { - pr_err("Missing slave[%d] sliver_reg_ofs property\n", - i); - ret = -EINVAL; - goto error_ret; - } - slave_data->sliver_reg_ofs = prop; + mdio_node = of_find_node_by_phandle(be32_to_cpup(parp)); + phyid = be32_to_cpup(parp+1); + mdio = of_find_device_by_node(mdio_node); + snprintf(slave_data->phy_id, sizeof(slave_data->phy_id), + PHY_ID_FMT, mdio->name, phyid); mac_addr = of_get_mac_address(slave_node); if (mac_addr) @@ -1179,14 +1130,6 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, i++; } - /* - * Populate all the child nodes here... - */ - ret = of_platform_populate(node, NULL, NULL, &pdev->dev); - /* We do not want to force this, as in some cases may not have child */ - if (ret) - pr_warn("Doesn't have any child node\n"); - return 0; error_ret: @@ -1201,8 +1144,9 @@ static int __devinit cpsw_probe(struct platform_device *pdev) struct cpsw_priv *priv; struct cpdma_params dma_params; struct cpsw_ale_params ale_params; - void __iomem *regs; + void __iomem *ss_regs, *wr_regs; struct resource *res; + u32 slave_offset, sliver_offset, slave_size; int ret = 0, i, k = 0; ndev = alloc_etherdev(sizeof(struct cpsw_priv)); @@ -1270,15 +1214,14 @@ static int __devinit cpsw_probe(struct platform_device *pdev) ret = -ENXIO; goto clean_clk_ret; } - regs = ioremap(priv->cpsw_res->start, resource_size(priv->cpsw_res)); - if (!regs) { + ss_regs = ioremap(priv->cpsw_res->start, resource_size(priv->cpsw_res)); + if (!ss_regs) { dev_err(priv->dev, "unable to map i/o region\n"); goto clean_cpsw_iores_ret; } - priv->regs = regs; - priv->host_port = data->host_port_num; - priv->host_port_regs = regs + data->host_port_reg_ofs; - priv->cpts.reg = regs + data->cpts_reg_ofs; + priv->regs = ss_regs; + priv->version = __raw_readl(&priv->regs->id_ver); + priv->host_port = HOST_PORT_NUM; priv->cpsw_wr_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!priv->cpsw_wr_res) { @@ -1292,32 +1235,59 @@ static int __devinit cpsw_probe(struct platform_device *pdev) ret = -ENXIO; goto clean_iomap_ret; } - regs = ioremap(priv->cpsw_wr_res->start, + wr_regs = ioremap(priv->cpsw_wr_res->start, resource_size(priv->cpsw_wr_res)); - if (!regs) { + if (!wr_regs) { dev_err(priv->dev, "unable to map i/o region\n"); goto clean_cpsw_wr_iores_ret; } - priv->wr_regs = regs; - - for_each_slave(priv, cpsw_slave_init, priv); + priv->wr_regs = wr_regs; memset(&dma_params, 0, sizeof(dma_params)); + memset(&ale_params, 0, sizeof(ale_params)); + + switch (priv->version) { + case CPSW_VERSION_1: + priv->host_port_regs = ss_regs + CPSW1_HOST_PORT_OFFSET; + priv->cpts.reg = ss_regs + CPSW1_CPTS_OFFSET; + dma_params.dmaregs = ss_regs + CPSW1_CPDMA_OFFSET; + dma_params.txhdp = ss_regs + CPSW1_STATERAM_OFFSET; + ale_params.ale_regs = ss_regs + CPSW1_ALE_OFFSET; + slave_offset = CPSW1_SLAVE_OFFSET; + slave_size = CPSW1_SLAVE_SIZE; + sliver_offset = CPSW1_SLIVER_OFFSET; + dma_params.desc_mem_phys = 0; + break; + case CPSW_VERSION_2: + priv->host_port_regs = ss_regs + CPSW2_HOST_PORT_OFFSET; + priv->cpts.reg = ss_regs + CPSW2_CPTS_OFFSET; + dma_params.dmaregs = ss_regs + CPSW2_CPDMA_OFFSET; + dma_params.txhdp = ss_regs + CPSW2_STATERAM_OFFSET; + ale_params.ale_regs = ss_regs + CPSW2_ALE_OFFSET; + slave_offset = CPSW2_SLAVE_OFFSET; + slave_size = CPSW2_SLAVE_SIZE; + sliver_offset = CPSW2_SLIVER_OFFSET; + dma_params.desc_mem_phys = + (u32 __force) priv->cpsw_res->start + CPSW2_BD_OFFSET; + break; + default: + dev_err(priv->dev, "unknown version 0x%08x\n", priv->version); + ret = -ENODEV; + goto clean_cpsw_wr_iores_ret; + } + for (i = 0; i < priv->data.slaves; i++) { + struct cpsw_slave *slave = &priv->slaves[i]; + cpsw_slave_init(slave, priv, slave_offset, sliver_offset); + slave_offset += slave_size; + sliver_offset += SLIVER_SIZE; + } + dma_params.dev = &pdev->dev; - dma_params.dmaregs = cpsw_dma_regs((u32)priv->regs, - data->cpdma_reg_ofs); - dma_params.rxthresh = cpsw_dma_rxthresh((u32)priv->regs, - data->cpdma_reg_ofs); - dma_params.rxfree = cpsw_dma_rxfree((u32)priv->regs, - data->cpdma_reg_ofs); - dma_params.txhdp = cpsw_dma_txhdp((u32)priv->regs, - data->cpdma_sram_ofs); - dma_params.rxhdp = cpsw_dma_rxhdp((u32)priv->regs, - data->cpdma_sram_ofs); - dma_params.txcp = cpsw_dma_txcp((u32)priv->regs, - data->cpdma_sram_ofs); - dma_params.rxcp = cpsw_dma_rxcp((u32)priv->regs, - data->cpdma_sram_ofs); + dma_params.rxthresh = dma_params.dmaregs + CPDMA_RXTHRESH; + dma_params.rxfree = dma_params.dmaregs + CPDMA_RXFREE; + dma_params.rxhdp = dma_params.txhdp + CPDMA_RXHDP; + dma_params.txcp = dma_params.txhdp + CPDMA_TXCP; + dma_params.rxcp = dma_params.txhdp + CPDMA_RXCP; dma_params.num_chan = data->channels; dma_params.has_soft_reset = true; @@ -1325,10 +1295,7 @@ static int __devinit cpsw_probe(struct platform_device *pdev) dma_params.desc_mem_size = data->bd_ram_size; dma_params.desc_align = 16; dma_params.has_ext_regs = true; - dma_params.desc_mem_phys = data->no_bd_ram ? 0 : - (u32 __force)priv->cpsw_res->start + data->bd_ram_ofs; - dma_params.desc_hw_addr = data->hw_ram_addr ? - data->hw_ram_addr : dma_params.desc_mem_phys ; + dma_params.desc_hw_addr = dma_params.desc_mem_phys; priv->dma = cpdma_ctlr_create(&dma_params); if (!priv->dma) { @@ -1348,10 +1315,7 @@ static int __devinit cpsw_probe(struct platform_device *pdev) goto clean_dma_ret; } - memset(&ale_params, 0, sizeof(ale_params)); ale_params.dev = &ndev->dev; - ale_params.ale_regs = (void *)((u32)priv->regs) + - ((u32)data->ale_reg_ofs); ale_params.ale_ageout = ale_ageout; ale_params.ale_entries = data->ale_entries; ale_params.ale_ports = data->slaves; diff --git a/include/linux/platform_data/cpsw.h b/include/linux/platform_data/cpsw.h index b5c16c3df458..24368a2e8b87 100644 --- a/include/linux/platform_data/cpsw.h +++ b/include/linux/platform_data/cpsw.h @@ -18,9 +18,7 @@ #include struct cpsw_slave_data { - u32 slave_reg_ofs; - u32 sliver_reg_ofs; - const char *phy_id; + char phy_id[MII_BUS_ID_SIZE]; int phy_if; u8 mac_addr[ETH_ALEN]; }; @@ -28,31 +26,14 @@ struct cpsw_slave_data { struct cpsw_platform_data { u32 ss_reg_ofs; /* Subsystem control register offset */ u32 channels; /* number of cpdma channels (symmetric) */ - u32 cpdma_reg_ofs; /* cpdma register offset */ - u32 cpdma_sram_ofs; /* cpdma sram offset */ - u32 slaves; /* number of slave cpgmac ports */ struct cpsw_slave_data *slave_data; u32 cpts_active_slave; /* time stamping slave */ u32 cpts_clock_mult; /* convert input clock ticks to nanoseconds */ u32 cpts_clock_shift; /* convert input clock ticks to nanoseconds */ - - u32 ale_reg_ofs; /* address lookup engine reg offset */ u32 ale_entries; /* ale table size */ - - u32 host_port_reg_ofs; /* cpsw cpdma host port registers */ - u32 host_port_num; /* The port number for the host port */ - - u32 hw_stats_reg_ofs; /* cpsw hardware statistics counters */ - u32 cpts_reg_ofs; /* cpts registers */ - - u32 bd_ram_ofs; /* embedded buffer descriptor RAM offset*/ u32 bd_ram_size; /*buffer descriptor ram size */ - u32 hw_ram_addr; /*if the HW address for BD RAM is different */ - bool no_bd_ram; /* no embedded BD ram*/ - u32 rx_descs; /* Number of Rx Descriptios */ - u32 mac_control; /* Mac control register */ }; -- cgit v1.2.3 From aa1acb0451bb27add173d9641d0b74c58889e693 Mon Sep 17 00:00:00 2001 From: "hongbo.zhang" Date: Thu, 15 Nov 2012 18:56:42 +0800 Subject: Thermal: Add ST-Ericsson DB8500 thermal driver. This driver is based on the thermal management framework in thermal_sys.c. A thermal zone device is created with the trip points to which cooling devices can be bound, the current cooling device is cpufreq, e.g. CPU frequency is clipped down to cool the CPU, and other cooling devices can be added and bound to the trip points dynamically. The platform specific PRCMU interrupts are used to active thermal update when trip points are reached. Signed-off-by: hongbo.zhang Reviewed-by: Viresh Kumar Reviewed-by: Francesco Lavra Signed-off-by: Zhang Rui --- .../devicetree/bindings/thermal/db8500-thermal.txt | 44 ++ drivers/thermal/Kconfig | 20 + drivers/thermal/Makefile | 2 + drivers/thermal/db8500_cpufreq_cooling.c | 108 +++++ drivers/thermal/db8500_thermal.c | 531 +++++++++++++++++++++ include/linux/platform_data/db8500_thermal.h | 38 ++ 6 files changed, 743 insertions(+) create mode 100644 Documentation/devicetree/bindings/thermal/db8500-thermal.txt create mode 100644 drivers/thermal/db8500_cpufreq_cooling.c create mode 100644 drivers/thermal/db8500_thermal.c create mode 100644 include/linux/platform_data/db8500_thermal.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/thermal/db8500-thermal.txt b/Documentation/devicetree/bindings/thermal/db8500-thermal.txt new file mode 100644 index 000000000000..2e1c06fad81f --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/db8500-thermal.txt @@ -0,0 +1,44 @@ +* ST-Ericsson DB8500 Thermal + +** Thermal node properties: + +- compatible : "stericsson,db8500-thermal"; +- reg : address range of the thermal sensor registers; +- interrupts : interrupts generated from PRCMU; +- interrupt-names : "IRQ_HOTMON_LOW" and "IRQ_HOTMON_HIGH"; +- num-trips : number of total trip points, this is required, set it 0 if none, + if greater than 0, the following properties must be defined; +- tripN-temp : temperature of trip point N, should be in ascending order; +- tripN-type : type of trip point N, should be one of "active" "passive" "hot" + "critical"; +- tripN-cdev-num : number of the cooling devices which can be bound to trip + point N, this is required if trip point N is defined, set it 0 if none, + otherwise the following cooling device names must be defined; +- tripN-cdev-nameM : name of the No. M cooling device of trip point N; + +Usually the num-trips and tripN-*** are separated in board related dts files. + +Example: +thermal@801573c0 { + compatible = "stericsson,db8500-thermal"; + reg = <0x801573c0 0x40>; + interrupts = <21 0x4>, <22 0x4>; + interrupt-names = "IRQ_HOTMON_LOW", "IRQ_HOTMON_HIGH"; + + num-trips = <3>; + + trip0-temp = <75000>; + trip0-type = "active"; + trip0-cdev-num = <1>; + trip0-cdev-name0 = "thermal-cpufreq-0"; + + trip1-temp = <80000>; + trip1-type = "active"; + trip1-cdev-num = <2>; + trip1-cdev-name0 = "thermal-cpufreq-0"; + trip1-cdev-name1 = "thermal-fan"; + + trip2-temp = <85000>; + trip2-type = "critical"; + trip2-cdev-num = <0>; +} diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 99b6587ab8b3..d96da075c9f6 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -101,5 +101,25 @@ config EXYNOS_THERMAL If you say yes here you get support for TMU (Thermal Managment Unit) on SAMSUNG EXYNOS series of SoC. +config DB8500_THERMAL + bool "DB8500 thermal management" + depends on ARCH_U8500 + default y + help + Adds DB8500 thermal management implementation according to the thermal + management framework. A thermal zone with several trip points will be + created. Cooling devices can be bound to the trip points to cool this + thermal zone if trip points reached. + +config DB8500_CPUFREQ_COOLING + tristate "DB8500 cpufreq cooling" + depends on ARCH_U8500 + depends on CPU_THERMAL + default y + help + Adds DB8500 cpufreq cooling devices, and these cooling devices can be + bound to thermal zone trip points. When a trip point reached, the + bound cpufreq cooling device turns active to set CPU frequency low to + cool down the CPU. endif diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 0b6b048f236d..d8da683245fc 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -16,3 +16,5 @@ obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o +obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o +obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o diff --git a/drivers/thermal/db8500_cpufreq_cooling.c b/drivers/thermal/db8500_cpufreq_cooling.c new file mode 100644 index 000000000000..4cf8e72af90a --- /dev/null +++ b/drivers/thermal/db8500_cpufreq_cooling.c @@ -0,0 +1,108 @@ +/* + * db8500_cpufreq_cooling.c - DB8500 cpufreq works as cooling device. + * + * Copyright (C) 2012 ST-Ericsson + * Copyright (C) 2012 Linaro Ltd. + * + * Author: Hongbo Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +static int db8500_cpufreq_cooling_probe(struct platform_device *pdev) +{ + struct thermal_cooling_device *cdev; + struct cpumask mask_val; + + /* make sure cpufreq driver has been initialized */ + if (!cpufreq_frequency_get_table(0)) + return -EPROBE_DEFER; + + cpumask_set_cpu(0, &mask_val); + cdev = cpufreq_cooling_register(&mask_val); + + if (IS_ERR_OR_NULL(cdev)) { + dev_err(&pdev->dev, "Failed to register cooling device\n"); + return PTR_ERR(cdev); + } + + platform_set_drvdata(pdev, cdev); + + dev_info(&pdev->dev, "Cooling device registered: %s\n", cdev->type); + + return 0; +} + +static int db8500_cpufreq_cooling_remove(struct platform_device *pdev) +{ + struct thermal_cooling_device *cdev = platform_get_drvdata(pdev); + + cpufreq_cooling_unregister(cdev); + + return 0; +} + +static int db8500_cpufreq_cooling_suspend(struct platform_device *pdev, + pm_message_t state) +{ + return -ENOSYS; +} + +static int db8500_cpufreq_cooling_resume(struct platform_device *pdev) +{ + return -ENOSYS; +} + +#ifdef CONFIG_OF +static const struct of_device_id db8500_cpufreq_cooling_match[] = { + { .compatible = "stericsson,db8500-cpufreq-cooling" }, + {}, +}; +#else +#define db8500_cpufreq_cooling_match NULL +#endif + +static struct platform_driver db8500_cpufreq_cooling_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "db8500-cpufreq-cooling", + .of_match_table = db8500_cpufreq_cooling_match, + }, + .probe = db8500_cpufreq_cooling_probe, + .suspend = db8500_cpufreq_cooling_suspend, + .resume = db8500_cpufreq_cooling_resume, + .remove = db8500_cpufreq_cooling_remove, +}; + +static int __init db8500_cpufreq_cooling_init(void) +{ + return platform_driver_register(&db8500_cpufreq_cooling_driver); +} + +static void __exit db8500_cpufreq_cooling_exit(void) +{ + platform_driver_unregister(&db8500_cpufreq_cooling_driver); +} + +/* Should be later than db8500_cpufreq_register */ +late_initcall(db8500_cpufreq_cooling_init); +module_exit(db8500_cpufreq_cooling_exit); + +MODULE_AUTHOR("Hongbo Zhang "); +MODULE_DESCRIPTION("DB8500 cpufreq cooling driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/thermal/db8500_thermal.c b/drivers/thermal/db8500_thermal.c new file mode 100644 index 000000000000..ec71ade3e317 --- /dev/null +++ b/drivers/thermal/db8500_thermal.c @@ -0,0 +1,531 @@ +/* + * db8500_thermal.c - DB8500 Thermal Management Implementation + * + * Copyright (C) 2012 ST-Ericsson + * Copyright (C) 2012 Linaro Ltd. + * + * Author: Hongbo Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PRCMU_DEFAULT_MEASURE_TIME 0xFFF +#define PRCMU_DEFAULT_LOW_TEMP 0 + +struct db8500_thermal_zone { + struct thermal_zone_device *therm_dev; + struct mutex th_lock; + struct work_struct therm_work; + struct db8500_thsens_platform_data *trip_tab; + enum thermal_device_mode mode; + enum thermal_trend trend; + unsigned long cur_temp_pseudo; + unsigned int cur_index; +}; + +/* Local function to check if thermal zone matches cooling devices */ +static int db8500_thermal_match_cdev(struct thermal_cooling_device *cdev, + struct db8500_trip_point *trip_point) +{ + int i; + + if (!strlen(cdev->type)) + return -EINVAL; + + for (i = 0; i < COOLING_DEV_MAX; i++) { + if (!strcmp(trip_point->cdev_name[i], cdev->type)) + return 0; + } + + return -ENODEV; +} + +/* Callback to bind cooling device to thermal zone */ +static int db8500_cdev_bind(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev) +{ + struct db8500_thermal_zone *pzone = thermal->devdata; + struct db8500_thsens_platform_data *ptrips = pzone->trip_tab; + unsigned long max_state, upper, lower; + int i, ret = -EINVAL; + + cdev->ops->get_max_state(cdev, &max_state); + + for (i = 0; i < ptrips->num_trips; i++) { + if (db8500_thermal_match_cdev(cdev, &ptrips->trip_points[i])) + continue; + + upper = lower = i > max_state ? max_state : i; + + ret = thermal_zone_bind_cooling_device(thermal, i, cdev, + upper, lower); + + dev_info(&cdev->device, "%s bind to %d: %d-%s\n", cdev->type, + i, ret, ret ? "fail" : "succeed"); + } + + return ret; +} + +/* Callback to unbind cooling device from thermal zone */ +static int db8500_cdev_unbind(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev) +{ + struct db8500_thermal_zone *pzone = thermal->devdata; + struct db8500_thsens_platform_data *ptrips = pzone->trip_tab; + int i, ret = -EINVAL; + + for (i = 0; i < ptrips->num_trips; i++) { + if (db8500_thermal_match_cdev(cdev, &ptrips->trip_points[i])) + continue; + + ret = thermal_zone_unbind_cooling_device(thermal, i, cdev); + + dev_info(&cdev->device, "%s unbind from %d: %s\n", cdev->type, + i, ret ? "fail" : "succeed"); + } + + return ret; +} + +/* Callback to get current temperature */ +static int db8500_sys_get_temp(struct thermal_zone_device *thermal, + unsigned long *temp) +{ + struct db8500_thermal_zone *pzone = thermal->devdata; + + /* + * TODO: There is no PRCMU interface to get temperature data currently, + * so a pseudo temperature is returned , it works for thermal framework + * and this will be fixed when the PRCMU interface is available. + */ + *temp = pzone->cur_temp_pseudo; + + return 0; +} + +/* Callback to get temperature changing trend */ +static int db8500_sys_get_trend(struct thermal_zone_device *thermal, + int trip, enum thermal_trend *trend) +{ + struct db8500_thermal_zone *pzone = thermal->devdata; + + *trend = pzone->trend; + + return 0; +} + +/* Callback to get thermal zone mode */ +static int db8500_sys_get_mode(struct thermal_zone_device *thermal, + enum thermal_device_mode *mode) +{ + struct db8500_thermal_zone *pzone = thermal->devdata; + + mutex_lock(&pzone->th_lock); + *mode = pzone->mode; + mutex_unlock(&pzone->th_lock); + + return 0; +} + +/* Callback to set thermal zone mode */ +static int db8500_sys_set_mode(struct thermal_zone_device *thermal, + enum thermal_device_mode mode) +{ + struct db8500_thermal_zone *pzone = thermal->devdata; + + mutex_lock(&pzone->th_lock); + + pzone->mode = mode; + if (mode == THERMAL_DEVICE_ENABLED) + schedule_work(&pzone->therm_work); + + mutex_unlock(&pzone->th_lock); + + return 0; +} + +/* Callback to get trip point type */ +static int db8500_sys_get_trip_type(struct thermal_zone_device *thermal, + int trip, enum thermal_trip_type *type) +{ + struct db8500_thermal_zone *pzone = thermal->devdata; + struct db8500_thsens_platform_data *ptrips = pzone->trip_tab; + + if (trip >= ptrips->num_trips) + return -EINVAL; + + *type = ptrips->trip_points[trip].type; + + return 0; +} + +/* Callback to get trip point temperature */ +static int db8500_sys_get_trip_temp(struct thermal_zone_device *thermal, + int trip, unsigned long *temp) +{ + struct db8500_thermal_zone *pzone = thermal->devdata; + struct db8500_thsens_platform_data *ptrips = pzone->trip_tab; + + if (trip >= ptrips->num_trips) + return -EINVAL; + + *temp = ptrips->trip_points[trip].temp; + + return 0; +} + +/* Callback to get critical trip point temperature */ +static int db8500_sys_get_crit_temp(struct thermal_zone_device *thermal, + unsigned long *temp) +{ + struct db8500_thermal_zone *pzone = thermal->devdata; + struct db8500_thsens_platform_data *ptrips = pzone->trip_tab; + int i; + + for (i = ptrips->num_trips - 1; i > 0; i--) { + if (ptrips->trip_points[i].type == THERMAL_TRIP_CRITICAL) { + *temp = ptrips->trip_points[i].temp; + return 0; + } + } + + return -EINVAL; +} + +static struct thermal_zone_device_ops thdev_ops = { + .bind = db8500_cdev_bind, + .unbind = db8500_cdev_unbind, + .get_temp = db8500_sys_get_temp, + .get_trend = db8500_sys_get_trend, + .get_mode = db8500_sys_get_mode, + .set_mode = db8500_sys_set_mode, + .get_trip_type = db8500_sys_get_trip_type, + .get_trip_temp = db8500_sys_get_trip_temp, + .get_crit_temp = db8500_sys_get_crit_temp, +}; + +static void db8500_thermal_update_config(struct db8500_thermal_zone *pzone, + unsigned int idx, enum thermal_trend trend, + unsigned long next_low, unsigned long next_high) +{ + prcmu_stop_temp_sense(); + + pzone->cur_index = idx; + pzone->cur_temp_pseudo = (next_low + next_high)/2; + pzone->trend = trend; + + prcmu_config_hotmon((u8)(next_low/1000), (u8)(next_high/1000)); + prcmu_start_temp_sense(PRCMU_DEFAULT_MEASURE_TIME); +} + +static irqreturn_t prcmu_low_irq_handler(int irq, void *irq_data) +{ + struct db8500_thermal_zone *pzone = irq_data; + struct db8500_thsens_platform_data *ptrips = pzone->trip_tab; + unsigned int idx = pzone->cur_index; + unsigned long next_low, next_high; + + if (unlikely(idx == 0)) + /* Meaningless for thermal management, ignoring it */ + return IRQ_HANDLED; + + if (idx == 1) { + next_high = ptrips->trip_points[0].temp; + next_low = PRCMU_DEFAULT_LOW_TEMP; + } else { + next_high = ptrips->trip_points[idx-1].temp; + next_low = ptrips->trip_points[idx-2].temp; + } + idx -= 1; + + db8500_thermal_update_config(pzone, idx, THERMAL_TREND_DROPPING, + next_low, next_high); + + dev_dbg(&pzone->therm_dev->device, + "PRCMU set max %ld, min %ld\n", next_high, next_low); + + schedule_work(&pzone->therm_work); + + return IRQ_HANDLED; +} + +static irqreturn_t prcmu_high_irq_handler(int irq, void *irq_data) +{ + struct db8500_thermal_zone *pzone = irq_data; + struct db8500_thsens_platform_data *ptrips = pzone->trip_tab; + unsigned int idx = pzone->cur_index; + unsigned long next_low, next_high; + + if (idx < ptrips->num_trips - 1) { + next_high = ptrips->trip_points[idx+1].temp; + next_low = ptrips->trip_points[idx].temp; + idx += 1; + + db8500_thermal_update_config(pzone, idx, THERMAL_TREND_RAISING, + next_low, next_high); + + dev_dbg(&pzone->therm_dev->device, + "PRCMU set max %ld, min %ld\n", next_high, next_low); + } else if (idx == ptrips->num_trips - 1) + pzone->cur_temp_pseudo = ptrips->trip_points[idx].temp + 1; + + schedule_work(&pzone->therm_work); + + return IRQ_HANDLED; +} + +static void db8500_thermal_work(struct work_struct *work) +{ + enum thermal_device_mode cur_mode; + struct db8500_thermal_zone *pzone; + + pzone = container_of(work, struct db8500_thermal_zone, therm_work); + + mutex_lock(&pzone->th_lock); + cur_mode = pzone->mode; + mutex_unlock(&pzone->th_lock); + + if (cur_mode == THERMAL_DEVICE_DISABLED) + return; + + thermal_zone_device_update(pzone->therm_dev); + dev_dbg(&pzone->therm_dev->device, "thermal work finished.\n"); +} + +#ifdef CONFIG_OF +static struct db8500_thsens_platform_data* + db8500_thermal_parse_dt(struct platform_device *pdev) +{ + struct db8500_thsens_platform_data *ptrips; + struct device_node *np = pdev->dev.of_node; + char prop_name[32]; + const char *tmp_str; + u32 tmp_data; + int i, j; + + ptrips = devm_kzalloc(&pdev->dev, sizeof(*ptrips), GFP_KERNEL); + if (!ptrips) + return NULL; + + if (of_property_read_u32(np, "num-trips", &tmp_data)) + goto err_parse_dt; + + if (tmp_data > THERMAL_MAX_TRIPS) + goto err_parse_dt; + + ptrips->num_trips = tmp_data; + + for (i = 0; i < ptrips->num_trips; i++) { + sprintf(prop_name, "trip%d-temp", i); + if (of_property_read_u32(np, prop_name, &tmp_data)) + goto err_parse_dt; + + ptrips->trip_points[i].temp = tmp_data; + + sprintf(prop_name, "trip%d-type", i); + if (of_property_read_string(np, prop_name, &tmp_str)) + goto err_parse_dt; + + if (!strcmp(tmp_str, "active")) + ptrips->trip_points[i].type = THERMAL_TRIP_ACTIVE; + else if (!strcmp(tmp_str, "passive")) + ptrips->trip_points[i].type = THERMAL_TRIP_PASSIVE; + else if (!strcmp(tmp_str, "hot")) + ptrips->trip_points[i].type = THERMAL_TRIP_HOT; + else if (!strcmp(tmp_str, "critical")) + ptrips->trip_points[i].type = THERMAL_TRIP_CRITICAL; + else + goto err_parse_dt; + + sprintf(prop_name, "trip%d-cdev-num", i); + if (of_property_read_u32(np, prop_name, &tmp_data)) + goto err_parse_dt; + + if (tmp_data > COOLING_DEV_MAX) + goto err_parse_dt; + + for (j = 0; j < tmp_data; j++) { + sprintf(prop_name, "trip%d-cdev-name%d", i, j); + if (of_property_read_string(np, prop_name, &tmp_str)) + goto err_parse_dt; + + if (strlen(tmp_str) >= THERMAL_NAME_LENGTH) + goto err_parse_dt; + + strcpy(ptrips->trip_points[i].cdev_name[j], tmp_str); + } + } + return ptrips; + +err_parse_dt: + dev_err(&pdev->dev, "Parsing device tree data error.\n"); + return NULL; +} +#else +static inline struct db8500_thsens_platform_data* + db8500_thermal_parse_dt(struct platform_device *pdev) +{ + return NULL; +} +#endif + +static int db8500_thermal_probe(struct platform_device *pdev) +{ + struct db8500_thermal_zone *pzone = NULL; + struct db8500_thsens_platform_data *ptrips = NULL; + struct device_node *np = pdev->dev.of_node; + int low_irq, high_irq, ret = 0; + unsigned long dft_low, dft_high; + + if (np) + ptrips = db8500_thermal_parse_dt(pdev); + else + ptrips = dev_get_platdata(&pdev->dev); + + if (!ptrips) + return -EINVAL; + + pzone = devm_kzalloc(&pdev->dev, sizeof(*pzone), GFP_KERNEL); + if (!pzone) + return -ENOMEM; + + mutex_init(&pzone->th_lock); + mutex_lock(&pzone->th_lock); + + pzone->mode = THERMAL_DEVICE_DISABLED; + pzone->trip_tab = ptrips; + + INIT_WORK(&pzone->therm_work, db8500_thermal_work); + + low_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_LOW"); + if (low_irq < 0) { + dev_err(&pdev->dev, "Get IRQ_HOTMON_LOW failed.\n"); + return low_irq; + } + + ret = devm_request_threaded_irq(&pdev->dev, low_irq, NULL, + prcmu_low_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT, + "dbx500_temp_low", pzone); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to allocate temp low irq.\n"); + return ret; + } + + high_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_HIGH"); + if (high_irq < 0) { + dev_err(&pdev->dev, "Get IRQ_HOTMON_HIGH failed.\n"); + return high_irq; + } + + ret = devm_request_threaded_irq(&pdev->dev, high_irq, NULL, + prcmu_high_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT, + "dbx500_temp_high", pzone); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to allocate temp high irq.\n"); + return ret; + } + + pzone->therm_dev = thermal_zone_device_register("db8500_thermal_zone", + ptrips->num_trips, 0, pzone, &thdev_ops, NULL, 0, 0); + + if (IS_ERR_OR_NULL(pzone->therm_dev)) { + dev_err(&pdev->dev, "Register thermal zone device failed.\n"); + return PTR_ERR(pzone->therm_dev); + } + dev_info(&pdev->dev, "Thermal zone device registered.\n"); + + dft_low = PRCMU_DEFAULT_LOW_TEMP; + dft_high = ptrips->trip_points[0].temp; + + db8500_thermal_update_config(pzone, 0, THERMAL_TREND_STABLE, + dft_low, dft_high); + + platform_set_drvdata(pdev, pzone); + pzone->mode = THERMAL_DEVICE_ENABLED; + mutex_unlock(&pzone->th_lock); + + return 0; +} + +static int db8500_thermal_remove(struct platform_device *pdev) +{ + struct db8500_thermal_zone *pzone = platform_get_drvdata(pdev); + + thermal_zone_device_unregister(pzone->therm_dev); + cancel_work_sync(&pzone->therm_work); + mutex_destroy(&pzone->th_lock); + + return 0; +} + +static int db8500_thermal_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct db8500_thermal_zone *pzone = platform_get_drvdata(pdev); + + flush_work(&pzone->therm_work); + prcmu_stop_temp_sense(); + + return 0; +} + +static int db8500_thermal_resume(struct platform_device *pdev) +{ + struct db8500_thermal_zone *pzone = platform_get_drvdata(pdev); + struct db8500_thsens_platform_data *ptrips = pzone->trip_tab; + unsigned long dft_low, dft_high; + + dft_low = PRCMU_DEFAULT_LOW_TEMP; + dft_high = ptrips->trip_points[0].temp; + + db8500_thermal_update_config(pzone, 0, THERMAL_TREND_STABLE, + dft_low, dft_high); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id db8500_thermal_match[] = { + { .compatible = "stericsson,db8500-thermal" }, + {}, +}; +#else +#define db8500_thermal_match NULL +#endif + +static struct platform_driver db8500_thermal_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "db8500-thermal", + .of_match_table = db8500_thermal_match, + }, + .probe = db8500_thermal_probe, + .suspend = db8500_thermal_suspend, + .resume = db8500_thermal_resume, + .remove = db8500_thermal_remove, +}; + +module_platform_driver(db8500_thermal_driver); + +MODULE_AUTHOR("Hongbo Zhang "); +MODULE_DESCRIPTION("DB8500 thermal driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/db8500_thermal.h b/include/linux/platform_data/db8500_thermal.h new file mode 100644 index 000000000000..3bf60902e902 --- /dev/null +++ b/include/linux/platform_data/db8500_thermal.h @@ -0,0 +1,38 @@ +/* + * db8500_thermal.h - DB8500 Thermal Management Implementation + * + * Copyright (C) 2012 ST-Ericsson + * Copyright (C) 2012 Linaro Ltd. + * + * Author: Hongbo Zhang + * + * 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 _DB8500_THERMAL_H_ +#define _DB8500_THERMAL_H_ + +#include + +#define COOLING_DEV_MAX 8 + +struct db8500_trip_point { + unsigned long temp; + enum thermal_trip_type type; + char cdev_name[COOLING_DEV_MAX][THERMAL_NAME_LENGTH]; +}; + +struct db8500_thsens_platform_data { + struct db8500_trip_point trip_points[THERMAL_MAX_TRIPS]; + int num_trips; +}; + +#endif /* _DB8500_THERMAL_H_ */ -- cgit v1.2.3 From 12ad2be9d1db3dcce08f29bdc5415db3af58fa60 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 24 Sep 2012 03:39:39 +0200 Subject: mtd: m25p80: Make fast read configurable via DT Add DT property "m25p,fast-read" that signalises the particular chip supports "fast read" opcode. Signed-off-by: Marek Vasut Signed-off-by: Artem Bityutskiy --- Documentation/devicetree/bindings/mtd/m25p80.txt | 29 ++++++++++++++++++++ drivers/mtd/devices/m25p80.c | 34 +++++++++++++++--------- 2 files changed, 50 insertions(+), 13 deletions(-) create mode 100644 Documentation/devicetree/bindings/mtd/m25p80.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mtd/m25p80.txt b/Documentation/devicetree/bindings/mtd/m25p80.txt new file mode 100644 index 000000000000..6d3d57609470 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/m25p80.txt @@ -0,0 +1,29 @@ +* MTD SPI driver for ST M25Pxx (and similar) serial flash chips + +Required properties: +- #address-cells, #size-cells : Must be present if the device has sub-nodes + representing partitions. +- compatible : Should be the manufacturer and the name of the chip. Bear in mind + the DT binding is not Linux-only, but in case of Linux, see the + "m25p_ids" table in drivers/mtd/devices/m25p80.c for the list of + supported chips. +- reg : Chip-Select number +- spi-max-frequency : Maximum frequency of the SPI bus the chip can operate at + +Optional properties: +- m25p,fast-read : Use the "fast read" opcode to read data from the chip instead + of the usual "read" opcode. This opcode is not supported by + all chips and support for it can not be detected at runtime. + Refer to your chips' datasheet to check if this is supported + by your chip. + +Example: + + flash: m25p80@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spansion,m25p80"; + reg = <0>; + spi-max-frequency = <40000000>; + m25p,fast-read; + }; diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 03838bab1f59..82f977456b88 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -73,14 +73,6 @@ #define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ #define MAX_CMD_SIZE 5 -#ifdef CONFIG_M25PXX_USE_FAST_READ -#define OPCODE_READ OPCODE_FAST_READ -#define FAST_READ_DUMMY_BYTE 1 -#else -#define OPCODE_READ OPCODE_NORM_READ -#define FAST_READ_DUMMY_BYTE 0 -#endif - #define JEDEC_MFR(_jedec_id) ((_jedec_id) >> 16) /****************************************************************************/ @@ -93,6 +85,7 @@ struct m25p { u16 addr_width; u8 erase_opcode; u8 *command; + bool fast_read; }; static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd) @@ -342,6 +335,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, struct m25p *flash = mtd_to_m25p(mtd); struct spi_transfer t[2]; struct spi_message m; + uint8_t opcode; pr_debug("%s: %s from 0x%08x, len %zd\n", dev_name(&flash->spi->dev), __func__, (u32)from, len); @@ -354,7 +348,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, * Should add 1 byte DUMMY_BYTE. */ t[0].tx_buf = flash->command; - t[0].len = m25p_cmdsz(flash) + FAST_READ_DUMMY_BYTE; + t[0].len = m25p_cmdsz(flash) + (flash->fast_read ? 1 : 0); spi_message_add_tail(&t[0], &m); t[1].rx_buf = buf; @@ -376,12 +370,14 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, */ /* Set up the write data buffer. */ - flash->command[0] = OPCODE_READ; + opcode = flash->fast_read ? OPCODE_FAST_READ : OPCODE_NORM_READ; + flash->command[0] = opcode; m25p_addr2cmd(flash, from, flash->command); spi_sync(flash->spi, &m); - *retlen = m.actual_length - m25p_cmdsz(flash) - FAST_READ_DUMMY_BYTE; + *retlen = m.actual_length - m25p_cmdsz(flash) - + (flash->fast_read ? 1 : 0); mutex_unlock(&flash->lock); @@ -809,9 +805,10 @@ static int __devinit m25p_probe(struct spi_device *spi) struct flash_info *info; unsigned i; struct mtd_part_parser_data ppdata; + struct device_node __maybe_unused *np = spi->dev.of_node; #ifdef CONFIG_MTD_OF_PARTS - if (!of_device_is_available(spi->dev.of_node)) + if (!of_device_is_available(np)) return -ENODEV; #endif @@ -863,7 +860,8 @@ static int __devinit m25p_probe(struct spi_device *spi) flash = kzalloc(sizeof *flash, GFP_KERNEL); if (!flash) return -ENOMEM; - flash->command = kmalloc(MAX_CMD_SIZE + FAST_READ_DUMMY_BYTE, GFP_KERNEL); + flash->command = kmalloc(MAX_CMD_SIZE + (flash->fast_read ? 1 : 0), + GFP_KERNEL); if (!flash->command) { kfree(flash); return -ENOMEM; @@ -920,6 +918,16 @@ static int __devinit m25p_probe(struct spi_device *spi) flash->page_size = info->page_size; flash->mtd.writebufsize = flash->page_size; + flash->fast_read = false; +#ifdef CONFIG_OF + if (np && of_property_read_bool(np, "m25p,fast-read")) + flash->fast_read = true; +#endif + +#ifdef CONFIG_M25PXX_USE_FAST_READ + flash->fast_read = true; +#endif + if (info->addr_width) flash->addr_width = info->addr_width; else { -- cgit v1.2.3 From 30f9f2fb7ba032665c8cea7694c815f18ed47a34 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Thu, 27 Sep 2012 10:58:06 -0600 Subject: mtd: denali: add a DT driver Add a device tree version of the Denali NAND driver. Based on an original patch from Jamie Iles to add a MMIO version of this driver. Signed-off-by: Dinh Nguyen Signed-off-by: Artem Bityutskiy --- .../devicetree/bindings/mtd/denali-nand.txt | 23 +++ drivers/mtd/nand/Kconfig | 9 +- drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/denali.h | 1 + drivers/mtd/nand/denali_dt.c | 167 +++++++++++++++++++++ 5 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/mtd/denali-nand.txt create mode 100644 drivers/mtd/nand/denali_dt.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt new file mode 100644 index 000000000000..b04d03a1d499 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt @@ -0,0 +1,23 @@ +* Denali NAND controller + +Required properties: + - compatible : should be "denali,denali-nand-dt" + - reg : should contain registers location and length for data and reg. + - reg-names: Should contain the reg names "nand_data" and "denali_reg" + - interrupts : The interrupt number. + - dm-mask : DMA bit mask + +The device tree may optionally contain sub-nodes describing partitions of the +address space. See partition.txt for more detail. + +Examples: + +nand: nand@ff900000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "denali,denali-nand-dt"; + reg = <0xff900000 0x100000>, <0xffb80000 0x10000>; + reg-names = "nand_data", "denali_reg"; + interrupts = <0 144 4>; + dma-mask = <0xffffffff>; +}; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 5102e87f8df0..ee803d611e4e 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -69,7 +69,14 @@ config MTD_NAND_DENALI_PCI help Enable the driver for NAND flash on Intel Moorestown, using the Denali NAND controller core. - + +config MTD_NAND_DENALI_DT + tristate "Support Denali NAND controller as a DT device" + depends on HAVE_CLK && MTD_NAND_DENALI + help + Enable the driver for NAND flash on platforms using a Denali NAND + controller as a DT device. + config MTD_NAND_DENALI_SCRATCH_REG_ADDR hex "Denali NAND size scratch register address" default "0xFF108018" diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 96a6171c9d8f..38358c90771e 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o obj-$(CONFIG_MTD_NAND_DENALI) += denali.o obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o +obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h index e5aa99583e43..cec5712862c9 100644 --- a/drivers/mtd/nand/denali.h +++ b/drivers/mtd/nand/denali.h @@ -466,6 +466,7 @@ struct nand_buf { #define INTEL_CE4100 1 #define INTEL_MRST 2 +#define DT 3 struct denali_nand_info { struct mtd_info mtd; diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c new file mode 100644 index 000000000000..fbabbaa04374 --- /dev/null +++ b/drivers/mtd/nand/denali_dt.c @@ -0,0 +1,167 @@ +/* + * NAND Flash Controller Device Driver for DT + * + * Copyright © 2011, Picochip. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "denali.h" + +struct denali_dt { + struct denali_nand_info denali; + struct clk *clk; +}; + +static void __iomem *request_and_map(struct device *dev, + const struct resource *res) +{ + void __iomem *ptr; + + if (!devm_request_mem_region(dev, res->start, resource_size(res), + "denali-dt")) { + dev_err(dev, "unable to request %s\n", res->name); + return NULL; + } + + ptr = devm_ioremap_nocache(dev, res->start, resource_size(res)); + if (!res) + dev_err(dev, "ioremap_nocache of %s failed!", res->name); + + return ptr; +} + +static const struct of_device_id denali_nand_dt_ids[] = { + { .compatible = "denali,denali-nand-dt" }, + { /* sentinel */ } + }; + +MODULE_DEVICE_TABLE(of, denali_nand_dt_ids); + +static u64 denali_dma_mask; + +static int __devinit denali_dt_probe(struct platform_device *ofdev) +{ + struct resource *denali_reg, *nand_data; + struct denali_dt *dt; + struct denali_nand_info *denali; + int ret; + const struct of_device_id *of_id; + + of_id = of_match_device(denali_nand_dt_ids, &ofdev->dev); + if (of_id) { + ofdev->id_entry = of_id->data; + } else { + pr_err("Failed to find the right device id.\n"); + return -ENOMEM; + } + + dt = devm_kzalloc(&ofdev->dev, sizeof(*dt), GFP_KERNEL); + if (!dt) + return -ENOMEM; + denali = &dt->denali; + + denali_reg = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "denali_reg"); + nand_data = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "nand_data"); + if (!denali_reg || !nand_data) { + dev_err(&ofdev->dev, "resources not completely defined\n"); + return -EINVAL; + } + + denali->platform = DT; + denali->dev = &ofdev->dev; + denali->irq = platform_get_irq(ofdev, 0); + if (denali->irq < 0) { + dev_err(&ofdev->dev, "no irq defined\n"); + return -ENXIO; + } + + denali->flash_reg = request_and_map(&ofdev->dev, denali_reg); + if (!denali->flash_reg) + return -ENOMEM; + + denali->flash_mem = request_and_map(&ofdev->dev, nand_data); + if (!denali->flash_mem) + return -ENOMEM; + + if (!of_property_read_u32(ofdev->dev.of_node, + "dma-mask", (u32 *)&denali_dma_mask)) { + denali->dev->dma_mask = &denali_dma_mask; + } else { + denali->dev->dma_mask = NULL; + } + + dt->clk = clk_get(&ofdev->dev, NULL); + if (IS_ERR(dt->clk)) { + dev_err(&ofdev->dev, "no clk available\n"); + return PTR_ERR(dt->clk); + } + clk_prepare_enable(dt->clk); + + ret = denali_init(denali); + if (ret) + goto out_disable_clk; + + platform_set_drvdata(ofdev, dt); + return 0; + +out_disable_clk: + clk_disable_unprepare(dt->clk); + clk_put(dt->clk); + + return ret; +} + +static int __devexit denali_dt_remove(struct platform_device *ofdev) +{ + struct denali_dt *dt = platform_get_drvdata(ofdev); + + denali_remove(&dt->denali); + clk_disable(dt->clk); + clk_put(dt->clk); + + return 0; +} + +static struct platform_driver denali_dt_driver = { + .probe = denali_dt_probe, + .remove = __devexit_p(denali_dt_remove), + .driver = { + .name = "denali-nand-dt", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(denali_nand_dt_ids), + }, +}; + +static int __init denali_init_dt(void) +{ + return platform_driver_register(&denali_dt_driver); +} +module_init(denali_init_dt); + +static void __exit denali_exit_dt(void) +{ + platform_driver_unregister(&denali_dt_driver); +} +module_exit(denali_exit_dt); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jamie Iles"); +MODULE_DESCRIPTION("DT driver for Denali NAND controller"); -- cgit v1.2.3 From 6d7b42a447f92eb3e7e410bbf62042693eb040f7 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Thu, 4 Oct 2012 15:14:16 +0200 Subject: mtd: fsmc_nand: pass the ale and cmd resource via resource Do not use the platform_data to pass resource and be smart in the drivers. Just pass it via resource Switch to devm_request_and_ioremap at the sametime Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Linus Walleij Reviewed-By: Vipin Kumar Signed-off-by: Artem Bityutskiy --- .../devicetree/bindings/mtd/fsmc-nand.txt | 12 +++--- arch/arm/boot/dts/spear13xx.dtsi | 10 ++--- arch/arm/boot/dts/spear300.dtsi | 8 ++-- arch/arm/boot/dts/spear310.dtsi | 8 ++-- arch/arm/boot/dts/spear320.dtsi | 8 ++-- arch/arm/boot/dts/spear600.dtsi | 8 ++-- arch/arm/mach-u300/core.c | 14 ++++++- drivers/mtd/nand/fsmc_nand.c | 44 ++++++---------------- include/linux/mtd/fsmc.h | 3 -- 9 files changed, 49 insertions(+), 66 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mtd/fsmc-nand.txt b/Documentation/devicetree/bindings/mtd/fsmc-nand.txt index e2c663b354d2..e3ea32e7de3e 100644 --- a/Documentation/devicetree/bindings/mtd/fsmc-nand.txt +++ b/Documentation/devicetree/bindings/mtd/fsmc-nand.txt @@ -3,9 +3,7 @@ Required properties: - compatible : "st,spear600-fsmc-nand" - reg : Address range of the mtd chip -- reg-names: Should contain the reg names "fsmc_regs" and "nand_data" -- st,ale-off : Chip specific offset to ALE -- st,cle-off : Chip specific offset to CLE +- reg-names: Should contain the reg names "fsmc_regs", "nand_data", "nand_addr" and "nand_cmd" Optional properties: - bank-width : Width (in bytes) of the device. If not present, the width @@ -19,10 +17,10 @@ Example: #address-cells = <1>; #size-cells = <1>; reg = <0xd1800000 0x1000 /* FSMC Register */ - 0xd2000000 0x4000>; /* NAND Base */ - reg-names = "fsmc_regs", "nand_data"; - st,ale-off = <0x20000>; - st,cle-off = <0x10000>; + 0xd2000000 0x0010 /* NAND Base DATA */ + 0xd2020000 0x0010 /* NAND Base ADDR */ + 0xd2010000 0x0010>; /* NAND Base CMD */ + reg-names = "fsmc_regs", "nand_data", "nand_addr", "nand_cmd"; bank-width = <1>; nand-skip-bbtscan; diff --git a/arch/arm/boot/dts/spear13xx.dtsi b/arch/arm/boot/dts/spear13xx.dtsi index f7b84aced654..14a6d15c2a81 100644 --- a/arch/arm/boot/dts/spear13xx.dtsi +++ b/arch/arm/boot/dts/spear13xx.dtsi @@ -104,15 +104,15 @@ compatible = "st,spear600-fsmc-nand"; #address-cells = <1>; #size-cells = <1>; - reg = <0xb0000000 0x1000 /* FSMC Register */ - 0xb0800000 0x0010>; /* NAND Base */ - reg-names = "fsmc_regs", "nand_data"; + reg = <0xb0000000 0x1000 /* FSMC Register*/ + 0xb0800000 0x0010 /* NAND Base DATA */ + 0xb0820000 0x0010 /* NAND Base ADDR */ + 0xb0810000 0x0010>; /* NAND Base CMD */ + reg-names = "fsmc_regs", "nand_data", "nand_addr", "nand_cmd"; interrupts = <0 20 0x4 0 21 0x4 0 22 0x4 0 23 0x4>; - st,ale-off = <0x20000>; - st,cle-off = <0x10000>; status = "disabled"; }; diff --git a/arch/arm/boot/dts/spear300.dtsi b/arch/arm/boot/dts/spear300.dtsi index ed3627c116cc..bc436387d7f9 100644 --- a/arch/arm/boot/dts/spear300.dtsi +++ b/arch/arm/boot/dts/spear300.dtsi @@ -38,10 +38,10 @@ #address-cells = <1>; #size-cells = <1>; reg = <0x94000000 0x1000 /* FSMC Register */ - 0x80000000 0x0010>; /* NAND Base */ - reg-names = "fsmc_regs", "nand_data"; - st,ale-off = <0x20000>; - st,cle-off = <0x10000>; + 0x80000000 0x0010 /* NAND Base DATA */ + 0x80020000 0x0010 /* NAND Base ADDR */ + 0x80010000 0x0010>; /* NAND Base CMD */ + reg-names = "fsmc_regs", "nand_data", "nand_addr", "nand_cmd"; status = "disabled"; }; diff --git a/arch/arm/boot/dts/spear310.dtsi b/arch/arm/boot/dts/spear310.dtsi index 62fc4fb3e5f9..7840e529aba2 100644 --- a/arch/arm/boot/dts/spear310.dtsi +++ b/arch/arm/boot/dts/spear310.dtsi @@ -32,10 +32,10 @@ #address-cells = <1>; #size-cells = <1>; reg = <0x44000000 0x1000 /* FSMC Register */ - 0x40000000 0x0010>; /* NAND Base */ - reg-names = "fsmc_regs", "nand_data"; - st,ale-off = <0x10000>; - st,cle-off = <0x20000>; + 0x40000000 0x0010 /* NAND Base DATA */ + 0x40020000 0x0010 /* NAND Base ADDR */ + 0x40010000 0x0010>; /* NAND Base CMD */ + reg-names = "fsmc_regs", "nand_data", "nand_addr", "nand_cmd"; status = "disabled"; }; diff --git a/arch/arm/boot/dts/spear320.dtsi b/arch/arm/boot/dts/spear320.dtsi index 1f49d69595a0..5ad820641ac0 100644 --- a/arch/arm/boot/dts/spear320.dtsi +++ b/arch/arm/boot/dts/spear320.dtsi @@ -38,10 +38,10 @@ #address-cells = <1>; #size-cells = <1>; reg = <0x4c000000 0x1000 /* FSMC Register */ - 0x50000000 0x0010>; /* NAND Base */ - reg-names = "fsmc_regs", "nand_data"; - st,ale-off = <0x20000>; - st,cle-off = <0x10000>; + 0x50000000 0x0010 /* NAND Base DATA */ + 0x50020000 0x0010 /* NAND Base ADDR */ + 0x50010000 0x0010>; /* NAND Base CMD */ + reg-names = "fsmc_regs", "nand_data", "nand_addr", "nand_cmd"; status = "disabled"; }; diff --git a/arch/arm/boot/dts/spear600.dtsi b/arch/arm/boot/dts/spear600.dtsi index a3c36e47d7ef..4ecc66f5ac88 100644 --- a/arch/arm/boot/dts/spear600.dtsi +++ b/arch/arm/boot/dts/spear600.dtsi @@ -67,10 +67,10 @@ #address-cells = <1>; #size-cells = <1>; reg = <0xd1800000 0x1000 /* FSMC Register */ - 0xd2000000 0x4000>; /* NAND Base */ - reg-names = "fsmc_regs", "nand_data"; - st,ale-off = <0x20000>; - st,cle-off = <0x10000>; + 0xd2000000 0x0010 /* NAND Base DATA */ + 0xd2020000 0x0010 /* NAND Base ADDR */ + 0xd2010000 0x0010>; /* NAND Base CMD */ + reg-names = "fsmc_regs", "nand_data", "nand_addr", "nand_cmd"; status = "disabled"; }; diff --git a/arch/arm/mach-u300/core.c b/arch/arm/mach-u300/core.c index b8efac4daed8..f642a1552346 100644 --- a/arch/arm/mach-u300/core.c +++ b/arch/arm/mach-u300/core.c @@ -251,6 +251,18 @@ static struct resource rtc_resources[] = { * but these are not yet used by the driver. */ static struct resource fsmc_resources[] = { + { + .name = "nand_addr", + .start = U300_NAND_CS0_PHYS_BASE + PLAT_NAND_ALE, + .end = U300_NAND_CS0_PHYS_BASE + PLAT_NAND_ALE + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "nand_cmd", + .start = U300_NAND_CS0_PHYS_BASE + PLAT_NAND_CLE, + .end = U300_NAND_CS0_PHYS_BASE + PLAT_NAND_CLE + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, { .name = "nand_data", .start = U300_NAND_CS0_PHYS_BASE, @@ -1496,8 +1508,6 @@ static struct fsmc_nand_platform_data nand_platform_data = { .nr_partitions = ARRAY_SIZE(u300_partitions), .options = NAND_SKIP_BBTSCAN, .width = FSMC_NAND_BW8, - .ale_off = PLAT_NAND_ALE, - .cle_off = PLAT_NAND_CLE, }; static struct platform_device nand_device = { diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 38d26240d8b1..cb8645087151 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -876,8 +876,6 @@ static int __devinit fsmc_nand_probe_config_dt(struct platform_device *pdev, return -EINVAL; } } - of_property_read_u32(np, "st,ale-off", &pdata->ale_off); - of_property_read_u32(np, "st,cle-off", &pdata->cle_off); if (of_get_property(np, "nand-skip-bbtscan", NULL)) pdata->options = NAND_SKIP_BBTSCAN; @@ -935,41 +933,28 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) if (!res) return -EINVAL; - if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), - pdev->name)) { - dev_err(&pdev->dev, "Failed to get memory data resourse\n"); - return -ENOENT; - } - - host->data_pa = (dma_addr_t)res->start; - host->data_va = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); + host->data_va = devm_request_and_ioremap(&pdev->dev, res); if (!host->data_va) { dev_err(&pdev->dev, "data ioremap failed\n"); return -ENOMEM; } + host->data_pa = (dma_addr_t)res->start; - if (!devm_request_mem_region(&pdev->dev, res->start + pdata->ale_off, - resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "Failed to get memory ale resourse\n"); - return -ENOENT; - } + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr"); + if (!res) + return -EINVAL; - host->addr_va = devm_ioremap(&pdev->dev, res->start + pdata->ale_off, - resource_size(res)); + host->addr_va = devm_request_and_ioremap(&pdev->dev, res); if (!host->addr_va) { dev_err(&pdev->dev, "ale ioremap failed\n"); return -ENOMEM; } - if (!devm_request_mem_region(&pdev->dev, res->start + pdata->cle_off, - resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "Failed to get memory cle resourse\n"); - return -ENOENT; - } + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd"); + if (!res) + return -EINVAL; - host->cmd_va = devm_ioremap(&pdev->dev, res->start + pdata->cle_off, - resource_size(res)); + host->cmd_va = devm_request_and_ioremap(&pdev->dev, res); if (!host->cmd_va) { dev_err(&pdev->dev, "ale ioremap failed\n"); return -ENOMEM; @@ -979,14 +964,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) if (!res) return -EINVAL; - if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), - pdev->name)) { - dev_err(&pdev->dev, "Failed to get memory regs resourse\n"); - return -ENOENT; - } - - host->regs_va = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); + host->regs_va = devm_request_and_ioremap(&pdev->dev, res); if (!host->regs_va) { dev_err(&pdev->dev, "regs ioremap failed\n"); return -ENOMEM; diff --git a/include/linux/mtd/fsmc.h b/include/linux/mtd/fsmc.h index b20029221fb1..d6ed61ef451d 100644 --- a/include/linux/mtd/fsmc.h +++ b/include/linux/mtd/fsmc.h @@ -155,9 +155,6 @@ struct fsmc_nand_platform_data { unsigned int width; unsigned int bank; - /* CLE, ALE offsets */ - unsigned int cle_off; - unsigned int ale_off; enum access_mode mode; void (*select_bank)(uint32_t bank, uint32_t busw); -- cgit v1.2.3 From 7c8f680e96edbd9896b13b5e6ff39bc5852dce2a Mon Sep 17 00:00:00 2001 From: Bastian Hecht Date: Fri, 19 Oct 2012 12:15:36 +0200 Subject: mtd: sh_flctl: Add device tree support The flctl can now be probed via device tree setup in addition to the existing platform data way. SoC specific setup data is set in the .data member of the OF match, so kept within the driver itself, while board/user specific setup - like partitioning - is taken from the device tree. Actual configuration is added for the SoC sh7372. Signed-off-by: Bastian Hecht Signed-off-by: Artem Bityutskiy --- .../devicetree/bindings/mtd/flctl-nand.txt | 49 +++++++++++ drivers/mtd/nand/sh_flctl.c | 94 ++++++++++++++++++++-- 2 files changed, 136 insertions(+), 7 deletions(-) create mode 100644 Documentation/devicetree/bindings/mtd/flctl-nand.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mtd/flctl-nand.txt b/Documentation/devicetree/bindings/mtd/flctl-nand.txt new file mode 100644 index 000000000000..427f46dc60ad --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/flctl-nand.txt @@ -0,0 +1,49 @@ +FLCTL NAND controller + +Required properties: +- compatible : "renesas,shmobile-flctl-sh7372" +- reg : Address range of the FLCTL +- interrupts : flste IRQ number +- nand-bus-width : bus width to NAND chip + +Optional properties: +- dmas: DMA specifier(s) +- dma-names: name for each DMA specifier. Valid names are + "data_tx", "data_rx", "ecc_tx", "ecc_rx" + +The DMA fields are not used yet in the driver but are listed here for +completing the bindings. + +The device tree may optionally contain sub-nodes describing partitions of the +address space. See partition.txt for more detail. + +Example: + + flctl@e6a30000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "renesas,shmobile-flctl-sh7372"; + reg = <0xe6a30000 0x100>; + interrupts = <0x0d80>; + + nand-bus-width = <16>; + + dmas = <&dmac 1 /* data_tx */ + &dmac 2;> /* data_rx */ + dma-names = "data_tx", "data_rx"; + + system@0 { + label = "system"; + reg = <0x0 0x8000000>; + }; + + userdata@8000000 { + label = "userdata"; + reg = <0x8000000 0x10000000>; + }; + + cache@18000000 { + label = "cache"; + reg = <0x18000000 0x8000000>; + }; + }; diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 6dc0369aa44b..d51e3c15d379 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -29,6 +29,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -1016,6 +1019,73 @@ static irqreturn_t flctl_handle_flste(int irq, void *dev_id) return IRQ_HANDLED; } +#ifdef CONFIG_OF +struct flctl_soc_config { + unsigned long flcmncr_val; + unsigned has_hwecc:1; + unsigned use_holden:1; +}; + +static struct flctl_soc_config flctl_sh7372_config = { + .flcmncr_val = CLK_16B_12L_4H | TYPESEL_SET | SHBUSSEL, + .has_hwecc = 1, + .use_holden = 1, +}; + +static const struct of_device_id of_flctl_match[] = { + { .compatible = "renesas,shmobile-flctl-sh7372", + .data = &flctl_sh7372_config }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_flctl_match); + +static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev) +{ + const struct of_device_id *match; + struct flctl_soc_config *config; + struct sh_flctl_platform_data *pdata; + struct device_node *dn = dev->of_node; + int ret; + + match = of_match_device(of_flctl_match, dev); + if (match) + config = (struct flctl_soc_config *)match->data; + else { + dev_err(dev, "%s: no OF configuration attached\n", __func__); + return NULL; + } + + pdata = devm_kzalloc(dev, sizeof(struct sh_flctl_platform_data), + GFP_KERNEL); + if (!pdata) { + dev_err(dev, "%s: failed to allocate config data\n", __func__); + return NULL; + } + + /* set SoC specific options */ + pdata->flcmncr_val = config->flcmncr_val; + pdata->has_hwecc = config->has_hwecc; + pdata->use_holden = config->use_holden; + + /* parse user defined options */ + ret = of_get_nand_bus_width(dn); + if (ret == 16) + pdata->flcmncr_val |= SEL_16BIT; + else if (ret != 8) { + dev_err(dev, "%s: invalid bus width\n", __func__); + return NULL; + } + + return pdata; +} +#else /* CONFIG_OF */ +#define of_flctl_match NULL +static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev) +{ + return NULL; +} +#endif /* CONFIG_OF */ + static int __devinit flctl_probe(struct platform_device *pdev) { struct resource *res; @@ -1025,12 +1095,7 @@ static int __devinit flctl_probe(struct platform_device *pdev) struct sh_flctl_platform_data *pdata; int ret = -ENXIO; int irq; - - pdata = pdev->dev.platform_data; - if (pdata == NULL) { - dev_err(&pdev->dev, "no platform data defined\n"); - return -EINVAL; - } + struct mtd_part_parser_data ppdata = {}; flctl = kzalloc(sizeof(struct sh_flctl), GFP_KERNEL); if (!flctl) { @@ -1062,6 +1127,17 @@ static int __devinit flctl_probe(struct platform_device *pdev) goto err_flste; } + if (pdev->dev.of_node) + pdata = flctl_parse_dt(&pdev->dev); + else + pdata = pdev->dev.platform_data; + + if (!pdata) { + dev_err(&pdev->dev, "no setup data defined\n"); + ret = -EINVAL; + goto err_pdata; + } + platform_set_drvdata(pdev, flctl); flctl_mtd = &flctl->mtd; nand = &flctl->chip; @@ -1104,13 +1180,16 @@ static int __devinit flctl_probe(struct platform_device *pdev) if (ret) goto err_chip; - mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts); + ppdata.of_node = pdev->dev.of_node; + ret = mtd_device_parse_register(flctl_mtd, NULL, &ppdata, pdata->parts, + pdata->nr_parts); return 0; err_chip: flctl_release_dma(flctl); pm_runtime_disable(&pdev->dev); +err_pdata: free_irq(irq, flctl); err_flste: iounmap(flctl->reg); @@ -1138,6 +1217,7 @@ static struct platform_driver flctl_driver = { .driver = { .name = "sh_flctl", .owner = THIS_MODULE, + .of_match_table = of_flctl_match, }, }; -- cgit v1.2.3 From 16c4c52435cf8bb6e5986c14d36d93a0dd4c98df Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 13 Nov 2012 18:16:43 +0100 Subject: bindings: i2c: use consistent naming for i2c binding descriptions Filenames of devictree binding documentation seems to be arbitrary and for me it is unneeded hazzle to find the corresponding documentation for a specific driver. Naming the description the same as the driver is a lot easier and makes sense to me since the driver defines the binding it understands. Also, remove a reference in one source to the binding documentation, since path information easily gets stale. Signed-off-by: Wolfram Sang Cc: Rob Herring Cc: Grant Likely Signed-off-by: Grant Likely --- .../devicetree/bindings/i2c/arm-versatile.txt | 10 --- .../devicetree/bindings/i2c/atmel-i2c.txt | 30 ------- .../devicetree/bindings/i2c/cavium-i2c.txt | 34 -------- .../devicetree/bindings/i2c/ce4100-i2c.txt | 93 ---------------------- Documentation/devicetree/bindings/i2c/davinci.txt | 28 ------- Documentation/devicetree/bindings/i2c/fsl-i2c.txt | 64 --------------- .../devicetree/bindings/i2c/fsl-imx-i2c.txt | 25 ------ Documentation/devicetree/bindings/i2c/gpio-i2c.txt | 32 -------- Documentation/devicetree/bindings/i2c/i2c-at91.txt | 30 +++++++ .../devicetree/bindings/i2c/i2c-davinci.txt | 28 +++++++ Documentation/devicetree/bindings/i2c/i2c-gpio.txt | 32 ++++++++ Documentation/devicetree/bindings/i2c/i2c-imx.txt | 25 ++++++ Documentation/devicetree/bindings/i2c/i2c-mpc.txt | 64 +++++++++++++++ Documentation/devicetree/bindings/i2c/i2c-mux.txt | 60 ++++++++++++++ .../devicetree/bindings/i2c/i2c-mv64xxx.txt | 18 +++++ .../devicetree/bindings/i2c/i2c-nomadik.txt | 23 ++++++ .../devicetree/bindings/i2c/i2c-octeon.txt | 34 ++++++++ Documentation/devicetree/bindings/i2c/i2c-omap.txt | 30 +++++++ Documentation/devicetree/bindings/i2c/i2c-pnx.txt | 36 +++++++++ .../devicetree/bindings/i2c/i2c-pxa-pci-ce4100.txt | 93 ++++++++++++++++++++++ Documentation/devicetree/bindings/i2c/i2c-pxa.txt | 33 ++++++++ .../devicetree/bindings/i2c/i2c-s3c2410.txt | 43 ++++++++++ Documentation/devicetree/bindings/i2c/i2c-sirf.txt | 19 +++++ .../devicetree/bindings/i2c/i2c-versatile.txt | 10 +++ Documentation/devicetree/bindings/i2c/i2c-xiic.txt | 22 +++++ Documentation/devicetree/bindings/i2c/mrvl-i2c.txt | 51 ------------ Documentation/devicetree/bindings/i2c/mux.txt | 60 -------------- Documentation/devicetree/bindings/i2c/nomadik.txt | 23 ------ Documentation/devicetree/bindings/i2c/omap-i2c.txt | 30 ------- Documentation/devicetree/bindings/i2c/pnx.txt | 36 --------- .../devicetree/bindings/i2c/samsung-i2c.txt | 43 ---------- Documentation/devicetree/bindings/i2c/sirf-i2c.txt | 19 ----- Documentation/devicetree/bindings/i2c/xiic.txt | 22 ----- drivers/i2c/busses/i2c-ocores.c | 4 - 34 files changed, 600 insertions(+), 604 deletions(-) delete mode 100644 Documentation/devicetree/bindings/i2c/arm-versatile.txt delete mode 100644 Documentation/devicetree/bindings/i2c/atmel-i2c.txt delete mode 100644 Documentation/devicetree/bindings/i2c/cavium-i2c.txt delete mode 100644 Documentation/devicetree/bindings/i2c/ce4100-i2c.txt delete mode 100644 Documentation/devicetree/bindings/i2c/davinci.txt delete mode 100644 Documentation/devicetree/bindings/i2c/fsl-i2c.txt delete mode 100644 Documentation/devicetree/bindings/i2c/fsl-imx-i2c.txt delete mode 100644 Documentation/devicetree/bindings/i2c/gpio-i2c.txt create mode 100644 Documentation/devicetree/bindings/i2c/i2c-at91.txt create mode 100644 Documentation/devicetree/bindings/i2c/i2c-davinci.txt create mode 100644 Documentation/devicetree/bindings/i2c/i2c-gpio.txt create mode 100644 Documentation/devicetree/bindings/i2c/i2c-imx.txt create mode 100644 Documentation/devicetree/bindings/i2c/i2c-mpc.txt create mode 100644 Documentation/devicetree/bindings/i2c/i2c-mux.txt create mode 100644 Documentation/devicetree/bindings/i2c/i2c-mv64xxx.txt create mode 100644 Documentation/devicetree/bindings/i2c/i2c-nomadik.txt create mode 100644 Documentation/devicetree/bindings/i2c/i2c-octeon.txt create mode 100644 Documentation/devicetree/bindings/i2c/i2c-omap.txt create mode 100644 Documentation/devicetree/bindings/i2c/i2c-pnx.txt create mode 100644 Documentation/devicetree/bindings/i2c/i2c-pxa-pci-ce4100.txt create mode 100644 Documentation/devicetree/bindings/i2c/i2c-pxa.txt create mode 100644 Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt create mode 100644 Documentation/devicetree/bindings/i2c/i2c-sirf.txt create mode 100644 Documentation/devicetree/bindings/i2c/i2c-versatile.txt create mode 100644 Documentation/devicetree/bindings/i2c/i2c-xiic.txt delete mode 100644 Documentation/devicetree/bindings/i2c/mrvl-i2c.txt delete mode 100644 Documentation/devicetree/bindings/i2c/mux.txt delete mode 100644 Documentation/devicetree/bindings/i2c/nomadik.txt delete mode 100644 Documentation/devicetree/bindings/i2c/omap-i2c.txt delete mode 100644 Documentation/devicetree/bindings/i2c/pnx.txt delete mode 100644 Documentation/devicetree/bindings/i2c/samsung-i2c.txt delete mode 100644 Documentation/devicetree/bindings/i2c/sirf-i2c.txt delete mode 100644 Documentation/devicetree/bindings/i2c/xiic.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/i2c/arm-versatile.txt b/Documentation/devicetree/bindings/i2c/arm-versatile.txt deleted file mode 100644 index 361d31c51b6f..000000000000 --- a/Documentation/devicetree/bindings/i2c/arm-versatile.txt +++ /dev/null @@ -1,10 +0,0 @@ -i2c Controller on ARM Versatile platform: - -Required properties: -- compatible : Must be "arm,versatile-i2c"; -- reg -- #address-cells = <1>; -- #size-cells = <0>; - -Optional properties: -- Child nodes conforming to i2c bus binding diff --git a/Documentation/devicetree/bindings/i2c/atmel-i2c.txt b/Documentation/devicetree/bindings/i2c/atmel-i2c.txt deleted file mode 100644 index b689a0d9441c..000000000000 --- a/Documentation/devicetree/bindings/i2c/atmel-i2c.txt +++ /dev/null @@ -1,30 +0,0 @@ -I2C for Atmel platforms - -Required properties : -- compatible : Must be "atmel,at91rm9200-i2c", "atmel,at91sam9261-i2c", - "atmel,at91sam9260-i2c", "atmel,at91sam9g20-i2c", "atmel,at91sam9g10-i2c" - or "atmel,at91sam9x5-i2c" -- reg: physical base address of the controller and length of memory mapped - region. -- interrupts: interrupt number to the cpu. -- #address-cells = <1>; -- #size-cells = <0>; - -Optional properties: -- Child nodes conforming to i2c bus binding - -Examples : - -i2c0: i2c@fff84000 { - compatible = "atmel,at91sam9g20-i2c"; - reg = <0xfff84000 0x100>; - interrupts = <12 4 6>; - #address-cells = <1>; - #size-cells = <0>; - - 24c512@50 { - compatible = "24c512"; - reg = <0x50>; - pagesize = <128>; - } -} diff --git a/Documentation/devicetree/bindings/i2c/cavium-i2c.txt b/Documentation/devicetree/bindings/i2c/cavium-i2c.txt deleted file mode 100644 index dced82ebe31d..000000000000 --- a/Documentation/devicetree/bindings/i2c/cavium-i2c.txt +++ /dev/null @@ -1,34 +0,0 @@ -* Two Wire Serial Interface (TWSI) / I2C - -- compatible: "cavium,octeon-3860-twsi" - - Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs. - -- reg: The base address of the TWSI/I2C bus controller register bank. - -- #address-cells: Must be <1>. - -- #size-cells: Must be <0>. I2C addresses have no size component. - -- interrupts: A single interrupt specifier. - -- clock-frequency: The I2C bus clock rate in Hz. - -Example: - twsi0: i2c@1180000001000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "cavium,octeon-3860-twsi"; - reg = <0x11800 0x00001000 0x0 0x200>; - interrupts = <0 45>; - clock-frequency = <100000>; - - rtc@68 { - compatible = "dallas,ds1337"; - reg = <0x68>; - }; - tmp@4c { - compatible = "ti,tmp421"; - reg = <0x4c>; - }; - }; diff --git a/Documentation/devicetree/bindings/i2c/ce4100-i2c.txt b/Documentation/devicetree/bindings/i2c/ce4100-i2c.txt deleted file mode 100644 index 569b16248514..000000000000 --- a/Documentation/devicetree/bindings/i2c/ce4100-i2c.txt +++ /dev/null @@ -1,93 +0,0 @@ -CE4100 I2C ----------- - -CE4100 has one PCI device which is described as the I2C-Controller. This -PCI device has three PCI-bars, each bar contains a complete I2C -controller. So we have a total of three independent I2C-Controllers -which share only an interrupt line. -The driver is probed via the PCI-ID and is gathering the information of -attached devices from the devices tree. -Grant Likely recommended to use the ranges property to map the PCI-Bar -number to its physical address and to use this to find the child nodes -of the specific I2C controller. This were his exact words: - - Here's where the magic happens. Each entry in - ranges describes how the parent pci address space - (middle group of 3) is translated to the local - address space (first group of 2) and the size of - each range (last cell). In this particular case, - the first cell of the local address is chosen to be - 1:1 mapped to the BARs, and the second is the - offset from be base of the BAR (which would be - non-zero if you had 2 or more devices mapped off - the same BAR) - - ranges allows the address mapping to be described - in a way that the OS can interpret without - requiring custom device driver code. - -This is an example which is used on FalconFalls: ------------------------------------------------- - i2c-controller@b,2 { - #address-cells = <2>; - #size-cells = <1>; - compatible = "pci8086,2e68.2", - "pci8086,2e68", - "pciclass,ff0000", - "pciclass,ff00"; - - reg = <0x15a00 0x0 0x0 0x0 0x0>; - interrupts = <16 1>; - - /* as described by Grant, the first number in the group of - * three is the bar number followed by the 64bit bar address - * followed by size of the mapping. The bar address - * requires also a valid translation in parents ranges - * property. - */ - ranges = <0 0 0x02000000 0 0xdffe0500 0x100 - 1 0 0x02000000 0 0xdffe0600 0x100 - 2 0 0x02000000 0 0xdffe0700 0x100>; - - i2c@0 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "intel,ce4100-i2c-controller"; - - /* The first number in the reg property is the - * number of the bar - */ - reg = <0 0 0x100>; - - /* This I2C controller has no devices */ - }; - - i2c@1 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "intel,ce4100-i2c-controller"; - reg = <1 0 0x100>; - - /* This I2C controller has one gpio controller */ - gpio@26 { - #gpio-cells = <2>; - compatible = "ti,pcf8575"; - reg = <0x26>; - gpio-controller; - }; - }; - - i2c@2 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "intel,ce4100-i2c-controller"; - reg = <2 0 0x100>; - - gpio@26 { - #gpio-cells = <2>; - compatible = "ti,pcf8575"; - reg = <0x26>; - gpio-controller; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/i2c/davinci.txt b/Documentation/devicetree/bindings/i2c/davinci.txt deleted file mode 100644 index 2dc935b4113d..000000000000 --- a/Documentation/devicetree/bindings/i2c/davinci.txt +++ /dev/null @@ -1,28 +0,0 @@ -* Texas Instruments Davinci I2C - -This file provides information, what the device node for the -davinci i2c interface contain. - -Required properties: -- compatible: "ti,davinci-i2c"; -- reg : Offset and length of the register set for the device - -Recommended properties : -- interrupts : standard interrupt property. -- clock-frequency : desired I2C bus clock frequency in Hz. - -Example (enbw_cmc board): - i2c@1c22000 { - compatible = "ti,davinci-i2c"; - reg = <0x22000 0x1000>; - clock-frequency = <100000>; - interrupts = <15>; - interrupt-parent = <&intc>; - #address-cells = <1>; - #size-cells = <0>; - - dtt@48 { - compatible = "national,lm75"; - reg = <0x48>; - }; - }; diff --git a/Documentation/devicetree/bindings/i2c/fsl-i2c.txt b/Documentation/devicetree/bindings/i2c/fsl-i2c.txt deleted file mode 100644 index 1eacd6b20ed5..000000000000 --- a/Documentation/devicetree/bindings/i2c/fsl-i2c.txt +++ /dev/null @@ -1,64 +0,0 @@ -* I2C - -Required properties : - - - reg : Offset and length of the register set for the device - - compatible : should be "fsl,CHIP-i2c" where CHIP is the name of a - compatible processor, e.g. mpc8313, mpc8543, mpc8544, mpc5121, - mpc5200 or mpc5200b. For the mpc5121, an additional node - "fsl,mpc5121-i2c-ctrl" is required as shown in the example below. - -Recommended properties : - - - interrupts : where a is the interrupt number and b is a - field that represents an encoding of the sense and level - information for the interrupt. This should be encoded based on - the information in section 2) depending on the type of interrupt - controller you have. - - interrupt-parent : the phandle for the interrupt controller that - services interrupts for this device. - - fsl,preserve-clocking : boolean; if defined, the clock settings - from the bootloader are preserved (not touched). - - clock-frequency : desired I2C bus clock frequency in Hz. - - fsl,timeout : I2C bus timeout in microseconds. - -Examples : - - /* MPC5121 based board */ - i2c@1740 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "fsl,mpc5121-i2c", "fsl-i2c"; - reg = <0x1740 0x20>; - interrupts = <11 0x8>; - interrupt-parent = <&ipic>; - clock-frequency = <100000>; - }; - - i2ccontrol@1760 { - compatible = "fsl,mpc5121-i2c-ctrl"; - reg = <0x1760 0x8>; - }; - - /* MPC5200B based board */ - i2c@3d00 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "fsl,mpc5200b-i2c","fsl,mpc5200-i2c","fsl-i2c"; - reg = <0x3d00 0x40>; - interrupts = <2 15 0>; - interrupt-parent = <&mpc5200_pic>; - fsl,preserve-clocking; - }; - - /* MPC8544 base board */ - i2c@3100 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "fsl,mpc8544-i2c", "fsl-i2c"; - reg = <0x3100 0x100>; - interrupts = <43 2>; - interrupt-parent = <&mpic>; - clock-frequency = <400000>; - fsl,timeout = <10000>; - }; diff --git a/Documentation/devicetree/bindings/i2c/fsl-imx-i2c.txt b/Documentation/devicetree/bindings/i2c/fsl-imx-i2c.txt deleted file mode 100644 index f3cf43b66f7e..000000000000 --- a/Documentation/devicetree/bindings/i2c/fsl-imx-i2c.txt +++ /dev/null @@ -1,25 +0,0 @@ -* Freescale Inter IC (I2C) and High Speed Inter IC (HS-I2C) for i.MX - -Required properties: -- compatible : Should be "fsl,-i2c" -- reg : Should contain I2C/HS-I2C registers location and length -- interrupts : Should contain I2C/HS-I2C interrupt - -Optional properties: -- clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in Hz. - The absence of the propoerty indicates the default frequency 100 kHz. - -Examples: - -i2c@83fc4000 { /* I2C2 on i.MX51 */ - compatible = "fsl,imx51-i2c", "fsl,imx1-i2c"; - reg = <0x83fc4000 0x4000>; - interrupts = <63>; -}; - -i2c@70038000 { /* HS-I2C on i.MX51 */ - compatible = "fsl,imx51-i2c", "fsl,imx1-i2c"; - reg = <0x70038000 0x4000>; - interrupts = <64>; - clock-frequency = <400000>; -}; diff --git a/Documentation/devicetree/bindings/i2c/gpio-i2c.txt b/Documentation/devicetree/bindings/i2c/gpio-i2c.txt deleted file mode 100644 index 4f8ec947c6bd..000000000000 --- a/Documentation/devicetree/bindings/i2c/gpio-i2c.txt +++ /dev/null @@ -1,32 +0,0 @@ -Device-Tree bindings for i2c gpio driver - -Required properties: - - compatible = "i2c-gpio"; - - gpios: sda and scl gpio - - -Optional properties: - - i2c-gpio,sda-open-drain: sda as open drain - - i2c-gpio,scl-open-drain: scl as open drain - - i2c-gpio,scl-output-only: scl as output only - - i2c-gpio,delay-us: delay between GPIO operations (may depend on each platform) - - i2c-gpio,timeout-ms: timeout to get data - -Example nodes: - -i2c@0 { - compatible = "i2c-gpio"; - gpios = <&pioA 23 0 /* sda */ - &pioA 24 0 /* scl */ - >; - i2c-gpio,sda-open-drain; - i2c-gpio,scl-open-drain; - i2c-gpio,delay-us = <2>; /* ~100 kHz */ - #address-cells = <1>; - #size-cells = <0>; - - rv3029c2@56 { - compatible = "rv3029c2"; - reg = <0x56>; - }; -}; diff --git a/Documentation/devicetree/bindings/i2c/i2c-at91.txt b/Documentation/devicetree/bindings/i2c/i2c-at91.txt new file mode 100644 index 000000000000..b689a0d9441c --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-at91.txt @@ -0,0 +1,30 @@ +I2C for Atmel platforms + +Required properties : +- compatible : Must be "atmel,at91rm9200-i2c", "atmel,at91sam9261-i2c", + "atmel,at91sam9260-i2c", "atmel,at91sam9g20-i2c", "atmel,at91sam9g10-i2c" + or "atmel,at91sam9x5-i2c" +- reg: physical base address of the controller and length of memory mapped + region. +- interrupts: interrupt number to the cpu. +- #address-cells = <1>; +- #size-cells = <0>; + +Optional properties: +- Child nodes conforming to i2c bus binding + +Examples : + +i2c0: i2c@fff84000 { + compatible = "atmel,at91sam9g20-i2c"; + reg = <0xfff84000 0x100>; + interrupts = <12 4 6>; + #address-cells = <1>; + #size-cells = <0>; + + 24c512@50 { + compatible = "24c512"; + reg = <0x50>; + pagesize = <128>; + } +} diff --git a/Documentation/devicetree/bindings/i2c/i2c-davinci.txt b/Documentation/devicetree/bindings/i2c/i2c-davinci.txt new file mode 100644 index 000000000000..2dc935b4113d --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-davinci.txt @@ -0,0 +1,28 @@ +* Texas Instruments Davinci I2C + +This file provides information, what the device node for the +davinci i2c interface contain. + +Required properties: +- compatible: "ti,davinci-i2c"; +- reg : Offset and length of the register set for the device + +Recommended properties : +- interrupts : standard interrupt property. +- clock-frequency : desired I2C bus clock frequency in Hz. + +Example (enbw_cmc board): + i2c@1c22000 { + compatible = "ti,davinci-i2c"; + reg = <0x22000 0x1000>; + clock-frequency = <100000>; + interrupts = <15>; + interrupt-parent = <&intc>; + #address-cells = <1>; + #size-cells = <0>; + + dtt@48 { + compatible = "national,lm75"; + reg = <0x48>; + }; + }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-gpio.txt b/Documentation/devicetree/bindings/i2c/i2c-gpio.txt new file mode 100644 index 000000000000..4f8ec947c6bd --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-gpio.txt @@ -0,0 +1,32 @@ +Device-Tree bindings for i2c gpio driver + +Required properties: + - compatible = "i2c-gpio"; + - gpios: sda and scl gpio + + +Optional properties: + - i2c-gpio,sda-open-drain: sda as open drain + - i2c-gpio,scl-open-drain: scl as open drain + - i2c-gpio,scl-output-only: scl as output only + - i2c-gpio,delay-us: delay between GPIO operations (may depend on each platform) + - i2c-gpio,timeout-ms: timeout to get data + +Example nodes: + +i2c@0 { + compatible = "i2c-gpio"; + gpios = <&pioA 23 0 /* sda */ + &pioA 24 0 /* scl */ + >; + i2c-gpio,sda-open-drain; + i2c-gpio,scl-open-drain; + i2c-gpio,delay-us = <2>; /* ~100 kHz */ + #address-cells = <1>; + #size-cells = <0>; + + rv3029c2@56 { + compatible = "rv3029c2"; + reg = <0x56>; + }; +}; diff --git a/Documentation/devicetree/bindings/i2c/i2c-imx.txt b/Documentation/devicetree/bindings/i2c/i2c-imx.txt new file mode 100644 index 000000000000..f3cf43b66f7e --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-imx.txt @@ -0,0 +1,25 @@ +* Freescale Inter IC (I2C) and High Speed Inter IC (HS-I2C) for i.MX + +Required properties: +- compatible : Should be "fsl,-i2c" +- reg : Should contain I2C/HS-I2C registers location and length +- interrupts : Should contain I2C/HS-I2C interrupt + +Optional properties: +- clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in Hz. + The absence of the propoerty indicates the default frequency 100 kHz. + +Examples: + +i2c@83fc4000 { /* I2C2 on i.MX51 */ + compatible = "fsl,imx51-i2c", "fsl,imx1-i2c"; + reg = <0x83fc4000 0x4000>; + interrupts = <63>; +}; + +i2c@70038000 { /* HS-I2C on i.MX51 */ + compatible = "fsl,imx51-i2c", "fsl,imx1-i2c"; + reg = <0x70038000 0x4000>; + interrupts = <64>; + clock-frequency = <400000>; +}; diff --git a/Documentation/devicetree/bindings/i2c/i2c-mpc.txt b/Documentation/devicetree/bindings/i2c/i2c-mpc.txt new file mode 100644 index 000000000000..1eacd6b20ed5 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-mpc.txt @@ -0,0 +1,64 @@ +* I2C + +Required properties : + + - reg : Offset and length of the register set for the device + - compatible : should be "fsl,CHIP-i2c" where CHIP is the name of a + compatible processor, e.g. mpc8313, mpc8543, mpc8544, mpc5121, + mpc5200 or mpc5200b. For the mpc5121, an additional node + "fsl,mpc5121-i2c-ctrl" is required as shown in the example below. + +Recommended properties : + + - interrupts : where a is the interrupt number and b is a + field that represents an encoding of the sense and level + information for the interrupt. This should be encoded based on + the information in section 2) depending on the type of interrupt + controller you have. + - interrupt-parent : the phandle for the interrupt controller that + services interrupts for this device. + - fsl,preserve-clocking : boolean; if defined, the clock settings + from the bootloader are preserved (not touched). + - clock-frequency : desired I2C bus clock frequency in Hz. + - fsl,timeout : I2C bus timeout in microseconds. + +Examples : + + /* MPC5121 based board */ + i2c@1740 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,mpc5121-i2c", "fsl-i2c"; + reg = <0x1740 0x20>; + interrupts = <11 0x8>; + interrupt-parent = <&ipic>; + clock-frequency = <100000>; + }; + + i2ccontrol@1760 { + compatible = "fsl,mpc5121-i2c-ctrl"; + reg = <0x1760 0x8>; + }; + + /* MPC5200B based board */ + i2c@3d00 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,mpc5200b-i2c","fsl,mpc5200-i2c","fsl-i2c"; + reg = <0x3d00 0x40>; + interrupts = <2 15 0>; + interrupt-parent = <&mpc5200_pic>; + fsl,preserve-clocking; + }; + + /* MPC8544 base board */ + i2c@3100 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,mpc8544-i2c", "fsl-i2c"; + reg = <0x3100 0x100>; + interrupts = <43 2>; + interrupt-parent = <&mpic>; + clock-frequency = <400000>; + fsl,timeout = <10000>; + }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux.txt b/Documentation/devicetree/bindings/i2c/i2c-mux.txt new file mode 100644 index 000000000000..af84cce5cd7b --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-mux.txt @@ -0,0 +1,60 @@ +Common i2c bus multiplexer/switch properties. + +An i2c bus multiplexer/switch will have several child busses that are +numbered uniquely in a device dependent manner. The nodes for an i2c bus +multiplexer/switch will have one child node for each child +bus. + +Required properties: +- #address-cells = <1>; +- #size-cells = <0>; + +Required properties for child nodes: +- #address-cells = <1>; +- #size-cells = <0>; +- reg : The sub-bus number. + +Optional properties for child nodes: +- Other properties specific to the multiplexer/switch hardware. +- Child nodes conforming to i2c bus binding + + +Example : + + /* + An NXP pca9548 8 channel I2C multiplexer at address 0x70 + with two NXP pca8574 GPIO expanders attached, one each to + ports 3 and 4. + */ + + mux@70 { + compatible = "nxp,pca9548"; + reg = <0x70>; + #address-cells = <1>; + #size-cells = <0>; + + i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + + gpio1: gpio@38 { + compatible = "nxp,pca8574"; + reg = <0x38>; + #gpio-cells = <2>; + gpio-controller; + }; + }; + i2c@4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + + gpio2: gpio@38 { + compatible = "nxp,pca8574"; + reg = <0x38>; + #gpio-cells = <2>; + gpio-controller; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-mv64xxx.txt b/Documentation/devicetree/bindings/i2c/i2c-mv64xxx.txt new file mode 100644 index 000000000000..f46d928aa73d --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-mv64xxx.txt @@ -0,0 +1,18 @@ + +* Marvell MV64XXX I2C controller + +Required properties : + + - reg : Offset and length of the register set for the device + - compatible : Should be "marvell,mv64xxx-i2c" + - interrupts : The interrupt number + - clock-frequency : Desired I2C bus clock frequency in Hz. + +Examples: + + i2c@11000 { + compatible = "marvell,mv64xxx-i2c"; + reg = <0x11000 0x20>; + interrupts = <29>; + clock-frequency = <100000>; + }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-nomadik.txt b/Documentation/devicetree/bindings/i2c/i2c-nomadik.txt new file mode 100644 index 000000000000..72065b0ff680 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-nomadik.txt @@ -0,0 +1,23 @@ +I2C for Nomadik based systems + +Required (non-standard) properties: + - Nil + +Recommended (non-standard) properties: + - clock-frequency : Maximum bus clock frequency for the device + +Optional (non-standard) properties: + - Nil + +Example : + +i2c@80004000 { + compatible = "stericsson,db8500-i2c", "st,nomadik-i2c"; + reg = <0x80004000 0x1000>; + interrupts = <0 21 0x4>; + #address-cells = <1>; + #size-cells = <0>; + v-i2c-supply = <&db8500_vape_reg>; + + clock-frequency = <400000>; +}; diff --git a/Documentation/devicetree/bindings/i2c/i2c-octeon.txt b/Documentation/devicetree/bindings/i2c/i2c-octeon.txt new file mode 100644 index 000000000000..dced82ebe31d --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-octeon.txt @@ -0,0 +1,34 @@ +* Two Wire Serial Interface (TWSI) / I2C + +- compatible: "cavium,octeon-3860-twsi" + + Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs. + +- reg: The base address of the TWSI/I2C bus controller register bank. + +- #address-cells: Must be <1>. + +- #size-cells: Must be <0>. I2C addresses have no size component. + +- interrupts: A single interrupt specifier. + +- clock-frequency: The I2C bus clock rate in Hz. + +Example: + twsi0: i2c@1180000001000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "cavium,octeon-3860-twsi"; + reg = <0x11800 0x00001000 0x0 0x200>; + interrupts = <0 45>; + clock-frequency = <100000>; + + rtc@68 { + compatible = "dallas,ds1337"; + reg = <0x68>; + }; + tmp@4c { + compatible = "ti,tmp421"; + reg = <0x4c>; + }; + }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-omap.txt b/Documentation/devicetree/bindings/i2c/i2c-omap.txt new file mode 100644 index 000000000000..56564aa4b444 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-omap.txt @@ -0,0 +1,30 @@ +I2C for OMAP platforms + +Required properties : +- compatible : Must be "ti,omap3-i2c" or "ti,omap4-i2c" +- ti,hwmods : Must be "i2c", n being the instance number (1-based) +- #address-cells = <1>; +- #size-cells = <0>; + +Recommended properties : +- clock-frequency : Desired I2C bus clock frequency in Hz. Otherwise + the default 100 kHz frequency will be used. + +Optional properties: +- Child nodes conforming to i2c bus binding + +Note: Current implementation will fetch base address, irq and dma +from omap hwmod data base during device registration. +Future plan is to migrate hwmod data base contents into device tree +blob so that, all the required data will be used from device tree dts +file. + +Examples : + +i2c1: i2c@0 { + compatible = "ti,omap3-i2c"; + #address-cells = <1>; + #size-cells = <0>; + ti,hwmods = "i2c1"; + clock-frequency = <400000>; +}; diff --git a/Documentation/devicetree/bindings/i2c/i2c-pnx.txt b/Documentation/devicetree/bindings/i2c/i2c-pnx.txt new file mode 100644 index 000000000000..fe98ada33ee4 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-pnx.txt @@ -0,0 +1,36 @@ +* NXP PNX I2C Controller + +Required properties: + + - reg: Offset and length of the register set for the device + - compatible: should be "nxp,pnx-i2c" + - interrupts: configure one interrupt line + - #address-cells: always 1 (for i2c addresses) + - #size-cells: always 0 + - interrupt-parent: the phandle for the interrupt controller that + services interrupts for this device. + +Optional properties: + + - clock-frequency: desired I2C bus clock frequency in Hz, Default: 100000 Hz + +Examples: + + i2c1: i2c@400a0000 { + compatible = "nxp,pnx-i2c"; + reg = <0x400a0000 0x100>; + interrupt-parent = <&mic>; + interrupts = <51 0>; + #address-cells = <1>; + #size-cells = <0>; + }; + + i2c2: i2c@400a8000 { + compatible = "nxp,pnx-i2c"; + reg = <0x400a8000 0x100>; + interrupt-parent = <&mic>; + interrupts = <50 0>; + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <100000>; + }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-pxa-pci-ce4100.txt b/Documentation/devicetree/bindings/i2c/i2c-pxa-pci-ce4100.txt new file mode 100644 index 000000000000..569b16248514 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-pxa-pci-ce4100.txt @@ -0,0 +1,93 @@ +CE4100 I2C +---------- + +CE4100 has one PCI device which is described as the I2C-Controller. This +PCI device has three PCI-bars, each bar contains a complete I2C +controller. So we have a total of three independent I2C-Controllers +which share only an interrupt line. +The driver is probed via the PCI-ID and is gathering the information of +attached devices from the devices tree. +Grant Likely recommended to use the ranges property to map the PCI-Bar +number to its physical address and to use this to find the child nodes +of the specific I2C controller. This were his exact words: + + Here's where the magic happens. Each entry in + ranges describes how the parent pci address space + (middle group of 3) is translated to the local + address space (first group of 2) and the size of + each range (last cell). In this particular case, + the first cell of the local address is chosen to be + 1:1 mapped to the BARs, and the second is the + offset from be base of the BAR (which would be + non-zero if you had 2 or more devices mapped off + the same BAR) + + ranges allows the address mapping to be described + in a way that the OS can interpret without + requiring custom device driver code. + +This is an example which is used on FalconFalls: +------------------------------------------------ + i2c-controller@b,2 { + #address-cells = <2>; + #size-cells = <1>; + compatible = "pci8086,2e68.2", + "pci8086,2e68", + "pciclass,ff0000", + "pciclass,ff00"; + + reg = <0x15a00 0x0 0x0 0x0 0x0>; + interrupts = <16 1>; + + /* as described by Grant, the first number in the group of + * three is the bar number followed by the 64bit bar address + * followed by size of the mapping. The bar address + * requires also a valid translation in parents ranges + * property. + */ + ranges = <0 0 0x02000000 0 0xdffe0500 0x100 + 1 0 0x02000000 0 0xdffe0600 0x100 + 2 0 0x02000000 0 0xdffe0700 0x100>; + + i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "intel,ce4100-i2c-controller"; + + /* The first number in the reg property is the + * number of the bar + */ + reg = <0 0 0x100>; + + /* This I2C controller has no devices */ + }; + + i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "intel,ce4100-i2c-controller"; + reg = <1 0 0x100>; + + /* This I2C controller has one gpio controller */ + gpio@26 { + #gpio-cells = <2>; + compatible = "ti,pcf8575"; + reg = <0x26>; + gpio-controller; + }; + }; + + i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "intel,ce4100-i2c-controller"; + reg = <2 0 0x100>; + + gpio@26 { + #gpio-cells = <2>; + compatible = "ti,pcf8575"; + reg = <0x26>; + gpio-controller; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-pxa.txt b/Documentation/devicetree/bindings/i2c/i2c-pxa.txt new file mode 100644 index 000000000000..12b78ac507e9 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-pxa.txt @@ -0,0 +1,33 @@ +* Marvell MMP I2C controller + +Required properties : + + - reg : Offset and length of the register set for the device + - compatible : should be "mrvl,mmp-twsi" where mmp is the name of a + compatible processor, e.g. pxa168, pxa910, mmp2, mmp3. + For the pxa2xx/pxa3xx, an additional node "mrvl,pxa-i2c" is required + as shown in the example below. + +Recommended properties : + + - interrupts : the interrupt number + - interrupt-parent : the phandle for the interrupt controller that + services interrupts for this device. If the parent is the default + interrupt controller in device tree, it could be ignored. + - mrvl,i2c-polling : Disable interrupt of i2c controller. Polling + status register of i2c controller instead. + - mrvl,i2c-fast-mode : Enable fast mode of i2c controller. + +Examples: + twsi1: i2c@d4011000 { + compatible = "mrvl,mmp-twsi"; + reg = <0xd4011000 0x1000>; + interrupts = <7>; + mrvl,i2c-fast-mode; + }; + + twsi2: i2c@d4025000 { + compatible = "mrvl,mmp-twsi"; + reg = <0xd4025000 0x1000>; + interrupts = <58>; + }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt b/Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt new file mode 100644 index 000000000000..b6cb5a12c672 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt @@ -0,0 +1,43 @@ +* Samsung's I2C controller + +The Samsung's I2C controller is used to interface with I2C devices. + +Required properties: + - compatible: value should be either of the following. + (a) "samsung, s3c2410-i2c", for i2c compatible with s3c2410 i2c. + (b) "samsung, s3c2440-i2c", for i2c compatible with s3c2440 i2c. + (c) "samsung, s3c2440-hdmiphy-i2c", for s3c2440-like i2c used + inside HDMIPHY block found on several samsung SoCs + - reg: physical base address of the controller and length of memory mapped + region. + - interrupts: interrupt number to the cpu. + - samsung,i2c-sda-delay: Delay (in ns) applied to data line (SDA) edges. + +Optional properties: + - gpios: The order of the gpios should be the following: . + The gpio specifier depends on the gpio controller. Required in all + cases except for "samsung,s3c2440-hdmiphy-i2c" whose input/output + lines are permanently wired to the respective client + - samsung,i2c-slave-addr: Slave address in multi-master enviroment. If not + specified, default value is 0. + - samsung,i2c-max-bus-freq: Desired frequency in Hz of the bus. If not + specified, the default value in Hz is 100000. + +Example: + + i2c@13870000 { + compatible = "samsung,s3c2440-i2c"; + reg = <0x13870000 0x100>; + interrupts = <345>; + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <100000>; + gpios = <&gpd1 2 0 /* SDA */ + &gpd1 3 0 /* SCL */>; + #address-cells = <1>; + #size-cells = <0>; + + wm8994@1a { + compatible = "wlf,wm8994"; + reg = <0x1a>; + }; + }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-sirf.txt b/Documentation/devicetree/bindings/i2c/i2c-sirf.txt new file mode 100644 index 000000000000..7baf9e133fa8 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-sirf.txt @@ -0,0 +1,19 @@ +I2C for SiRFprimaII platforms + +Required properties : +- compatible : Must be "sirf,prima2-i2c" +- reg: physical base address of the controller and length of memory mapped + region. +- interrupts: interrupt number to the cpu. + +Optional properties: +- clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in Hz. + The absence of the propoerty indicates the default frequency 100 kHz. + +Examples : + +i2c0: i2c@b00e0000 { + compatible = "sirf,prima2-i2c"; + reg = <0xb00e0000 0x10000>; + interrupts = <24>; +}; diff --git a/Documentation/devicetree/bindings/i2c/i2c-versatile.txt b/Documentation/devicetree/bindings/i2c/i2c-versatile.txt new file mode 100644 index 000000000000..361d31c51b6f --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-versatile.txt @@ -0,0 +1,10 @@ +i2c Controller on ARM Versatile platform: + +Required properties: +- compatible : Must be "arm,versatile-i2c"; +- reg +- #address-cells = <1>; +- #size-cells = <0>; + +Optional properties: +- Child nodes conforming to i2c bus binding diff --git a/Documentation/devicetree/bindings/i2c/i2c-xiic.txt b/Documentation/devicetree/bindings/i2c/i2c-xiic.txt new file mode 100644 index 000000000000..ceabbe91ae44 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-xiic.txt @@ -0,0 +1,22 @@ +Xilinx IIC controller: + +Required properties: +- compatible : Must be "xlnx,xps-iic-2.00.a" +- reg : IIC register location and length +- interrupts : IIC controller unterrupt +- #address-cells = <1> +- #size-cells = <0> + +Optional properties: +- Child nodes conforming to i2c bus binding + +Example: + + axi_iic_0: i2c@40800000 { + compatible = "xlnx,xps-iic-2.00.a"; + interrupts = < 1 2 >; + reg = < 0x40800000 0x10000 >; + + #size-cells = <0>; + #address-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/i2c/mrvl-i2c.txt b/Documentation/devicetree/bindings/i2c/mrvl-i2c.txt deleted file mode 100644 index 0f7945019f6f..000000000000 --- a/Documentation/devicetree/bindings/i2c/mrvl-i2c.txt +++ /dev/null @@ -1,51 +0,0 @@ -* Marvell MMP I2C controller - -Required properties : - - - reg : Offset and length of the register set for the device - - compatible : should be "mrvl,mmp-twsi" where mmp is the name of a - compatible processor, e.g. pxa168, pxa910, mmp2, mmp3. - For the pxa2xx/pxa3xx, an additional node "mrvl,pxa-i2c" is required - as shown in the example below. - -Recommended properties : - - - interrupts : the interrupt number - - interrupt-parent : the phandle for the interrupt controller that - services interrupts for this device. If the parent is the default - interrupt controller in device tree, it could be ignored. - - mrvl,i2c-polling : Disable interrupt of i2c controller. Polling - status register of i2c controller instead. - - mrvl,i2c-fast-mode : Enable fast mode of i2c controller. - -Examples: - twsi1: i2c@d4011000 { - compatible = "mrvl,mmp-twsi"; - reg = <0xd4011000 0x1000>; - interrupts = <7>; - mrvl,i2c-fast-mode; - }; - - twsi2: i2c@d4025000 { - compatible = "mrvl,mmp-twsi"; - reg = <0xd4025000 0x1000>; - interrupts = <58>; - }; - -* Marvell MV64XXX I2C controller - -Required properties : - - - reg : Offset and length of the register set for the device - - compatible : Should be "marvell,mv64xxx-i2c" - - interrupts : The interrupt number - - clock-frequency : Desired I2C bus clock frequency in Hz. - -Examples: - - i2c@11000 { - compatible = "marvell,mv64xxx-i2c"; - reg = <0x11000 0x20>; - interrupts = <29>; - clock-frequency = <100000>; - }; diff --git a/Documentation/devicetree/bindings/i2c/mux.txt b/Documentation/devicetree/bindings/i2c/mux.txt deleted file mode 100644 index af84cce5cd7b..000000000000 --- a/Documentation/devicetree/bindings/i2c/mux.txt +++ /dev/null @@ -1,60 +0,0 @@ -Common i2c bus multiplexer/switch properties. - -An i2c bus multiplexer/switch will have several child busses that are -numbered uniquely in a device dependent manner. The nodes for an i2c bus -multiplexer/switch will have one child node for each child -bus. - -Required properties: -- #address-cells = <1>; -- #size-cells = <0>; - -Required properties for child nodes: -- #address-cells = <1>; -- #size-cells = <0>; -- reg : The sub-bus number. - -Optional properties for child nodes: -- Other properties specific to the multiplexer/switch hardware. -- Child nodes conforming to i2c bus binding - - -Example : - - /* - An NXP pca9548 8 channel I2C multiplexer at address 0x70 - with two NXP pca8574 GPIO expanders attached, one each to - ports 3 and 4. - */ - - mux@70 { - compatible = "nxp,pca9548"; - reg = <0x70>; - #address-cells = <1>; - #size-cells = <0>; - - i2c@3 { - #address-cells = <1>; - #size-cells = <0>; - reg = <3>; - - gpio1: gpio@38 { - compatible = "nxp,pca8574"; - reg = <0x38>; - #gpio-cells = <2>; - gpio-controller; - }; - }; - i2c@4 { - #address-cells = <1>; - #size-cells = <0>; - reg = <4>; - - gpio2: gpio@38 { - compatible = "nxp,pca8574"; - reg = <0x38>; - #gpio-cells = <2>; - gpio-controller; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/i2c/nomadik.txt b/Documentation/devicetree/bindings/i2c/nomadik.txt deleted file mode 100644 index 72065b0ff680..000000000000 --- a/Documentation/devicetree/bindings/i2c/nomadik.txt +++ /dev/null @@ -1,23 +0,0 @@ -I2C for Nomadik based systems - -Required (non-standard) properties: - - Nil - -Recommended (non-standard) properties: - - clock-frequency : Maximum bus clock frequency for the device - -Optional (non-standard) properties: - - Nil - -Example : - -i2c@80004000 { - compatible = "stericsson,db8500-i2c", "st,nomadik-i2c"; - reg = <0x80004000 0x1000>; - interrupts = <0 21 0x4>; - #address-cells = <1>; - #size-cells = <0>; - v-i2c-supply = <&db8500_vape_reg>; - - clock-frequency = <400000>; -}; diff --git a/Documentation/devicetree/bindings/i2c/omap-i2c.txt b/Documentation/devicetree/bindings/i2c/omap-i2c.txt deleted file mode 100644 index 56564aa4b444..000000000000 --- a/Documentation/devicetree/bindings/i2c/omap-i2c.txt +++ /dev/null @@ -1,30 +0,0 @@ -I2C for OMAP platforms - -Required properties : -- compatible : Must be "ti,omap3-i2c" or "ti,omap4-i2c" -- ti,hwmods : Must be "i2c", n being the instance number (1-based) -- #address-cells = <1>; -- #size-cells = <0>; - -Recommended properties : -- clock-frequency : Desired I2C bus clock frequency in Hz. Otherwise - the default 100 kHz frequency will be used. - -Optional properties: -- Child nodes conforming to i2c bus binding - -Note: Current implementation will fetch base address, irq and dma -from omap hwmod data base during device registration. -Future plan is to migrate hwmod data base contents into device tree -blob so that, all the required data will be used from device tree dts -file. - -Examples : - -i2c1: i2c@0 { - compatible = "ti,omap3-i2c"; - #address-cells = <1>; - #size-cells = <0>; - ti,hwmods = "i2c1"; - clock-frequency = <400000>; -}; diff --git a/Documentation/devicetree/bindings/i2c/pnx.txt b/Documentation/devicetree/bindings/i2c/pnx.txt deleted file mode 100644 index fe98ada33ee4..000000000000 --- a/Documentation/devicetree/bindings/i2c/pnx.txt +++ /dev/null @@ -1,36 +0,0 @@ -* NXP PNX I2C Controller - -Required properties: - - - reg: Offset and length of the register set for the device - - compatible: should be "nxp,pnx-i2c" - - interrupts: configure one interrupt line - - #address-cells: always 1 (for i2c addresses) - - #size-cells: always 0 - - interrupt-parent: the phandle for the interrupt controller that - services interrupts for this device. - -Optional properties: - - - clock-frequency: desired I2C bus clock frequency in Hz, Default: 100000 Hz - -Examples: - - i2c1: i2c@400a0000 { - compatible = "nxp,pnx-i2c"; - reg = <0x400a0000 0x100>; - interrupt-parent = <&mic>; - interrupts = <51 0>; - #address-cells = <1>; - #size-cells = <0>; - }; - - i2c2: i2c@400a8000 { - compatible = "nxp,pnx-i2c"; - reg = <0x400a8000 0x100>; - interrupt-parent = <&mic>; - interrupts = <50 0>; - #address-cells = <1>; - #size-cells = <0>; - clock-frequency = <100000>; - }; diff --git a/Documentation/devicetree/bindings/i2c/samsung-i2c.txt b/Documentation/devicetree/bindings/i2c/samsung-i2c.txt deleted file mode 100644 index b6cb5a12c672..000000000000 --- a/Documentation/devicetree/bindings/i2c/samsung-i2c.txt +++ /dev/null @@ -1,43 +0,0 @@ -* Samsung's I2C controller - -The Samsung's I2C controller is used to interface with I2C devices. - -Required properties: - - compatible: value should be either of the following. - (a) "samsung, s3c2410-i2c", for i2c compatible with s3c2410 i2c. - (b) "samsung, s3c2440-i2c", for i2c compatible with s3c2440 i2c. - (c) "samsung, s3c2440-hdmiphy-i2c", for s3c2440-like i2c used - inside HDMIPHY block found on several samsung SoCs - - reg: physical base address of the controller and length of memory mapped - region. - - interrupts: interrupt number to the cpu. - - samsung,i2c-sda-delay: Delay (in ns) applied to data line (SDA) edges. - -Optional properties: - - gpios: The order of the gpios should be the following: . - The gpio specifier depends on the gpio controller. Required in all - cases except for "samsung,s3c2440-hdmiphy-i2c" whose input/output - lines are permanently wired to the respective client - - samsung,i2c-slave-addr: Slave address in multi-master enviroment. If not - specified, default value is 0. - - samsung,i2c-max-bus-freq: Desired frequency in Hz of the bus. If not - specified, the default value in Hz is 100000. - -Example: - - i2c@13870000 { - compatible = "samsung,s3c2440-i2c"; - reg = <0x13870000 0x100>; - interrupts = <345>; - samsung,i2c-sda-delay = <100>; - samsung,i2c-max-bus-freq = <100000>; - gpios = <&gpd1 2 0 /* SDA */ - &gpd1 3 0 /* SCL */>; - #address-cells = <1>; - #size-cells = <0>; - - wm8994@1a { - compatible = "wlf,wm8994"; - reg = <0x1a>; - }; - }; diff --git a/Documentation/devicetree/bindings/i2c/sirf-i2c.txt b/Documentation/devicetree/bindings/i2c/sirf-i2c.txt deleted file mode 100644 index 7baf9e133fa8..000000000000 --- a/Documentation/devicetree/bindings/i2c/sirf-i2c.txt +++ /dev/null @@ -1,19 +0,0 @@ -I2C for SiRFprimaII platforms - -Required properties : -- compatible : Must be "sirf,prima2-i2c" -- reg: physical base address of the controller and length of memory mapped - region. -- interrupts: interrupt number to the cpu. - -Optional properties: -- clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in Hz. - The absence of the propoerty indicates the default frequency 100 kHz. - -Examples : - -i2c0: i2c@b00e0000 { - compatible = "sirf,prima2-i2c"; - reg = <0xb00e0000 0x10000>; - interrupts = <24>; -}; diff --git a/Documentation/devicetree/bindings/i2c/xiic.txt b/Documentation/devicetree/bindings/i2c/xiic.txt deleted file mode 100644 index ceabbe91ae44..000000000000 --- a/Documentation/devicetree/bindings/i2c/xiic.txt +++ /dev/null @@ -1,22 +0,0 @@ -Xilinx IIC controller: - -Required properties: -- compatible : Must be "xlnx,xps-iic-2.00.a" -- reg : IIC register location and length -- interrupts : IIC controller unterrupt -- #address-cells = <1> -- #size-cells = <0> - -Optional properties: -- Child nodes conforming to i2c bus binding - -Example: - - axi_iic_0: i2c@40800000 { - compatible = "xlnx,xps-iic-2.00.a"; - interrupts = < 1 2 >; - reg = < 0x40800000 0x10000 >; - - #size-cells = <0>; - #address-cells = <1>; - }; diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index bffd5501ac2d..15da1ac7cf9e 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -9,10 +9,6 @@ * kind, whether express or implied. */ -/* - * This driver can be used from the device tree, see - * Documentation/devicetree/bindings/i2c/ocore-i2c.txt - */ #include #include #include -- cgit v1.2.3 From 192afdbfbc5c3346e27b3c3e3be8337495b2a41b Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Fri, 2 Nov 2012 10:22:41 -0400 Subject: mtd: davinci: add support for parition binding nodes Enhance the driver to support partition subnodes inside the nand device bindings to describe partions on the nand device. Signed-off-by: Murali Karicheri Reviewed-by: Grant Likely Signed-off-by: Artem Bityutskiy --- Documentation/devicetree/bindings/arm/davinci/nand.txt | 8 ++++++++ drivers/mtd/nand/davinci_nand.c | 13 ++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/davinci/nand.txt b/Documentation/devicetree/bindings/arm/davinci/nand.txt index e37241f1fdd8..4746452c4f5d 100644 --- a/Documentation/devicetree/bindings/arm/davinci/nand.txt +++ b/Documentation/devicetree/bindings/arm/davinci/nand.txt @@ -23,6 +23,9 @@ Recommended properties : - ti,davinci-nand-buswidth: buswidth 8 or 16 - ti,davinci-nand-use-bbt: use flash based bad block table support. +nand device bindings may contain additional sub-nodes describing +partitions of the address space. See partition.txt for more detail. + Example (enbw_cmc board): aemif@60000000 { compatible = "ti,davinci-aemif"; @@ -47,5 +50,10 @@ aemif@60000000 { ti,davinci-ecc-mode = "hw"; ti,davinci-ecc-bits = <4>; ti,davinci-nand-use-bbt; + + partition@180000 { + label = "ubifs"; + reg = <0x180000 0x7e80000>; + }; }; }; diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 945047ad0952..3502606f6480 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -821,9 +821,16 @@ syndrome_done: if (ret < 0) goto err_scan; - ret = mtd_device_parse_register(&info->mtd, NULL, NULL, pdata->parts, - pdata->nr_parts); - + if (pdata->parts) + ret = mtd_device_parse_register(&info->mtd, NULL, NULL, + pdata->parts, pdata->nr_parts); + else { + struct mtd_part_parser_data ppdata; + + ppdata.of_node = pdev->dev.of_node; + ret = mtd_device_parse_register(&info->mtd, NULL, &ppdata, + NULL, 0); + } if (ret < 0) goto err_scan; -- cgit v1.2.3 From 6cec9b07fe6a0c4dfbcdcee7c6283529f087c521 Mon Sep 17 00:00:00 2001 From: Andreas Larsson Date: Thu, 15 Nov 2012 08:47:14 +0100 Subject: can: grcan: Add device driver for GRCAN and GRHCAN cores This driver supports GRCAN and CRHCAN CAN controllers available in the GRLIB VHDL IP core library. Signed-off-by: Andreas Larsson Acked-by: Wolfgang Grandegger Signed-off-by: Marc Kleine-Budde --- Documentation/ABI/testing/sysfs-class-net-grcan | 35 + .../devicetree/bindings/net/can/grcan.txt | 28 + Documentation/kernel-parameters.txt | 18 + drivers/net/can/Kconfig | 9 + drivers/net/can/Makefile | 1 + drivers/net/can/grcan.c | 1756 ++++++++++++++++++++ 6 files changed, 1847 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-net-grcan create mode 100644 Documentation/devicetree/bindings/net/can/grcan.txt create mode 100644 drivers/net/can/grcan.c (limited to 'Documentation/devicetree') diff --git a/Documentation/ABI/testing/sysfs-class-net-grcan b/Documentation/ABI/testing/sysfs-class-net-grcan new file mode 100644 index 000000000000..f418c92ca555 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-net-grcan @@ -0,0 +1,35 @@ + +What: /sys/class/net//grcan/enable0 +Date: October 2012 +KernelVersion: 3.8 +Contact: Andreas Larsson +Description: + Hardware configuration of physical interface 0. This file reads + and writes the "Enable 0" bit of the configuration register. + Possible values: 0 or 1. See the GRCAN chapter of the GRLIB IP + core library documentation for details. The default value is 0 + or set by the module parameter grcan.enable0 and can be read at + /sys/module/grcan/parameters/enable0. + +What: /sys/class/net//grcan/enable1 +Date: October 2012 +KernelVersion: 3.8 +Contact: Andreas Larsson +Description: + Hardware configuration of physical interface 1. This file reads + and writes the "Enable 1" bit of the configuration register. + Possible values: 0 or 1. See the GRCAN chapter of the GRLIB IP + core library documentation for details. The default value is 0 + or set by the module parameter grcan.enable1 and can be read at + /sys/module/grcan/parameters/enable1. + +What: /sys/class/net//grcan/select +Date: October 2012 +KernelVersion: 3.8 +Contact: Andreas Larsson +Description: + Configuration of which physical interface to be used. Possible + values: 0 or 1. See the GRCAN chapter of the GRLIB IP core + library documentation for details. The default value is 0 or is + set by the module parameter grcan.select and can be read at + /sys/module/grcan/parameters/select. diff --git a/Documentation/devicetree/bindings/net/can/grcan.txt b/Documentation/devicetree/bindings/net/can/grcan.txt new file mode 100644 index 000000000000..34ef3498f887 --- /dev/null +++ b/Documentation/devicetree/bindings/net/can/grcan.txt @@ -0,0 +1,28 @@ +Aeroflex Gaisler GRCAN and GRHCAN CAN controllers. + +The GRCAN and CRHCAN CAN controllers are available in the GRLIB VHDL IP core +library. + +Note: These properties are built from the AMBA plug&play in a Leon SPARC system +(the ordinary environment for GRCAN and GRHCAN). There are no dts files for +sparc. + +Required properties: + +- name : Should be "GAISLER_GRCAN", "01_03d", "GAISLER_GRHCAN" or "01_034" + +- reg : Address and length of the register set for the device + +- freq : Frequency of the external oscillator clock in Hz (the frequency of + the amba bus in the ordinary case) + +- interrupts : Interrupt number for this device + +Optional properties: + +- systemid : If not present or if the value of the least significant 16 bits + of this 32-bit property is smaller than GRCAN_TXBUG_SAFE_GRLIB_VERSION + a bug workaround is activated. + +For further information look in the documentation for the GLIB IP core library: +http://www.gaisler.com/products/grlib/grip.pdf diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 9776f068306b..3da4f9670d8b 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -905,6 +905,24 @@ bytes respectively. Such letter suffixes can also be entirely omitted. gpt [EFI] Forces disk with valid GPT signature but invalid Protective MBR to be treated as GPT. + grcan.enable0= [HW] Configuration of physical interface 0. Determines + the "Enable 0" bit of the configuration register. + Format: 0 | 1 + Default: 0 + grcan.enable1= [HW] Configuration of physical interface 1. Determines + the "Enable 0" bit of the configuration register. + Format: 0 | 1 + Default: 0 + grcan.select= [HW] Select which physical interface to use. + Format: 0 | 1 + Default: 0 + grcan.txsize= [HW] Sets the size of the tx buffer. + Format: such that (txsize & ~0x1fffc0) == 0. + Default: 1024 + grcan.rxsize= [HW] Sets the size of the rx buffer. + Format: such that (rxsize & ~0x1fffc0) == 0. + Default: 1024 + hashdist= [KNL,NUMA] Large hashes allocated during boot are distributed across NUMA nodes. Defaults on for 64-bit NUMA, off otherwise. diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index bb709fd66993..b56bd9e80957 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -110,6 +110,15 @@ config PCH_CAN is an IOH for x86 embedded processor (Intel Atom E6xx series). This driver can access CAN bus. +config CAN_GRCAN + tristate "Aeroflex Gaisler GRCAN and GRHCAN CAN devices" + depends on CAN_DEV && OF + ---help--- + Say Y here if you want to use Aeroflex Gaisler GRCAN or GRHCAN. + Note that the driver supports little endian, even though little + endian syntheses of the cores would need some modifications on + the hardware level to work. + source "drivers/net/can/mscan/Kconfig" source "drivers/net/can/sja1000/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 938be37b670c..7de59862bbe9 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -22,5 +22,6 @@ obj-$(CONFIG_CAN_BFIN) += bfin_can.o obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o obj-$(CONFIG_PCH_CAN) += pch_can.o +obj-$(CONFIG_CAN_GRCAN) += grcan.o ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/grcan.c b/drivers/net/can/grcan.c new file mode 100644 index 000000000000..391f484fccb7 --- /dev/null +++ b/drivers/net/can/grcan.c @@ -0,0 +1,1756 @@ +/* + * Socket CAN driver for Aeroflex Gaisler GRCAN and GRHCAN. + * + * 2012 (c) Aeroflex Gaisler AB + * + * This driver supports GRCAN and GRHCAN CAN controllers available in the GRLIB + * VHDL IP core library. + * + * Full documentation of the GRCAN core can be found here: + * http://www.gaisler.com/products/grlib/grip.pdf + * + * See "Documentation/devicetree/bindings/net/can/grcan.txt" for information on + * open firmware properties. + * + * See "Documentation/ABI/testing/sysfs-class-net-grcan" for information on the + * sysfs interface. + * + * See "Documentation/kernel-parameters.txt" for information on the module + * parameters. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Contributors: Andreas Larsson + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#define DRV_NAME "grcan" + +#define GRCAN_NAPI_WEIGHT 32 + +#define GRCAN_RESERVE_SIZE(slot1, slot2) (((slot2) - (slot1)) / 4 - 1) + +struct grcan_registers { + u32 conf; /* 0x00 */ + u32 stat; /* 0x04 */ + u32 ctrl; /* 0x08 */ + u32 __reserved1[GRCAN_RESERVE_SIZE(0x08, 0x18)]; + u32 smask; /* 0x18 - CanMASK */ + u32 scode; /* 0x1c - CanCODE */ + u32 __reserved2[GRCAN_RESERVE_SIZE(0x1c, 0x100)]; + u32 pimsr; /* 0x100 */ + u32 pimr; /* 0x104 */ + u32 pisr; /* 0x108 */ + u32 pir; /* 0x10C */ + u32 imr; /* 0x110 */ + u32 picr; /* 0x114 */ + u32 __reserved3[GRCAN_RESERVE_SIZE(0x114, 0x200)]; + u32 txctrl; /* 0x200 */ + u32 txaddr; /* 0x204 */ + u32 txsize; /* 0x208 */ + u32 txwr; /* 0x20C */ + u32 txrd; /* 0x210 */ + u32 txirq; /* 0x214 */ + u32 __reserved4[GRCAN_RESERVE_SIZE(0x214, 0x300)]; + u32 rxctrl; /* 0x300 */ + u32 rxaddr; /* 0x304 */ + u32 rxsize; /* 0x308 */ + u32 rxwr; /* 0x30C */ + u32 rxrd; /* 0x310 */ + u32 rxirq; /* 0x314 */ + u32 rxmask; /* 0x318 */ + u32 rxcode; /* 0x31C */ +}; + +#define GRCAN_CONF_ABORT 0x00000001 +#define GRCAN_CONF_ENABLE0 0x00000002 +#define GRCAN_CONF_ENABLE1 0x00000004 +#define GRCAN_CONF_SELECT 0x00000008 +#define GRCAN_CONF_SILENT 0x00000010 +#define GRCAN_CONF_SAM 0x00000020 /* Available in some hardware */ +#define GRCAN_CONF_BPR 0x00000300 /* Note: not BRP */ +#define GRCAN_CONF_RSJ 0x00007000 +#define GRCAN_CONF_PS1 0x00f00000 +#define GRCAN_CONF_PS2 0x000f0000 +#define GRCAN_CONF_SCALER 0xff000000 +#define GRCAN_CONF_OPERATION \ + (GRCAN_CONF_ABORT | GRCAN_CONF_ENABLE0 | GRCAN_CONF_ENABLE1 \ + | GRCAN_CONF_SELECT | GRCAN_CONF_SILENT | GRCAN_CONF_SAM) +#define GRCAN_CONF_TIMING \ + (GRCAN_CONF_BPR | GRCAN_CONF_RSJ | GRCAN_CONF_PS1 \ + | GRCAN_CONF_PS2 | GRCAN_CONF_SCALER) + +#define GRCAN_CONF_RSJ_MIN 1 +#define GRCAN_CONF_RSJ_MAX 4 +#define GRCAN_CONF_PS1_MIN 1 +#define GRCAN_CONF_PS1_MAX 15 +#define GRCAN_CONF_PS2_MIN 2 +#define GRCAN_CONF_PS2_MAX 8 +#define GRCAN_CONF_SCALER_MIN 0 +#define GRCAN_CONF_SCALER_MAX 255 +#define GRCAN_CONF_SCALER_INC 1 + +#define GRCAN_CONF_BPR_BIT 8 +#define GRCAN_CONF_RSJ_BIT 12 +#define GRCAN_CONF_PS1_BIT 20 +#define GRCAN_CONF_PS2_BIT 16 +#define GRCAN_CONF_SCALER_BIT 24 + +#define GRCAN_STAT_PASS 0x000001 +#define GRCAN_STAT_OFF 0x000002 +#define GRCAN_STAT_OR 0x000004 +#define GRCAN_STAT_AHBERR 0x000008 +#define GRCAN_STAT_ACTIVE 0x000010 +#define GRCAN_STAT_RXERRCNT 0x00ff00 +#define GRCAN_STAT_TXERRCNT 0xff0000 + +#define GRCAN_STAT_ERRCTR_RELATED (GRCAN_STAT_PASS | GRCAN_STAT_OFF) + +#define GRCAN_STAT_RXERRCNT_BIT 8 +#define GRCAN_STAT_TXERRCNT_BIT 16 + +#define GRCAN_STAT_ERRCNT_WARNING_LIMIT 96 +#define GRCAN_STAT_ERRCNT_PASSIVE_LIMIT 127 + +#define GRCAN_CTRL_RESET 0x2 +#define GRCAN_CTRL_ENABLE 0x1 + +#define GRCAN_TXCTRL_ENABLE 0x1 +#define GRCAN_TXCTRL_ONGOING 0x2 +#define GRCAN_TXCTRL_SINGLE 0x4 + +#define GRCAN_RXCTRL_ENABLE 0x1 +#define GRCAN_RXCTRL_ONGOING 0x2 + +/* Relative offset of IRQ sources to AMBA Plug&Play */ +#define GRCAN_IRQIX_IRQ 0 +#define GRCAN_IRQIX_TXSYNC 1 +#define GRCAN_IRQIX_RXSYNC 2 + +#define GRCAN_IRQ_PASS 0x00001 +#define GRCAN_IRQ_OFF 0x00002 +#define GRCAN_IRQ_OR 0x00004 +#define GRCAN_IRQ_RXAHBERR 0x00008 +#define GRCAN_IRQ_TXAHBERR 0x00010 +#define GRCAN_IRQ_RXIRQ 0x00020 +#define GRCAN_IRQ_TXIRQ 0x00040 +#define GRCAN_IRQ_RXFULL 0x00080 +#define GRCAN_IRQ_TXEMPTY 0x00100 +#define GRCAN_IRQ_RX 0x00200 +#define GRCAN_IRQ_TX 0x00400 +#define GRCAN_IRQ_RXSYNC 0x00800 +#define GRCAN_IRQ_TXSYNC 0x01000 +#define GRCAN_IRQ_RXERRCTR 0x02000 +#define GRCAN_IRQ_TXERRCTR 0x04000 +#define GRCAN_IRQ_RXMISS 0x08000 +#define GRCAN_IRQ_TXLOSS 0x10000 + +#define GRCAN_IRQ_NONE 0 +#define GRCAN_IRQ_ALL \ + (GRCAN_IRQ_PASS | GRCAN_IRQ_OFF | GRCAN_IRQ_OR \ + | GRCAN_IRQ_RXAHBERR | GRCAN_IRQ_TXAHBERR \ + | GRCAN_IRQ_RXIRQ | GRCAN_IRQ_TXIRQ \ + | GRCAN_IRQ_RXFULL | GRCAN_IRQ_TXEMPTY \ + | GRCAN_IRQ_RX | GRCAN_IRQ_TX | GRCAN_IRQ_RXSYNC \ + | GRCAN_IRQ_TXSYNC | GRCAN_IRQ_RXERRCTR \ + | GRCAN_IRQ_TXERRCTR | GRCAN_IRQ_RXMISS \ + | GRCAN_IRQ_TXLOSS) + +#define GRCAN_IRQ_ERRCTR_RELATED (GRCAN_IRQ_RXERRCTR | GRCAN_IRQ_TXERRCTR \ + | GRCAN_IRQ_PASS | GRCAN_IRQ_OFF) +#define GRCAN_IRQ_ERRORS (GRCAN_IRQ_ERRCTR_RELATED | GRCAN_IRQ_OR \ + | GRCAN_IRQ_TXAHBERR | GRCAN_IRQ_RXAHBERR \ + | GRCAN_IRQ_TXLOSS) +#define GRCAN_IRQ_DEFAULT (GRCAN_IRQ_RX | GRCAN_IRQ_TX | GRCAN_IRQ_ERRORS) + +#define GRCAN_MSG_SIZE 16 + +#define GRCAN_MSG_IDE 0x80000000 +#define GRCAN_MSG_RTR 0x40000000 +#define GRCAN_MSG_BID 0x1ffc0000 +#define GRCAN_MSG_EID 0x1fffffff +#define GRCAN_MSG_IDE_BIT 31 +#define GRCAN_MSG_RTR_BIT 30 +#define GRCAN_MSG_BID_BIT 18 +#define GRCAN_MSG_EID_BIT 0 + +#define GRCAN_MSG_DLC 0xf0000000 +#define GRCAN_MSG_TXERRC 0x00ff0000 +#define GRCAN_MSG_RXERRC 0x0000ff00 +#define GRCAN_MSG_DLC_BIT 28 +#define GRCAN_MSG_TXERRC_BIT 16 +#define GRCAN_MSG_RXERRC_BIT 8 +#define GRCAN_MSG_AHBERR 0x00000008 +#define GRCAN_MSG_OR 0x00000004 +#define GRCAN_MSG_OFF 0x00000002 +#define GRCAN_MSG_PASS 0x00000001 + +#define GRCAN_MSG_DATA_SLOT_INDEX(i) (2 + (i) / 4) +#define GRCAN_MSG_DATA_SHIFT(i) ((3 - (i) % 4) * 8) + +#define GRCAN_BUFFER_ALIGNMENT 1024 +#define GRCAN_DEFAULT_BUFFER_SIZE 1024 +#define GRCAN_VALID_TR_SIZE_MASK 0x001fffc0 + +#define GRCAN_INVALID_BUFFER_SIZE(s) \ + ((s) == 0 || ((s) & ~GRCAN_VALID_TR_SIZE_MASK)) + +#if GRCAN_INVALID_BUFFER_SIZE(GRCAN_DEFAULT_BUFFER_SIZE) +#error "Invalid default buffer size" +#endif + +struct grcan_dma_buffer { + size_t size; + void *buf; + dma_addr_t handle; +}; + +struct grcan_dma { + size_t base_size; + void *base_buf; + dma_addr_t base_handle; + struct grcan_dma_buffer tx; + struct grcan_dma_buffer rx; +}; + +/* GRCAN configuration parameters */ +struct grcan_device_config { + unsigned short enable0; + unsigned short enable1; + unsigned short select; + unsigned int txsize; + unsigned int rxsize; +}; + +#define GRCAN_DEFAULT_DEVICE_CONFIG { \ + .enable0 = 0, \ + .enable1 = 0, \ + .select = 0, \ + .txsize = GRCAN_DEFAULT_BUFFER_SIZE, \ + .rxsize = GRCAN_DEFAULT_BUFFER_SIZE, \ + } + +#define GRCAN_TXBUG_SAFE_GRLIB_VERSION 0x4100 +#define GRLIB_VERSION_MASK 0xffff + +/* GRCAN private data structure */ +struct grcan_priv { + struct can_priv can; /* must be the first member */ + struct net_device *dev; + struct napi_struct napi; + + struct grcan_registers __iomem *regs; /* ioremap'ed registers */ + struct grcan_device_config config; + struct grcan_dma dma; + + struct sk_buff **echo_skb; /* We allocate this on our own */ + u8 *txdlc; /* Length of queued frames */ + + /* The echo skb pointer, pointing into echo_skb and indicating which + * frames can be echoed back. See the "Notes on the tx cyclic buffer + * handling"-comment for grcan_start_xmit for more details. + */ + u32 eskbp; + + /* Lock for controlling changes to the netif tx queue state, accesses to + * the echo_skb pointer eskbp and for making sure that a running reset + * and/or a close of the interface is done without interference from + * other parts of the code. + * + * The echo_skb pointer, eskbp, should only be accessed under this lock + * as it can be changed in several places and together with decisions on + * whether to wake up the tx queue. + * + * The tx queue must never be woken up if there is a running reset or + * close in progress. + * + * A running reset (see below on need_txbug_workaround) should never be + * done if the interface is closing down and several running resets + * should never be scheduled simultaneously. + */ + spinlock_t lock; + + /* Whether a workaround is needed due to a bug in older hardware. In + * this case, the driver both tries to prevent the bug from being + * triggered and recovers, if the bug nevertheless happens, by doing a + * running reset. A running reset, resets the device and continues from + * where it were without being noticeable from outside the driver (apart + * from slight delays). + */ + bool need_txbug_workaround; + + /* To trigger initization of running reset and to trigger running reset + * respectively in the case of a hanged device due to a txbug. + */ + struct timer_list hang_timer; + struct timer_list rr_timer; + + /* To avoid waking up the netif queue and restarting timers + * when a reset is scheduled or when closing of the device is + * undergoing + */ + bool resetting; + bool closing; +}; + +/* Wait time for a short wait for ongoing to clear */ +#define GRCAN_SHORTWAIT_USECS 10 + +/* Limit on the number of transmitted bits of an eff frame according to the CAN + * specification: 1 bit start of frame, 32 bits arbitration field, 6 bits + * control field, 8 bytes data field, 16 bits crc field, 2 bits ACK field and 7 + * bits end of frame + */ +#define GRCAN_EFF_FRAME_MAX_BITS (1+32+6+8*8+16+2+7) + +#if defined(__BIG_ENDIAN) +static inline u32 grcan_read_reg(u32 __iomem *reg) +{ + return ioread32be(reg); +} + +static inline void grcan_write_reg(u32 __iomem *reg, u32 val) +{ + iowrite32be(val, reg); +} +#else +static inline u32 grcan_read_reg(u32 __iomem *reg) +{ + return ioread32(reg); +} + +static inline void grcan_write_reg(u32 __iomem *reg, u32 val) +{ + iowrite32(val, reg); +} +#endif + +static inline void grcan_clear_bits(u32 __iomem *reg, u32 mask) +{ + grcan_write_reg(reg, grcan_read_reg(reg) & ~mask); +} + +static inline void grcan_set_bits(u32 __iomem *reg, u32 mask) +{ + grcan_write_reg(reg, grcan_read_reg(reg) | mask); +} + +static inline u32 grcan_read_bits(u32 __iomem *reg, u32 mask) +{ + return grcan_read_reg(reg) & mask; +} + +static inline void grcan_write_bits(u32 __iomem *reg, u32 value, u32 mask) +{ + u32 old = grcan_read_reg(reg); + + grcan_write_reg(reg, (old & ~mask) | (value & mask)); +} + +/* a and b should both be in [0,size] and a == b == size should not hold */ +static inline u32 grcan_ring_add(u32 a, u32 b, u32 size) +{ + u32 sum = a + b; + + if (sum < size) + return sum; + else + return sum - size; +} + +/* a and b should both be in [0,size) */ +static inline u32 grcan_ring_sub(u32 a, u32 b, u32 size) +{ + return grcan_ring_add(a, size - b, size); +} + +/* Available slots for new transmissions */ +static inline u32 grcan_txspace(size_t txsize, u32 txwr, u32 eskbp) +{ + u32 slots = txsize / GRCAN_MSG_SIZE - 1; + u32 used = grcan_ring_sub(txwr, eskbp, txsize) / GRCAN_MSG_SIZE; + + return slots - used; +} + +/* Configuration parameters that can be set via module parameters */ +static struct grcan_device_config grcan_module_config = + GRCAN_DEFAULT_DEVICE_CONFIG; + +static const struct can_bittiming_const grcan_bittiming_const = { + .name = DRV_NAME, + .tseg1_min = GRCAN_CONF_PS1_MIN + 1, + .tseg1_max = GRCAN_CONF_PS1_MAX + 1, + .tseg2_min = GRCAN_CONF_PS2_MIN, + .tseg2_max = GRCAN_CONF_PS2_MAX, + .sjw_max = GRCAN_CONF_RSJ_MAX, + .brp_min = GRCAN_CONF_SCALER_MIN + 1, + .brp_max = GRCAN_CONF_SCALER_MAX + 1, + .brp_inc = GRCAN_CONF_SCALER_INC, +}; + +static int grcan_set_bittiming(struct net_device *dev) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + struct can_bittiming *bt = &priv->can.bittiming; + u32 timing = 0; + int bpr, rsj, ps1, ps2, scaler; + + /* Should never happen - function will not be called when + * device is up + */ + if (grcan_read_bits(®s->ctrl, GRCAN_CTRL_ENABLE)) + return -EBUSY; + + bpr = 0; /* Note bpr and brp are different concepts */ + rsj = bt->sjw; + ps1 = (bt->prop_seg + bt->phase_seg1) - 1; /* tseg1 - 1 */ + ps2 = bt->phase_seg2; + scaler = (bt->brp - 1); + netdev_dbg(dev, "Request for BPR=%d, RSJ=%d, PS1=%d, PS2=%d, SCALER=%d", + bpr, rsj, ps1, ps2, scaler); + if (!(ps1 > ps2)) { + netdev_err(dev, "PS1 > PS2 must hold: PS1=%d, PS2=%d\n", + ps1, ps2); + return -EINVAL; + } + if (!(ps2 >= rsj)) { + netdev_err(dev, "PS2 >= RSJ must hold: PS2=%d, RSJ=%d\n", + ps2, rsj); + return -EINVAL; + } + + timing |= (bpr << GRCAN_CONF_BPR_BIT) & GRCAN_CONF_BPR; + timing |= (rsj << GRCAN_CONF_RSJ_BIT) & GRCAN_CONF_RSJ; + timing |= (ps1 << GRCAN_CONF_PS1_BIT) & GRCAN_CONF_PS1; + timing |= (ps2 << GRCAN_CONF_PS2_BIT) & GRCAN_CONF_PS2; + timing |= (scaler << GRCAN_CONF_SCALER_BIT) & GRCAN_CONF_SCALER; + netdev_info(dev, "setting timing=0x%x\n", timing); + grcan_write_bits(®s->conf, timing, GRCAN_CONF_TIMING); + + return 0; +} + +static int grcan_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + u32 status = grcan_read_reg(®s->stat); + + bec->txerr = (status & GRCAN_STAT_TXERRCNT) >> GRCAN_STAT_TXERRCNT_BIT; + bec->rxerr = (status & GRCAN_STAT_RXERRCNT) >> GRCAN_STAT_RXERRCNT_BIT; + return 0; +} + +static int grcan_poll(struct napi_struct *napi, int budget); + +/* Reset device, but keep configuration information */ +static void grcan_reset(struct net_device *dev) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + u32 config = grcan_read_reg(®s->conf); + + grcan_set_bits(®s->ctrl, GRCAN_CTRL_RESET); + grcan_write_reg(®s->conf, config); + + priv->eskbp = grcan_read_reg(®s->txrd); + priv->can.state = CAN_STATE_STOPPED; + + /* Turn off hardware filtering - regs->rxcode set to 0 by reset */ + grcan_write_reg(®s->rxmask, 0); +} + +/* stop device without changing any configurations */ +static void grcan_stop_hardware(struct net_device *dev) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + + grcan_write_reg(®s->imr, GRCAN_IRQ_NONE); + grcan_clear_bits(®s->txctrl, GRCAN_TXCTRL_ENABLE); + grcan_clear_bits(®s->rxctrl, GRCAN_RXCTRL_ENABLE); + grcan_clear_bits(®s->ctrl, GRCAN_CTRL_ENABLE); +} + +/* Let priv->eskbp catch up to regs->txrd and echo back the skbs if echo + * is true and free them otherwise. + * + * If budget is >= 0, stop after handling at most budget skbs. Otherwise, + * continue until priv->eskbp catches up to regs->txrd. + * + * priv->lock *must* be held when calling this function + */ +static int catch_up_echo_skb(struct net_device *dev, int budget, bool echo) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + struct grcan_dma *dma = &priv->dma; + struct net_device_stats *stats = &dev->stats; + int i, work_done; + + /* Updates to priv->eskbp and wake-ups of the queue needs to + * be atomic towards the reads of priv->eskbp and shut-downs + * of the queue in grcan_start_xmit. + */ + u32 txrd = grcan_read_reg(®s->txrd); + + for (work_done = 0; work_done < budget || budget < 0; work_done++) { + if (priv->eskbp == txrd) + break; + i = priv->eskbp / GRCAN_MSG_SIZE; + if (echo) { + /* Normal echo of messages */ + stats->tx_packets++; + stats->tx_bytes += priv->txdlc[i]; + priv->txdlc[i] = 0; + can_get_echo_skb(dev, i); + } else { + /* For cleanup of untransmitted messages */ + can_free_echo_skb(dev, i); + } + + priv->eskbp = grcan_ring_add(priv->eskbp, GRCAN_MSG_SIZE, + dma->tx.size); + txrd = grcan_read_reg(®s->txrd); + } + return work_done; +} + +static void grcan_lost_one_shot_frame(struct net_device *dev) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + struct grcan_dma *dma = &priv->dma; + u32 txrd; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + catch_up_echo_skb(dev, -1, true); + + if (unlikely(grcan_read_bits(®s->txctrl, GRCAN_TXCTRL_ENABLE))) { + /* Should never happen */ + netdev_err(dev, "TXCTRL enabled at TXLOSS in one shot mode\n"); + } else { + /* By the time an GRCAN_IRQ_TXLOSS is generated in + * one-shot mode there is no problem in writing + * to TXRD even in versions of the hardware in + * which GRCAN_TXCTRL_ONGOING is not cleared properly + * in one-shot mode. + */ + + /* Skip message and discard echo-skb */ + txrd = grcan_read_reg(®s->txrd); + txrd = grcan_ring_add(txrd, GRCAN_MSG_SIZE, dma->tx.size); + grcan_write_reg(®s->txrd, txrd); + catch_up_echo_skb(dev, -1, false); + + if (!priv->resetting && !priv->closing && + !(priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)) { + netif_wake_queue(dev); + grcan_set_bits(®s->txctrl, GRCAN_TXCTRL_ENABLE); + } + } + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void grcan_err(struct net_device *dev, u32 sources, u32 status) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + struct grcan_dma *dma = &priv->dma; + struct net_device_stats *stats = &dev->stats; + struct can_frame cf; + + /* Zero potential error_frame */ + memset(&cf, 0, sizeof(cf)); + + /* Message lost interrupt. This might be due to arbitration error, but + * is also triggered when there is no one else on the can bus or when + * there is a problem with the hardware interface or the bus itself. As + * arbitration errors can not be singled out, no error frames are + * generated reporting this event as an arbitration error. + */ + if (sources & GRCAN_IRQ_TXLOSS) { + /* Take care of failed one-shot transmit */ + if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT) + grcan_lost_one_shot_frame(dev); + + /* Stop printing as soon as error passive or bus off is in + * effect to limit the amount of txloss debug printouts. + */ + if (!(status & GRCAN_STAT_ERRCTR_RELATED)) { + netdev_dbg(dev, "tx message lost\n"); + stats->tx_errors++; + } + } + + /* Conditions dealing with the error counters. There is no interrupt for + * error warning, but there are interrupts for increases of the error + * counters. + */ + if ((sources & GRCAN_IRQ_ERRCTR_RELATED) || + (status & GRCAN_STAT_ERRCTR_RELATED)) { + enum can_state state = priv->can.state; + enum can_state oldstate = state; + u32 txerr = (status & GRCAN_STAT_TXERRCNT) + >> GRCAN_STAT_TXERRCNT_BIT; + u32 rxerr = (status & GRCAN_STAT_RXERRCNT) + >> GRCAN_STAT_RXERRCNT_BIT; + + /* Figure out current state */ + if (status & GRCAN_STAT_OFF) { + state = CAN_STATE_BUS_OFF; + } else if (status & GRCAN_STAT_PASS) { + state = CAN_STATE_ERROR_PASSIVE; + } else if (txerr >= GRCAN_STAT_ERRCNT_WARNING_LIMIT || + rxerr >= GRCAN_STAT_ERRCNT_WARNING_LIMIT) { + state = CAN_STATE_ERROR_WARNING; + } else { + state = CAN_STATE_ERROR_ACTIVE; + } + + /* Handle and report state changes */ + if (state != oldstate) { + switch (state) { + case CAN_STATE_BUS_OFF: + netdev_dbg(dev, "bus-off\n"); + netif_carrier_off(dev); + priv->can.can_stats.bus_off++; + + /* Prevent the hardware from recovering from bus + * off on its own if restart is disabled. + */ + if (!priv->can.restart_ms) + grcan_stop_hardware(dev); + + cf.can_id |= CAN_ERR_BUSOFF; + break; + + case CAN_STATE_ERROR_PASSIVE: + netdev_dbg(dev, "Error passive condition\n"); + priv->can.can_stats.error_passive++; + + cf.can_id |= CAN_ERR_CRTL; + if (txerr >= GRCAN_STAT_ERRCNT_PASSIVE_LIMIT) + cf.data[1] |= CAN_ERR_CRTL_TX_PASSIVE; + if (rxerr >= GRCAN_STAT_ERRCNT_PASSIVE_LIMIT) + cf.data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + break; + + case CAN_STATE_ERROR_WARNING: + netdev_dbg(dev, "Error warning condition\n"); + priv->can.can_stats.error_warning++; + + cf.can_id |= CAN_ERR_CRTL; + if (txerr >= GRCAN_STAT_ERRCNT_WARNING_LIMIT) + cf.data[1] |= CAN_ERR_CRTL_TX_WARNING; + if (rxerr >= GRCAN_STAT_ERRCNT_WARNING_LIMIT) + cf.data[1] |= CAN_ERR_CRTL_RX_WARNING; + break; + + case CAN_STATE_ERROR_ACTIVE: + netdev_dbg(dev, "Error active condition\n"); + cf.can_id |= CAN_ERR_CRTL; + break; + + default: + /* There are no others at this point */ + break; + } + cf.data[6] = txerr; + cf.data[7] = rxerr; + priv->can.state = state; + } + + /* Report automatic restarts */ + if (priv->can.restart_ms && oldstate == CAN_STATE_BUS_OFF) { + unsigned long flags; + + cf.can_id |= CAN_ERR_RESTARTED; + netdev_dbg(dev, "restarted\n"); + priv->can.can_stats.restarts++; + netif_carrier_on(dev); + + spin_lock_irqsave(&priv->lock, flags); + + if (!priv->resetting && !priv->closing) { + u32 txwr = grcan_read_reg(®s->txwr); + + if (grcan_txspace(dma->tx.size, txwr, + priv->eskbp)) + netif_wake_queue(dev); + } + + spin_unlock_irqrestore(&priv->lock, flags); + } + } + + /* Data overrun interrupt */ + if ((sources & GRCAN_IRQ_OR) || (status & GRCAN_STAT_OR)) { + netdev_dbg(dev, "got data overrun interrupt\n"); + stats->rx_over_errors++; + stats->rx_errors++; + + cf.can_id |= CAN_ERR_CRTL; + cf.data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + } + + /* AHB bus error interrupts (not CAN bus errors) - shut down the + * device. + */ + if (sources & (GRCAN_IRQ_TXAHBERR | GRCAN_IRQ_RXAHBERR) || + (status & GRCAN_STAT_AHBERR)) { + char *txrx = ""; + unsigned long flags; + + if (sources & GRCAN_IRQ_TXAHBERR) { + txrx = "on tx "; + stats->tx_errors++; + } else if (sources & GRCAN_IRQ_RXAHBERR) { + txrx = "on rx "; + stats->rx_errors++; + } + netdev_err(dev, "Fatal AHB buss error %s- halting device\n", + txrx); + + spin_lock_irqsave(&priv->lock, flags); + + /* Prevent anything to be enabled again and halt device */ + priv->closing = true; + netif_stop_queue(dev); + grcan_stop_hardware(dev); + priv->can.state = CAN_STATE_STOPPED; + + spin_unlock_irqrestore(&priv->lock, flags); + } + + /* Pass on error frame if something to report, + * i.e. id contains some information + */ + if (cf.can_id) { + struct can_frame *skb_cf; + struct sk_buff *skb = alloc_can_err_skb(dev, &skb_cf); + + if (skb == NULL) { + netdev_dbg(dev, "could not allocate error frame\n"); + return; + } + skb_cf->can_id |= cf.can_id; + memcpy(skb_cf->data, cf.data, sizeof(cf.data)); + + netif_rx(skb); + } +} + +static irqreturn_t grcan_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + u32 sources, status; + + /* Find out the source */ + sources = grcan_read_reg(®s->pimsr); + if (!sources) + return IRQ_NONE; + grcan_write_reg(®s->picr, sources); + status = grcan_read_reg(®s->stat); + + /* If we got TX progress, the device has not hanged, + * so disable the hang timer + */ + if (priv->need_txbug_workaround && + (sources & (GRCAN_IRQ_TX | GRCAN_IRQ_TXLOSS))) { + del_timer(&priv->hang_timer); + } + + /* Frame(s) received or transmitted */ + if (sources & (GRCAN_IRQ_TX | GRCAN_IRQ_RX)) { + /* Disable tx/rx interrupts and schedule poll(). No need for + * locking as interference from a running reset at worst leads + * to an extra interrupt. + */ + grcan_clear_bits(®s->imr, GRCAN_IRQ_TX | GRCAN_IRQ_RX); + napi_schedule(&priv->napi); + } + + /* (Potential) error conditions to take care of */ + if (sources & GRCAN_IRQ_ERRORS) + grcan_err(dev, sources, status); + + return IRQ_HANDLED; +} + +/* Reset device and restart operations from where they were. + * + * This assumes that RXCTRL & RXCTRL is properly disabled and that RX + * is not ONGOING (TX might be stuck in ONGOING due to a harwrware bug + * for single shot) + */ +static void grcan_running_reset(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + unsigned long flags; + + /* This temporarily messes with eskbp, so we need to lock + * priv->lock + */ + spin_lock_irqsave(&priv->lock, flags); + + priv->resetting = false; + del_timer(&priv->hang_timer); + del_timer(&priv->rr_timer); + + if (!priv->closing) { + /* Save and reset - config register preserved by grcan_reset */ + u32 imr = grcan_read_reg(®s->imr); + + u32 txaddr = grcan_read_reg(®s->txaddr); + u32 txsize = grcan_read_reg(®s->txsize); + u32 txwr = grcan_read_reg(®s->txwr); + u32 txrd = grcan_read_reg(®s->txrd); + u32 eskbp = priv->eskbp; + + u32 rxaddr = grcan_read_reg(®s->rxaddr); + u32 rxsize = grcan_read_reg(®s->rxsize); + u32 rxwr = grcan_read_reg(®s->rxwr); + u32 rxrd = grcan_read_reg(®s->rxrd); + + grcan_reset(dev); + + /* Restore */ + grcan_write_reg(®s->txaddr, txaddr); + grcan_write_reg(®s->txsize, txsize); + grcan_write_reg(®s->txwr, txwr); + grcan_write_reg(®s->txrd, txrd); + priv->eskbp = eskbp; + + grcan_write_reg(®s->rxaddr, rxaddr); + grcan_write_reg(®s->rxsize, rxsize); + grcan_write_reg(®s->rxwr, rxwr); + grcan_write_reg(®s->rxrd, rxrd); + + /* Turn on device again */ + grcan_write_reg(®s->imr, imr); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + grcan_write_reg(®s->txctrl, GRCAN_TXCTRL_ENABLE + | (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT + ? GRCAN_TXCTRL_SINGLE : 0)); + grcan_write_reg(®s->rxctrl, GRCAN_RXCTRL_ENABLE); + grcan_write_reg(®s->ctrl, GRCAN_CTRL_ENABLE); + + /* Start queue if there is size and listen-onle mode is not + * enabled + */ + if (grcan_txspace(priv->dma.tx.size, txwr, priv->eskbp) && + !(priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)) + netif_wake_queue(dev); + } + + spin_unlock_irqrestore(&priv->lock, flags); + + netdev_err(dev, "Device reset and restored\n"); +} + +/* Waiting time in usecs corresponding to the transmission of three maximum + * sized can frames in the given bitrate (in bits/sec). Waiting for this amount + * of time makes sure that the can controller have time to finish sending or + * receiving a frame with a good margin. + * + * usecs/sec * number of frames * bits/frame / bits/sec + */ +static inline u32 grcan_ongoing_wait_usecs(__u32 bitrate) +{ + return 1000000 * 3 * GRCAN_EFF_FRAME_MAX_BITS / bitrate; +} + +/* Set timer so that it will not fire until after a period in which the can + * controller have a good margin to finish transmitting a frame unless it has + * hanged + */ +static inline void grcan_reset_timer(struct timer_list *timer, __u32 bitrate) +{ + u32 wait_jiffies = usecs_to_jiffies(grcan_ongoing_wait_usecs(bitrate)); + + mod_timer(timer, jiffies + wait_jiffies); +} + +/* Disable channels and schedule a running reset */ +static void grcan_initiate_running_reset(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + unsigned long flags; + + netdev_err(dev, "Device seems hanged - reset scheduled\n"); + + spin_lock_irqsave(&priv->lock, flags); + + /* The main body of this function must never be executed again + * until after an execution of grcan_running_reset + */ + if (!priv->resetting && !priv->closing) { + priv->resetting = true; + netif_stop_queue(dev); + grcan_clear_bits(®s->txctrl, GRCAN_TXCTRL_ENABLE); + grcan_clear_bits(®s->rxctrl, GRCAN_RXCTRL_ENABLE); + grcan_reset_timer(&priv->rr_timer, priv->can.bittiming.bitrate); + } + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void grcan_free_dma_buffers(struct net_device *dev) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_dma *dma = &priv->dma; + + dma_free_coherent(&dev->dev, dma->base_size, dma->base_buf, + dma->base_handle); + memset(dma, 0, sizeof(*dma)); +} + +static int grcan_allocate_dma_buffers(struct net_device *dev, + size_t tsize, size_t rsize) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_dma *dma = &priv->dma; + struct grcan_dma_buffer *large = rsize > tsize ? &dma->rx : &dma->tx; + struct grcan_dma_buffer *small = rsize > tsize ? &dma->tx : &dma->rx; + size_t shift; + + /* Need a whole number of GRCAN_BUFFER_ALIGNMENT for the large, + * i.e. first buffer + */ + size_t maxs = max(tsize, rsize); + size_t lsize = ALIGN(maxs, GRCAN_BUFFER_ALIGNMENT); + + /* Put the small buffer after that */ + size_t ssize = min(tsize, rsize); + + /* Extra GRCAN_BUFFER_ALIGNMENT to allow for alignment */ + dma->base_size = lsize + ssize + GRCAN_BUFFER_ALIGNMENT; + dma->base_buf = dma_alloc_coherent(&dev->dev, + dma->base_size, + &dma->base_handle, + GFP_KERNEL); + + if (!dma->base_buf) + return -ENOMEM; + + dma->tx.size = tsize; + dma->rx.size = rsize; + + large->handle = ALIGN(dma->base_handle, GRCAN_BUFFER_ALIGNMENT); + small->handle = large->handle + lsize; + shift = large->handle - dma->base_handle; + + large->buf = dma->base_buf + shift; + small->buf = large->buf + lsize; + + return 0; +} + +/* priv->lock *must* be held when calling this function */ +static int grcan_start(struct net_device *dev) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + u32 confop, txctrl; + + grcan_reset(dev); + + grcan_write_reg(®s->txaddr, priv->dma.tx.handle); + grcan_write_reg(®s->txsize, priv->dma.tx.size); + /* regs->txwr, regs->txrd and priv->eskbp already set to 0 by reset */ + + grcan_write_reg(®s->rxaddr, priv->dma.rx.handle); + grcan_write_reg(®s->rxsize, priv->dma.rx.size); + /* regs->rxwr and regs->rxrd already set to 0 by reset */ + + /* Enable interrupts */ + grcan_read_reg(®s->pir); + grcan_write_reg(®s->imr, GRCAN_IRQ_DEFAULT); + + /* Enable interfaces, channels and device */ + confop = GRCAN_CONF_ABORT + | (priv->config.enable0 ? GRCAN_CONF_ENABLE0 : 0) + | (priv->config.enable1 ? GRCAN_CONF_ENABLE1 : 0) + | (priv->config.select ? GRCAN_CONF_SELECT : 0) + | (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY ? + GRCAN_CONF_SILENT : 0) + | (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES ? + GRCAN_CONF_SAM : 0); + grcan_write_bits(®s->conf, confop, GRCAN_CONF_OPERATION); + txctrl = GRCAN_TXCTRL_ENABLE + | (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT + ? GRCAN_TXCTRL_SINGLE : 0); + grcan_write_reg(®s->txctrl, txctrl); + grcan_write_reg(®s->rxctrl, GRCAN_RXCTRL_ENABLE); + grcan_write_reg(®s->ctrl, GRCAN_CTRL_ENABLE); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; +} + +static int grcan_set_mode(struct net_device *dev, enum can_mode mode) +{ + struct grcan_priv *priv = netdev_priv(dev); + unsigned long flags; + int err = 0; + + if (mode == CAN_MODE_START) { + /* This might be called to restart the device to recover from + * bus off errors + */ + spin_lock_irqsave(&priv->lock, flags); + if (priv->closing || priv->resetting) { + err = -EBUSY; + } else { + netdev_info(dev, "Restarting device\n"); + grcan_start(dev); + if (!(priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)) + netif_wake_queue(dev); + } + spin_unlock_irqrestore(&priv->lock, flags); + return err; + } + return -EOPNOTSUPP; +} + +static int grcan_open(struct net_device *dev) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_dma *dma = &priv->dma; + unsigned long flags; + int err; + + /* Allocate memory */ + err = grcan_allocate_dma_buffers(dev, priv->config.txsize, + priv->config.rxsize); + if (err) { + netdev_err(dev, "could not allocate DMA buffers\n"); + return err; + } + + priv->echo_skb = kzalloc(dma->tx.size * sizeof(*priv->echo_skb), + GFP_KERNEL); + if (!priv->echo_skb) { + err = -ENOMEM; + goto exit_free_dma_buffers; + } + priv->can.echo_skb_max = dma->tx.size; + priv->can.echo_skb = priv->echo_skb; + + priv->txdlc = kzalloc(dma->tx.size * sizeof(*priv->txdlc), GFP_KERNEL); + if (!priv->txdlc) { + err = -ENOMEM; + goto exit_free_echo_skb; + } + + /* Get can device up */ + err = open_candev(dev); + if (err) + goto exit_free_txdlc; + + err = request_irq(dev->irq, grcan_interrupt, IRQF_SHARED, + dev->name, dev); + if (err) + goto exit_close_candev; + + spin_lock_irqsave(&priv->lock, flags); + + napi_enable(&priv->napi); + grcan_start(dev); + if (!(priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)) + netif_start_queue(dev); + priv->resetting = false; + priv->closing = false; + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; + +exit_close_candev: + close_candev(dev); +exit_free_txdlc: + kfree(priv->txdlc); +exit_free_echo_skb: + kfree(priv->echo_skb); +exit_free_dma_buffers: + grcan_free_dma_buffers(dev); + return err; +} + +static int grcan_close(struct net_device *dev) +{ + struct grcan_priv *priv = netdev_priv(dev); + unsigned long flags; + + napi_disable(&priv->napi); + + spin_lock_irqsave(&priv->lock, flags); + + priv->closing = true; + if (priv->need_txbug_workaround) { + del_timer_sync(&priv->hang_timer); + del_timer_sync(&priv->rr_timer); + } + netif_stop_queue(dev); + grcan_stop_hardware(dev); + priv->can.state = CAN_STATE_STOPPED; + + spin_unlock_irqrestore(&priv->lock, flags); + + free_irq(dev->irq, dev); + close_candev(dev); + + grcan_free_dma_buffers(dev); + priv->can.echo_skb_max = 0; + priv->can.echo_skb = NULL; + kfree(priv->echo_skb); + kfree(priv->txdlc); + + return 0; +} + +static int grcan_transmit_catch_up(struct net_device *dev, int budget) +{ + struct grcan_priv *priv = netdev_priv(dev); + unsigned long flags; + int work_done; + + spin_lock_irqsave(&priv->lock, flags); + + work_done = catch_up_echo_skb(dev, budget, true); + if (work_done) { + if (!priv->resetting && !priv->closing && + !(priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)) + netif_wake_queue(dev); + + /* With napi we don't get TX interrupts for a while, + * so prevent a running reset while catching up + */ + if (priv->need_txbug_workaround) + del_timer(&priv->hang_timer); + } + + spin_unlock_irqrestore(&priv->lock, flags); + + return work_done; +} + +static int grcan_receive(struct net_device *dev, int budget) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + struct grcan_dma *dma = &priv->dma; + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 wr, rd, startrd; + u32 *slot; + u32 i, rtr, eff, j, shift; + int work_done = 0; + + rd = grcan_read_reg(®s->rxrd); + startrd = rd; + for (work_done = 0; work_done < budget; work_done++) { + /* Check for packet to receive */ + wr = grcan_read_reg(®s->rxwr); + if (rd == wr) + break; + + /* Take care of packet */ + skb = alloc_can_skb(dev, &cf); + if (skb == NULL) { + netdev_err(dev, + "dropping frame: skb allocation failed\n"); + stats->rx_dropped++; + continue; + } + + slot = dma->rx.buf + rd; + eff = slot[0] & GRCAN_MSG_IDE; + rtr = slot[0] & GRCAN_MSG_RTR; + if (eff) { + cf->can_id = ((slot[0] & GRCAN_MSG_EID) + >> GRCAN_MSG_EID_BIT); + cf->can_id |= CAN_EFF_FLAG; + } else { + cf->can_id = ((slot[0] & GRCAN_MSG_BID) + >> GRCAN_MSG_BID_BIT); + } + cf->can_dlc = get_can_dlc((slot[1] & GRCAN_MSG_DLC) + >> GRCAN_MSG_DLC_BIT); + if (rtr) { + cf->can_id |= CAN_RTR_FLAG; + } else { + for (i = 0; i < cf->can_dlc; i++) { + j = GRCAN_MSG_DATA_SLOT_INDEX(i); + shift = GRCAN_MSG_DATA_SHIFT(i); + cf->data[i] = (u8)(slot[j] >> shift); + } + } + netif_receive_skb(skb); + + /* Update statistics and read pointer */ + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + rd = grcan_ring_add(rd, GRCAN_MSG_SIZE, dma->rx.size); + } + + /* Make sure everything is read before allowing hardware to + * use the memory + */ + mb(); + + /* Update read pointer - no need to check for ongoing */ + if (likely(rd != startrd)) + grcan_write_reg(®s->rxrd, rd); + + return work_done; +} + +static int grcan_poll(struct napi_struct *napi, int budget) +{ + struct grcan_priv *priv = container_of(napi, struct grcan_priv, napi); + struct net_device *dev = priv->dev; + struct grcan_registers __iomem *regs = priv->regs; + unsigned long flags; + int tx_work_done, rx_work_done; + int rx_budget = budget / 2; + int tx_budget = budget - rx_budget; + + /* Half of the budget for receiveing messages */ + rx_work_done = grcan_receive(dev, rx_budget); + + /* Half of the budget for transmitting messages as that can trigger echo + * frames being received + */ + tx_work_done = grcan_transmit_catch_up(dev, tx_budget); + + if (rx_work_done < rx_budget && tx_work_done < tx_budget) { + napi_complete(napi); + + /* Guarantee no interference with a running reset that otherwise + * could turn off interrupts. + */ + spin_lock_irqsave(&priv->lock, flags); + + /* Enable tx and rx interrupts again. No need to check + * priv->closing as napi_disable in grcan_close is waiting for + * scheduled napi calls to finish. + */ + grcan_set_bits(®s->imr, GRCAN_IRQ_TX | GRCAN_IRQ_RX); + + spin_unlock_irqrestore(&priv->lock, flags); + } + + return rx_work_done + tx_work_done; +} + +/* Work tx bug by waiting while for the risky situation to clear. If that fails, + * drop a frame in one-shot mode or indicate a busy device otherwise. + * + * Returns 0 on successful wait. Otherwise it sets *netdev_tx_status to the + * value that should be returned by grcan_start_xmit when aborting the xmit. + */ +static int grcan_txbug_workaround(struct net_device *dev, struct sk_buff *skb, + u32 txwr, u32 oneshotmode, + netdev_tx_t *netdev_tx_status) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + struct grcan_dma *dma = &priv->dma; + int i; + unsigned long flags; + + /* Wait a while for ongoing to be cleared or read pointer to catch up to + * write pointer. The latter is needed due to a bug in older versions of + * GRCAN in which ONGOING is not cleared properly one-shot mode when a + * transmission fails. + */ + for (i = 0; i < GRCAN_SHORTWAIT_USECS; i++) { + udelay(1); + if (!grcan_read_bits(®s->txctrl, GRCAN_TXCTRL_ONGOING) || + grcan_read_reg(®s->txrd) == txwr) { + return 0; + } + } + + /* Clean up, in case the situation was not resolved */ + spin_lock_irqsave(&priv->lock, flags); + if (!priv->resetting && !priv->closing) { + /* Queue might have been stopped earlier in grcan_start_xmit */ + if (grcan_txspace(dma->tx.size, txwr, priv->eskbp)) + netif_wake_queue(dev); + /* Set a timer to resolve a hanged tx controller */ + if (!timer_pending(&priv->hang_timer)) + grcan_reset_timer(&priv->hang_timer, + priv->can.bittiming.bitrate); + } + spin_unlock_irqrestore(&priv->lock, flags); + + if (oneshotmode) { + /* In one-shot mode we should never end up here because + * then the interrupt handler increases txrd on TXLOSS, + * but it is consistent with one-shot mode to drop the + * frame in this case. + */ + kfree_skb(skb); + *netdev_tx_status = NETDEV_TX_OK; + } else { + /* In normal mode the socket-can transmission queue get + * to keep the frame so that it can be retransmitted + * later + */ + *netdev_tx_status = NETDEV_TX_BUSY; + } + return -EBUSY; +} + +/* Notes on the tx cyclic buffer handling: + * + * regs->txwr - the next slot for the driver to put data to be sent + * regs->txrd - the next slot for the device to read data + * priv->eskbp - the next slot for the driver to call can_put_echo_skb for + * + * grcan_start_xmit can enter more messages as long as regs->txwr does + * not reach priv->eskbp (within 1 message gap) + * + * The device sends messages until regs->txrd reaches regs->txwr + * + * The interrupt calls handler calls can_put_echo_skb until + * priv->eskbp reaches regs->txrd + */ +static netdev_tx_t grcan_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct grcan_priv *priv = netdev_priv(dev); + struct grcan_registers __iomem *regs = priv->regs; + struct grcan_dma *dma = &priv->dma; + struct can_frame *cf = (struct can_frame *)skb->data; + u32 id, txwr, txrd, space, txctrl; + int slotindex; + u32 *slot; + u32 i, rtr, eff, dlc, tmp, err; + int j, shift; + unsigned long flags; + u32 oneshotmode = priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + /* Trying to transmit in silent mode will generate error interrupts, but + * this should never happen - the queue should not have been started. + */ + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + return NETDEV_TX_BUSY; + + /* Reads of priv->eskbp and shut-downs of the queue needs to + * be atomic towards the updates to priv->eskbp and wake-ups + * of the queue in the interrupt handler. + */ + spin_lock_irqsave(&priv->lock, flags); + + txwr = grcan_read_reg(®s->txwr); + space = grcan_txspace(dma->tx.size, txwr, priv->eskbp); + + slotindex = txwr / GRCAN_MSG_SIZE; + slot = dma->tx.buf + txwr; + + if (unlikely(space == 1)) + netif_stop_queue(dev); + + spin_unlock_irqrestore(&priv->lock, flags); + /* End of critical section*/ + + /* This should never happen. If circular buffer is full, the + * netif_stop_queue should have been stopped already. + */ + if (unlikely(!space)) { + netdev_err(dev, "No buffer space, but queue is non-stopped.\n"); + return NETDEV_TX_BUSY; + } + + /* Convert and write CAN message to DMA buffer */ + eff = cf->can_id & CAN_EFF_FLAG; + rtr = cf->can_id & CAN_RTR_FLAG; + id = cf->can_id & (eff ? CAN_EFF_MASK : CAN_SFF_MASK); + dlc = cf->can_dlc; + if (eff) + tmp = (id << GRCAN_MSG_EID_BIT) & GRCAN_MSG_EID; + else + tmp = (id << GRCAN_MSG_BID_BIT) & GRCAN_MSG_BID; + slot[0] = (eff ? GRCAN_MSG_IDE : 0) | (rtr ? GRCAN_MSG_RTR : 0) | tmp; + + slot[1] = ((dlc << GRCAN_MSG_DLC_BIT) & GRCAN_MSG_DLC); + slot[2] = 0; + slot[3] = 0; + for (i = 0; i < dlc; i++) { + j = GRCAN_MSG_DATA_SLOT_INDEX(i); + shift = GRCAN_MSG_DATA_SHIFT(i); + slot[j] |= cf->data[i] << shift; + } + + /* Checking that channel has not been disabled. These cases + * should never happen + */ + txctrl = grcan_read_reg(®s->txctrl); + if (!(txctrl & GRCAN_TXCTRL_ENABLE)) + netdev_err(dev, "tx channel spuriously disabled\n"); + + if (oneshotmode && !(txctrl & GRCAN_TXCTRL_SINGLE)) + netdev_err(dev, "one-shot mode spuriously disabled\n"); + + /* Bug workaround for old version of grcan where updating txwr + * in the same clock cycle as the controller updates txrd to + * the current txwr could hang the can controller + */ + if (priv->need_txbug_workaround) { + txrd = grcan_read_reg(®s->txrd); + if (unlikely(grcan_ring_sub(txwr, txrd, dma->tx.size) == 1)) { + netdev_tx_t txstatus; + + err = grcan_txbug_workaround(dev, skb, txwr, + oneshotmode, &txstatus); + if (err) + return txstatus; + } + } + + /* Prepare skb for echoing. This must be after the bug workaround above + * as ownership of the skb is passed on by calling can_put_echo_skb. + * Returning NETDEV_TX_BUSY or accessing skb or cf after a call to + * can_put_echo_skb would be an error unless other measures are + * taken. + */ + priv->txdlc[slotindex] = cf->can_dlc; /* Store dlc for statistics */ + can_put_echo_skb(skb, dev, slotindex); + + /* Make sure everything is written before allowing hardware to + * read from the memory + */ + wmb(); + + /* Update write pointer to start transmission */ + grcan_write_reg(®s->txwr, + grcan_ring_add(txwr, GRCAN_MSG_SIZE, dma->tx.size)); + + return NETDEV_TX_OK; +} + +/* ========== Setting up sysfs interface and module parameters ========== */ + +#define GRCAN_NOT_BOOL(unsigned_val) ((unsigned_val) > 1) + +#define GRCAN_MODULE_PARAM(name, mtype, valcheckf, desc) \ + static void grcan_sanitize_##name(struct platform_device *pd) \ + { \ + struct grcan_device_config grcan_default_config \ + = GRCAN_DEFAULT_DEVICE_CONFIG; \ + if (valcheckf(grcan_module_config.name)) { \ + dev_err(&pd->dev, \ + "Invalid module parameter value for " \ + #name " - setting default\n"); \ + grcan_module_config.name = \ + grcan_default_config.name; \ + } \ + } \ + module_param_named(name, grcan_module_config.name, \ + mtype, S_IRUGO); \ + MODULE_PARM_DESC(name, desc) + +#define GRCAN_CONFIG_ATTR(name, desc) \ + static ssize_t grcan_store_##name(struct device *sdev, \ + struct device_attribute *att, \ + const char *buf, \ + size_t count) \ + { \ + struct net_device *dev = to_net_dev(sdev); \ + struct grcan_priv *priv = netdev_priv(dev); \ + u8 val; \ + int ret; \ + if (dev->flags & IFF_UP) \ + return -EBUSY; \ + ret = kstrtou8(buf, 0, &val); \ + if (ret < 0 || val > 1) \ + return -EINVAL; \ + priv->config.name = val; \ + return count; \ + } \ + static ssize_t grcan_show_##name(struct device *sdev, \ + struct device_attribute *att, \ + char *buf) \ + { \ + struct net_device *dev = to_net_dev(sdev); \ + struct grcan_priv *priv = netdev_priv(dev); \ + return sprintf(buf, "%d\n", priv->config.name); \ + } \ + static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, \ + grcan_show_##name, \ + grcan_store_##name); \ + GRCAN_MODULE_PARAM(name, ushort, GRCAN_NOT_BOOL, desc) + +/* The following configuration options are made available both via module + * parameters and writable sysfs files. See the chapter about GRCAN in the + * documentation for the GRLIB VHDL library for further details. + */ +GRCAN_CONFIG_ATTR(enable0, + "Configuration of physical interface 0. Determines\n" \ + "the \"Enable 0\" bit of the configuration register.\n" \ + "Format: 0 | 1\nDefault: 0\n"); + +GRCAN_CONFIG_ATTR(enable1, + "Configuration of physical interface 1. Determines\n" \ + "the \"Enable 1\" bit of the configuration register.\n" \ + "Format: 0 | 1\nDefault: 0\n"); + +GRCAN_CONFIG_ATTR(select, + "Select which physical interface to use.\n" \ + "Format: 0 | 1\nDefault: 0\n"); + +/* The tx and rx buffer size configuration options are only available via module + * parameters. + */ +GRCAN_MODULE_PARAM(txsize, uint, GRCAN_INVALID_BUFFER_SIZE, + "Sets the size of the tx buffer.\n" \ + "Format: where (txsize & ~0x1fffc0) == 0\n" \ + "Default: 1024\n"); +GRCAN_MODULE_PARAM(rxsize, uint, GRCAN_INVALID_BUFFER_SIZE, + "Sets the size of the rx buffer.\n" \ + "Format: where (size & ~0x1fffc0) == 0\n" \ + "Default: 1024\n"); + +/* Function that makes sure that configuration done using + * module parameters are set to valid values + */ +static void grcan_sanitize_module_config(struct platform_device *ofdev) +{ + grcan_sanitize_enable0(ofdev); + grcan_sanitize_enable1(ofdev); + grcan_sanitize_select(ofdev); + grcan_sanitize_txsize(ofdev); + grcan_sanitize_rxsize(ofdev); +} + +static const struct attribute *const sysfs_grcan_attrs[] = { + /* Config attrs */ + &dev_attr_enable0.attr, + &dev_attr_enable1.attr, + &dev_attr_select.attr, + NULL, +}; + +static const struct attribute_group sysfs_grcan_group = { + .name = "grcan", + .attrs = (struct attribute **)sysfs_grcan_attrs, +}; + +/* ========== Setting up the driver ========== */ + +static const struct net_device_ops grcan_netdev_ops = { + .ndo_open = grcan_open, + .ndo_stop = grcan_close, + .ndo_start_xmit = grcan_start_xmit, +}; + +static int grcan_setup_netdev(struct platform_device *ofdev, + void __iomem *base, + int irq, u32 ambafreq, bool txbug) +{ + struct net_device *dev; + struct grcan_priv *priv; + struct grcan_registers __iomem *regs; + int err; + + dev = alloc_candev(sizeof(struct grcan_priv), 0); + if (!dev) + return -ENOMEM; + + dev->irq = irq; + dev->flags |= IFF_ECHO; + dev->netdev_ops = &grcan_netdev_ops; + dev->sysfs_groups[0] = &sysfs_grcan_group; + + priv = netdev_priv(dev); + memcpy(&priv->config, &grcan_module_config, + sizeof(struct grcan_device_config)); + priv->dev = dev; + priv->regs = base; + priv->can.bittiming_const = &grcan_bittiming_const; + priv->can.do_set_bittiming = grcan_set_bittiming; + priv->can.do_set_mode = grcan_set_mode; + priv->can.do_get_berr_counter = grcan_get_berr_counter; + priv->can.clock.freq = ambafreq; + priv->can.ctrlmode_supported = + CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_ONE_SHOT; + priv->need_txbug_workaround = txbug; + + /* Discover if triple sampling is supported by hardware */ + regs = priv->regs; + grcan_set_bits(®s->ctrl, GRCAN_CTRL_RESET); + grcan_set_bits(®s->conf, GRCAN_CONF_SAM); + if (grcan_read_bits(®s->conf, GRCAN_CONF_SAM)) { + priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; + dev_dbg(&ofdev->dev, "Hardware supports triple-sampling\n"); + } + + spin_lock_init(&priv->lock); + + if (priv->need_txbug_workaround) { + init_timer(&priv->rr_timer); + priv->rr_timer.function = grcan_running_reset; + priv->rr_timer.data = (unsigned long)dev; + + init_timer(&priv->hang_timer); + priv->hang_timer.function = grcan_initiate_running_reset; + priv->hang_timer.data = (unsigned long)dev; + } + + netif_napi_add(dev, &priv->napi, grcan_poll, GRCAN_NAPI_WEIGHT); + + SET_NETDEV_DEV(dev, &ofdev->dev); + dev_info(&ofdev->dev, "regs=0x%p, irq=%d, clock=%d\n", + priv->regs, dev->irq, priv->can.clock.freq); + + err = register_candev(dev); + if (err) + goto exit_free_candev; + + dev_set_drvdata(&ofdev->dev, dev); + + /* Reset device to allow bit-timing to be set. No need to call + * grcan_reset at this stage. That is done in grcan_open. + */ + grcan_write_reg(®s->ctrl, GRCAN_CTRL_RESET); + + return 0; +exit_free_candev: + free_candev(dev); + return err; +} + +static int __devinit grcan_probe(struct platform_device *ofdev) +{ + struct device_node *np = ofdev->dev.of_node; + struct resource *res; + u32 sysid, ambafreq; + int irq, err; + void __iomem *base; + bool txbug = true; + + /* Compare GRLIB version number with the first that does not + * have the tx bug (see start_xmit) + */ + err = of_property_read_u32(np, "systemid", &sysid); + if (!err && ((sysid & GRLIB_VERSION_MASK) + >= GRCAN_TXBUG_SAFE_GRLIB_VERSION)) + txbug = false; + + err = of_property_read_u32(np, "freq", &ambafreq); + if (err) { + dev_err(&ofdev->dev, "unable to fetch \"freq\" property\n"); + goto exit_error; + } + + res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); + base = devm_request_and_ioremap(&ofdev->dev, res); + if (!base) { + dev_err(&ofdev->dev, "couldn't map IO resource\n"); + err = -EADDRNOTAVAIL; + goto exit_error; + } + + irq = irq_of_parse_and_map(np, GRCAN_IRQIX_IRQ); + if (!irq) { + dev_err(&ofdev->dev, "no irq found\n"); + err = -ENODEV; + goto exit_error; + } + + grcan_sanitize_module_config(ofdev); + + err = grcan_setup_netdev(ofdev, base, irq, ambafreq, txbug); + if (err) + goto exit_dispose_irq; + + return 0; + +exit_dispose_irq: + irq_dispose_mapping(irq); +exit_error: + dev_err(&ofdev->dev, + "%s socket CAN driver initialization failed with error %d\n", + DRV_NAME, err); + return err; +} + +static int __devexit grcan_remove(struct platform_device *ofdev) +{ + struct net_device *dev = dev_get_drvdata(&ofdev->dev); + struct grcan_priv *priv = netdev_priv(dev); + + unregister_candev(dev); /* Will in turn call grcan_close */ + + irq_dispose_mapping(dev->irq); + dev_set_drvdata(&ofdev->dev, NULL); + netif_napi_del(&priv->napi); + free_candev(dev); + + return 0; +} + +static struct of_device_id grcan_match[] __devinitconst = { + {.name = "GAISLER_GRCAN"}, + {.name = "01_03d"}, + {.name = "GAISLER_GRHCAN"}, + {.name = "01_034"}, + {}, +}; + +MODULE_DEVICE_TABLE(of, grcan_match); + +static struct platform_driver grcan_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = grcan_match, + }, + .probe = grcan_probe, + .remove = __devexit_p(grcan_remove), +}; + +module_platform_driver(grcan_driver); + +MODULE_AUTHOR("Aeroflex Gaisler AB."); +MODULE_DESCRIPTION("Socket CAN driver for Aeroflex Gaisler GRCAN"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From c42cb1c379a6e59e2a04380616f530713658c68b Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 31 Oct 2012 14:32:54 +0530 Subject: ARM: tegra: dts: cardhu: enable SLINK4 Enable SLINK4 and connected device in Tegra30 based platform Cardhu. Setting maximum spi frequency to 25MHz. SPI serial flash is connected on CS1 of SLINK4 on cardhu platform. Signed-off-by: Laxman Dewangan [swarren: swapped reg/compatible order to be consistent] Signed-off-by: Stephen Warren --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + arch/arm/boot/dts/tegra30-cardhu.dtsi | 10 ++++++++++ 2 files changed, 11 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 9de2b9ff9d6e..ac2c2c416a14 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -51,4 +51,5 @@ ti Texas Instruments via VIA Technologies, Inc. wlf Wolfson Microelectronics wm Wondermedia Technologies, Inc. +winbond Winbond Electronics corp. xlnx Xilinx diff --git a/arch/arm/boot/dts/tegra30-cardhu.dtsi b/arch/arm/boot/dts/tegra30-cardhu.dtsi index b245e6cf6338..bdb2a660f376 100644 --- a/arch/arm/boot/dts/tegra30-cardhu.dtsi +++ b/arch/arm/boot/dts/tegra30-cardhu.dtsi @@ -275,6 +275,16 @@ }; }; + spi@7000da00 { + status = "okay"; + spi-max-frequency = <25000000>; + spi-flash@1 { + compatible = "winbond,w25q32"; + reg = <1>; + spi-max-frequency = <20000000>; + }; + }; + ahub { i2s@70080400 { status = "okay"; -- cgit v1.2.3 From 531f67e41dcde1e358cf821d056241a66355cf03 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Wed, 14 Nov 2012 18:09:11 +0800 Subject: ASoC: at91sam9g20ek-wm8731: convert to dt support convert at91sam9g20ek with wm8731 to device tree support Signed-off-by: Bo Shen Signed-off-by: Mark Brown --- .../sound/atmel-at91sam9g20ek-wm8731-audio.txt | 26 +++++++++ arch/arm/boot/dts/at91sam9g20ek_common.dtsi | 32 ++++++++++- sound/soc/atmel/Kconfig | 3 +- sound/soc/atmel/sam9g20_wm8731.c | 63 +++++++++++++++++++++- 4 files changed, 119 insertions(+), 5 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/atmel-at91sam9g20ek-wm8731-audio.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/atmel-at91sam9g20ek-wm8731-audio.txt b/Documentation/devicetree/bindings/sound/atmel-at91sam9g20ek-wm8731-audio.txt new file mode 100644 index 000000000000..9c5a9947b64d --- /dev/null +++ b/Documentation/devicetree/bindings/sound/atmel-at91sam9g20ek-wm8731-audio.txt @@ -0,0 +1,26 @@ +* Atmel at91sam9g20ek wm8731 audio complex + +Required properties: + - compatible: "atmel,at91sam9g20ek-wm8731-audio" + - atmel,model: The user-visible name of this sound complex. + - atmel,audio-routing: A list of the connections between audio components. + - atmel,ssc-controller: The phandle of the SSC controller + - atmel,audio-codec: The phandle of the WM8731 audio codec +Optional properties: + - pinctrl-names, pinctrl-0: Please refer to pinctrl-bindings.txt + +Example: +sound { + compatible = "atmel,at91sam9g20ek-wm8731-audio"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pck0_as_mck>; + + atmel,model = "wm8731 @ AT91SAMG20EK"; + + atmel,audio-routing = + "Ext Spk", "LHPOUT", + "Int MIC", "MICIN"; + + atmel,ssc-controller = <&ssc0>; + atmel,audio-codec = <&wm8731>; +}; diff --git a/arch/arm/boot/dts/at91sam9g20ek_common.dtsi b/arch/arm/boot/dts/at91sam9g20ek_common.dtsi index e6391a4e6649..2dcec8de759f 100644 --- a/arch/arm/boot/dts/at91sam9g20ek_common.dtsi +++ b/arch/arm/boot/dts/at91sam9g20ek_common.dtsi @@ -30,6 +30,16 @@ ahb { apb { + pinctrl@fffff400 { + board { + pinctrl_pck0_as_mck: pck0_as_mck { + atmel,pins = + <2 1 0x2 0x0>; /* PC1 periph B */ + }; + + }; + }; + dbgu: serial@fffff200 { status = "okay"; }; @@ -51,6 +61,11 @@ atmel,vbus-gpio = <&pioC 5 0>; status = "okay"; }; + + ssc0: ssc@fffbc000 { + status = "okay"; + pinctrl-0 = <&pinctrl_ssc0_tx>; + }; }; nand0: nand@40000000 { @@ -114,7 +129,7 @@ reg = <0x50>; }; - wm8731@1b { + wm8731: wm8731@1b { compatible = "wm8731"; reg = <0x1b>; }; @@ -139,4 +154,19 @@ gpio-key,wakeup; }; }; + + sound { + compatible = "atmel,at91sam9g20ek-wm8731-audio"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pck0_as_mck>; + + atmel,model = "wm8731 @ AT91SAMG20EK"; + + atmel,audio-routing = + "Ext Spk", "LHPOUT", + "Int Mic", "MICIN"; + + atmel,ssc-controller = <&ssc0>; + atmel,audio-codec = <&wm8731>; + }; }; diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 72b09cfd3dc3..397ec7526a5a 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -16,8 +16,7 @@ config SND_ATMEL_SOC_SSC config SND_AT91_SOC_SAM9G20_WM8731 tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board" - depends on ATMEL_SSC && ARCH_AT91SAM9G20 && SND_ATMEL_SOC && \ - AT91_PROGRAMMABLE_CLOCKS + depends on ATMEL_SSC && SND_ATMEL_SOC && AT91_PROGRAMMABLE_CLOCKS select SND_ATMEL_SOC_SSC select SND_SOC_WM8731 help diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index 4deba189bf10..0744610e53dd 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -38,6 +38,8 @@ #include #include +#include + #include #include @@ -197,12 +199,24 @@ static struct snd_soc_card snd_soc_at91sam9g20ek = { static int __devinit at91sam9g20ek_audio_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; + struct device_node *codec_np, *cpu_np; struct clk *pllb; struct snd_soc_card *card = &snd_soc_at91sam9g20ek; + struct pinctrl *pinctrl; int ret; - if (!(machine_is_at91sam9g20ek() || machine_is_at91sam9g20ek_2mmc())) - return -ENODEV; + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) { + dev_err(&pdev->dev, "Failed to request pinctrl for mck\n"); + return PTR_ERR(pinctrl); + } + + if (!np) { + if (!(machine_is_at91sam9g20ek() || + machine_is_at91sam9g20ek_2mmc())) + return -ENODEV; + } ret = atmel_ssc_set_audio(0); if (ret) { @@ -236,6 +250,42 @@ static int __devinit at91sam9g20ek_audio_probe(struct platform_device *pdev) clk_set_rate(mclk, MCLK_RATE); card->dev = &pdev->dev; + + /* Parse device node info */ + if (np) { + ret = snd_soc_of_parse_card_name(card, "atmel,model"); + if (ret) + goto err; + + ret = snd_soc_of_parse_audio_routing(card, + "atmel,audio-routing"); + if (ret) + goto err; + + /* Parse codec info */ + at91sam9g20ek_dai.codec_name = NULL; + codec_np = of_parse_phandle(np, "atmel,audio-codec", 0); + if (!codec_np) { + dev_err(&pdev->dev, "codec info missing\n"); + return -EINVAL; + } + at91sam9g20ek_dai.codec_of_node = codec_np; + + /* Parse dai and platform info */ + at91sam9g20ek_dai.cpu_dai_name = NULL; + at91sam9g20ek_dai.platform_name = NULL; + cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0); + if (!cpu_np) { + dev_err(&pdev->dev, "dai and pcm info missing\n"); + return -EINVAL; + } + at91sam9g20ek_dai.cpu_of_node = cpu_np; + at91sam9g20ek_dai.platform_of_node = cpu_np; + + of_node_put(codec_np); + of_node_put(cpu_np); + } + ret = snd_soc_register_card(card); if (ret) { printk(KERN_ERR "ASoC: snd_soc_register_card() failed\n"); @@ -263,10 +313,19 @@ static int __devexit at91sam9g20ek_audio_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id at91sam9g20ek_wm8731_dt_ids[] = { + { .compatible = "atmel,at91sam9g20ek-wm8731-audio", }, + { } +}; +MODULE_DEVICE_TABLE(of, at91sam9g20ek_wm8731_dt_ids); +#endif + static struct platform_driver at91sam9g20ek_audio_driver = { .driver = { .name = "at91sam9g20ek-audio", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(at91sam9g20ek_wm8731_dt_ids), }, .probe = at91sam9g20ek_audio_probe, .remove = __devexit_p(at91sam9g20ek_audio_remove), -- cgit v1.2.3 From 5c70cb01b89895189025f8de8ee2823f516eab6a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sun, 7 Oct 2012 14:12:49 -0300 Subject: ARM: dts: imx6q-sabreauto: Add basic support mx6qsabreauto is a board based on mx6q SoC with the following features: - 2GB of DDR3 - 2 USB ports - 1 HDMI output port - SPI NOR - 2 LVDS LCD ports - Gigabit Ethernet - Camera - eMMC and SD card slot - Multichannel Audio - CAN - SATA - NAND - PCIE - Video Input Add very basic support for it. Signed-off-by: Fabio Estevam Signed-off-by: Shawn Guo --- Documentation/devicetree/bindings/arm/fsl.txt | 4 ++ arch/arm/boot/dts/Makefile | 1 + arch/arm/boot/dts/imx6q-sabreauto.dts | 64 +++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 arch/arm/boot/dts/imx6q-sabreauto.dts (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/fsl.txt b/Documentation/devicetree/bindings/arm/fsl.txt index ac9e7516756e..f79818711e83 100644 --- a/Documentation/devicetree/bindings/arm/fsl.txt +++ b/Documentation/devicetree/bindings/arm/fsl.txt @@ -41,6 +41,10 @@ i.MX6 Quad SABRE Smart Device Board Required root node properties: - compatible = "fsl,imx6q-sabresd", "fsl,imx6q"; +i.MX6 Quad SABRE Automotive Board +Required root node properties: + - compatible = "fsl,imx6q-sabreauto", "fsl,imx6q"; + Generic i.MX boards ------------------- diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index c1ce813fcc4a..ccafe36679b9 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -49,6 +49,7 @@ dtb-$(CONFIG_ARCH_MXC) += imx51-babbage.dtb \ imx53-qsb.dtb \ imx53-smd.dtb \ imx6q-arm2.dtb \ + imx6q-sabreauto.dtb \ imx6q-sabrelite.dtb \ imx6q-sabresd.dtb dtb-$(CONFIG_ARCH_MXS) += imx23-evk.dtb \ diff --git a/arch/arm/boot/dts/imx6q-sabreauto.dts b/arch/arm/boot/dts/imx6q-sabreauto.dts new file mode 100644 index 000000000000..826e4ad1477e --- /dev/null +++ b/arch/arm/boot/dts/imx6q-sabreauto.dts @@ -0,0 +1,64 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2011 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/dts-v1/; +/include/ "imx6q.dtsi" + +/ { + model = "Freescale i.MX6 Quad SABRE Automotive Board"; + compatible = "fsl,imx6q-sabreauto", "fsl,imx6q"; + + memory { + reg = <0x10000000 0x80000000>; + }; + + soc { + aips-bus@02000000 { /* AIPS1 */ + iomuxc@020e0000 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + hog { + pinctrl_hog: hoggrp { + fsl,pins = < + 1376 0x80000000 /* MX6Q_PAD_NANDF_CS2__GPIO_6_15 */ + 13 0x80000000 /* MX6Q_PAD_SD2_DAT2__GPIO_1_13 */ + >; + }; + }; + }; + }; + + aips-bus@02100000 { /* AIPS2 */ + uart4: serial@021f0000 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart4_1>; + status = "okay"; + }; + + ethernet@02188000 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet_2>; + phy-mode = "rgmii"; + status = "okay"; + }; + + usdhc@02198000 { /* uSDHC3 */ + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc3_1>; + cd-gpios = <&gpio6 15 0>; + wp-gpios = <&gpio1 13 0>; + status = "okay"; + }; + }; + }; +}; -- cgit v1.2.3 From 9b7a0c40de6c5c04371f8cdb9153a0a5a0af5243 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 25 Oct 2012 18:23:53 +0200 Subject: i2c: mux: Add dt support to i2c-mux-gpio driver Allow the i2c-mux-gpio to be used by a device tree enabled device. The bindings are inspired by the one found in the i2c-mux-pinctrl driver. Signed-off-by: Maxime Ripard Reviewed-by: Stephen Warren Acked-by: Peter Korsgaard [wsa: fixed some whitespace] Signed-off-by: Wolfram Sang --- .../devicetree/bindings/i2c/i2c-mux-gpio.txt | 81 ++++++++++++ drivers/i2c/muxes/i2c-mux-gpio.c | 145 ++++++++++++++++----- 2 files changed, 195 insertions(+), 31 deletions(-) create mode 100644 Documentation/devicetree/bindings/i2c/i2c-mux-gpio.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-gpio.txt b/Documentation/devicetree/bindings/i2c/i2c-mux-gpio.txt new file mode 100644 index 000000000000..66709a825541 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-mux-gpio.txt @@ -0,0 +1,81 @@ +GPIO-based I2C Bus Mux + +This binding describes an I2C bus multiplexer that uses GPIOs to +route the I2C signals. + + +-----+ +-----+ + | dev | | dev | + +------------+ +-----+ +-----+ + | SoC | | | + | | /--------+--------+ + | +------+ | +------+ child bus A, on GPIO value set to 0 + | | I2C |-|--| Mux | + | +------+ | +--+---+ child bus B, on GPIO value set to 1 + | | | \----------+--------+--------+ + | +------+ | | | | | + | | GPIO |-|-----+ +-----+ +-----+ +-----+ + | +------+ | | dev | | dev | | dev | + +------------+ +-----+ +-----+ +-----+ + +Required properties: +- compatible: i2c-mux-gpio +- i2c-parent: The phandle of the I2C bus that this multiplexer's master-side + port is connected to. +- mux-gpios: list of gpios used to control the muxer +* Standard I2C mux properties. See mux.txt in this directory. +* I2C child bus nodes. See mux.txt in this directory. + +Optional properties: +- idle-state: value to set the muxer to when idle. When no value is + given, it defaults to the last value used. + +For each i2c child node, an I2C child bus will be created. They will +be numbered based on their order in the device tree. + +Whenever an access is made to a device on a child bus, the value set +in the revelant node's reg property will be output using the list of +GPIOs, the first in the list holding the least-significant value. + +If an idle state is defined, using the idle-state (optional) property, +whenever an access is not being made to a device on a child bus, the +GPIOs will be set according to the idle value. + +If an idle state is not defined, the most recently used value will be +left programmed into hardware whenever no access is being made to a +device on a child bus. + +Example: + i2cmux { + compatible = "i2c-mux-gpio"; + #address-cells = <1>; + #size-cells = <0>; + mux-gpios = <&gpio1 22 0 &gpio1 23 0>; + i2c-parent = <&i2c1>; + + i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + ssd1307: oled@3c { + compatible = "solomon,ssd1307fb-i2c"; + reg = <0x3c>; + pwms = <&pwm 4 3000>; + reset-gpios = <&gpio2 7 1>; + reset-active-low; + }; + }; + + i2c@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + + pca9555: pca9555@20 { + compatible = "nxp,pca9555"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x20>; + }; + }; + }; diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c index 566a6757a33d..3b7bc06fe8a6 100644 --- a/drivers/i2c/muxes/i2c-mux-gpio.c +++ b/drivers/i2c/muxes/i2c-mux-gpio.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include struct gpiomux { struct i2c_adapter *parent; @@ -57,29 +59,110 @@ static int __devinit match_gpio_chip_by_label(struct gpio_chip *chip, return !strcmp(chip->label, data); } +#ifdef CONFIG_OF +static int __devinit i2c_mux_gpio_probe_dt(struct gpiomux *mux, + struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *adapter_np, *child; + struct i2c_adapter *adapter; + unsigned *values, *gpios; + int i = 0; + + if (!np) + return -ENODEV; + + adapter_np = of_parse_phandle(np, "i2c-parent", 0); + if (!adapter_np) { + dev_err(&pdev->dev, "Cannot parse i2c-parent\n"); + return -ENODEV; + } + adapter = of_find_i2c_adapter_by_node(adapter_np); + if (!adapter) { + dev_err(&pdev->dev, "Cannot find parent bus\n"); + return -ENODEV; + } + mux->data.parent = i2c_adapter_id(adapter); + put_device(&adapter->dev); + + mux->data.n_values = of_get_child_count(np); + + values = devm_kzalloc(&pdev->dev, + sizeof(*mux->data.values) * mux->data.n_values, + GFP_KERNEL); + if (!values) { + dev_err(&pdev->dev, "Cannot allocate values array"); + return -ENOMEM; + } + + for_each_child_of_node(np, child) { + of_property_read_u32(child, "reg", values + i); + i++; + } + mux->data.values = values; + + if (of_property_read_u32(np, "idle-state", &mux->data.idle)) + mux->data.idle = I2C_MUX_GPIO_NO_IDLE; + + mux->data.n_gpios = of_gpio_named_count(np, "mux-gpios"); + if (mux->data.n_gpios < 0) { + dev_err(&pdev->dev, "Missing mux-gpios property in the DT.\n"); + return -EINVAL; + } + + gpios = devm_kzalloc(&pdev->dev, + sizeof(*mux->data.gpios) * mux->data.n_gpios, GFP_KERNEL); + if (!gpios) { + dev_err(&pdev->dev, "Cannot allocate gpios array"); + return -ENOMEM; + } + + for (i = 0; i < mux->data.n_gpios; i++) + gpios[i] = of_get_named_gpio(np, "mux-gpios", i); + + mux->data.gpios = gpios; + + return 0; +} +#else +static int __devinit i2c_mux_gpio_probe_dt(struct gpiomux *mux, + struct platform_device *pdev) +{ + return 0; +} +#endif + static int __devinit i2c_mux_gpio_probe(struct platform_device *pdev) { struct gpiomux *mux; - struct i2c_mux_gpio_platform_data *pdata; struct i2c_adapter *parent; int (*deselect) (struct i2c_adapter *, void *, u32); unsigned initial_state, gpio_base; int i, ret; - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "Missing platform data\n"); - return -ENODEV; + mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); + if (!mux) { + dev_err(&pdev->dev, "Cannot allocate gpiomux structure"); + return -ENOMEM; } + platform_set_drvdata(pdev, mux); + + if (!pdev->dev.platform_data) { + ret = i2c_mux_gpio_probe_dt(mux, pdev); + if (ret < 0) + return ret; + } else + memcpy(&mux->data, pdev->dev.platform_data, sizeof(mux->data)); + /* * If a GPIO chip name is provided, the GPIO pin numbers provided are * relative to its base GPIO number. Otherwise they are absolute. */ - if (pdata->gpio_chip) { + if (mux->data.gpio_chip) { struct gpio_chip *gpio; - gpio = gpiochip_find(pdata->gpio_chip, + gpio = gpiochip_find(mux->data.gpio_chip, match_gpio_chip_by_label); if (!gpio) return -EPROBE_DEFER; @@ -89,49 +172,44 @@ static int __devinit i2c_mux_gpio_probe(struct platform_device *pdev) gpio_base = 0; } - parent = i2c_get_adapter(pdata->parent); + parent = i2c_get_adapter(mux->data.parent); if (!parent) { dev_err(&pdev->dev, "Parent adapter (%d) not found\n", - pdata->parent); + mux->data.parent); return -ENODEV; } - mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); - if (!mux) { - ret = -ENOMEM; - goto alloc_failed; - } - mux->parent = parent; - mux->data = *pdata; mux->gpio_base = gpio_base; + mux->adap = devm_kzalloc(&pdev->dev, - sizeof(*mux->adap) * pdata->n_values, + sizeof(*mux->adap) * mux->data.n_values, GFP_KERNEL); if (!mux->adap) { + dev_err(&pdev->dev, "Cannot allocate i2c_adapter structure"); ret = -ENOMEM; goto alloc_failed; } - if (pdata->idle != I2C_MUX_GPIO_NO_IDLE) { - initial_state = pdata->idle; + if (mux->data.idle != I2C_MUX_GPIO_NO_IDLE) { + initial_state = mux->data.idle; deselect = i2c_mux_gpio_deselect; } else { - initial_state = pdata->values[0]; + initial_state = mux->data.values[0]; deselect = NULL; } - for (i = 0; i < pdata->n_gpios; i++) { - ret = gpio_request(gpio_base + pdata->gpios[i], "i2c-mux-gpio"); + for (i = 0; i < mux->data.n_gpios; i++) { + ret = gpio_request(gpio_base + mux->data.gpios[i], "i2c-mux-gpio"); if (ret) goto err_request_gpio; - gpio_direction_output(gpio_base + pdata->gpios[i], + gpio_direction_output(gpio_base + mux->data.gpios[i], initial_state & (1 << i)); } - for (i = 0; i < pdata->n_values; i++) { - u32 nr = pdata->base_nr ? (pdata->base_nr + i) : 0; - unsigned int class = pdata->classes ? pdata->classes[i] : 0; + for (i = 0; i < mux->data.n_values; i++) { + u32 nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0; + unsigned int class = mux->data.classes ? mux->data.classes[i] : 0; mux->adap[i] = i2c_add_mux_adapter(parent, &pdev->dev, mux, nr, i, class, @@ -144,19 +222,17 @@ static int __devinit i2c_mux_gpio_probe(struct platform_device *pdev) } dev_info(&pdev->dev, "%d port mux on %s adapter\n", - pdata->n_values, parent->name); - - platform_set_drvdata(pdev, mux); + mux->data.n_values, parent->name); return 0; add_adapter_failed: for (; i > 0; i--) i2c_del_mux_adapter(mux->adap[i - 1]); - i = pdata->n_gpios; + i = mux->data.n_gpios; err_request_gpio: for (; i > 0; i--) - gpio_free(gpio_base + pdata->gpios[i - 1]); + gpio_free(gpio_base + mux->data.gpios[i - 1]); alloc_failed: i2c_put_adapter(parent); @@ -180,12 +256,19 @@ static int __devexit i2c_mux_gpio_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id i2c_mux_gpio_of_match[] __devinitconst = { + { .compatible = "i2c-mux-gpio", }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_mux_gpio_of_match); + static struct platform_driver i2c_mux_gpio_driver = { .probe = i2c_mux_gpio_probe, .remove = __devexit_p(i2c_mux_gpio_remove), .driver = { .owner = THIS_MODULE, .name = "i2c-mux-gpio", + .of_match_table = of_match_ptr(i2c_mux_gpio_of_match), }, }; -- cgit v1.2.3 From d68cbdd4fb04d2b756ad53c22f36943167b16340 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Wed, 7 Nov 2012 16:32:16 +0100 Subject: mtd: physmap_of: allow to specify the mtd name for retro compatiblity linux,mtd-name allow to specify the mtd name for retro capability with physmap-flash drivers as boot loader pass the mtd partition via the old device name physmap-flash. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Artem Bityutskiy --- Documentation/devicetree/bindings/mtd/mtd-physmap.txt | 3 +++ drivers/mtd/maps/physmap_of.c | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mtd/mtd-physmap.txt b/Documentation/devicetree/bindings/mtd/mtd-physmap.txt index 94de19b8f16b..dab7847fc800 100644 --- a/Documentation/devicetree/bindings/mtd/mtd-physmap.txt +++ b/Documentation/devicetree/bindings/mtd/mtd-physmap.txt @@ -23,6 +23,9 @@ file systems on embedded devices. unaligned accesses as implemented in the JFFS2 code via memcpy(). By defining "no-unaligned-direct-access", the flash will not be exposed directly to the MTD users (e.g. JFFS2) any more. + - linux,mtd-name: allow to specify the mtd name for retro capability with + physmap-flash drivers as boot loader pass the mtd partition via the old + device name physmap-flash. For JEDEC compatible devices, the following additional properties are defined: diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 6f19acadb06c..d7f19bc6de4a 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -170,6 +170,7 @@ static int __devinit of_flash_probe(struct platform_device *dev) resource_size_t res_size; struct mtd_part_parser_data ppdata; bool map_indirect; + const char *mtd_name; match = of_match_device(of_flash_match, &dev->dev); if (!match) @@ -178,6 +179,8 @@ static int __devinit of_flash_probe(struct platform_device *dev) reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32); + of_property_read_string(dp, "linux,mtd-name", &mtd_name); + /* * Get number of "reg" tuples. Scan for MTD devices on area's * described by each "reg" region. This makes it possible (including @@ -234,7 +237,7 @@ static int __devinit of_flash_probe(struct platform_device *dev) goto err_out; } - info->list[i].map.name = dev_name(&dev->dev); + info->list[i].map.name = mtd_name ?: dev_name(&dev->dev); info->list[i].map.phys = res.start; info->list[i].map.size = res_size; info->list[i].map.bankwidth = be32_to_cpup(width); -- cgit v1.2.3 From fc8f5aded1cf9f5505c55694b36174621c7ac88c Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 12 Nov 2012 17:03:47 +0100 Subject: net: mvmdio: new Marvell MDIO driver This patch adds a separate driver for the MDIO interface of the Marvell Ethernet controllers. There are two reasons to have a separate driver rather than including it inside the MAC driver itself: *) The MDIO interface is shared by all Ethernet ports, so a driver must guarantee non-concurrent accesses to this MDIO interface. The most logical way is to have a separate driver that handles this single MDIO interface, used by all Ethernet ports. *) The MDIO interface is the same between the existing mv643xx_eth driver and the new mvneta driver. Even though it is for now only used by the mvneta driver, it will in the future be used by the mv643xx_eth driver as well. Signed-off-by: Thomas Petazzoni Acked-by: David S. Miller --- .../devicetree/bindings/net/marvell-orion-mdio.txt | 35 ++++ drivers/net/ethernet/marvell/Kconfig | 11 + drivers/net/ethernet/marvell/Makefile | 1 + drivers/net/ethernet/marvell/mvmdio.c | 230 +++++++++++++++++++++ 4 files changed, 277 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/marvell-orion-mdio.txt create mode 100644 drivers/net/ethernet/marvell/mvmdio.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt b/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt new file mode 100644 index 000000000000..34e7aafa321c --- /dev/null +++ b/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt @@ -0,0 +1,35 @@ +* Marvell MDIO Ethernet Controller interface + +The Ethernet controllers of the Marvel Kirkwood, Dove, Orion5x, +MV78xx0, Armada 370 and Armada XP have an identical unit that provides +an interface with the MDIO bus. This driver handles this MDIO +interface. + +Required properties: +- compatible: "marvell,orion-mdio" +- reg: address and length of the SMI register + +The child nodes of the MDIO driver are the individual PHY devices +connected to this MDIO bus. They must have a "reg" property given the +PHY address on the MDIO bus. + +Example at the SoC level: + +mdio { + #address-cells = <1>; + #size-cells = <0>; + compatible = "marvell,orion-mdio"; + reg = <0xd0072004 0x4>; +}; + +And at the board level: + +mdio { + phy0: ethernet-phy@0 { + reg = <0>; + }; + + phy1: ethernet-phy@1 { + reg = <1>; + }; +} diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig index 0029934748bc..232ccb3cb08b 100644 --- a/drivers/net/ethernet/marvell/Kconfig +++ b/drivers/net/ethernet/marvell/Kconfig @@ -31,6 +31,17 @@ config MV643XX_ETH Some boards that use the Discovery chipset are the Momenco Ocelot C and Jaguar ATX and Pegasos II. +config MVMDIO + tristate "Marvell MDIO interface support" + ---help--- + This driver supports the MDIO interface found in the network + interface units of the Marvell EBU SoCs (Kirkwood, Orion5x, + Dove, Armada 370 and Armada XP). + + For now, this driver is only needed for the MVNETA driver + (used on Armada 370 and XP), but it could be used in the + future by the MV643XX_ETH driver. + config PXA168_ETH tristate "Marvell pxa168 ethernet support" depends on CPU_PXA168 diff --git a/drivers/net/ethernet/marvell/Makefile b/drivers/net/ethernet/marvell/Makefile index 57e3234a37ba..0438599fba47 100644 --- a/drivers/net/ethernet/marvell/Makefile +++ b/drivers/net/ethernet/marvell/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o +obj-$(CONFIG_MVMDIO) += mvmdio.o obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o obj-$(CONFIG_SKGE) += skge.o obj-$(CONFIG_SKY2) += sky2.o diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c new file mode 100644 index 000000000000..82fbd235e502 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvmdio.c @@ -0,0 +1,230 @@ +/* + * Driver for the MDIO interface of Marvell network interfaces. + * + * Since the MDIO interface of Marvell network interfaces is shared + * between all network interfaces, having a single driver allows to + * handle concurrent accesses properly (you may have four Ethernet + * ports, but they in fact share the same SMI interface to access the + * MDIO bus). Moreover, this MDIO interface code is similar between + * the mv643xx_eth driver and the mvneta driver. For now, it is only + * used by the mvneta driver, but it could later be used by the + * mv643xx_eth driver as well. + * + * Copyright (C) 2012 Marvell + * + * Thomas Petazzoni + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MVMDIO_SMI_DATA_SHIFT 0 +#define MVMDIO_SMI_PHY_ADDR_SHIFT 16 +#define MVMDIO_SMI_PHY_REG_SHIFT 21 +#define MVMDIO_SMI_READ_OPERATION BIT(26) +#define MVMDIO_SMI_WRITE_OPERATION 0 +#define MVMDIO_SMI_READ_VALID BIT(27) +#define MVMDIO_SMI_BUSY BIT(28) + +struct orion_mdio_dev { + struct mutex lock; + void __iomem *smireg; +}; + +/* + * Wait for the SMI unit to be ready for another operation + */ +static int orion_mdio_wait_ready(struct mii_bus *bus) +{ + struct orion_mdio_dev *dev = bus->priv; + int count; + u32 val; + + count = 0; + while (1) { + val = readl(dev->smireg); + if (!(val & MVMDIO_SMI_BUSY)) + break; + + if (count > 100) { + dev_err(bus->parent, "Timeout: SMI busy for too long\n"); + return -ETIMEDOUT; + } + + udelay(10); + count++; + } + + return 0; +} + +static int orion_mdio_read(struct mii_bus *bus, int mii_id, + int regnum) +{ + struct orion_mdio_dev *dev = bus->priv; + int count; + u32 val; + int ret; + + mutex_lock(&dev->lock); + + ret = orion_mdio_wait_ready(bus); + if (ret < 0) { + mutex_unlock(&dev->lock); + return ret; + } + + writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) | + (regnum << MVMDIO_SMI_PHY_REG_SHIFT) | + MVMDIO_SMI_READ_OPERATION), + dev->smireg); + + /* Wait for the value to become available */ + count = 0; + while (1) { + val = readl(dev->smireg); + if (val & MVMDIO_SMI_READ_VALID) + break; + + if (count > 100) { + dev_err(bus->parent, "Timeout when reading PHY\n"); + mutex_unlock(&dev->lock); + return -ETIMEDOUT; + } + + udelay(10); + count++; + } + + mutex_unlock(&dev->lock); + + return val & 0xFFFF; +} + +static int orion_mdio_write(struct mii_bus *bus, int mii_id, + int regnum, u16 value) +{ + struct orion_mdio_dev *dev = bus->priv; + int ret; + + mutex_lock(&dev->lock); + + ret = orion_mdio_wait_ready(bus); + if (ret < 0) { + mutex_unlock(&dev->lock); + return ret; + } + + writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) | + (regnum << MVMDIO_SMI_PHY_REG_SHIFT) | + MVMDIO_SMI_WRITE_OPERATION | + (value << MVMDIO_SMI_DATA_SHIFT)), + dev->smireg); + + mutex_unlock(&dev->lock); + + return 0; +} + +static int orion_mdio_reset(struct mii_bus *bus) +{ + return 0; +} + +static int __devinit orion_mdio_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct mii_bus *bus; + struct orion_mdio_dev *dev; + int i, ret; + + bus = mdiobus_alloc_size(sizeof(struct orion_mdio_dev)); + if (!bus) { + dev_err(&pdev->dev, "Cannot allocate MDIO bus\n"); + return -ENOMEM; + } + + bus->name = "orion_mdio_bus"; + bus->read = orion_mdio_read; + bus->write = orion_mdio_write; + bus->reset = orion_mdio_reset; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", + dev_name(&pdev->dev)); + bus->parent = &pdev->dev; + + bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); + if (!bus->irq) { + dev_err(&pdev->dev, "Cannot allocate PHY IRQ array\n"); + mdiobus_free(bus); + return -ENOMEM; + } + + for (i = 0; i < PHY_MAX_ADDR; i++) + bus->irq[i] = PHY_POLL; + + dev = bus->priv; + dev->smireg = of_iomap(pdev->dev.of_node, 0); + if (!dev->smireg) { + dev_err(&pdev->dev, "No SMI register address given in DT\n"); + kfree(bus->irq); + mdiobus_free(bus); + return -ENODEV; + } + + mutex_init(&dev->lock); + + ret = of_mdiobus_register(bus, np); + if (ret < 0) { + dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret); + iounmap(dev->smireg); + kfree(bus->irq); + mdiobus_free(bus); + return ret; + } + + platform_set_drvdata(pdev, bus); + + return 0; +} + +static int __devexit orion_mdio_remove(struct platform_device *pdev) +{ + struct mii_bus *bus = platform_get_drvdata(pdev); + mdiobus_unregister(bus); + kfree(bus->irq); + mdiobus_free(bus); + return 0; +} + +static const struct of_device_id orion_mdio_match[] = { + { .compatible = "marvell,orion-mdio" }, + { } +}; +MODULE_DEVICE_TABLE(of, orion_mdio_match); + +static struct platform_driver orion_mdio_driver = { + .probe = orion_mdio_probe, + .remove = __devexit_p(orion_mdio_remove), + .driver = { + .name = "orion-mdio", + .of_match_table = orion_mdio_match, + }, +}; + +module_platform_driver(orion_mdio_driver); + +MODULE_DESCRIPTION("Marvell MDIO interface driver"); +MODULE_AUTHOR("Thomas Petazzoni "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From c5aff18204da025fdf714f8f6423372b4b8efd00 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 17 Aug 2012 14:04:28 +0300 Subject: net: mvneta: driver for Marvell Armada 370/XP network unit This patch contains a new network driver for the network unit of the ARM Marvell Armada 370 and the Armada XP. Both SoCs use the PJ4B processor, a Marvell-developed ARM core that implements the ARMv7 instruction set. Compared to previous ARM Marvell SoCs (Kirkwood, Orion, Discovery), the network unit in Armada 370 and Armada XP is highly different. This is the reason why this new 'mvneta' driver is needed, while the older ARM Marvell SoCs use the 'mv643xx_eth' driver. Here is an overview of the most important hardware changes that require a new, specific, driver for the network unit of Armada 370/XP: - The new network unit has a completely different design and layout for the RX and TX descriptors. They are now organized as a simple array (each RX and TX queue has base address and size of this array) rather than a linked list as in the old SoCs. - The new network unit has a different RXQ and TXQ management: this management is done using special read/write counter registers, while in the Old SocS, it was done using the Ownership bit in RX and TX descriptors. - The new network unit has different interrupt registers - The new network unit way of cleaning of interrupts is not done by writing to the cause register, but by updating per-queue counters - The new network unit has different GMAC registers (link, speed, duplex configuration) and different WRR registers. - The new network unit has lots of new units like PnC (Parser and Classifier), PMT, BM (Memory Buffer Management), xPON, and more. The driver proposed in the current patch only handles the basic features. Additional hardware features will progressively be supported as needed. This code has originally been written by Rami Rosen , and then reviewed and cleaned up by Thomas Petazzoni . Signed-off-by: Thomas Petazzoni Acked-by: David S. Miller --- .../bindings/net/marvell-armada-370-neta.txt | 23 + drivers/net/ethernet/marvell/Kconfig | 13 + drivers/net/ethernet/marvell/Makefile | 1 + drivers/net/ethernet/marvell/mvneta.c | 2839 ++++++++++++++++++++ 4 files changed, 2876 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt create mode 100644 drivers/net/ethernet/marvell/mvneta.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt new file mode 100644 index 000000000000..c4e87f0e450e --- /dev/null +++ b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt @@ -0,0 +1,23 @@ +* Marvell Armada 370 / Armada XP Ethernet Controller (NETA) + +Required properties: +- compatible: should be "marvell,armada-370-neta". +- reg: address and length of the register set for the device. +- interrupts: interrupt for the device +- phy: A phandle to a phy node defining the PHY address (as the reg + property, a single integer). +- phy-mode: The interface between the SoC and the PHY (a string that + of_get_phy_mode() can understand) +- clock-frequency: frequency of the peripheral clock of the SoC. + +Example: + +ethernet@d0070000 { + compatible = "marvell,armada-370-neta"; + reg = <0xd0070000 0x2500>; + interrupts = <8>; + clock-frequency = <250000000>; + status = "okay"; + phy = <&phy0>; + phy-mode = "rgmii-id"; +}; diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig index 232ccb3cb08b..edfba9370922 100644 --- a/drivers/net/ethernet/marvell/Kconfig +++ b/drivers/net/ethernet/marvell/Kconfig @@ -42,6 +42,19 @@ config MVMDIO (used on Armada 370 and XP), but it could be used in the future by the MV643XX_ETH driver. +config MVNETA + tristate "Marvell Armada 370/XP network interface support" + depends on MACH_ARMADA_370_XP + select PHYLIB + select MVMDIO + ---help--- + This driver supports the network interface units in the + Marvell ARMADA XP and ARMADA 370 SoC family. + + Note that this driver is distinct from the mv643xx_eth + driver, which should be used for the older Marvell SoCs + (Dove, Orion, Discovery, Kirkwood). + config PXA168_ETH tristate "Marvell pxa168 ethernet support" depends on CPU_PXA168 diff --git a/drivers/net/ethernet/marvell/Makefile b/drivers/net/ethernet/marvell/Makefile index 0438599fba47..7f63b4aac434 100644 --- a/drivers/net/ethernet/marvell/Makefile +++ b/drivers/net/ethernet/marvell/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o obj-$(CONFIG_MVMDIO) += mvmdio.o +obj-$(CONFIG_MVNETA) += mvneta.o obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o obj-$(CONFIG_SKGE) += skge.o obj-$(CONFIG_SKY2) += sky2.o diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c new file mode 100644 index 000000000000..a7826f0a968e --- /dev/null +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -0,0 +1,2839 @@ +/* + * Driver for Marvell NETA network card for Armada XP and Armada 370 SoCs. + * + * Copyright (C) 2012 Marvell + * + * Rami Rosen + * Thomas Petazzoni + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Registers */ +#define MVNETA_RXQ_CONFIG_REG(q) (0x1400 + ((q) << 2)) +#define MVNETA_RXQ_HW_BUF_ALLOC BIT(1) +#define MVNETA_RXQ_PKT_OFFSET_ALL_MASK (0xf << 8) +#define MVNETA_RXQ_PKT_OFFSET_MASK(offs) ((offs) << 8) +#define MVNETA_RXQ_THRESHOLD_REG(q) (0x14c0 + ((q) << 2)) +#define MVNETA_RXQ_NON_OCCUPIED(v) ((v) << 16) +#define MVNETA_RXQ_BASE_ADDR_REG(q) (0x1480 + ((q) << 2)) +#define MVNETA_RXQ_SIZE_REG(q) (0x14a0 + ((q) << 2)) +#define MVNETA_RXQ_BUF_SIZE_SHIFT 19 +#define MVNETA_RXQ_BUF_SIZE_MASK (0x1fff << 19) +#define MVNETA_RXQ_STATUS_REG(q) (0x14e0 + ((q) << 2)) +#define MVNETA_RXQ_OCCUPIED_ALL_MASK 0x3fff +#define MVNETA_RXQ_STATUS_UPDATE_REG(q) (0x1500 + ((q) << 2)) +#define MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT 16 +#define MVNETA_RXQ_ADD_NON_OCCUPIED_MAX 255 +#define MVNETA_PORT_RX_RESET 0x1cc0 +#define MVNETA_PORT_RX_DMA_RESET BIT(0) +#define MVNETA_PHY_ADDR 0x2000 +#define MVNETA_PHY_ADDR_MASK 0x1f +#define MVNETA_MBUS_RETRY 0x2010 +#define MVNETA_UNIT_INTR_CAUSE 0x2080 +#define MVNETA_UNIT_CONTROL 0x20B0 +#define MVNETA_PHY_POLLING_ENABLE BIT(1) +#define MVNETA_WIN_BASE(w) (0x2200 + ((w) << 3)) +#define MVNETA_WIN_SIZE(w) (0x2204 + ((w) << 3)) +#define MVNETA_WIN_REMAP(w) (0x2280 + ((w) << 2)) +#define MVNETA_BASE_ADDR_ENABLE 0x2290 +#define MVNETA_PORT_CONFIG 0x2400 +#define MVNETA_UNI_PROMISC_MODE BIT(0) +#define MVNETA_DEF_RXQ(q) ((q) << 1) +#define MVNETA_DEF_RXQ_ARP(q) ((q) << 4) +#define MVNETA_TX_UNSET_ERR_SUM BIT(12) +#define MVNETA_DEF_RXQ_TCP(q) ((q) << 16) +#define MVNETA_DEF_RXQ_UDP(q) ((q) << 19) +#define MVNETA_DEF_RXQ_BPDU(q) ((q) << 22) +#define MVNETA_RX_CSUM_WITH_PSEUDO_HDR BIT(25) +#define MVNETA_PORT_CONFIG_DEFL_VALUE(q) (MVNETA_DEF_RXQ(q) | \ + MVNETA_DEF_RXQ_ARP(q) | \ + MVNETA_DEF_RXQ_TCP(q) | \ + MVNETA_DEF_RXQ_UDP(q) | \ + MVNETA_DEF_RXQ_BPDU(q) | \ + MVNETA_TX_UNSET_ERR_SUM | \ + MVNETA_RX_CSUM_WITH_PSEUDO_HDR) +#define MVNETA_PORT_CONFIG_EXTEND 0x2404 +#define MVNETA_MAC_ADDR_LOW 0x2414 +#define MVNETA_MAC_ADDR_HIGH 0x2418 +#define MVNETA_SDMA_CONFIG 0x241c +#define MVNETA_SDMA_BRST_SIZE_16 4 +#define MVNETA_NO_DESC_SWAP 0x0 +#define MVNETA_RX_BRST_SZ_MASK(burst) ((burst) << 1) +#define MVNETA_RX_NO_DATA_SWAP BIT(4) +#define MVNETA_TX_NO_DATA_SWAP BIT(5) +#define MVNETA_TX_BRST_SZ_MASK(burst) ((burst) << 22) +#define MVNETA_PORT_STATUS 0x2444 +#define MVNETA_TX_IN_PRGRS BIT(1) +#define MVNETA_TX_FIFO_EMPTY BIT(8) +#define MVNETA_RX_MIN_FRAME_SIZE 0x247c +#define MVNETA_TYPE_PRIO 0x24bc +#define MVNETA_FORCE_UNI BIT(21) +#define MVNETA_TXQ_CMD_1 0x24e4 +#define MVNETA_TXQ_CMD 0x2448 +#define MVNETA_TXQ_DISABLE_SHIFT 8 +#define MVNETA_TXQ_ENABLE_MASK 0x000000ff +#define MVNETA_ACC_MODE 0x2500 +#define MVNETA_CPU_MAP(cpu) (0x2540 + ((cpu) << 2)) +#define MVNETA_CPU_RXQ_ACCESS_ALL_MASK 0x000000ff +#define MVNETA_CPU_TXQ_ACCESS_ALL_MASK 0x0000ff00 +#define MVNETA_RXQ_TIME_COAL_REG(q) (0x2580 + ((q) << 2)) +#define MVNETA_INTR_NEW_CAUSE 0x25a0 +#define MVNETA_RX_INTR_MASK(nr_rxqs) (((1 << nr_rxqs) - 1) << 8) +#define MVNETA_INTR_NEW_MASK 0x25a4 +#define MVNETA_INTR_OLD_CAUSE 0x25a8 +#define MVNETA_INTR_OLD_MASK 0x25ac +#define MVNETA_INTR_MISC_CAUSE 0x25b0 +#define MVNETA_INTR_MISC_MASK 0x25b4 +#define MVNETA_INTR_ENABLE 0x25b8 +#define MVNETA_TXQ_INTR_ENABLE_ALL_MASK 0x0000ff00 +#define MVNETA_RXQ_INTR_ENABLE_ALL_MASK 0xff000000 +#define MVNETA_RXQ_CMD 0x2680 +#define MVNETA_RXQ_DISABLE_SHIFT 8 +#define MVNETA_RXQ_ENABLE_MASK 0x000000ff +#define MVETH_TXQ_TOKEN_COUNT_REG(q) (0x2700 + ((q) << 4)) +#define MVETH_TXQ_TOKEN_CFG_REG(q) (0x2704 + ((q) << 4)) +#define MVNETA_GMAC_CTRL_0 0x2c00 +#define MVNETA_GMAC_MAX_RX_SIZE_SHIFT 2 +#define MVNETA_GMAC_MAX_RX_SIZE_MASK 0x7ffc +#define MVNETA_GMAC0_PORT_ENABLE BIT(0) +#define MVNETA_GMAC_CTRL_2 0x2c08 +#define MVNETA_GMAC2_PSC_ENABLE BIT(3) +#define MVNETA_GMAC2_PORT_RGMII BIT(4) +#define MVNETA_GMAC2_PORT_RESET BIT(6) +#define MVNETA_GMAC_STATUS 0x2c10 +#define MVNETA_GMAC_LINK_UP BIT(0) +#define MVNETA_GMAC_SPEED_1000 BIT(1) +#define MVNETA_GMAC_SPEED_100 BIT(2) +#define MVNETA_GMAC_FULL_DUPLEX BIT(3) +#define MVNETA_GMAC_RX_FLOW_CTRL_ENABLE BIT(4) +#define MVNETA_GMAC_TX_FLOW_CTRL_ENABLE BIT(5) +#define MVNETA_GMAC_RX_FLOW_CTRL_ACTIVE BIT(6) +#define MVNETA_GMAC_TX_FLOW_CTRL_ACTIVE BIT(7) +#define MVNETA_GMAC_AUTONEG_CONFIG 0x2c0c +#define MVNETA_GMAC_FORCE_LINK_DOWN BIT(0) +#define MVNETA_GMAC_FORCE_LINK_PASS BIT(1) +#define MVNETA_GMAC_CONFIG_MII_SPEED BIT(5) +#define MVNETA_GMAC_CONFIG_GMII_SPEED BIT(6) +#define MVNETA_GMAC_CONFIG_FULL_DUPLEX BIT(12) +#define MVNETA_MIB_COUNTERS_BASE 0x3080 +#define MVNETA_MIB_LATE_COLLISION 0x7c +#define MVNETA_DA_FILT_SPEC_MCAST 0x3400 +#define MVNETA_DA_FILT_OTH_MCAST 0x3500 +#define MVNETA_DA_FILT_UCAST_BASE 0x3600 +#define MVNETA_TXQ_BASE_ADDR_REG(q) (0x3c00 + ((q) << 2)) +#define MVNETA_TXQ_SIZE_REG(q) (0x3c20 + ((q) << 2)) +#define MVNETA_TXQ_SENT_THRESH_ALL_MASK 0x3fff0000 +#define MVNETA_TXQ_SENT_THRESH_MASK(coal) ((coal) << 16) +#define MVNETA_TXQ_UPDATE_REG(q) (0x3c60 + ((q) << 2)) +#define MVNETA_TXQ_DEC_SENT_SHIFT 16 +#define MVNETA_TXQ_STATUS_REG(q) (0x3c40 + ((q) << 2)) +#define MVNETA_TXQ_SENT_DESC_SHIFT 16 +#define MVNETA_TXQ_SENT_DESC_MASK 0x3fff0000 +#define MVNETA_PORT_TX_RESET 0x3cf0 +#define MVNETA_PORT_TX_DMA_RESET BIT(0) +#define MVNETA_TX_MTU 0x3e0c +#define MVNETA_TX_TOKEN_SIZE 0x3e14 +#define MVNETA_TX_TOKEN_SIZE_MAX 0xffffffff +#define MVNETA_TXQ_TOKEN_SIZE_REG(q) (0x3e40 + ((q) << 2)) +#define MVNETA_TXQ_TOKEN_SIZE_MAX 0x7fffffff + +#define MVNETA_CAUSE_TXQ_SENT_DESC_ALL_MASK 0xff + +/* Descriptor ring Macros */ +#define MVNETA_QUEUE_NEXT_DESC(q, index) \ + (((index) < (q)->last_desc) ? ((index) + 1) : 0) + +/* Various constants */ + +/* Coalescing */ +#define MVNETA_TXDONE_COAL_PKTS 16 +#define MVNETA_RX_COAL_PKTS 32 +#define MVNETA_RX_COAL_USEC 100 + +/* Timer */ +#define MVNETA_TX_DONE_TIMER_PERIOD 10 + +/* Napi polling weight */ +#define MVNETA_RX_POLL_WEIGHT 64 + +/* + * The two bytes Marvell header. Either contains a special value used + * by Marvell switches when a specific hardware mode is enabled (not + * supported by this driver) or is filled automatically by zeroes on + * the RX side. Those two bytes being at the front of the Ethernet + * header, they allow to have the IP header aligned on a 4 bytes + * boundary automatically: the hardware skips those two bytes on its + * own. + */ +#define MVNETA_MH_SIZE 2 + +#define MVNETA_VLAN_TAG_LEN 4 + +#define MVNETA_CPU_D_CACHE_LINE_SIZE 32 +#define MVNETA_TX_CSUM_MAX_SIZE 9800 +#define MVNETA_ACC_MODE_EXT 1 + +/* Timeout constants */ +#define MVNETA_TX_DISABLE_TIMEOUT_MSEC 1000 +#define MVNETA_RX_DISABLE_TIMEOUT_MSEC 1000 +#define MVNETA_TX_FIFO_EMPTY_TIMEOUT 10000 + +#define MVNETA_TX_MTU_MAX 0x3ffff + +/* Max number of Rx descriptors */ +#define MVNETA_MAX_RXD 128 + +/* Max number of Tx descriptors */ +#define MVNETA_MAX_TXD 532 + +/* descriptor aligned size */ +#define MVNETA_DESC_ALIGNED_SIZE 32 + +#define MVNETA_RX_PKT_SIZE(mtu) \ + ALIGN((mtu) + MVNETA_MH_SIZE + MVNETA_VLAN_TAG_LEN + \ + ETH_HLEN + ETH_FCS_LEN, \ + MVNETA_CPU_D_CACHE_LINE_SIZE) + +#define MVNETA_RX_BUF_SIZE(pkt_size) ((pkt_size) + NET_SKB_PAD) + +struct mvneta_stats { + struct u64_stats_sync syncp; + u64 packets; + u64 bytes; +}; + +struct mvneta_port { + int pkt_size; + void __iomem *base; + struct mvneta_rx_queue *rxqs; + struct mvneta_tx_queue *txqs; + struct timer_list tx_done_timer; + struct net_device *dev; + + u32 cause_rx_tx; + struct napi_struct napi; + + /* Flags */ + unsigned long flags; +#define MVNETA_F_TX_DONE_TIMER_BIT 0 + + /* Napi weight */ + int weight; + + /* Core clock */ + unsigned int clk_rate_hz; + u8 mcast_count[256]; + u16 tx_ring_size; + u16 rx_ring_size; + struct mvneta_stats tx_stats; + struct mvneta_stats rx_stats; + + struct mii_bus *mii_bus; + struct phy_device *phy_dev; + phy_interface_t phy_interface; + struct device_node *phy_node; + unsigned int link; + unsigned int duplex; + unsigned int speed; +}; + +/* + * The mvneta_tx_desc and mvneta_rx_desc structures describe the + * layout of the transmit and reception DMA descriptors, and their + * layout is therefore defined by the hardware design + */ +struct mvneta_tx_desc { + u32 command; /* Options used by HW for packet transmitting.*/ +#define MVNETA_TX_L3_OFF_SHIFT 0 +#define MVNETA_TX_IP_HLEN_SHIFT 8 +#define MVNETA_TX_L4_UDP BIT(16) +#define MVNETA_TX_L3_IP6 BIT(17) +#define MVNETA_TXD_IP_CSUM BIT(18) +#define MVNETA_TXD_Z_PAD BIT(19) +#define MVNETA_TXD_L_DESC BIT(20) +#define MVNETA_TXD_F_DESC BIT(21) +#define MVNETA_TXD_FLZ_DESC (MVNETA_TXD_Z_PAD | \ + MVNETA_TXD_L_DESC | \ + MVNETA_TXD_F_DESC) +#define MVNETA_TX_L4_CSUM_FULL BIT(30) +#define MVNETA_TX_L4_CSUM_NOT BIT(31) + + u16 reserverd1; /* csum_l4 (for future use) */ + u16 data_size; /* Data size of transmitted packet in bytes */ + u32 buf_phys_addr; /* Physical addr of transmitted buffer */ + u32 reserved2; /* hw_cmd - (for future use, PMT) */ + u32 reserved3[4]; /* Reserved - (for future use) */ +}; + +struct mvneta_rx_desc { + u32 status; /* Info about received packet */ +#define MVNETA_RXD_ERR_CRC 0x0 +#define MVNETA_RXD_ERR_SUMMARY BIT(16) +#define MVNETA_RXD_ERR_OVERRUN BIT(17) +#define MVNETA_RXD_ERR_LEN BIT(18) +#define MVNETA_RXD_ERR_RESOURCE (BIT(17) | BIT(18)) +#define MVNETA_RXD_ERR_CODE_MASK (BIT(17) | BIT(18)) +#define MVNETA_RXD_L3_IP4 BIT(25) +#define MVNETA_RXD_FIRST_LAST_DESC (BIT(26) | BIT(27)) +#define MVNETA_RXD_L4_CSUM_OK BIT(30) + + u16 reserved1; /* pnc_info - (for future use, PnC) */ + u16 data_size; /* Size of received packet in bytes */ + u32 buf_phys_addr; /* Physical address of the buffer */ + u32 reserved2; /* pnc_flow_id (for future use, PnC) */ + u32 buf_cookie; /* cookie for access to RX buffer in rx path */ + u16 reserved3; /* prefetch_cmd, for future use */ + u16 reserved4; /* csum_l4 - (for future use, PnC) */ + u32 reserved5; /* pnc_extra PnC (for future use, PnC) */ + u32 reserved6; /* hw_cmd (for future use, PnC and HWF) */ +}; + +struct mvneta_tx_queue { + /* Number of this TX queue, in the range 0-7 */ + u8 id; + + /* Number of TX DMA descriptors in the descriptor ring */ + int size; + + /* Number of currently used TX DMA descriptor in the + * descriptor ring */ + int count; + + /* Array of transmitted skb */ + struct sk_buff **tx_skb; + + /* Index of last TX DMA descriptor that was inserted */ + int txq_put_index; + + /* Index of the TX DMA descriptor to be cleaned up */ + int txq_get_index; + + u32 done_pkts_coal; + + /* Virtual address of the TX DMA descriptors array */ + struct mvneta_tx_desc *descs; + + /* DMA address of the TX DMA descriptors array */ + dma_addr_t descs_phys; + + /* Index of the last TX DMA descriptor */ + int last_desc; + + /* Index of the next TX DMA descriptor to process */ + int next_desc_to_proc; +}; + +struct mvneta_rx_queue { + /* rx queue number, in the range 0-7 */ + u8 id; + + /* num of rx descriptors in the rx descriptor ring */ + int size; + + /* counter of times when mvneta_refill() failed */ + int missed; + + u32 pkts_coal; + u32 time_coal; + + /* Virtual address of the RX DMA descriptors array */ + struct mvneta_rx_desc *descs; + + /* DMA address of the RX DMA descriptors array */ + dma_addr_t descs_phys; + + /* Index of the last RX DMA descriptor */ + int last_desc; + + /* Index of the next RX DMA descriptor to process */ + int next_desc_to_proc; +}; + +static int rxq_number = 8; +static int txq_number = 8; + +static int rxq_def; +static int txq_def; + +#define MVNETA_DRIVER_NAME "mvneta" +#define MVNETA_DRIVER_VERSION "1.0" + +/* Utility/helper methods */ + +/* Write helper method */ +static void mvreg_write(struct mvneta_port *pp, u32 offset, u32 data) +{ + writel(data, pp->base + offset); +} + +/* Read helper method */ +static u32 mvreg_read(struct mvneta_port *pp, u32 offset) +{ + return readl(pp->base + offset); +} + +/* Increment txq get counter */ +static void mvneta_txq_inc_get(struct mvneta_tx_queue *txq) +{ + txq->txq_get_index++; + if (txq->txq_get_index == txq->size) + txq->txq_get_index = 0; +} + +/* Increment txq put counter */ +static void mvneta_txq_inc_put(struct mvneta_tx_queue *txq) +{ + txq->txq_put_index++; + if (txq->txq_put_index == txq->size) + txq->txq_put_index = 0; +} + + +/* Clear all MIB counters */ +static void mvneta_mib_counters_clear(struct mvneta_port *pp) +{ + int i; + u32 dummy; + + /* Perform dummy reads from MIB counters */ + for (i = 0; i < MVNETA_MIB_LATE_COLLISION; i += 4) + dummy = mvreg_read(pp, (MVNETA_MIB_COUNTERS_BASE + i)); +} + +/* Get System Network Statistics */ +struct rtnl_link_stats64 *mvneta_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + struct mvneta_port *pp = netdev_priv(dev); + unsigned int start; + + memset(stats, 0, sizeof(struct rtnl_link_stats64)); + + do { + start = u64_stats_fetch_begin_bh(&pp->rx_stats.syncp); + stats->rx_packets = pp->rx_stats.packets; + stats->rx_bytes = pp->rx_stats.bytes; + } while (u64_stats_fetch_retry_bh(&pp->rx_stats.syncp, start)); + + + do { + start = u64_stats_fetch_begin_bh(&pp->tx_stats.syncp); + stats->tx_packets = pp->tx_stats.packets; + stats->tx_bytes = pp->tx_stats.bytes; + } while (u64_stats_fetch_retry_bh(&pp->tx_stats.syncp, start)); + + stats->rx_errors = dev->stats.rx_errors; + stats->rx_dropped = dev->stats.rx_dropped; + + stats->tx_dropped = dev->stats.tx_dropped; + + return stats; +} + +/* Rx descriptors helper methods */ + +/* + * Checks whether the given RX descriptor is both the first and the + * last descriptor for the RX packet. Each RX packet is currently + * received through a single RX descriptor, so not having each RX + * descriptor with its first and last bits set is an error + */ +static int mvneta_rxq_desc_is_first_last(struct mvneta_rx_desc *desc) +{ + return (desc->status & MVNETA_RXD_FIRST_LAST_DESC) == + MVNETA_RXD_FIRST_LAST_DESC; +} + +/* Add number of descriptors ready to receive new packets */ +static void mvneta_rxq_non_occup_desc_add(struct mvneta_port *pp, + struct mvneta_rx_queue *rxq, + int ndescs) +{ + /* Only MVNETA_RXQ_ADD_NON_OCCUPIED_MAX (255) descriptors can + * be added at once */ + while (ndescs > MVNETA_RXQ_ADD_NON_OCCUPIED_MAX) { + mvreg_write(pp, MVNETA_RXQ_STATUS_UPDATE_REG(rxq->id), + (MVNETA_RXQ_ADD_NON_OCCUPIED_MAX << + MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT)); + ndescs -= MVNETA_RXQ_ADD_NON_OCCUPIED_MAX; + } + + mvreg_write(pp, MVNETA_RXQ_STATUS_UPDATE_REG(rxq->id), + (ndescs << MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT)); +} + +/* Get number of RX descriptors occupied by received packets */ +static int mvneta_rxq_busy_desc_num_get(struct mvneta_port *pp, + struct mvneta_rx_queue *rxq) +{ + u32 val; + + val = mvreg_read(pp, MVNETA_RXQ_STATUS_REG(rxq->id)); + return val & MVNETA_RXQ_OCCUPIED_ALL_MASK; +} + +/* + * Update num of rx desc called upon return from rx path or + * from mvneta_rxq_drop_pkts(). + */ +static void mvneta_rxq_desc_num_update(struct mvneta_port *pp, + struct mvneta_rx_queue *rxq, + int rx_done, int rx_filled) +{ + u32 val; + + if ((rx_done <= 0xff) && (rx_filled <= 0xff)) { + val = rx_done | + (rx_filled << MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT); + mvreg_write(pp, MVNETA_RXQ_STATUS_UPDATE_REG(rxq->id), val); + return; + } + + /* Only 255 descriptors can be added at once */ + while ((rx_done > 0) || (rx_filled > 0)) { + if (rx_done <= 0xff) { + val = rx_done; + rx_done = 0; + } else { + val = 0xff; + rx_done -= 0xff; + } + if (rx_filled <= 0xff) { + val |= rx_filled << MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT; + rx_filled = 0; + } else { + val |= 0xff << MVNETA_RXQ_ADD_NON_OCCUPIED_SHIFT; + rx_filled -= 0xff; + } + mvreg_write(pp, MVNETA_RXQ_STATUS_UPDATE_REG(rxq->id), val); + } +} + +/* Get pointer to next RX descriptor to be processed by SW */ +static struct mvneta_rx_desc * +mvneta_rxq_next_desc_get(struct mvneta_rx_queue *rxq) +{ + int rx_desc = rxq->next_desc_to_proc; + + rxq->next_desc_to_proc = MVNETA_QUEUE_NEXT_DESC(rxq, rx_desc); + return rxq->descs + rx_desc; +} + +/* Change maximum receive size of the port. */ +static void mvneta_max_rx_size_set(struct mvneta_port *pp, int max_rx_size) +{ + u32 val; + + val = mvreg_read(pp, MVNETA_GMAC_CTRL_0); + val &= ~MVNETA_GMAC_MAX_RX_SIZE_MASK; + val |= ((max_rx_size - MVNETA_MH_SIZE) / 2) << + MVNETA_GMAC_MAX_RX_SIZE_SHIFT; + mvreg_write(pp, MVNETA_GMAC_CTRL_0, val); +} + + +/* Set rx queue offset */ +static void mvneta_rxq_offset_set(struct mvneta_port *pp, + struct mvneta_rx_queue *rxq, + int offset) +{ + u32 val; + + val = mvreg_read(pp, MVNETA_RXQ_CONFIG_REG(rxq->id)); + val &= ~MVNETA_RXQ_PKT_OFFSET_ALL_MASK; + + /* Offset is in */ + val |= MVNETA_RXQ_PKT_OFFSET_MASK(offset >> 3); + mvreg_write(pp, MVNETA_RXQ_CONFIG_REG(rxq->id), val); +} + + +/* Tx descriptors helper methods */ + +/* Update HW with number of TX descriptors to be sent */ +static void mvneta_txq_pend_desc_add(struct mvneta_port *pp, + struct mvneta_tx_queue *txq, + int pend_desc) +{ + u32 val; + + /* Only 255 descriptors can be added at once ; Assume caller + process TX desriptors in quanta less than 256 */ + val = pend_desc; + mvreg_write(pp, MVNETA_TXQ_UPDATE_REG(txq->id), val); +} + +/* Get pointer to next TX descriptor to be processed (send) by HW */ +static struct mvneta_tx_desc * +mvneta_txq_next_desc_get(struct mvneta_tx_queue *txq) +{ + int tx_desc = txq->next_desc_to_proc; + + txq->next_desc_to_proc = MVNETA_QUEUE_NEXT_DESC(txq, tx_desc); + return txq->descs + tx_desc; +} + +/* Release the last allocated TX descriptor. Useful to handle DMA + * mapping failures in the TX path. */ +static void mvneta_txq_desc_put(struct mvneta_tx_queue *txq) +{ + if (txq->next_desc_to_proc == 0) + txq->next_desc_to_proc = txq->last_desc - 1; + else + txq->next_desc_to_proc--; +} + +/* Set rxq buf size */ +static void mvneta_rxq_buf_size_set(struct mvneta_port *pp, + struct mvneta_rx_queue *rxq, + int buf_size) +{ + u32 val; + + val = mvreg_read(pp, MVNETA_RXQ_SIZE_REG(rxq->id)); + + val &= ~MVNETA_RXQ_BUF_SIZE_MASK; + val |= ((buf_size >> 3) << MVNETA_RXQ_BUF_SIZE_SHIFT); + + mvreg_write(pp, MVNETA_RXQ_SIZE_REG(rxq->id), val); +} + +/* Disable buffer management (BM) */ +static void mvneta_rxq_bm_disable(struct mvneta_port *pp, + struct mvneta_rx_queue *rxq) +{ + u32 val; + + val = mvreg_read(pp, MVNETA_RXQ_CONFIG_REG(rxq->id)); + val &= ~MVNETA_RXQ_HW_BUF_ALLOC; + mvreg_write(pp, MVNETA_RXQ_CONFIG_REG(rxq->id), val); +} + + + +/* Sets the RGMII Enable bit (RGMIIEn) in port MAC control register */ +static void __devinit mvneta_gmac_rgmii_set(struct mvneta_port *pp, int enable) +{ + u32 val; + + val = mvreg_read(pp, MVNETA_GMAC_CTRL_2); + + if (enable) + val |= MVNETA_GMAC2_PORT_RGMII; + else + val &= ~MVNETA_GMAC2_PORT_RGMII; + + mvreg_write(pp, MVNETA_GMAC_CTRL_2, val); +} + +/* Config SGMII port */ +static void __devinit mvneta_port_sgmii_config(struct mvneta_port *pp) +{ + u32 val; + + val = mvreg_read(pp, MVNETA_GMAC_CTRL_2); + val |= MVNETA_GMAC2_PSC_ENABLE; + mvreg_write(pp, MVNETA_GMAC_CTRL_2, val); +} + +/* Start the Ethernet port RX and TX activity */ +static void mvneta_port_up(struct mvneta_port *pp) +{ + int queue; + u32 q_map; + + /* Enable all initialized TXs. */ + mvneta_mib_counters_clear(pp); + q_map = 0; + for (queue = 0; queue < txq_number; queue++) { + struct mvneta_tx_queue *txq = &pp->txqs[queue]; + if (txq->descs != NULL) + q_map |= (1 << queue); + } + mvreg_write(pp, MVNETA_TXQ_CMD, q_map); + + /* Enable all initialized RXQs. */ + q_map = 0; + for (queue = 0; queue < rxq_number; queue++) { + struct mvneta_rx_queue *rxq = &pp->rxqs[queue]; + if (rxq->descs != NULL) + q_map |= (1 << queue); + } + + mvreg_write(pp, MVNETA_RXQ_CMD, q_map); +} + +/* Stop the Ethernet port activity */ +static void mvneta_port_down(struct mvneta_port *pp) +{ + u32 val; + int count; + + /* Stop Rx port activity. Check port Rx activity. */ + val = mvreg_read(pp, MVNETA_RXQ_CMD) & MVNETA_RXQ_ENABLE_MASK; + + /* Issue stop command for active channels only */ + if (val != 0) + mvreg_write(pp, MVNETA_RXQ_CMD, + val << MVNETA_RXQ_DISABLE_SHIFT); + + /* Wait for all Rx activity to terminate. */ + count = 0; + do { + if (count++ >= MVNETA_RX_DISABLE_TIMEOUT_MSEC) { + netdev_warn(pp->dev, + "TIMEOUT for RX stopped ! rx_queue_cmd: 0x08%x\n", + val); + break; + } + mdelay(1); + + val = mvreg_read(pp, MVNETA_RXQ_CMD); + } while (val & 0xff); + + /* Stop Tx port activity. Check port Tx activity. Issue stop + command for active channels only */ + val = (mvreg_read(pp, MVNETA_TXQ_CMD)) & MVNETA_TXQ_ENABLE_MASK; + + if (val != 0) + mvreg_write(pp, MVNETA_TXQ_CMD, + (val << MVNETA_TXQ_DISABLE_SHIFT)); + + /* Wait for all Tx activity to terminate. */ + count = 0; + do { + if (count++ >= MVNETA_TX_DISABLE_TIMEOUT_MSEC) { + netdev_warn(pp->dev, + "TIMEOUT for TX stopped status=0x%08x\n", + val); + break; + } + mdelay(1); + + /* Check TX Command reg that all Txqs are stopped */ + val = mvreg_read(pp, MVNETA_TXQ_CMD); + + } while (val & 0xff); + + /* Double check to verify that TX FIFO is empty */ + count = 0; + do { + if (count++ >= MVNETA_TX_FIFO_EMPTY_TIMEOUT) { + netdev_warn(pp->dev, + "TX FIFO empty timeout status=0x08%x\n", + val); + break; + } + mdelay(1); + + val = mvreg_read(pp, MVNETA_PORT_STATUS); + } while (!(val & MVNETA_TX_FIFO_EMPTY) && + (val & MVNETA_TX_IN_PRGRS)); + + udelay(200); +} + +/* Enable the port by setting the port enable bit of the MAC control register */ +static void mvneta_port_enable(struct mvneta_port *pp) +{ + u32 val; + + /* Enable port */ + val = mvreg_read(pp, MVNETA_GMAC_CTRL_0); + val |= MVNETA_GMAC0_PORT_ENABLE; + mvreg_write(pp, MVNETA_GMAC_CTRL_0, val); +} + +/* Disable the port and wait for about 200 usec before retuning */ +static void mvneta_port_disable(struct mvneta_port *pp) +{ + u32 val; + + /* Reset the Enable bit in the Serial Control Register */ + val = mvreg_read(pp, MVNETA_GMAC_CTRL_0); + val &= ~MVNETA_GMAC0_PORT_ENABLE; + mvreg_write(pp, MVNETA_GMAC_CTRL_0, val); + + udelay(200); +} + +/* Multicast tables methods */ + +/* Set all entries in Unicast MAC Table; queue==-1 means reject all */ +static void mvneta_set_ucast_table(struct mvneta_port *pp, int queue) +{ + int offset; + u32 val; + + if (queue == -1) { + val = 0; + } else { + val = 0x1 | (queue << 1); + val |= (val << 24) | (val << 16) | (val << 8); + } + + for (offset = 0; offset <= 0xc; offset += 4) + mvreg_write(pp, MVNETA_DA_FILT_UCAST_BASE + offset, val); +} + +/* Set all entries in Special Multicast MAC Table; queue==-1 means reject all */ +static void mvneta_set_special_mcast_table(struct mvneta_port *pp, int queue) +{ + int offset; + u32 val; + + if (queue == -1) { + val = 0; + } else { + val = 0x1 | (queue << 1); + val |= (val << 24) | (val << 16) | (val << 8); + } + + for (offset = 0; offset <= 0xfc; offset += 4) + mvreg_write(pp, MVNETA_DA_FILT_SPEC_MCAST + offset, val); + +} + +/* Set all entries in Other Multicast MAC Table. queue==-1 means reject all */ +static void mvneta_set_other_mcast_table(struct mvneta_port *pp, int queue) +{ + int offset; + u32 val; + + if (queue == -1) { + memset(pp->mcast_count, 0, sizeof(pp->mcast_count)); + val = 0; + } else { + memset(pp->mcast_count, 1, sizeof(pp->mcast_count)); + val = 0x1 | (queue << 1); + val |= (val << 24) | (val << 16) | (val << 8); + } + + for (offset = 0; offset <= 0xfc; offset += 4) + mvreg_write(pp, MVNETA_DA_FILT_OTH_MCAST + offset, val); +} + +/* This method sets defaults to the NETA port: + * Clears interrupt Cause and Mask registers. + * Clears all MAC tables. + * Sets defaults to all registers. + * Resets RX and TX descriptor rings. + * Resets PHY. + * This method can be called after mvneta_port_down() to return the port + * settings to defaults. + */ +static void mvneta_defaults_set(struct mvneta_port *pp) +{ + int cpu; + int queue; + u32 val; + + /* Clear all Cause registers */ + mvreg_write(pp, MVNETA_INTR_NEW_CAUSE, 0); + mvreg_write(pp, MVNETA_INTR_OLD_CAUSE, 0); + mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0); + + /* Mask all interrupts */ + mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0); + mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0); + mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0); + mvreg_write(pp, MVNETA_INTR_ENABLE, 0); + + /* Enable MBUS Retry bit16 */ + mvreg_write(pp, MVNETA_MBUS_RETRY, 0x20); + + /* Set CPU queue access map - all CPUs have access to all RX + queues and to all TX queues */ + for (cpu = 0; cpu < CONFIG_NR_CPUS; cpu++) + mvreg_write(pp, MVNETA_CPU_MAP(cpu), + (MVNETA_CPU_RXQ_ACCESS_ALL_MASK | + MVNETA_CPU_TXQ_ACCESS_ALL_MASK)); + + /* Reset RX and TX DMAs */ + mvreg_write(pp, MVNETA_PORT_RX_RESET, MVNETA_PORT_RX_DMA_RESET); + mvreg_write(pp, MVNETA_PORT_TX_RESET, MVNETA_PORT_TX_DMA_RESET); + + /* Disable Legacy WRR, Disable EJP, Release from reset */ + mvreg_write(pp, MVNETA_TXQ_CMD_1, 0); + for (queue = 0; queue < txq_number; queue++) { + mvreg_write(pp, MVETH_TXQ_TOKEN_COUNT_REG(queue), 0); + mvreg_write(pp, MVETH_TXQ_TOKEN_CFG_REG(queue), 0); + } + + mvreg_write(pp, MVNETA_PORT_TX_RESET, 0); + mvreg_write(pp, MVNETA_PORT_RX_RESET, 0); + + /* Set Port Acceleration Mode */ + val = MVNETA_ACC_MODE_EXT; + mvreg_write(pp, MVNETA_ACC_MODE, val); + + /* Update val of portCfg register accordingly with all RxQueue types */ + val = MVNETA_PORT_CONFIG_DEFL_VALUE(rxq_def); + mvreg_write(pp, MVNETA_PORT_CONFIG, val); + + val = 0; + mvreg_write(pp, MVNETA_PORT_CONFIG_EXTEND, val); + mvreg_write(pp, MVNETA_RX_MIN_FRAME_SIZE, 64); + + /* Build PORT_SDMA_CONFIG_REG */ + val = 0; + + /* Default burst size */ + val |= MVNETA_TX_BRST_SZ_MASK(MVNETA_SDMA_BRST_SIZE_16); + val |= MVNETA_RX_BRST_SZ_MASK(MVNETA_SDMA_BRST_SIZE_16); + + val |= (MVNETA_RX_NO_DATA_SWAP | MVNETA_TX_NO_DATA_SWAP | + MVNETA_NO_DESC_SWAP); + + /* Assign port SDMA configuration */ + mvreg_write(pp, MVNETA_SDMA_CONFIG, val); + + mvneta_set_ucast_table(pp, -1); + mvneta_set_special_mcast_table(pp, -1); + mvneta_set_other_mcast_table(pp, -1); + + /* Set port interrupt enable register - default enable all */ + mvreg_write(pp, MVNETA_INTR_ENABLE, + (MVNETA_RXQ_INTR_ENABLE_ALL_MASK + | MVNETA_TXQ_INTR_ENABLE_ALL_MASK)); +} + +/* Set max sizes for tx queues */ +static void mvneta_txq_max_tx_size_set(struct mvneta_port *pp, int max_tx_size) + +{ + u32 val, size, mtu; + int queue; + + mtu = max_tx_size * 8; + if (mtu > MVNETA_TX_MTU_MAX) + mtu = MVNETA_TX_MTU_MAX; + + /* Set MTU */ + val = mvreg_read(pp, MVNETA_TX_MTU); + val &= ~MVNETA_TX_MTU_MAX; + val |= mtu; + mvreg_write(pp, MVNETA_TX_MTU, val); + + /* TX token size and all TXQs token size must be larger that MTU */ + val = mvreg_read(pp, MVNETA_TX_TOKEN_SIZE); + + size = val & MVNETA_TX_TOKEN_SIZE_MAX; + if (size < mtu) { + size = mtu; + val &= ~MVNETA_TX_TOKEN_SIZE_MAX; + val |= size; + mvreg_write(pp, MVNETA_TX_TOKEN_SIZE, val); + } + for (queue = 0; queue < txq_number; queue++) { + val = mvreg_read(pp, MVNETA_TXQ_TOKEN_SIZE_REG(queue)); + + size = val & MVNETA_TXQ_TOKEN_SIZE_MAX; + if (size < mtu) { + size = mtu; + val &= ~MVNETA_TXQ_TOKEN_SIZE_MAX; + val |= size; + mvreg_write(pp, MVNETA_TXQ_TOKEN_SIZE_REG(queue), val); + } + } +} + +/* Set unicast address */ +static void mvneta_set_ucast_addr(struct mvneta_port *pp, u8 last_nibble, + int queue) +{ + unsigned int unicast_reg; + unsigned int tbl_offset; + unsigned int reg_offset; + + /* Locate the Unicast table entry */ + last_nibble = (0xf & last_nibble); + + /* offset from unicast tbl base */ + tbl_offset = (last_nibble / 4) * 4; + + /* offset within the above reg */ + reg_offset = last_nibble % 4; + + unicast_reg = mvreg_read(pp, (MVNETA_DA_FILT_UCAST_BASE + tbl_offset)); + + if (queue == -1) { + /* Clear accepts frame bit at specified unicast DA tbl entry */ + unicast_reg &= ~(0xff << (8 * reg_offset)); + } else { + unicast_reg &= ~(0xff << (8 * reg_offset)); + unicast_reg |= ((0x01 | (queue << 1)) << (8 * reg_offset)); + } + + mvreg_write(pp, (MVNETA_DA_FILT_UCAST_BASE + tbl_offset), unicast_reg); +} + +/* Set mac address */ +static void mvneta_mac_addr_set(struct mvneta_port *pp, unsigned char *addr, + int queue) +{ + unsigned int mac_h; + unsigned int mac_l; + + if (queue != -1) { + mac_l = (addr[4] << 8) | (addr[5]); + mac_h = (addr[0] << 24) | (addr[1] << 16) | + (addr[2] << 8) | (addr[3] << 0); + + mvreg_write(pp, MVNETA_MAC_ADDR_LOW, mac_l); + mvreg_write(pp, MVNETA_MAC_ADDR_HIGH, mac_h); + } + + /* Accept frames of this address */ + mvneta_set_ucast_addr(pp, addr[5], queue); +} + +/* + * Set the number of packets that will be received before + * RX interrupt will be generated by HW. + */ +static void mvneta_rx_pkts_coal_set(struct mvneta_port *pp, + struct mvneta_rx_queue *rxq, u32 value) +{ + mvreg_write(pp, MVNETA_RXQ_THRESHOLD_REG(rxq->id), + value | MVNETA_RXQ_NON_OCCUPIED(0)); + rxq->pkts_coal = value; +} + +/* + * Set the time delay in usec before + * RX interrupt will be generated by HW. + */ +static void mvneta_rx_time_coal_set(struct mvneta_port *pp, + struct mvneta_rx_queue *rxq, u32 value) +{ + u32 val = (pp->clk_rate_hz / 1000000) * value; + + mvreg_write(pp, MVNETA_RXQ_TIME_COAL_REG(rxq->id), val); + rxq->time_coal = value; +} + +/* Set threshold for TX_DONE pkts coalescing */ +static void mvneta_tx_done_pkts_coal_set(struct mvneta_port *pp, + struct mvneta_tx_queue *txq, u32 value) +{ + u32 val; + + val = mvreg_read(pp, MVNETA_TXQ_SIZE_REG(txq->id)); + + val &= ~MVNETA_TXQ_SENT_THRESH_ALL_MASK; + val |= MVNETA_TXQ_SENT_THRESH_MASK(value); + + mvreg_write(pp, MVNETA_TXQ_SIZE_REG(txq->id), val); + + txq->done_pkts_coal = value; +} + +/* Trigger tx done timer in MVNETA_TX_DONE_TIMER_PERIOD msecs */ +static void mvneta_add_tx_done_timer(struct mvneta_port *pp) +{ + if (test_and_set_bit(MVNETA_F_TX_DONE_TIMER_BIT, &pp->flags) == 0) { + pp->tx_done_timer.expires = jiffies + + msecs_to_jiffies(MVNETA_TX_DONE_TIMER_PERIOD); + add_timer(&pp->tx_done_timer); + } +} + + +/* Handle rx descriptor fill by setting buf_cookie and buf_phys_addr */ +static void mvneta_rx_desc_fill(struct mvneta_rx_desc *rx_desc, + u32 phys_addr, u32 cookie) +{ + rx_desc->buf_cookie = cookie; + rx_desc->buf_phys_addr = phys_addr; +} + +/* Decrement sent descriptors counter */ +static void mvneta_txq_sent_desc_dec(struct mvneta_port *pp, + struct mvneta_tx_queue *txq, + int sent_desc) +{ + u32 val; + + /* Only 255 TX descriptors can be updated at once */ + while (sent_desc > 0xff) { + val = 0xff << MVNETA_TXQ_DEC_SENT_SHIFT; + mvreg_write(pp, MVNETA_TXQ_UPDATE_REG(txq->id), val); + sent_desc = sent_desc - 0xff; + } + + val = sent_desc << MVNETA_TXQ_DEC_SENT_SHIFT; + mvreg_write(pp, MVNETA_TXQ_UPDATE_REG(txq->id), val); +} + +/* Get number of TX descriptors already sent by HW */ +static int mvneta_txq_sent_desc_num_get(struct mvneta_port *pp, + struct mvneta_tx_queue *txq) +{ + u32 val; + int sent_desc; + + val = mvreg_read(pp, MVNETA_TXQ_STATUS_REG(txq->id)); + sent_desc = (val & MVNETA_TXQ_SENT_DESC_MASK) >> + MVNETA_TXQ_SENT_DESC_SHIFT; + + return sent_desc; +} + +/* + * Get number of sent descriptors and decrement counter. + * The number of sent descriptors is returned. + */ +static int mvneta_txq_sent_desc_proc(struct mvneta_port *pp, + struct mvneta_tx_queue *txq) +{ + int sent_desc; + + /* Get number of sent descriptors */ + sent_desc = mvneta_txq_sent_desc_num_get(pp, txq); + + /* Decrement sent descriptors counter */ + if (sent_desc) + mvneta_txq_sent_desc_dec(pp, txq, sent_desc); + + return sent_desc; +} + +/* Set TXQ descriptors fields relevant for CSUM calculation */ +static u32 mvneta_txq_desc_csum(int l3_offs, int l3_proto, + int ip_hdr_len, int l4_proto) +{ + u32 command; + + /* Fields: L3_offset, IP_hdrlen, L3_type, G_IPv4_chk, + G_L4_chk, L4_type; required only for checksum + calculation */ + command = l3_offs << MVNETA_TX_L3_OFF_SHIFT; + command |= ip_hdr_len << MVNETA_TX_IP_HLEN_SHIFT; + + if (l3_proto == swab16(ETH_P_IP)) + command |= MVNETA_TXD_IP_CSUM; + else + command |= MVNETA_TX_L3_IP6; + + if (l4_proto == IPPROTO_TCP) + command |= MVNETA_TX_L4_CSUM_FULL; + else if (l4_proto == IPPROTO_UDP) + command |= MVNETA_TX_L4_UDP | MVNETA_TX_L4_CSUM_FULL; + else + command |= MVNETA_TX_L4_CSUM_NOT; + + return command; +} + + +/* Display more error info */ +static void mvneta_rx_error(struct mvneta_port *pp, + struct mvneta_rx_desc *rx_desc) +{ + u32 status = rx_desc->status; + + if (!mvneta_rxq_desc_is_first_last(rx_desc)) { + netdev_err(pp->dev, + "bad rx status %08x (buffer oversize), size=%d\n", + rx_desc->status, rx_desc->data_size); + return; + } + + switch (status & MVNETA_RXD_ERR_CODE_MASK) { + case MVNETA_RXD_ERR_CRC: + netdev_err(pp->dev, "bad rx status %08x (crc error), size=%d\n", + status, rx_desc->data_size); + break; + case MVNETA_RXD_ERR_OVERRUN: + netdev_err(pp->dev, "bad rx status %08x (overrun error), size=%d\n", + status, rx_desc->data_size); + break; + case MVNETA_RXD_ERR_LEN: + netdev_err(pp->dev, "bad rx status %08x (max frame length error), size=%d\n", + status, rx_desc->data_size); + break; + case MVNETA_RXD_ERR_RESOURCE: + netdev_err(pp->dev, "bad rx status %08x (resource error), size=%d\n", + status, rx_desc->data_size); + break; + } +} + +/* Handle RX checksum offload */ +static void mvneta_rx_csum(struct mvneta_port *pp, + struct mvneta_rx_desc *rx_desc, + struct sk_buff *skb) +{ + if ((rx_desc->status & MVNETA_RXD_L3_IP4) && + (rx_desc->status & MVNETA_RXD_L4_CSUM_OK)) { + skb->csum = 0; + skb->ip_summed = CHECKSUM_UNNECESSARY; + return; + } + + skb->ip_summed = CHECKSUM_NONE; +} + +/* Return tx queue pointer (find last set bit) according to causeTxDone reg */ +static struct mvneta_tx_queue *mvneta_tx_done_policy(struct mvneta_port *pp, + u32 cause) +{ + int queue = fls(cause) - 1; + + return (queue < 0 || queue >= txq_number) ? NULL : &pp->txqs[queue]; +} + +/* Free tx queue skbuffs */ +static void mvneta_txq_bufs_free(struct mvneta_port *pp, + struct mvneta_tx_queue *txq, int num) +{ + int i; + + for (i = 0; i < num; i++) { + struct mvneta_tx_desc *tx_desc = txq->descs + + txq->txq_get_index; + struct sk_buff *skb = txq->tx_skb[txq->txq_get_index]; + + mvneta_txq_inc_get(txq); + + if (!skb) + continue; + + dma_unmap_single(pp->dev->dev.parent, tx_desc->buf_phys_addr, + tx_desc->data_size, DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + } +} + +/* Handle end of transmission */ +static int mvneta_txq_done(struct mvneta_port *pp, + struct mvneta_tx_queue *txq) +{ + struct netdev_queue *nq = netdev_get_tx_queue(pp->dev, txq->id); + int tx_done; + + tx_done = mvneta_txq_sent_desc_proc(pp, txq); + if (tx_done == 0) + return tx_done; + mvneta_txq_bufs_free(pp, txq, tx_done); + + txq->count -= tx_done; + + if (netif_tx_queue_stopped(nq)) { + if (txq->size - txq->count >= MAX_SKB_FRAGS + 1) + netif_tx_wake_queue(nq); + } + + return tx_done; +} + +/* Refill processing */ +static int mvneta_rx_refill(struct mvneta_port *pp, + struct mvneta_rx_desc *rx_desc) + +{ + dma_addr_t phys_addr; + struct sk_buff *skb; + + skb = netdev_alloc_skb(pp->dev, pp->pkt_size); + if (!skb) + return -ENOMEM; + + phys_addr = dma_map_single(pp->dev->dev.parent, skb->head, + MVNETA_RX_BUF_SIZE(pp->pkt_size), + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(pp->dev->dev.parent, phys_addr))) { + dev_kfree_skb(skb); + return -ENOMEM; + } + + mvneta_rx_desc_fill(rx_desc, phys_addr, (u32)skb); + + return 0; +} + +/* Handle tx checksum */ +static u32 mvneta_skb_tx_csum(struct mvneta_port *pp, struct sk_buff *skb) +{ + if (skb->ip_summed == CHECKSUM_PARTIAL) { + int ip_hdr_len = 0; + u8 l4_proto; + + if (skb->protocol == htons(ETH_P_IP)) { + struct iphdr *ip4h = ip_hdr(skb); + + /* Calculate IPv4 checksum and L4 checksum */ + ip_hdr_len = ip4h->ihl; + l4_proto = ip4h->protocol; + } else if (skb->protocol == htons(ETH_P_IPV6)) { + struct ipv6hdr *ip6h = ipv6_hdr(skb); + + /* Read l4_protocol from one of IPv6 extra headers */ + if (skb_network_header_len(skb) > 0) + ip_hdr_len = (skb_network_header_len(skb) >> 2); + l4_proto = ip6h->nexthdr; + } else + return MVNETA_TX_L4_CSUM_NOT; + + return mvneta_txq_desc_csum(skb_network_offset(skb), + skb->protocol, ip_hdr_len, l4_proto); + } + + return MVNETA_TX_L4_CSUM_NOT; +} + +/* + * Returns rx queue pointer (find last set bit) according to causeRxTx + * value + */ +static struct mvneta_rx_queue *mvneta_rx_policy(struct mvneta_port *pp, + u32 cause) +{ + int queue = fls(cause >> 8) - 1; + + return (queue < 0 || queue >= rxq_number) ? NULL : &pp->rxqs[queue]; +} + +/* Drop packets received by the RXQ and free buffers */ +static void mvneta_rxq_drop_pkts(struct mvneta_port *pp, + struct mvneta_rx_queue *rxq) +{ + int rx_done, i; + + rx_done = mvneta_rxq_busy_desc_num_get(pp, rxq); + for (i = 0; i < rxq->size; i++) { + struct mvneta_rx_desc *rx_desc = rxq->descs + i; + struct sk_buff *skb = (struct sk_buff *)rx_desc->buf_cookie; + + dev_kfree_skb_any(skb); + dma_unmap_single(pp->dev->dev.parent, rx_desc->buf_phys_addr, + rx_desc->data_size, DMA_FROM_DEVICE); + } + + if (rx_done) + mvneta_rxq_desc_num_update(pp, rxq, rx_done, rx_done); +} + +/* Main rx processing */ +static int mvneta_rx(struct mvneta_port *pp, int rx_todo, + struct mvneta_rx_queue *rxq) +{ + struct net_device *dev = pp->dev; + int rx_done, rx_filled; + + /* Get number of received packets */ + rx_done = mvneta_rxq_busy_desc_num_get(pp, rxq); + + if (rx_todo > rx_done) + rx_todo = rx_done; + + rx_done = 0; + rx_filled = 0; + + /* Fairness NAPI loop */ + while (rx_done < rx_todo) { + struct mvneta_rx_desc *rx_desc = mvneta_rxq_next_desc_get(rxq); + struct sk_buff *skb; + u32 rx_status; + int rx_bytes, err; + + prefetch(rx_desc); + rx_done++; + rx_filled++; + rx_status = rx_desc->status; + skb = (struct sk_buff *)rx_desc->buf_cookie; + + if (!mvneta_rxq_desc_is_first_last(rx_desc) || + (rx_status & MVNETA_RXD_ERR_SUMMARY)) { + dev->stats.rx_errors++; + mvneta_rx_error(pp, rx_desc); + mvneta_rx_desc_fill(rx_desc, rx_desc->buf_phys_addr, + (u32)skb); + continue; + } + + dma_unmap_single(pp->dev->dev.parent, rx_desc->buf_phys_addr, + rx_desc->data_size, DMA_FROM_DEVICE); + + rx_bytes = rx_desc->data_size - + (ETH_FCS_LEN + MVNETA_MH_SIZE); + u64_stats_update_begin(&pp->rx_stats.syncp); + pp->rx_stats.packets++; + pp->rx_stats.bytes += rx_bytes; + u64_stats_update_end(&pp->rx_stats.syncp); + + /* Linux processing */ + skb_reserve(skb, MVNETA_MH_SIZE); + skb_put(skb, rx_bytes); + + skb->protocol = eth_type_trans(skb, dev); + + mvneta_rx_csum(pp, rx_desc, skb); + + napi_gro_receive(&pp->napi, skb); + + /* Refill processing */ + err = mvneta_rx_refill(pp, rx_desc); + if (err) { + netdev_err(pp->dev, "Linux processing - Can't refill\n"); + rxq->missed++; + rx_filled--; + } + } + + /* Update rxq management counters */ + mvneta_rxq_desc_num_update(pp, rxq, rx_done, rx_filled); + + return rx_done; +} + +/* Handle tx fragmentation processing */ +static int mvneta_tx_frag_process(struct mvneta_port *pp, struct sk_buff *skb, + struct mvneta_tx_queue *txq) +{ + struct mvneta_tx_desc *tx_desc; + int i; + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + void *addr = page_address(frag->page.p) + frag->page_offset; + + tx_desc = mvneta_txq_next_desc_get(txq); + tx_desc->data_size = frag->size; + + tx_desc->buf_phys_addr = + dma_map_single(pp->dev->dev.parent, addr, + tx_desc->data_size, DMA_TO_DEVICE); + + if (dma_mapping_error(pp->dev->dev.parent, + tx_desc->buf_phys_addr)) { + mvneta_txq_desc_put(txq); + goto error; + } + + if (i == (skb_shinfo(skb)->nr_frags - 1)) { + /* Last descriptor */ + tx_desc->command = MVNETA_TXD_L_DESC | MVNETA_TXD_Z_PAD; + + txq->tx_skb[txq->txq_put_index] = skb; + + mvneta_txq_inc_put(txq); + } else { + /* Descriptor in the middle: Not First, Not Last */ + tx_desc->command = 0; + + txq->tx_skb[txq->txq_put_index] = NULL; + mvneta_txq_inc_put(txq); + } + } + + return 0; + +error: + /* Release all descriptors that were used to map fragments of + * this packet, as well as the corresponding DMA mappings */ + for (i = i - 1; i >= 0; i--) { + tx_desc = txq->descs + i; + dma_unmap_single(pp->dev->dev.parent, + tx_desc->buf_phys_addr, + tx_desc->data_size, + DMA_TO_DEVICE); + mvneta_txq_desc_put(txq); + } + + return -ENOMEM; +} + +/* Main tx processing */ +static int mvneta_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct mvneta_port *pp = netdev_priv(dev); + struct mvneta_tx_queue *txq = &pp->txqs[txq_def]; + struct mvneta_tx_desc *tx_desc; + struct netdev_queue *nq; + int frags = 0; + u32 tx_cmd; + + if (!netif_running(dev)) + goto out; + + frags = skb_shinfo(skb)->nr_frags + 1; + nq = netdev_get_tx_queue(dev, txq_def); + + /* Get a descriptor for the first part of the packet */ + tx_desc = mvneta_txq_next_desc_get(txq); + + tx_cmd = mvneta_skb_tx_csum(pp, skb); + + tx_desc->data_size = skb_headlen(skb); + + tx_desc->buf_phys_addr = dma_map_single(dev->dev.parent, skb->data, + tx_desc->data_size, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev->dev.parent, + tx_desc->buf_phys_addr))) { + mvneta_txq_desc_put(txq); + frags = 0; + goto out; + } + + if (frags == 1) { + /* First and Last descriptor */ + tx_cmd |= MVNETA_TXD_FLZ_DESC; + tx_desc->command = tx_cmd; + txq->tx_skb[txq->txq_put_index] = skb; + mvneta_txq_inc_put(txq); + } else { + /* First but not Last */ + tx_cmd |= MVNETA_TXD_F_DESC; + txq->tx_skb[txq->txq_put_index] = NULL; + mvneta_txq_inc_put(txq); + tx_desc->command = tx_cmd; + /* Continue with other skb fragments */ + if (mvneta_tx_frag_process(pp, skb, txq)) { + dma_unmap_single(dev->dev.parent, + tx_desc->buf_phys_addr, + tx_desc->data_size, + DMA_TO_DEVICE); + mvneta_txq_desc_put(txq); + frags = 0; + goto out; + } + } + + txq->count += frags; + mvneta_txq_pend_desc_add(pp, txq, frags); + + if (txq->size - txq->count < MAX_SKB_FRAGS + 1) + netif_tx_stop_queue(nq); + +out: + if (frags > 0) { + u64_stats_update_begin(&pp->tx_stats.syncp); + pp->tx_stats.packets++; + pp->tx_stats.bytes += skb->len; + u64_stats_update_end(&pp->tx_stats.syncp); + + } else { + dev->stats.tx_dropped++; + dev_kfree_skb_any(skb); + } + + if (txq->count >= MVNETA_TXDONE_COAL_PKTS) + mvneta_txq_done(pp, txq); + + /* If after calling mvneta_txq_done, count equals + frags, we need to set the timer */ + if (txq->count == frags && frags > 0) + mvneta_add_tx_done_timer(pp); + + return NETDEV_TX_OK; +} + + +/* Free tx resources, when resetting a port */ +static void mvneta_txq_done_force(struct mvneta_port *pp, + struct mvneta_tx_queue *txq) + +{ + int tx_done = txq->count; + + mvneta_txq_bufs_free(pp, txq, tx_done); + + /* reset txq */ + txq->count = 0; + txq->txq_put_index = 0; + txq->txq_get_index = 0; +} + +/* handle tx done - called from tx done timer callback */ +static u32 mvneta_tx_done_gbe(struct mvneta_port *pp, u32 cause_tx_done, + int *tx_todo) +{ + struct mvneta_tx_queue *txq; + u32 tx_done = 0; + struct netdev_queue *nq; + + *tx_todo = 0; + while (cause_tx_done != 0) { + txq = mvneta_tx_done_policy(pp, cause_tx_done); + if (!txq) + break; + + nq = netdev_get_tx_queue(pp->dev, txq->id); + __netif_tx_lock(nq, smp_processor_id()); + + if (txq->count) { + tx_done += mvneta_txq_done(pp, txq); + *tx_todo += txq->count; + } + + __netif_tx_unlock(nq); + cause_tx_done &= ~((1 << txq->id)); + } + + return tx_done; +} + +/* + * Compute crc8 of the specified address, using a unique algorithm , + * according to hw spec, different than generic crc8 algorithm + */ +static int mvneta_addr_crc(unsigned char *addr) +{ + int crc = 0; + int i; + + for (i = 0; i < ETH_ALEN; i++) { + int j; + + crc = (crc ^ addr[i]) << 8; + for (j = 7; j >= 0; j--) { + if (crc & (0x100 << j)) + crc ^= 0x107 << j; + } + } + + return crc; +} + +/* This method controls the net device special MAC multicast support. + * The Special Multicast Table for MAC addresses supports MAC of the form + * 0x01-00-5E-00-00-XX (where XX is between 0x00 and 0xFF). + * The MAC DA[7:0] bits are used as a pointer to the Special Multicast + * Table entries in the DA-Filter table. This method set the Special + * Multicast Table appropriate entry. + */ +static void mvneta_set_special_mcast_addr(struct mvneta_port *pp, + unsigned char last_byte, + int queue) +{ + unsigned int smc_table_reg; + unsigned int tbl_offset; + unsigned int reg_offset; + + /* Register offset from SMC table base */ + tbl_offset = (last_byte / 4); + /* Entry offset within the above reg */ + reg_offset = last_byte % 4; + + smc_table_reg = mvreg_read(pp, (MVNETA_DA_FILT_SPEC_MCAST + + tbl_offset * 4)); + + if (queue == -1) + smc_table_reg &= ~(0xff << (8 * reg_offset)); + else { + smc_table_reg &= ~(0xff << (8 * reg_offset)); + smc_table_reg |= ((0x01 | (queue << 1)) << (8 * reg_offset)); + } + + mvreg_write(pp, MVNETA_DA_FILT_SPEC_MCAST + tbl_offset * 4, + smc_table_reg); +} + +/* This method controls the network device Other MAC multicast support. + * The Other Multicast Table is used for multicast of another type. + * A CRC-8 is used as an index to the Other Multicast Table entries + * in the DA-Filter table. + * The method gets the CRC-8 value from the calling routine and + * sets the Other Multicast Table appropriate entry according to the + * specified CRC-8 . + */ +static void mvneta_set_other_mcast_addr(struct mvneta_port *pp, + unsigned char crc8, + int queue) +{ + unsigned int omc_table_reg; + unsigned int tbl_offset; + unsigned int reg_offset; + + tbl_offset = (crc8 / 4) * 4; /* Register offset from OMC table base */ + reg_offset = crc8 % 4; /* Entry offset within the above reg */ + + omc_table_reg = mvreg_read(pp, MVNETA_DA_FILT_OTH_MCAST + tbl_offset); + + if (queue == -1) { + /* Clear accepts frame bit at specified Other DA table entry */ + omc_table_reg &= ~(0xff << (8 * reg_offset)); + } else { + omc_table_reg &= ~(0xff << (8 * reg_offset)); + omc_table_reg |= ((0x01 | (queue << 1)) << (8 * reg_offset)); + } + + mvreg_write(pp, MVNETA_DA_FILT_OTH_MCAST + tbl_offset, omc_table_reg); +} + +/* The network device supports multicast using two tables: + * 1) Special Multicast Table for MAC addresses of the form + * 0x01-00-5E-00-00-XX (where XX is between 0x00 and 0xFF). + * The MAC DA[7:0] bits are used as a pointer to the Special Multicast + * Table entries in the DA-Filter table. + * 2) Other Multicast Table for multicast of another type. A CRC-8 value + * is used as an index to the Other Multicast Table entries in the + * DA-Filter table. + */ +static int mvneta_mcast_addr_set(struct mvneta_port *pp, unsigned char *p_addr, + int queue) +{ + unsigned char crc_result = 0; + + if (memcmp(p_addr, "\x01\x00\x5e\x00\x00", 5) == 0) { + mvneta_set_special_mcast_addr(pp, p_addr[5], queue); + return 0; + } + + crc_result = mvneta_addr_crc(p_addr); + if (queue == -1) { + if (pp->mcast_count[crc_result] == 0) { + netdev_info(pp->dev, "No valid Mcast for crc8=0x%02x\n", + crc_result); + return -EINVAL; + } + + pp->mcast_count[crc_result]--; + if (pp->mcast_count[crc_result] != 0) { + netdev_info(pp->dev, + "After delete there are %d valid Mcast for crc8=0x%02x\n", + pp->mcast_count[crc_result], crc_result); + return -EINVAL; + } + } else + pp->mcast_count[crc_result]++; + + mvneta_set_other_mcast_addr(pp, crc_result, queue); + + return 0; +} + +/* Configure Fitering mode of Ethernet port */ +static void mvneta_rx_unicast_promisc_set(struct mvneta_port *pp, + int is_promisc) +{ + u32 port_cfg_reg, val; + + port_cfg_reg = mvreg_read(pp, MVNETA_PORT_CONFIG); + + val = mvreg_read(pp, MVNETA_TYPE_PRIO); + + /* Set / Clear UPM bit in port configuration register */ + if (is_promisc) { + /* Accept all Unicast addresses */ + port_cfg_reg |= MVNETA_UNI_PROMISC_MODE; + val |= MVNETA_FORCE_UNI; + mvreg_write(pp, MVNETA_MAC_ADDR_LOW, 0xffff); + mvreg_write(pp, MVNETA_MAC_ADDR_HIGH, 0xffffffff); + } else { + /* Reject all Unicast addresses */ + port_cfg_reg &= ~MVNETA_UNI_PROMISC_MODE; + val &= ~MVNETA_FORCE_UNI; + } + + mvreg_write(pp, MVNETA_PORT_CONFIG, port_cfg_reg); + mvreg_write(pp, MVNETA_TYPE_PRIO, val); +} + +/* register unicast and multicast addresses */ +static void mvneta_set_rx_mode(struct net_device *dev) +{ + struct mvneta_port *pp = netdev_priv(dev); + struct netdev_hw_addr *ha; + + if (dev->flags & IFF_PROMISC) { + /* Accept all: Multicast + Unicast */ + mvneta_rx_unicast_promisc_set(pp, 1); + mvneta_set_ucast_table(pp, rxq_def); + mvneta_set_special_mcast_table(pp, rxq_def); + mvneta_set_other_mcast_table(pp, rxq_def); + } else { + /* Accept single Unicast */ + mvneta_rx_unicast_promisc_set(pp, 0); + mvneta_set_ucast_table(pp, -1); + mvneta_mac_addr_set(pp, dev->dev_addr, rxq_def); + + if (dev->flags & IFF_ALLMULTI) { + /* Accept all multicast */ + mvneta_set_special_mcast_table(pp, rxq_def); + mvneta_set_other_mcast_table(pp, rxq_def); + } else { + /* Accept only initialized multicast */ + mvneta_set_special_mcast_table(pp, -1); + mvneta_set_other_mcast_table(pp, -1); + + if (!netdev_mc_empty(dev)) { + netdev_for_each_mc_addr(ha, dev) { + mvneta_mcast_addr_set(pp, ha->addr, + rxq_def); + } + } + } + } +} + +/* Interrupt handling - the callback for request_irq() */ +static irqreturn_t mvneta_isr(int irq, void *dev_id) +{ + struct mvneta_port *pp = (struct mvneta_port *)dev_id; + + /* Mask all interrupts */ + mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0); + + napi_schedule(&pp->napi); + + return IRQ_HANDLED; +} + +/* NAPI handler + * Bits 0 - 7 of the causeRxTx register indicate that are transmitted + * packets on the corresponding TXQ (Bit 0 is for TX queue 1). + * Bits 8 -15 of the cause Rx Tx register indicate that are received + * packets on the corresponding RXQ (Bit 8 is for RX queue 0). + * Each CPU has its own causeRxTx register + */ +static int mvneta_poll(struct napi_struct *napi, int budget) +{ + int rx_done = 0; + u32 cause_rx_tx; + unsigned long flags; + struct mvneta_port *pp = netdev_priv(napi->dev); + + if (!netif_running(pp->dev)) { + napi_complete(napi); + return rx_done; + } + + /* Read cause register */ + cause_rx_tx = mvreg_read(pp, MVNETA_INTR_NEW_CAUSE) & + MVNETA_RX_INTR_MASK(rxq_number); + + /* + * For the case where the last mvneta_poll did not process all + * RX packets + */ + cause_rx_tx |= pp->cause_rx_tx; + if (rxq_number > 1) { + while ((cause_rx_tx != 0) && (budget > 0)) { + int count; + struct mvneta_rx_queue *rxq; + /* get rx queue number from cause_rx_tx */ + rxq = mvneta_rx_policy(pp, cause_rx_tx); + if (!rxq) + break; + + /* process the packet in that rx queue */ + count = mvneta_rx(pp, budget, rxq); + rx_done += count; + budget -= count; + if (budget > 0) { + /* set off the rx bit of the corresponding bit + in the cause rx tx register, so that next + iteration will find the next rx queue where + packets are received on */ + cause_rx_tx &= ~((1 << rxq->id) << 8); + } + } + } else { + rx_done = mvneta_rx(pp, budget, &pp->rxqs[rxq_def]); + budget -= rx_done; + } + + if (budget > 0) { + cause_rx_tx = 0; + napi_complete(napi); + local_irq_save(flags); + mvreg_write(pp, MVNETA_INTR_NEW_MASK, + MVNETA_RX_INTR_MASK(rxq_number)); + local_irq_restore(flags); + } + + pp->cause_rx_tx = cause_rx_tx; + return rx_done; +} + +/* tx done timer callback */ +static void mvneta_tx_done_timer_callback(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct mvneta_port *pp = netdev_priv(dev); + int tx_done = 0, tx_todo = 0; + + if (!netif_running(dev)) + return ; + + clear_bit(MVNETA_F_TX_DONE_TIMER_BIT, &pp->flags); + + tx_done = mvneta_tx_done_gbe(pp, + (((1 << txq_number) - 1) & + MVNETA_CAUSE_TXQ_SENT_DESC_ALL_MASK), + &tx_todo); + if (tx_todo > 0) + mvneta_add_tx_done_timer(pp); +} + +/* Handle rxq fill: allocates rxq skbs; called when initializing a port */ +static int mvneta_rxq_fill(struct mvneta_port *pp, struct mvneta_rx_queue *rxq, + int num) +{ + struct net_device *dev = pp->dev; + int i; + + for (i = 0; i < num; i++) { + struct sk_buff *skb; + struct mvneta_rx_desc *rx_desc; + unsigned long phys_addr; + + skb = dev_alloc_skb(pp->pkt_size); + if (!skb) { + netdev_err(dev, "%s:rxq %d, %d of %d buffs filled\n", + __func__, rxq->id, i, num); + break; + } + + rx_desc = rxq->descs + i; + memset(rx_desc, 0, sizeof(struct mvneta_rx_desc)); + phys_addr = dma_map_single(dev->dev.parent, skb->head, + MVNETA_RX_BUF_SIZE(pp->pkt_size), + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(dev->dev.parent, phys_addr))) { + dev_kfree_skb(skb); + break; + } + + mvneta_rx_desc_fill(rx_desc, phys_addr, (u32)skb); + } + + /* Add this number of RX descriptors as non occupied (ready to + get packets) */ + mvneta_rxq_non_occup_desc_add(pp, rxq, i); + + return i; +} + +/* Free all packets pending transmit from all TXQs and reset TX port */ +static void mvneta_tx_reset(struct mvneta_port *pp) +{ + int queue; + + /* free the skb's in the hal tx ring */ + for (queue = 0; queue < txq_number; queue++) + mvneta_txq_done_force(pp, &pp->txqs[queue]); + + mvreg_write(pp, MVNETA_PORT_TX_RESET, MVNETA_PORT_TX_DMA_RESET); + mvreg_write(pp, MVNETA_PORT_TX_RESET, 0); +} + +static void mvneta_rx_reset(struct mvneta_port *pp) +{ + mvreg_write(pp, MVNETA_PORT_RX_RESET, MVNETA_PORT_RX_DMA_RESET); + mvreg_write(pp, MVNETA_PORT_RX_RESET, 0); +} + +/* Rx/Tx queue initialization/cleanup methods */ + +/* Create a specified RX queue */ +static int mvneta_rxq_init(struct mvneta_port *pp, + struct mvneta_rx_queue *rxq) + +{ + rxq->size = pp->rx_ring_size; + + /* Allocate memory for RX descriptors */ + rxq->descs = dma_alloc_coherent(pp->dev->dev.parent, + rxq->size * MVNETA_DESC_ALIGNED_SIZE, + &rxq->descs_phys, GFP_KERNEL); + if (rxq->descs == NULL) { + netdev_err(pp->dev, + "rxq=%d: Can't allocate %d bytes for %d RX descr\n", + rxq->id, rxq->size * MVNETA_DESC_ALIGNED_SIZE, + rxq->size); + return -ENOMEM; + } + + BUG_ON(rxq->descs != + PTR_ALIGN(rxq->descs, MVNETA_CPU_D_CACHE_LINE_SIZE)); + + rxq->last_desc = rxq->size - 1; + + /* Set Rx descriptors queue starting address */ + mvreg_write(pp, MVNETA_RXQ_BASE_ADDR_REG(rxq->id), rxq->descs_phys); + mvreg_write(pp, MVNETA_RXQ_SIZE_REG(rxq->id), rxq->size); + + /* Set Offset */ + mvneta_rxq_offset_set(pp, rxq, NET_SKB_PAD); + + /* Set coalescing pkts and time */ + mvneta_rx_pkts_coal_set(pp, rxq, rxq->pkts_coal); + mvneta_rx_time_coal_set(pp, rxq, rxq->time_coal); + + /* Fill RXQ with buffers from RX pool */ + mvneta_rxq_buf_size_set(pp, rxq, MVNETA_RX_BUF_SIZE(pp->pkt_size)); + mvneta_rxq_bm_disable(pp, rxq); + mvneta_rxq_fill(pp, rxq, rxq->size); + + return 0; +} + +/* Cleanup Rx queue */ +static void mvneta_rxq_deinit(struct mvneta_port *pp, + struct mvneta_rx_queue *rxq) +{ + mvneta_rxq_drop_pkts(pp, rxq); + + if (rxq->descs) + dma_free_coherent(pp->dev->dev.parent, + rxq->size * MVNETA_DESC_ALIGNED_SIZE, + rxq->descs, + rxq->descs_phys); + + rxq->descs = NULL; + rxq->last_desc = 0; + rxq->next_desc_to_proc = 0; + rxq->descs_phys = 0; +} + +/* Create and initialize a tx queue */ +static int mvneta_txq_init(struct mvneta_port *pp, + struct mvneta_tx_queue *txq) +{ + txq->size = pp->tx_ring_size; + + /* Allocate memory for TX descriptors */ + txq->descs = dma_alloc_coherent(pp->dev->dev.parent, + txq->size * MVNETA_DESC_ALIGNED_SIZE, + &txq->descs_phys, GFP_KERNEL); + if (txq->descs == NULL) { + netdev_err(pp->dev, + "txQ=%d: Can't allocate %d bytes for %d TX descr\n", + txq->id, txq->size * MVNETA_DESC_ALIGNED_SIZE, + txq->size); + return -ENOMEM; + } + + /* Make sure descriptor address is cache line size aligned */ + BUG_ON(txq->descs != + PTR_ALIGN(txq->descs, MVNETA_CPU_D_CACHE_LINE_SIZE)); + + txq->last_desc = txq->size - 1; + + /* Set maximum bandwidth for enabled TXQs */ + mvreg_write(pp, MVETH_TXQ_TOKEN_CFG_REG(txq->id), 0x03ffffff); + mvreg_write(pp, MVETH_TXQ_TOKEN_COUNT_REG(txq->id), 0x3fffffff); + + /* Set Tx descriptors queue starting address */ + mvreg_write(pp, MVNETA_TXQ_BASE_ADDR_REG(txq->id), txq->descs_phys); + mvreg_write(pp, MVNETA_TXQ_SIZE_REG(txq->id), txq->size); + + txq->tx_skb = kmalloc(txq->size * sizeof(*txq->tx_skb), GFP_KERNEL); + if (txq->tx_skb == NULL) { + dma_free_coherent(pp->dev->dev.parent, + txq->size * MVNETA_DESC_ALIGNED_SIZE, + txq->descs, txq->descs_phys); + return -ENOMEM; + } + mvneta_tx_done_pkts_coal_set(pp, txq, txq->done_pkts_coal); + + return 0; +} + +/* Free allocated resources when mvneta_txq_init() fails to allocate memory*/ +static void mvneta_txq_deinit(struct mvneta_port *pp, + struct mvneta_tx_queue *txq) +{ + kfree(txq->tx_skb); + + if (txq->descs) + dma_free_coherent(pp->dev->dev.parent, + txq->size * MVNETA_DESC_ALIGNED_SIZE, + txq->descs, txq->descs_phys); + + txq->descs = NULL; + txq->last_desc = 0; + txq->next_desc_to_proc = 0; + txq->descs_phys = 0; + + /* Set minimum bandwidth for disabled TXQs */ + mvreg_write(pp, MVETH_TXQ_TOKEN_CFG_REG(txq->id), 0); + mvreg_write(pp, MVETH_TXQ_TOKEN_COUNT_REG(txq->id), 0); + + /* Set Tx descriptors queue starting address and size */ + mvreg_write(pp, MVNETA_TXQ_BASE_ADDR_REG(txq->id), 0); + mvreg_write(pp, MVNETA_TXQ_SIZE_REG(txq->id), 0); +} + +/* Cleanup all Tx queues */ +static void mvneta_cleanup_txqs(struct mvneta_port *pp) +{ + int queue; + + for (queue = 0; queue < txq_number; queue++) + mvneta_txq_deinit(pp, &pp->txqs[queue]); +} + +/* Cleanup all Rx queues */ +static void mvneta_cleanup_rxqs(struct mvneta_port *pp) +{ + int queue; + + for (queue = 0; queue < rxq_number; queue++) + mvneta_rxq_deinit(pp, &pp->rxqs[queue]); +} + + +/* Init all Rx queues */ +static int mvneta_setup_rxqs(struct mvneta_port *pp) +{ + int queue; + + for (queue = 0; queue < rxq_number; queue++) { + int err = mvneta_rxq_init(pp, &pp->rxqs[queue]); + if (err) { + netdev_err(pp->dev, "%s: can't create rxq=%d\n", + __func__, queue); + mvneta_cleanup_rxqs(pp); + return err; + } + } + + return 0; +} + +/* Init all tx queues */ +static int mvneta_setup_txqs(struct mvneta_port *pp) +{ + int queue; + + for (queue = 0; queue < txq_number; queue++) { + int err = mvneta_txq_init(pp, &pp->txqs[queue]); + if (err) { + netdev_err(pp->dev, "%s: can't create txq=%d\n", + __func__, queue); + mvneta_cleanup_txqs(pp); + return err; + } + } + + return 0; +} + +static void mvneta_start_dev(struct mvneta_port *pp) +{ + mvneta_max_rx_size_set(pp, pp->pkt_size); + mvneta_txq_max_tx_size_set(pp, pp->pkt_size); + + /* start the Rx/Tx activity */ + mvneta_port_enable(pp); + + /* Enable polling on the port */ + napi_enable(&pp->napi); + + /* Unmask interrupts */ + mvreg_write(pp, MVNETA_INTR_NEW_MASK, + MVNETA_RX_INTR_MASK(rxq_number)); + + phy_start(pp->phy_dev); + netif_tx_start_all_queues(pp->dev); +} + +static void mvneta_stop_dev(struct mvneta_port *pp) +{ + phy_stop(pp->phy_dev); + + napi_disable(&pp->napi); + + netif_carrier_off(pp->dev); + + mvneta_port_down(pp); + netif_tx_stop_all_queues(pp->dev); + + /* Stop the port activity */ + mvneta_port_disable(pp); + + /* Clear all ethernet port interrupts */ + mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0); + mvreg_write(pp, MVNETA_INTR_OLD_CAUSE, 0); + + /* Mask all ethernet port interrupts */ + mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0); + mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0); + mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0); + + mvneta_tx_reset(pp); + mvneta_rx_reset(pp); +} + +/* tx timeout callback - display a message and stop/start the network device */ +static void mvneta_tx_timeout(struct net_device *dev) +{ + struct mvneta_port *pp = netdev_priv(dev); + + netdev_info(dev, "tx timeout\n"); + mvneta_stop_dev(pp); + mvneta_start_dev(pp); +} + +/* Return positive if MTU is valid */ +static int mvneta_check_mtu_valid(struct net_device *dev, int mtu) +{ + if (mtu < 68) { + netdev_err(dev, "cannot change mtu to less than 68\n"); + return -EINVAL; + } + + /* 9676 == 9700 - 20 and rounding to 8 */ + if (mtu > 9676) { + netdev_info(dev, "Illegal MTU value %d, round to 9676\n", mtu); + mtu = 9676; + } + + if (!IS_ALIGNED(MVNETA_RX_PKT_SIZE(mtu), 8)) { + netdev_info(dev, "Illegal MTU value %d, rounding to %d\n", + mtu, ALIGN(MVNETA_RX_PKT_SIZE(mtu), 8)); + mtu = ALIGN(MVNETA_RX_PKT_SIZE(mtu), 8); + } + + return mtu; +} + +/* Change the device mtu */ +static int mvneta_change_mtu(struct net_device *dev, int mtu) +{ + struct mvneta_port *pp = netdev_priv(dev); + int ret; + + mtu = mvneta_check_mtu_valid(dev, mtu); + if (mtu < 0) + return -EINVAL; + + dev->mtu = mtu; + + if (!netif_running(dev)) + return 0; + + /* + * The interface is running, so we have to force a + * reallocation of the RXQs + */ + mvneta_stop_dev(pp); + + mvneta_cleanup_txqs(pp); + mvneta_cleanup_rxqs(pp); + + pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu); + + ret = mvneta_setup_rxqs(pp); + if (ret) { + netdev_err(pp->dev, "unable to setup rxqs after MTU change\n"); + return ret; + } + + mvneta_setup_txqs(pp); + + mvneta_start_dev(pp); + mvneta_port_up(pp); + + return 0; +} + +/* Handle setting mac address */ +static int mvneta_set_mac_addr(struct net_device *dev, void *addr) +{ + struct mvneta_port *pp = netdev_priv(dev); + u8 *mac = addr + 2; + int i; + + if (netif_running(dev)) + return -EBUSY; + + /* Remove previous address table entry */ + mvneta_mac_addr_set(pp, dev->dev_addr, -1); + + /* Set new addr in hw */ + mvneta_mac_addr_set(pp, mac, rxq_def); + + /* Set addr in the device */ + for (i = 0; i < ETH_ALEN; i++) + dev->dev_addr[i] = mac[i]; + + return 0; +} + +static void mvneta_adjust_link(struct net_device *ndev) +{ + struct mvneta_port *pp = netdev_priv(ndev); + struct phy_device *phydev = pp->phy_dev; + int status_change = 0; + + if (phydev->link) { + if ((pp->speed != phydev->speed) || + (pp->duplex != phydev->duplex)) { + u32 val; + + val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); + val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED | + MVNETA_GMAC_CONFIG_GMII_SPEED | + MVNETA_GMAC_CONFIG_FULL_DUPLEX); + + if (phydev->duplex) + val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; + + if (phydev->speed == SPEED_1000) + val |= MVNETA_GMAC_CONFIG_GMII_SPEED; + else + val |= MVNETA_GMAC_CONFIG_MII_SPEED; + + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + + pp->duplex = phydev->duplex; + pp->speed = phydev->speed; + } + } + + if (phydev->link != pp->link) { + if (!phydev->link) { + pp->duplex = -1; + pp->speed = 0; + } + + pp->link = phydev->link; + status_change = 1; + } + + if (status_change) { + if (phydev->link) { + u32 val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); + val |= (MVNETA_GMAC_FORCE_LINK_PASS | + MVNETA_GMAC_FORCE_LINK_DOWN); + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + mvneta_port_up(pp); + netdev_info(pp->dev, "link up\n"); + } else { + mvneta_port_down(pp); + netdev_info(pp->dev, "link down\n"); + } + } +} + +static int mvneta_mdio_probe(struct mvneta_port *pp) +{ + struct phy_device *phy_dev; + + phy_dev = of_phy_connect(pp->dev, pp->phy_node, mvneta_adjust_link, 0, + pp->phy_interface); + if (!phy_dev) { + netdev_err(pp->dev, "could not find the PHY\n"); + return -ENODEV; + } + + phy_dev->supported &= PHY_GBIT_FEATURES; + phy_dev->advertising = phy_dev->supported; + + pp->phy_dev = phy_dev; + pp->link = 0; + pp->duplex = 0; + pp->speed = 0; + + return 0; +} + +static void mvneta_mdio_remove(struct mvneta_port *pp) +{ + phy_disconnect(pp->phy_dev); + pp->phy_dev = NULL; +} + +static int mvneta_open(struct net_device *dev) +{ + struct mvneta_port *pp = netdev_priv(dev); + int ret; + + mvneta_mac_addr_set(pp, dev->dev_addr, rxq_def); + + pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu); + + ret = mvneta_setup_rxqs(pp); + if (ret) + return ret; + + ret = mvneta_setup_txqs(pp); + if (ret) + goto err_cleanup_rxqs; + + /* Connect to port interrupt line */ + ret = request_irq(pp->dev->irq, mvneta_isr, 0, + MVNETA_DRIVER_NAME, pp); + if (ret) { + netdev_err(pp->dev, "cannot request irq %d\n", pp->dev->irq); + goto err_cleanup_txqs; + } + + /* In default link is down */ + netif_carrier_off(pp->dev); + + ret = mvneta_mdio_probe(pp); + if (ret < 0) { + netdev_err(dev, "cannot probe MDIO bus\n"); + goto err_free_irq; + } + + mvneta_start_dev(pp); + + return 0; + +err_free_irq: + free_irq(pp->dev->irq, pp); +err_cleanup_txqs: + mvneta_cleanup_txqs(pp); +err_cleanup_rxqs: + mvneta_cleanup_rxqs(pp); + return ret; +} + +/* Stop the port, free port interrupt line */ +static int mvneta_stop(struct net_device *dev) +{ + struct mvneta_port *pp = netdev_priv(dev); + + mvneta_stop_dev(pp); + mvneta_mdio_remove(pp); + free_irq(dev->irq, pp); + mvneta_cleanup_rxqs(pp); + mvneta_cleanup_txqs(pp); + del_timer(&pp->tx_done_timer); + clear_bit(MVNETA_F_TX_DONE_TIMER_BIT, &pp->flags); + + return 0; +} + +/* Ethtool methods */ + +/* Get settings (phy address, speed) for ethtools */ +int mvneta_ethtool_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct mvneta_port *pp = netdev_priv(dev); + + if (!pp->phy_dev) + return -ENODEV; + + return phy_ethtool_gset(pp->phy_dev, cmd); +} + +/* Set settings (phy address, speed) for ethtools */ +int mvneta_ethtool_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct mvneta_port *pp = netdev_priv(dev); + + if (!pp->phy_dev) + return -ENODEV; + + return phy_ethtool_sset(pp->phy_dev, cmd); +} + +/* Set interrupt coalescing for ethtools */ +static int mvneta_ethtool_set_coalesce(struct net_device *dev, + struct ethtool_coalesce *c) +{ + struct mvneta_port *pp = netdev_priv(dev); + int queue; + + for (queue = 0; queue < rxq_number; queue++) { + struct mvneta_rx_queue *rxq = &pp->rxqs[queue]; + rxq->time_coal = c->rx_coalesce_usecs; + rxq->pkts_coal = c->rx_max_coalesced_frames; + mvneta_rx_pkts_coal_set(pp, rxq, rxq->pkts_coal); + mvneta_rx_time_coal_set(pp, rxq, rxq->time_coal); + } + + for (queue = 0; queue < txq_number; queue++) { + struct mvneta_tx_queue *txq = &pp->txqs[queue]; + txq->done_pkts_coal = c->tx_max_coalesced_frames; + mvneta_tx_done_pkts_coal_set(pp, txq, txq->done_pkts_coal); + } + + return 0; +} + +/* get coalescing for ethtools */ +static int mvneta_ethtool_get_coalesce(struct net_device *dev, + struct ethtool_coalesce *c) +{ + struct mvneta_port *pp = netdev_priv(dev); + + c->rx_coalesce_usecs = pp->rxqs[0].time_coal; + c->rx_max_coalesced_frames = pp->rxqs[0].pkts_coal; + + c->tx_max_coalesced_frames = pp->txqs[0].done_pkts_coal; + return 0; +} + + +static void mvneta_ethtool_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *drvinfo) +{ + strlcpy(drvinfo->driver, MVNETA_DRIVER_NAME, + sizeof(drvinfo->driver)); + strlcpy(drvinfo->version, MVNETA_DRIVER_VERSION, + sizeof(drvinfo->version)); + strlcpy(drvinfo->bus_info, dev_name(&dev->dev), + sizeof(drvinfo->bus_info)); +} + + +static void mvneta_ethtool_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) +{ + struct mvneta_port *pp = netdev_priv(netdev); + + ring->rx_max_pending = MVNETA_MAX_RXD; + ring->tx_max_pending = MVNETA_MAX_TXD; + ring->rx_pending = pp->rx_ring_size; + ring->tx_pending = pp->tx_ring_size; +} + +static int mvneta_ethtool_set_ringparam(struct net_device *dev, + struct ethtool_ringparam *ring) +{ + struct mvneta_port *pp = netdev_priv(dev); + + if ((ring->rx_pending == 0) || (ring->tx_pending == 0)) + return -EINVAL; + pp->rx_ring_size = ring->rx_pending < MVNETA_MAX_RXD ? + ring->rx_pending : MVNETA_MAX_RXD; + pp->tx_ring_size = ring->tx_pending < MVNETA_MAX_TXD ? + ring->tx_pending : MVNETA_MAX_TXD; + + if (netif_running(dev)) { + mvneta_stop(dev); + if (mvneta_open(dev)) { + netdev_err(dev, + "error on opening device after ring param change\n"); + return -ENOMEM; + } + } + + return 0; +} + +static const struct net_device_ops mvneta_netdev_ops = { + .ndo_open = mvneta_open, + .ndo_stop = mvneta_stop, + .ndo_start_xmit = mvneta_tx, + .ndo_set_rx_mode = mvneta_set_rx_mode, + .ndo_set_mac_address = mvneta_set_mac_addr, + .ndo_change_mtu = mvneta_change_mtu, + .ndo_tx_timeout = mvneta_tx_timeout, + .ndo_get_stats64 = mvneta_get_stats64, +}; + +const struct ethtool_ops mvneta_eth_tool_ops = { + .get_link = ethtool_op_get_link, + .get_settings = mvneta_ethtool_get_settings, + .set_settings = mvneta_ethtool_set_settings, + .set_coalesce = mvneta_ethtool_set_coalesce, + .get_coalesce = mvneta_ethtool_get_coalesce, + .get_drvinfo = mvneta_ethtool_get_drvinfo, + .get_ringparam = mvneta_ethtool_get_ringparam, + .set_ringparam = mvneta_ethtool_set_ringparam, +}; + +/* Initialize hw */ +static int __devinit mvneta_init(struct mvneta_port *pp, int phy_addr) +{ + int queue; + + /* Disable port */ + mvneta_port_disable(pp); + + /* Set port default values */ + mvneta_defaults_set(pp); + + pp->txqs = kzalloc(txq_number * sizeof(struct mvneta_tx_queue), + GFP_KERNEL); + if (!pp->txqs) + return -ENOMEM; + + /* Initialize TX descriptor rings */ + for (queue = 0; queue < txq_number; queue++) { + struct mvneta_tx_queue *txq = &pp->txqs[queue]; + txq->id = queue; + txq->size = pp->tx_ring_size; + txq->done_pkts_coal = MVNETA_TXDONE_COAL_PKTS; + } + + pp->rxqs = kzalloc(rxq_number * sizeof(struct mvneta_rx_queue), + GFP_KERNEL); + if (!pp->rxqs) { + kfree(pp->txqs); + return -ENOMEM; + } + + /* Create Rx descriptor rings */ + for (queue = 0; queue < rxq_number; queue++) { + struct mvneta_rx_queue *rxq = &pp->rxqs[queue]; + rxq->id = queue; + rxq->size = pp->rx_ring_size; + rxq->pkts_coal = MVNETA_RX_COAL_PKTS; + rxq->time_coal = MVNETA_RX_COAL_USEC; + } + + return 0; +} + +static void __devexit mvneta_deinit(struct mvneta_port *pp) +{ + kfree(pp->txqs); + kfree(pp->rxqs); +} + +/* platform glue : initialize decoding windows */ +static void __devinit +mvneta_conf_mbus_windows(struct mvneta_port *pp, + const struct mbus_dram_target_info *dram) +{ + u32 win_enable; + u32 win_protect; + int i; + + for (i = 0; i < 6; i++) { + mvreg_write(pp, MVNETA_WIN_BASE(i), 0); + mvreg_write(pp, MVNETA_WIN_SIZE(i), 0); + + if (i < 4) + mvreg_write(pp, MVNETA_WIN_REMAP(i), 0); + } + + win_enable = 0x3f; + win_protect = 0; + + for (i = 0; i < dram->num_cs; i++) { + const struct mbus_dram_window *cs = dram->cs + i; + mvreg_write(pp, MVNETA_WIN_BASE(i), (cs->base & 0xffff0000) | + (cs->mbus_attr << 8) | dram->mbus_dram_target_id); + + mvreg_write(pp, MVNETA_WIN_SIZE(i), + (cs->size - 1) & 0xffff0000); + + win_enable &= ~(1 << i); + win_protect |= 3 << (2 * i); + } + + mvreg_write(pp, MVNETA_BASE_ADDR_ENABLE, win_enable); +} + +/* Power up the port */ +static void __devinit mvneta_port_power_up(struct mvneta_port *pp, int phy_mode) +{ + u32 val; + + /* MAC Cause register should be cleared */ + mvreg_write(pp, MVNETA_UNIT_INTR_CAUSE, 0); + + if (phy_mode == PHY_INTERFACE_MODE_SGMII) + mvneta_port_sgmii_config(pp); + + mvneta_gmac_rgmii_set(pp, 1); + + /* Cancel Port Reset */ + val = mvreg_read(pp, MVNETA_GMAC_CTRL_2); + val &= ~MVNETA_GMAC2_PORT_RESET; + mvreg_write(pp, MVNETA_GMAC_CTRL_2, val); + + while ((mvreg_read(pp, MVNETA_GMAC_CTRL_2) & + MVNETA_GMAC2_PORT_RESET) != 0) + continue; +} + +/* Device initialization routine */ +static int __devinit mvneta_probe(struct platform_device *pdev) +{ + const struct mbus_dram_target_info *dram_target_info; + struct device_node *dn = pdev->dev.of_node; + struct device_node *phy_node; + u32 phy_addr, clk_rate_hz; + struct mvneta_port *pp; + struct net_device *dev; + const char *mac_addr; + int phy_mode; + int err; + + /* + * Our multiqueue support is not complete, so for now, only + * allow the usage of the first RX queue + */ + if (rxq_def != 0) { + dev_err(&pdev->dev, "Invalid rxq_def argument: %d\n", rxq_def); + return -EINVAL; + } + + dev = alloc_etherdev_mq(sizeof(struct mvneta_port), 8); + if (!dev) + return -ENOMEM; + + dev->irq = irq_of_parse_and_map(dn, 0); + if (dev->irq == 0) { + err = -EINVAL; + goto err_free_netdev; + } + + phy_node = of_parse_phandle(dn, "phy", 0); + if (!phy_node) { + dev_err(&pdev->dev, "no associated PHY\n"); + err = -ENODEV; + goto err_free_irq; + } + + phy_mode = of_get_phy_mode(dn); + if (phy_mode < 0) { + dev_err(&pdev->dev, "incorrect phy-mode\n"); + err = -EINVAL; + goto err_free_irq; + } + + if (of_property_read_u32(dn, "clock-frequency", &clk_rate_hz) != 0) { + dev_err(&pdev->dev, "could not read clock-frequency\n"); + err = -EINVAL; + goto err_free_irq; + } + + mac_addr = of_get_mac_address(dn); + + if (!mac_addr || !is_valid_ether_addr(mac_addr)) + eth_hw_addr_random(dev); + else + memcpy(dev->dev_addr, mac_addr, ETH_ALEN); + + dev->tx_queue_len = MVNETA_MAX_TXD; + dev->watchdog_timeo = 5 * HZ; + dev->netdev_ops = &mvneta_netdev_ops; + + SET_ETHTOOL_OPS(dev, &mvneta_eth_tool_ops); + + pp = netdev_priv(dev); + + pp->tx_done_timer.function = mvneta_tx_done_timer_callback; + init_timer(&pp->tx_done_timer); + clear_bit(MVNETA_F_TX_DONE_TIMER_BIT, &pp->flags); + + pp->weight = MVNETA_RX_POLL_WEIGHT; + pp->clk_rate_hz = clk_rate_hz; + pp->phy_node = phy_node; + pp->phy_interface = phy_mode; + + pp->base = of_iomap(dn, 0); + if (pp->base == NULL) { + err = -ENOMEM; + goto err_free_irq; + } + + pp->tx_done_timer.data = (unsigned long)dev; + + pp->tx_ring_size = MVNETA_MAX_TXD; + pp->rx_ring_size = MVNETA_MAX_RXD; + + pp->dev = dev; + SET_NETDEV_DEV(dev, &pdev->dev); + + err = mvneta_init(pp, phy_addr); + if (err < 0) { + dev_err(&pdev->dev, "can't init eth hal\n"); + goto err_unmap; + } + mvneta_port_power_up(pp, phy_mode); + + dram_target_info = mv_mbus_dram_info(); + if (dram_target_info) + mvneta_conf_mbus_windows(pp, dram_target_info); + + netif_napi_add(dev, &pp->napi, mvneta_poll, pp->weight); + + err = register_netdev(dev); + if (err < 0) { + dev_err(&pdev->dev, "failed to register\n"); + goto err_deinit; + } + + dev->features = NETIF_F_SG | NETIF_F_IP_CSUM; + dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM; + dev->priv_flags |= IFF_UNICAST_FLT; + + netdev_info(dev, "mac: %pM\n", dev->dev_addr); + + platform_set_drvdata(pdev, pp->dev); + + return 0; + +err_deinit: + mvneta_deinit(pp); +err_unmap: + iounmap(pp->base); +err_free_irq: + irq_dispose_mapping(dev->irq); +err_free_netdev: + free_netdev(dev); + return err; +} + +/* Device removal routine */ +static int __devexit mvneta_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct mvneta_port *pp = netdev_priv(dev); + + unregister_netdev(dev); + mvneta_deinit(pp); + iounmap(pp->base); + irq_dispose_mapping(dev->irq); + free_netdev(dev); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id mvneta_match[] = { + { .compatible = "marvell,armada-370-neta" }, + { } +}; +MODULE_DEVICE_TABLE(of, mvneta_match); + +static struct platform_driver mvneta_driver = { + .probe = mvneta_probe, + .remove = __devexit_p(mvneta_remove), + .driver = { + .name = MVNETA_DRIVER_NAME, + .of_match_table = mvneta_match, + }, +}; + +module_platform_driver(mvneta_driver); + +MODULE_DESCRIPTION("Marvell NETA Ethernet Driver - www.marvell.com"); +MODULE_AUTHOR("Rami Rosen , Thomas Petazzoni "); +MODULE_LICENSE("GPL"); + +module_param(rxq_number, int, S_IRUGO); +module_param(txq_number, int, S_IRUGO); + +module_param(rxq_def, int, S_IRUGO); +module_param(txq_def, int, S_IRUGO); -- cgit v1.2.3 From 2693ac69880a33d4d9df6f128415b65e745f00ba Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Tue, 13 Nov 2012 11:33:40 +0100 Subject: i2c: s3c2410: Add support for pinctrl This patch adds support for pin configuration using pinctrl subsystem to the i2c-s3c2410 driver. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Signed-off-by: Wolfram Sang --- .../devicetree/bindings/i2c/samsung-i2c.txt | 20 ++++++++++++++++---- drivers/i2c/busses/i2c-s3c2410.c | 12 ++++++++---- 2 files changed, 24 insertions(+), 8 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/i2c/samsung-i2c.txt b/Documentation/devicetree/bindings/i2c/samsung-i2c.txt index b6cb5a12c672..e9611ace8792 100644 --- a/Documentation/devicetree/bindings/i2c/samsung-i2c.txt +++ b/Documentation/devicetree/bindings/i2c/samsung-i2c.txt @@ -13,11 +13,17 @@ Required properties: - interrupts: interrupt number to the cpu. - samsung,i2c-sda-delay: Delay (in ns) applied to data line (SDA) edges. +Required for all cases except "samsung,s3c2440-hdmiphy-i2c": + - Samsung GPIO variant (deprecated): + - gpios: The order of the gpios should be the following: . + The gpio specifier depends on the gpio controller. Required in all + cases except for "samsung,s3c2440-hdmiphy-i2c" whose input/output + lines are permanently wired to the respective clienta + - Pinctrl variant (preferred, if available): + - pinctrl-0: Pin control group to be used for this controller. + - pinctrl-names: Should contain only one value - "default". + Optional properties: - - gpios: The order of the gpios should be the following: . - The gpio specifier depends on the gpio controller. Required in all - cases except for "samsung,s3c2440-hdmiphy-i2c" whose input/output - lines are permanently wired to the respective client - samsung,i2c-slave-addr: Slave address in multi-master enviroment. If not specified, default value is 0. - samsung,i2c-max-bus-freq: Desired frequency in Hz of the bus. If not @@ -31,8 +37,14 @@ Example: interrupts = <345>; samsung,i2c-sda-delay = <100>; samsung,i2c-max-bus-freq = <100000>; + /* Samsung GPIO variant begins here */ gpios = <&gpd1 2 0 /* SDA */ &gpd1 3 0 /* SCL */>; + /* Samsung GPIO variant ends here */ + /* Pinctrl variant begins here */ + pinctrl-0 = <&i2c3_bus>; + pinctrl-names = "default"; + /* Pinctrl variant ends here */ #address-cells = <1>; #size-cells = <0>; diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index f82d11f9b8f2..40e2d40bbdb5 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -38,6 +38,7 @@ #include #include #include +#include #include @@ -82,6 +83,7 @@ struct s3c24xx_i2c { struct s3c2410_platform_i2c *pdata; int gpios[2]; + struct pinctrl *pctrl; #ifdef CONFIG_CPU_FREQ struct notifier_block freq_transition; #endif @@ -860,9 +862,8 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) if (pdata->cfg_gpio) pdata->cfg_gpio(to_platform_device(i2c->dev)); - else - if (s3c24xx_i2c_parse_dt_gpio(i2c)) - return -EINVAL; + else if (IS_ERR(i2c->pctrl) && s3c24xx_i2c_parse_dt_gpio(i2c)) + return -EINVAL; /* write slave address */ @@ -1003,6 +1004,8 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) i2c->adap.algo_data = i2c; i2c->adap.dev.parent = &pdev->dev; + i2c->pctrl = devm_pinctrl_get_select_default(i2c->dev); + /* initialise the i2c controller */ ret = s3c24xx_i2c_init(i2c); @@ -1092,7 +1095,8 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev) clk_disable_unprepare(i2c->clk); clk_put(i2c->clk); - s3c24xx_i2c_dt_gpio_free(i2c); + if (pdev->dev.of_node && IS_ERR(i2c->pctrl)) + s3c24xx_i2c_dt_gpio_free(i2c); return 0; } -- cgit v1.2.3 From e8001632816e600ced7d9d4790930fd87935c654 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 16 Nov 2012 16:03:53 +0800 Subject: serial: mxs-auart: add the DMA support for mx28 Only we meet the following conditions, we can enable the DMA support for auart: (1) We enable the DMA support in the dts file, such as arch/arm/boot/dts/imx28.dtsi. (2) We enable the hardware flow control. (3) We use the mx28, not the mx23. Due to hardware bug(see errata: 2836), we can not add the DMA support to mx23. Signed-off-by: Huang Shijie Signed-off-by: Greg Kroah-Hartman --- .../bindings/tty/serial/fsl-mxs-auart.txt | 8 + drivers/tty/serial/mxs-auart.c | 322 ++++++++++++++++++++- 2 files changed, 325 insertions(+), 5 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/tty/serial/fsl-mxs-auart.txt b/Documentation/devicetree/bindings/tty/serial/fsl-mxs-auart.txt index 2ee903fad25c..273a8d5b3300 100644 --- a/Documentation/devicetree/bindings/tty/serial/fsl-mxs-auart.txt +++ b/Documentation/devicetree/bindings/tty/serial/fsl-mxs-auart.txt @@ -6,11 +6,19 @@ Required properties: - reg : Address and length of the register set for the device - interrupts : Should contain the auart interrupt numbers +Optional properties: +- fsl,auart-dma-channel : The DMA channels, the first is for RX, the other + is for TX. If you add this property, it also means that you + will enable the DMA support for the auart. + Note: due to the hardware bug in imx23(see errata : 2836), + only the imx28 can enable the DMA support for the auart. + Example: auart0: serial@8006a000 { compatible = "fsl,imx28-auart", "fsl,imx23-auart"; reg = <0x8006a000 0x2000>; interrupts = <112 70 71>; + fsl,auart-dma-channel = <8 9>; }; Note: Each auart port should have an alias correctly numbered in "aliases" diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 06d72713fb9c..d5b9e3039013 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include @@ -71,6 +73,15 @@ #define AUART_CTRL0_SFTRST (1 << 31) #define AUART_CTRL0_CLKGATE (1 << 30) +#define AUART_CTRL0_RXTO_ENABLE (1 << 27) +#define AUART_CTRL0_RXTIMEOUT(v) (((v) & 0x7ff) << 16) +#define AUART_CTRL0_XFER_COUNT(v) ((v) & 0xffff) + +#define AUART_CTRL1_XFER_COUNT(v) ((v) & 0xffff) + +#define AUART_CTRL2_DMAONERR (1 << 26) +#define AUART_CTRL2_TXDMAE (1 << 25) +#define AUART_CTRL2_RXDMAE (1 << 24) #define AUART_CTRL2_CTSEN (1 << 15) #define AUART_CTRL2_RTSEN (1 << 14) @@ -111,6 +122,7 @@ #define AUART_STAT_BERR (1 << 18) #define AUART_STAT_PERR (1 << 17) #define AUART_STAT_FERR (1 << 16) +#define AUART_STAT_RXCOUNT_MASK 0xffff static struct uart_driver auart_driver; @@ -122,7 +134,11 @@ enum mxs_auart_type { struct mxs_auart_port { struct uart_port port; - unsigned int flags; +#define MXS_AUART_DMA_CONFIG 0x1 +#define MXS_AUART_DMA_ENABLED 0x2 +#define MXS_AUART_DMA_TX_SYNC 2 /* bit 2 */ +#define MXS_AUART_DMA_RX_READY 3 /* bit 3 */ + unsigned long flags; unsigned int ctrl; enum mxs_auart_type devtype; @@ -130,6 +146,20 @@ struct mxs_auart_port { struct clk *clk; struct device *dev; + + /* for DMA */ + struct mxs_dma_data dma_data; + int dma_channel_rx, dma_channel_tx; + int dma_irq_rx, dma_irq_tx; + int dma_channel; + + struct scatterlist tx_sgl; + struct dma_chan *tx_dma_chan; + void *tx_dma_buf; + + struct scatterlist rx_sgl; + struct dma_chan *rx_dma_chan; + void *rx_dma_buf; }; static struct platform_device_id mxs_auart_devtype[] = { @@ -155,14 +185,107 @@ static inline int is_imx28_auart(struct mxs_auart_port *s) return s->devtype == IMX28_AUART; } +static inline bool auart_dma_enabled(struct mxs_auart_port *s) +{ + return s->flags & MXS_AUART_DMA_ENABLED; +} + static void mxs_auart_stop_tx(struct uart_port *u); #define to_auart_port(u) container_of(u, struct mxs_auart_port, port) -static inline void mxs_auart_tx_chars(struct mxs_auart_port *s) +static void mxs_auart_tx_chars(struct mxs_auart_port *s); + +static void dma_tx_callback(void *param) { + struct mxs_auart_port *s = param; struct circ_buf *xmit = &s->port.state->xmit; + dma_unmap_sg(s->dev, &s->tx_sgl, 1, DMA_TO_DEVICE); + + /* clear the bit used to serialize the DMA tx. */ + clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags); + smp_mb__after_clear_bit(); + + /* wake up the possible processes. */ + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&s->port); + + mxs_auart_tx_chars(s); +} + +static int mxs_auart_dma_tx(struct mxs_auart_port *s, int size) +{ + struct dma_async_tx_descriptor *desc; + struct scatterlist *sgl = &s->tx_sgl; + struct dma_chan *channel = s->tx_dma_chan; + u32 pio; + + /* [1] : send PIO. Note, the first pio word is CTRL1. */ + pio = AUART_CTRL1_XFER_COUNT(size); + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)&pio, + 1, DMA_TRANS_NONE, 0); + if (!desc) { + dev_err(s->dev, "step 1 error\n"); + return -EINVAL; + } + + /* [2] : set DMA buffer. */ + sg_init_one(sgl, s->tx_dma_buf, size); + dma_map_sg(s->dev, sgl, 1, DMA_TO_DEVICE); + desc = dmaengine_prep_slave_sg(channel, sgl, + 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(s->dev, "step 2 error\n"); + return -EINVAL; + } + + /* [3] : submit the DMA */ + desc->callback = dma_tx_callback; + desc->callback_param = s; + dmaengine_submit(desc); + dma_async_issue_pending(channel); + return 0; +} + +static void mxs_auart_tx_chars(struct mxs_auart_port *s) +{ + struct circ_buf *xmit = &s->port.state->xmit; + + if (auart_dma_enabled(s)) { + int i = 0; + int size; + void *buffer = s->tx_dma_buf; + + if (test_and_set_bit(MXS_AUART_DMA_TX_SYNC, &s->flags)) + return; + + while (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) { + size = min_t(u32, UART_XMIT_SIZE - i, + CIRC_CNT_TO_END(xmit->head, + xmit->tail, + UART_XMIT_SIZE)); + memcpy(buffer + i, xmit->buf + xmit->tail, size); + xmit->tail = (xmit->tail + size) & (UART_XMIT_SIZE - 1); + + i += size; + if (i >= UART_XMIT_SIZE) + break; + } + + if (uart_tx_stopped(&s->port)) + mxs_auart_stop_tx(&s->port); + + if (i) { + mxs_auart_dma_tx(s, i); + } else { + clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags); + smp_mb__after_clear_bit(); + } + return; + } + + while (!(readl(s->port.membase + AUART_STAT) & AUART_STAT_TXFF)) { if (s->port.x_char) { @@ -316,10 +439,157 @@ static u32 mxs_auart_get_mctrl(struct uart_port *u) return mctrl; } +static bool mxs_auart_dma_filter(struct dma_chan *chan, void *param) +{ + struct mxs_auart_port *s = param; + + if (!mxs_dma_is_apbx(chan)) + return false; + + if (s->dma_channel == chan->chan_id) { + chan->private = &s->dma_data; + return true; + } + return false; +} + +static int mxs_auart_dma_prep_rx(struct mxs_auart_port *s); +static void dma_rx_callback(void *arg) +{ + struct mxs_auart_port *s = (struct mxs_auart_port *) arg; + struct tty_struct *tty = s->port.state->port.tty; + int count; + u32 stat; + + stat = readl(s->port.membase + AUART_STAT); + stat &= ~(AUART_STAT_OERR | AUART_STAT_BERR | + AUART_STAT_PERR | AUART_STAT_FERR); + + count = stat & AUART_STAT_RXCOUNT_MASK; + tty_insert_flip_string(tty, s->rx_dma_buf, count); + + writel(stat, s->port.membase + AUART_STAT); + tty_flip_buffer_push(tty); + + /* start the next DMA for RX. */ + mxs_auart_dma_prep_rx(s); +} + +static int mxs_auart_dma_prep_rx(struct mxs_auart_port *s) +{ + struct dma_async_tx_descriptor *desc; + struct scatterlist *sgl = &s->rx_sgl; + struct dma_chan *channel = s->rx_dma_chan; + u32 pio[1]; + + /* [1] : send PIO */ + pio[0] = AUART_CTRL0_RXTO_ENABLE + | AUART_CTRL0_RXTIMEOUT(0x80) + | AUART_CTRL0_XFER_COUNT(UART_XMIT_SIZE); + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, + 1, DMA_TRANS_NONE, 0); + if (!desc) { + dev_err(s->dev, "step 1 error\n"); + return -EINVAL; + } + + /* [2] : send DMA request */ + sg_init_one(sgl, s->rx_dma_buf, UART_XMIT_SIZE); + dma_map_sg(s->dev, sgl, 1, DMA_FROM_DEVICE); + desc = dmaengine_prep_slave_sg(channel, sgl, 1, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(s->dev, "step 2 error\n"); + return -1; + } + + /* [3] : submit the DMA, but do not issue it. */ + desc->callback = dma_rx_callback; + desc->callback_param = s; + dmaengine_submit(desc); + dma_async_issue_pending(channel); + return 0; +} + +static void mxs_auart_dma_exit_channel(struct mxs_auart_port *s) +{ + if (s->tx_dma_chan) { + dma_release_channel(s->tx_dma_chan); + s->tx_dma_chan = NULL; + } + if (s->rx_dma_chan) { + dma_release_channel(s->rx_dma_chan); + s->rx_dma_chan = NULL; + } + + kfree(s->tx_dma_buf); + kfree(s->rx_dma_buf); + s->tx_dma_buf = NULL; + s->rx_dma_buf = NULL; +} + +static void mxs_auart_dma_exit(struct mxs_auart_port *s) +{ + + writel(AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE | AUART_CTRL2_DMAONERR, + s->port.membase + AUART_CTRL2_CLR); + + mxs_auart_dma_exit_channel(s); + s->flags &= ~MXS_AUART_DMA_ENABLED; + clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags); + clear_bit(MXS_AUART_DMA_RX_READY, &s->flags); +} + +static int mxs_auart_dma_init(struct mxs_auart_port *s) +{ + dma_cap_mask_t mask; + + if (auart_dma_enabled(s)) + return 0; + + /* We do not get the right DMA channels. */ + if (s->dma_channel_rx == -1 || s->dma_channel_rx == -1) + return -EINVAL; + + /* init for RX */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + s->dma_channel = s->dma_channel_rx; + s->dma_data.chan_irq = s->dma_irq_rx; + s->rx_dma_chan = dma_request_channel(mask, mxs_auart_dma_filter, s); + if (!s->rx_dma_chan) + goto err_out; + s->rx_dma_buf = kzalloc(UART_XMIT_SIZE, GFP_KERNEL | GFP_DMA); + if (!s->rx_dma_buf) + goto err_out; + + /* init for TX */ + s->dma_channel = s->dma_channel_tx; + s->dma_data.chan_irq = s->dma_irq_tx; + s->tx_dma_chan = dma_request_channel(mask, mxs_auart_dma_filter, s); + if (!s->tx_dma_chan) + goto err_out; + s->tx_dma_buf = kzalloc(UART_XMIT_SIZE, GFP_KERNEL | GFP_DMA); + if (!s->tx_dma_buf) + goto err_out; + + /* set the flags */ + s->flags |= MXS_AUART_DMA_ENABLED; + dev_dbg(s->dev, "enabled the DMA support."); + + return 0; + +err_out: + mxs_auart_dma_exit_channel(s); + return -EINVAL; + +} + static void mxs_auart_settermios(struct uart_port *u, struct ktermios *termios, struct ktermios *old) { + struct mxs_auart_port *s = to_auart_port(u); u32 bm, ctrl, ctrl2, div; unsigned int cflag, baud; @@ -391,10 +661,23 @@ static void mxs_auart_settermios(struct uart_port *u, ctrl |= AUART_LINECTRL_STP2; /* figure out the hardware flow control settings */ - if (cflag & CRTSCTS) + if (cflag & CRTSCTS) { + /* + * The DMA has a bug(see errata:2836) in mx23. + * So we can not implement the DMA for auart in mx23, + * we can only implement the DMA support for auart + * in mx28. + */ + if (is_imx28_auart(s) && (s->flags & MXS_AUART_DMA_CONFIG)) { + if (!mxs_auart_dma_init(s)) + /* enable DMA tranfer */ + ctrl2 |= AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE + | AUART_CTRL2_DMAONERR; + } ctrl2 |= AUART_CTRL2_CTSEN | AUART_CTRL2_RTSEN; - else + } else { ctrl2 &= ~(AUART_CTRL2_CTSEN | AUART_CTRL2_RTSEN); + } /* set baud rate */ baud = uart_get_baud_rate(u, termios, old, 0, u->uartclk); @@ -406,6 +689,18 @@ static void mxs_auart_settermios(struct uart_port *u, writel(ctrl2, u->membase + AUART_CTRL2); uart_update_timeout(u, termios->c_cflag, baud); + + /* prepare for the DMA RX. */ + if (auart_dma_enabled(s) && + !test_and_set_bit(MXS_AUART_DMA_RX_READY, &s->flags)) { + if (!mxs_auart_dma_prep_rx(s)) { + /* Disable the normal RX interrupt. */ + writel(AUART_INTR_RXIEN, u->membase + AUART_INTR_CLR); + } else { + mxs_auart_dma_exit(s); + dev_err(s->dev, "We can not start up the DMA.\n"); + } + } } static irqreturn_t mxs_auart_irq_handle(int irq, void *context) @@ -484,6 +779,9 @@ static void mxs_auart_shutdown(struct uart_port *u) { struct mxs_auart_port *s = to_auart_port(u); + if (auart_dma_enabled(s)) + mxs_auart_dma_exit(s); + writel(AUART_CTRL2_UARTEN, u->membase + AUART_CTRL2_CLR); writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN, @@ -717,6 +1015,7 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s, struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + u32 dma_channel[2]; int ret; if (!np) @@ -730,6 +1029,20 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s, } s->port.line = ret; + s->dma_irq_rx = platform_get_irq(pdev, 1); + s->dma_irq_tx = platform_get_irq(pdev, 2); + + ret = of_property_read_u32_array(np, "fsl,auart-dma-channel", + dma_channel, 2); + if (ret == 0) { + s->dma_channel_rx = dma_channel[0]; + s->dma_channel_tx = dma_channel[1]; + + s->flags |= MXS_AUART_DMA_CONFIG; + } else { + s->dma_channel_rx = -1; + s->dma_channel_tx = -1; + } return 0; } @@ -787,7 +1100,6 @@ static int __devinit mxs_auart_probe(struct platform_device *pdev) s->port.type = PORT_IMX; s->port.dev = s->dev = get_device(&pdev->dev); - s->flags = 0; s->ctrl = 0; s->irq = platform_get_irq(pdev, 0); -- cgit v1.2.3 From be49bbae13ba9128ac69dd4b98027f6412155abe Mon Sep 17 00:00:00 2001 From: Fabio Porcedda Date: Mon, 12 Nov 2012 09:37:25 +0100 Subject: watchdog: at91sam9_wdt: add device tree support Tested on an at91sam9260 board (evk-pro3) Signed-off-by: Fabio Porcedda Signed-off-by: Nicolas Ferre --- Documentation/devicetree/bindings/watchdog/atmel-wdt.txt | 15 +++++++++++++++ drivers/watchdog/at91sam9_wdt.c | 11 +++++++++++ 2 files changed, 26 insertions(+) create mode 100644 Documentation/devicetree/bindings/watchdog/atmel-wdt.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/watchdog/atmel-wdt.txt b/Documentation/devicetree/bindings/watchdog/atmel-wdt.txt new file mode 100644 index 000000000000..2957ebb5aa71 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/atmel-wdt.txt @@ -0,0 +1,15 @@ +* Atmel Watchdog Timers + +** at91sam9-wdt + +Required properties: +- compatible: must be "atmel,at91sam9260-wdt". +- reg: physical base address of the controller and length of memory mapped + region. + +Example: + + watchdog@fffffd40 { + compatible = "atmel,at91sam9260-wdt"; + reg = <0xfffffd40 0x10>; + }; diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c index 05e1be85fdee..dc42e44b6bc1 100644 --- a/drivers/watchdog/at91sam9_wdt.c +++ b/drivers/watchdog/at91sam9_wdt.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "at91sam9_wdt.h" @@ -302,11 +303,21 @@ static int __exit at91wdt_remove(struct platform_device *pdev) return res; } +#if defined(CONFIG_OF) +static const struct of_device_id at91_wdt_dt_ids[] __initconst = { + { .compatible = "atmel,at91sam9260-wdt" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, at91_wdt_dt_ids); +#endif + static struct platform_driver at91wdt_driver = { .remove = __exit_p(at91wdt_remove), .driver = { .name = "at91_wdt", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(at91_wdt_dt_ids), }, }; -- cgit v1.2.3 From a000b8c1e30115800d3de86b4b058cadd9cba59d Mon Sep 17 00:00:00 2001 From: Andreas Larsson Date: Thu, 15 Nov 2012 16:50:59 +0100 Subject: i2c: ocores: Add support for the GRLIB port of the controller and use function pointers for getreg and setreg functions The registers in the GRLIB port of the controller are 32-bit and in big endian byte order. The PRELOW and PREHIGH registers are merged into one register. The subsequent registers have their offset decreased accordingly. Hence the register access needs to be handled in a non-standard manner using custom getreg and setreg functions. Add setreg and getreg functions for different register widths and let oc_setreg and oc_getreg use function pointers to call the appropriate functions. A type is added as the data of the of match table entries. A new entry with a different compatible string is added to the table. The type of that entry triggers usage of the custom grlib functions by setting the setreg and getreg function pointers. Signed-off-by: Andreas Larsson Signed-off-by: Wolfram Sang --- .../devicetree/bindings/i2c/i2c-ocores.txt | 2 +- drivers/i2c/busses/i2c-ocores.c | 138 ++++++++++++++++++--- 2 files changed, 121 insertions(+), 19 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/i2c/i2c-ocores.txt b/Documentation/devicetree/bindings/i2c/i2c-ocores.txt index c15781f4dc8c..1637c298a1b3 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-ocores.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-ocores.txt @@ -1,7 +1,7 @@ Device tree configuration for i2c-ocores Required properties: -- compatible : "opencores,i2c-ocores" +- compatible : "opencores,i2c-ocores" or "aeroflexgaisler,i2cmst" - reg : bus address start and address range size of device - interrupts : interrupt number - clock-frequency : frequency of bus clock in Hz diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index dafd26beecbb..0ea84199b507 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -4,6 +4,9 @@ * * Peter Korsgaard * + * Support for the GRLIB port of the controller by + * Andreas Larsson + * * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any * kind, whether express or implied. @@ -38,6 +41,8 @@ struct ocores_i2c { int nmsgs; int state; /* see STATE_ */ int clock_khz; + void (*setreg)(struct ocores_i2c *i2c, int reg, u8 value); + u8 (*getreg)(struct ocores_i2c *i2c, int reg); }; /* registers */ @@ -71,24 +76,81 @@ struct ocores_i2c { #define STATE_READ 3 #define STATE_ERROR 4 -static inline void oc_setreg(struct ocores_i2c *i2c, int reg, u8 value) +#define TYPE_OCORES 0 +#define TYPE_GRLIB 1 + +static void oc_setreg_8(struct ocores_i2c *i2c, int reg, u8 value) { - if (i2c->reg_io_width == 4) - iowrite32(value, i2c->base + (reg << i2c->reg_shift)); - else if (i2c->reg_io_width == 2) - iowrite16(value, i2c->base + (reg << i2c->reg_shift)); + iowrite8(value, i2c->base + (reg << i2c->reg_shift)); +} + +static void oc_setreg_16(struct ocores_i2c *i2c, int reg, u8 value) +{ + iowrite16(value, i2c->base + (reg << i2c->reg_shift)); +} + +static void oc_setreg_32(struct ocores_i2c *i2c, int reg, u8 value) +{ + iowrite32(value, i2c->base + (reg << i2c->reg_shift)); +} + +static inline u8 oc_getreg_8(struct ocores_i2c *i2c, int reg) +{ + return ioread8(i2c->base + (reg << i2c->reg_shift)); +} + +static inline u8 oc_getreg_16(struct ocores_i2c *i2c, int reg) +{ + return ioread16(i2c->base + (reg << i2c->reg_shift)); +} + +static inline u8 oc_getreg_32(struct ocores_i2c *i2c, int reg) +{ + return ioread32(i2c->base + (reg << i2c->reg_shift)); +} + +/* Read and write functions for the GRLIB port of the controller. Registers are + * 32-bit big endian and the PRELOW and PREHIGH registers are merged into one + * register. The subsequent registers has their offset decreased accordingly. */ +static u8 oc_getreg_grlib(struct ocores_i2c *i2c, int reg) +{ + u32 rd; + int rreg = reg; + if (reg != OCI2C_PRELOW) + rreg--; + rd = ioread32be(i2c->base + (rreg << i2c->reg_shift)); + if (reg == OCI2C_PREHIGH) + return (u8)(rd >> 8); else - iowrite8(value, i2c->base + (reg << i2c->reg_shift)); + return (u8)rd; +} + +static void oc_setreg_grlib(struct ocores_i2c *i2c, int reg, u8 value) +{ + u32 curr, wr; + int rreg = reg; + if (reg != OCI2C_PRELOW) + rreg--; + if (reg == OCI2C_PRELOW || reg == OCI2C_PREHIGH) { + curr = ioread32be(i2c->base + (rreg << i2c->reg_shift)); + if (reg == OCI2C_PRELOW) + wr = (curr & 0xff00) | value; + else + wr = (((u32)value) << 8) | (curr & 0xff); + } else { + wr = value; + } + iowrite32be(wr, i2c->base + (rreg << i2c->reg_shift)); +} + +static inline void oc_setreg(struct ocores_i2c *i2c, int reg, u8 value) +{ + i2c->setreg(i2c, reg, value); } static inline u8 oc_getreg(struct ocores_i2c *i2c, int reg) { - if (i2c->reg_io_width == 4) - return ioread32(i2c->base + (reg << i2c->reg_shift)); - else if (i2c->reg_io_width == 2) - return ioread16(i2c->base + (reg << i2c->reg_shift)); - else - return ioread8(i2c->base + (reg << i2c->reg_shift)); + return i2c->getreg(i2c, reg); } static void ocores_process(struct ocores_i2c *i2c) @@ -227,11 +289,25 @@ static struct i2c_adapter ocores_adapter = { .algo = &ocores_algorithm, }; +static struct of_device_id ocores_i2c_match[] = { + { + .compatible = "opencores,i2c-ocores", + .data = (void *)TYPE_OCORES, + }, + { + .compatible = "aeroflexgaisler,i2cmst", + .data = (void *)TYPE_GRLIB, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ocores_i2c_match); + #ifdef CONFIG_OF static int ocores_i2c_of_probe(struct platform_device *pdev, struct ocores_i2c *i2c) { struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; u32 val; if (of_property_read_u32(np, "reg-shift", &i2c->reg_shift)) { @@ -257,6 +333,14 @@ static int ocores_i2c_of_probe(struct platform_device *pdev, of_property_read_u32(pdev->dev.of_node, "reg-io-width", &i2c->reg_io_width); + + match = of_match_node(ocores_i2c_match, pdev->dev.of_node); + if (match && (int)match->data == TYPE_GRLIB) { + dev_dbg(&pdev->dev, "GRLIB variant of i2c-ocores\n"); + i2c->setreg = oc_setreg_grlib; + i2c->getreg = oc_getreg_grlib; + } + return 0; } #else @@ -302,6 +386,30 @@ static int __devinit ocores_i2c_probe(struct platform_device *pdev) if (i2c->reg_io_width == 0) i2c->reg_io_width = 1; /* Set to default value */ + if (!i2c->setreg || !i2c->getreg) { + switch (i2c->reg_io_width) { + case 1: + i2c->setreg = oc_setreg_8; + i2c->getreg = oc_getreg_8; + break; + + case 2: + i2c->setreg = oc_setreg_16; + i2c->getreg = oc_getreg_16; + break; + + case 4: + i2c->setreg = oc_setreg_32; + i2c->getreg = oc_getreg_32; + break; + + default: + dev_err(&pdev->dev, "Unsupported I/O width (%d)\n", + i2c->reg_io_width); + return -EINVAL; + } + } + ocores_init(i2c); init_waitqueue_head(&i2c->wait); @@ -379,12 +487,6 @@ static SIMPLE_DEV_PM_OPS(ocores_i2c_pm, ocores_i2c_suspend, ocores_i2c_resume); #define OCORES_I2C_PM NULL #endif -static struct of_device_id ocores_i2c_match[] = { - { .compatible = "opencores,i2c-ocores", }, - {}, -}; -MODULE_DEVICE_TABLE(of, ocores_i2c_match); - static struct platform_driver ocores_i2c_driver = { .probe = ocores_i2c_probe, .remove = __devexit_p(ocores_i2c_remove), -- cgit v1.2.3 From f58945392aad6a07ede5455ebb584aa729ac1ef0 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 22 Sep 2012 13:54:55 -0300 Subject: clk: mxs: Use a better name for the USB PHY clock Use a better name for the USB PHY clock. Signed-off-by: Fabio Estevam Signed-off-by: Shawn Guo Signed-off-by: Mike Turquette --- Documentation/devicetree/bindings/clock/imx23-clock.txt | 2 +- Documentation/devicetree/bindings/clock/imx28-clock.txt | 4 ++-- drivers/clk/mxs/clk-imx23.c | 6 +++--- drivers/clk/mxs/clk-imx28.c | 10 +++++----- 4 files changed, 11 insertions(+), 11 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/clock/imx23-clock.txt b/Documentation/devicetree/bindings/clock/imx23-clock.txt index a0b867ef8d96..baadbb11fe98 100644 --- a/Documentation/devicetree/bindings/clock/imx23-clock.txt +++ b/Documentation/devicetree/bindings/clock/imx23-clock.txt @@ -52,7 +52,7 @@ clocks and IDs. lcdif 38 etm 39 usb 40 - usb_pwr 41 + usb_phy 41 Examples: diff --git a/Documentation/devicetree/bindings/clock/imx28-clock.txt b/Documentation/devicetree/bindings/clock/imx28-clock.txt index aa2af2866fe8..52a49a4a50b3 100644 --- a/Documentation/devicetree/bindings/clock/imx28-clock.txt +++ b/Documentation/devicetree/bindings/clock/imx28-clock.txt @@ -73,8 +73,8 @@ clocks and IDs. can1 59 usb0 60 usb1 61 - usb0_pwr 62 - usb1_pwr 63 + usb0_phy 62 + usb1_phy 63 enet_out 64 Examples: diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c index f00dffb9ad60..8dd476e2a9c5 100644 --- a/drivers/clk/mxs/clk-imx23.c +++ b/drivers/clk/mxs/clk-imx23.c @@ -85,7 +85,7 @@ enum imx23_clk { cpu_xtal, hbus, xbus, lcdif_div, ssp_div, gpmi_div, emi_pll, emi_xtal, etm_div, saif_div, clk32k_div, rtc, adc, spdif_div, clk32k, dri, pwm, filt, uart, ssp, gpmi, spdif, emi, saif, - lcdif, etm, usb, usb_pwr, + lcdif, etm, usb, usb_phy, clk_max }; @@ -143,8 +143,8 @@ int __init mx23_clocks_init(void) clks[saif] = mxs_clk_gate("saif", "saif_div", SAIF, 31); clks[lcdif] = mxs_clk_gate("lcdif", "lcdif_div", PIX, 31); clks[etm] = mxs_clk_gate("etm", "etm_div", ETM, 31); - clks[usb] = mxs_clk_gate("usb", "usb_pwr", DIGCTRL, 2); - clks[usb_pwr] = clk_register_gate(NULL, "usb_pwr", "pll", 0, PLLCTRL0, 18, 0, &mxs_lock); + clks[usb] = mxs_clk_gate("usb", "usb_phy", DIGCTRL, 2); + clks[usb_phy] = clk_register_gate(NULL, "usb_phy", "pll", 0, PLLCTRL0, 18, 0, &mxs_lock); for (i = 0; i < ARRAY_SIZE(clks); i++) if (IS_ERR(clks[i])) { diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c index 42978f1b4bd2..db3af0874121 100644 --- a/drivers/clk/mxs/clk-imx28.c +++ b/drivers/clk/mxs/clk-imx28.c @@ -140,7 +140,7 @@ enum imx28_clk { emi_xtal, lcdif_div, etm_div, ptp, saif0_div, saif1_div, clk32k_div, rtc, lradc, spdif_div, clk32k, pwm, uart, ssp0, ssp1, ssp2, ssp3, gpmi, spdif, emi, saif0, saif1, lcdif, etm, - fec, can0, can1, usb0, usb1, usb0_pwr, usb1_pwr, enet_out, + fec, can0, can1, usb0, usb1, usb0_phy, usb1_phy, enet_out, clk_max }; @@ -218,10 +218,10 @@ int __init mx28_clocks_init(void) clks[fec] = mxs_clk_gate("fec", "hbus", ENET, 30); clks[can0] = mxs_clk_gate("can0", "ref_xtal", FLEXCAN, 30); clks[can1] = mxs_clk_gate("can1", "ref_xtal", FLEXCAN, 28); - clks[usb0] = mxs_clk_gate("usb0", "usb0_pwr", DIGCTRL, 2); - clks[usb1] = mxs_clk_gate("usb1", "usb1_pwr", DIGCTRL, 16); - clks[usb0_pwr] = clk_register_gate(NULL, "usb0_pwr", "pll0", 0, PLL0CTRL0, 18, 0, &mxs_lock); - clks[usb1_pwr] = clk_register_gate(NULL, "usb1_pwr", "pll1", 0, PLL1CTRL0, 18, 0, &mxs_lock); + clks[usb0] = mxs_clk_gate("usb0", "usb0_phy", DIGCTRL, 2); + clks[usb1] = mxs_clk_gate("usb1", "usb1_phy", DIGCTRL, 16); + clks[usb0_phy] = clk_register_gate(NULL, "usb0_phy", "pll0", 0, PLL0CTRL0, 18, 0, &mxs_lock); + clks[usb1_phy] = clk_register_gate(NULL, "usb1_phy", "pll1", 0, PLL1CTRL0, 18, 0, &mxs_lock); clks[enet_out] = clk_register_gate(NULL, "enet_out", "pll2", 0, ENET, 18, 0, &mxs_lock); for (i = 0; i < ARRAY_SIZE(clks); i++) -- cgit v1.2.3 From 2f2b7fb202a2fa93702a79d36033e5c8bee0120d Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 19 Sep 2012 12:02:31 -0600 Subject: ARM: tegra: define DT bindings for and instantiate timer The Tegra timer provides a number of 29-bit timer channels, a single 32-bit free running counter, and in the Tegra30 variant, 5 watchdog modules. The first two channels may also trigger a legacy watchdog reset. Define a DT binding for this HW module, and add the module into the Tegra device tree files. Signed-off-by: Stephen Warren --- .../bindings/timer/nvidia,tegra20-timer.txt | 21 ++++++++++++++++++++ .../bindings/timer/nvidia,tegra30-timer.txt | 23 ++++++++++++++++++++++ arch/arm/boot/dts/tegra20.dtsi | 9 +++++++++ arch/arm/boot/dts/tegra30.dtsi | 11 +++++++++++ 4 files changed, 64 insertions(+) create mode 100644 Documentation/devicetree/bindings/timer/nvidia,tegra20-timer.txt create mode 100644 Documentation/devicetree/bindings/timer/nvidia,tegra30-timer.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/timer/nvidia,tegra20-timer.txt b/Documentation/devicetree/bindings/timer/nvidia,tegra20-timer.txt new file mode 100644 index 000000000000..e019fdc38773 --- /dev/null +++ b/Documentation/devicetree/bindings/timer/nvidia,tegra20-timer.txt @@ -0,0 +1,21 @@ +NVIDIA Tegra20 timer + +The Tegra20 timer provides four 29-bit timer channels and a single 32-bit free +running counter. The first two channels may also trigger a watchdog reset. + +Required properties: + +- compatible : should be "nvidia,tegra20-timer". +- reg : Specifies base physical address and size of the registers. +- interrupts : A list of 4 interrupts; one per timer channel. + +Example: + +timer { + compatible = "nvidia,tegra20-timer"; + reg = <0x60005000 0x60>; + interrupts = <0 0 0x04 + 0 1 0x04 + 0 41 0x04 + 0 42 0x04>; +}; diff --git a/Documentation/devicetree/bindings/timer/nvidia,tegra30-timer.txt b/Documentation/devicetree/bindings/timer/nvidia,tegra30-timer.txt new file mode 100644 index 000000000000..906109d4c593 --- /dev/null +++ b/Documentation/devicetree/bindings/timer/nvidia,tegra30-timer.txt @@ -0,0 +1,23 @@ +NVIDIA Tegra30 timer + +The Tegra30 timer provides ten 29-bit timer channels, a single 32-bit free +running counter, and 5 watchdog modules. The first two channels may also +trigger a legacy watchdog reset. + +Required properties: + +- compatible : should be "nvidia,tegra30-timer", "nvidia,tegra20-timer". +- reg : Specifies base physical address and size of the registers. +- interrupts : A list of 6 interrupts; one per each of timer channels 1 + through 5, and one for the shared interrupt for the remaining channels. + +timer { + compatible = "nvidia,tegra30-timer", "nvidia,tegra20-timer"; + reg = <0x60005000 0x400>; + interrupts = <0 0 0x04 + 0 1 0x04 + 0 41 0x04 + 0 42 0x04 + 0 121 0x04 + 0 122 0x04>; +}; diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi index fba998e3954a..96c922d8bb39 100644 --- a/arch/arm/boot/dts/tegra20.dtsi +++ b/arch/arm/boot/dts/tegra20.dtsi @@ -108,6 +108,15 @@ #interrupt-cells = <3>; }; + timer@60005000 { + compatible = "nvidia,tegra20-timer"; + reg = <0x60005000 0x60>; + interrupts = <0 0 0x04 + 0 1 0x04 + 0 41 0x04 + 0 42 0x04>; + }; + apbdma: dma { compatible = "nvidia,tegra20-apbdma"; reg = <0x6000a000 0x1200>; diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi index 1f7f49aabe6b..48a8320ebf03 100644 --- a/arch/arm/boot/dts/tegra30.dtsi +++ b/arch/arm/boot/dts/tegra30.dtsi @@ -108,6 +108,17 @@ #interrupt-cells = <3>; }; + timer@60005000 { + compatible = "nvidia,tegra30-timer", "nvidia,tegra20-timer"; + reg = <0x60005000 0x400>; + interrupts = <0 0 0x04 + 0 1 0x04 + 0 41 0x04 + 0 42 0x04 + 0 121 0x04 + 0 122 0x04>; + }; + apbdma: dma { compatible = "nvidia,tegra30-apbdma", "nvidia,tegra20-apbdma"; reg = <0x6000a000 0x1400>; -- cgit v1.2.3 From 380e04ac2cd96a4c36ad164fce556427dcd2ea0f Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 19 Sep 2012 12:13:16 -0600 Subject: ARM: tegra: define DT bindings for and instantiate RTC The Tegra RTC maintains seconds and milliseconds counters, and five alarm registers. The alarms and other interrupts may wake the system from low-power state. Define a DT binding for this HW module, and add the module into the Tegra device tree files. Signed-off-by: Stephen Warren --- .../devicetree/bindings/rtc/nvidia,tegra20-rtc.txt | 19 +++++++++++++++++++ arch/arm/boot/dts/tegra20.dtsi | 6 ++++++ arch/arm/boot/dts/tegra30.dtsi | 6 ++++++ 3 files changed, 31 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/nvidia,tegra20-rtc.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/rtc/nvidia,tegra20-rtc.txt b/Documentation/devicetree/bindings/rtc/nvidia,tegra20-rtc.txt new file mode 100644 index 000000000000..93f45e9dce7c --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/nvidia,tegra20-rtc.txt @@ -0,0 +1,19 @@ +NVIDIA Tegra20 real-time clock + +The Tegra RTC maintains seconds and milliseconds counters, and five alarm +registers. The alarms and other interrupts may wake the system from low-power +state. + +Required properties: + +- compatible : should be "nvidia,tegra20-rtc". +- reg : Specifies base physical address and size of the registers. +- interrupts : A single interrupt specifier. + +Example: + +timer { + compatible = "nvidia,tegra20-rtc"; + reg = <0x7000e000 0x100>; + interrupts = <0 2 0x04>; +}; diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi index 96c922d8bb39..63c25cefaad7 100644 --- a/arch/arm/boot/dts/tegra20.dtsi +++ b/arch/arm/boot/dts/tegra20.dtsi @@ -234,6 +234,12 @@ #pwm-cells = <2>; }; + rtc { + compatible = "nvidia,tegra20-rtc"; + reg = <0x7000e000 0x100>; + interrupts = <0 2 0x04>; + }; + i2c@7000c000 { compatible = "nvidia,tegra20-i2c"; reg = <0x7000c000 0x100>; diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi index 48a8320ebf03..f7af06621cc8 100644 --- a/arch/arm/boot/dts/tegra30.dtsi +++ b/arch/arm/boot/dts/tegra30.dtsi @@ -230,6 +230,12 @@ #pwm-cells = <2>; }; + rtc { + compatible = "nvidia,tegra30-rtc", "nvidia,tegra20-rtc"; + reg = <0x7000e000 0x100>; + interrupts = <0 2 0x04>; + }; + i2c@7000c000 { compatible = "nvidia,tegra30-i2c", "nvidia,tegra20-i2c"; reg = <0x7000c000 0x100>; -- cgit v1.2.3 From b2ac5d7549710173ea0217bf8c7b3f71da5220d4 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 12 Nov 2012 15:07:50 +0100 Subject: clocksource: sunxi: Add Allwinner A1X Timer Driver Signed-off-by: Maxime Ripard CC: Thomas Gleixner CC: John Stultz --- .../bindings/timer/allwinner,sunxi-timer.txt | 17 +++ drivers/clocksource/Kconfig | 3 + drivers/clocksource/Makefile | 1 + drivers/clocksource/sunxi_timer.c | 170 +++++++++++++++++++++ include/linux/sunxi_timer.h | 24 +++ 5 files changed, 215 insertions(+) create mode 100644 Documentation/devicetree/bindings/timer/allwinner,sunxi-timer.txt create mode 100644 drivers/clocksource/sunxi_timer.c create mode 100644 include/linux/sunxi_timer.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/timer/allwinner,sunxi-timer.txt b/Documentation/devicetree/bindings/timer/allwinner,sunxi-timer.txt new file mode 100644 index 000000000000..0c7b64e95a61 --- /dev/null +++ b/Documentation/devicetree/bindings/timer/allwinner,sunxi-timer.txt @@ -0,0 +1,17 @@ +Allwinner A1X SoCs Timer Controller + +Required properties: + +- compatible : should be "allwinner,sunxi-timer" +- reg : Specifies base physical address and size of the registers. +- interrupts : The interrupt of the first timer +- clocks: phandle to the source clock (usually a 24 MHz fixed clock) + +Example: + +timer { + compatible = "allwinner,sunxi-timer"; + reg = <0x01c20c00 0x400>; + interrupts = <22>; + clocks = <&osc>; +}; diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 6a78073c3808..a0985732f1e2 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -22,6 +22,9 @@ config DW_APB_TIMER_OF config ARMADA_370_XP_TIMER bool +config SUNXI_TIMER + bool + config CLKSRC_DBX500_PRCMU bool "Clocksource PRCMU Timer" depends on UX500_SOC_DB8500 diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 603be366f762..36f06de4c5ab 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -14,5 +14,6 @@ obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o +obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o obj-$(CONFIG_CLKSRC_ARM_GENERIC) += arm_generic.o diff --git a/drivers/clocksource/sunxi_timer.c b/drivers/clocksource/sunxi_timer.c new file mode 100644 index 000000000000..3c46434b64cb --- /dev/null +++ b/drivers/clocksource/sunxi_timer.c @@ -0,0 +1,170 @@ +/* + * Allwinner A1X SoCs timer handling. + * + * Copyright (C) 2012 Maxime Ripard + * + * Maxime Ripard + * + * Based on code from + * Allwinner Technology Co., Ltd. + * Benn Huang + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMER_CTL_REG 0x00 +#define TIMER_CTL_ENABLE (1 << 0) +#define TIMER_IRQ_ST_REG 0x04 +#define TIMER0_CTL_REG 0x10 +#define TIMER0_CTL_ENABLE (1 << 0) +#define TIMER0_CTL_AUTORELOAD (1 << 1) +#define TIMER0_CTL_ONESHOT (1 << 7) +#define TIMER0_INTVAL_REG 0x14 +#define TIMER0_CNTVAL_REG 0x18 + +#define TIMER_SCAL 16 + +static void __iomem *timer_base; + +static void sunxi_clkevt_mode(enum clock_event_mode mode, + struct clock_event_device *clk) +{ + u32 u = readl(timer_base + TIMER0_CTL_REG); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + u &= ~(TIMER0_CTL_ONESHOT); + writel(u | TIMER0_CTL_ENABLE, timer_base + TIMER0_CTL_REG); + break; + + case CLOCK_EVT_MODE_ONESHOT: + writel(u | TIMER0_CTL_ONESHOT, timer_base + TIMER0_CTL_REG); + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + default: + writel(u & ~(TIMER0_CTL_ENABLE), timer_base + TIMER0_CTL_REG); + break; + } +} + +static int sunxi_clkevt_next_event(unsigned long evt, + struct clock_event_device *unused) +{ + u32 u = readl(timer_base + TIMER0_CTL_REG); + writel(evt, timer_base + TIMER0_CNTVAL_REG); + writel(u | TIMER0_CTL_ENABLE | TIMER0_CTL_AUTORELOAD, + timer_base + TIMER0_CTL_REG); + + return 0; +} + +static struct clock_event_device sunxi_clockevent = { + .name = "sunxi_tick", + .shift = 32, + .rating = 300, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = sunxi_clkevt_mode, + .set_next_event = sunxi_clkevt_next_event, +}; + + +static irqreturn_t sunxi_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = (struct clock_event_device *)dev_id; + + writel(0x1, timer_base + TIMER_IRQ_ST_REG); + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct irqaction sunxi_timer_irq = { + .name = "sunxi_timer0", + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = sunxi_timer_interrupt, + .dev_id = &sunxi_clockevent, +}; + +static struct of_device_id sunxi_timer_dt_ids[] = { + { .compatible = "allwinner,sunxi-timer" }, +}; + +static void __init sunxi_timer_init(void) +{ + struct device_node *node; + unsigned long rate = 0; + struct clk *clk; + int ret, irq; + u32 val; + + node = of_find_matching_node(NULL, sunxi_timer_dt_ids); + if (!node) + panic("No sunxi timer node"); + + timer_base = of_iomap(node, 0); + if (!timer_base) + panic("Can't map registers"); + + irq = irq_of_parse_and_map(node, 0); + if (irq <= 0) + panic("Can't parse IRQ"); + + sunxi_init_clocks(); + + clk = of_clk_get(node, 0); + if (IS_ERR(clk)) + panic("Can't get timer clock"); + + rate = clk_get_rate(clk); + + writel(rate / (TIMER_SCAL * HZ), + timer_base + TIMER0_INTVAL_REG); + + /* set clock source to HOSC, 16 pre-division */ + val = readl(timer_base + TIMER0_CTL_REG); + val &= ~(0x07 << 4); + val &= ~(0x03 << 2); + val |= (4 << 4) | (1 << 2); + writel(val, timer_base + TIMER0_CTL_REG); + + /* set mode to auto reload */ + val = readl(timer_base + TIMER0_CTL_REG); + writel(val | TIMER0_CTL_AUTORELOAD, timer_base + TIMER0_CTL_REG); + + ret = setup_irq(irq, &sunxi_timer_irq); + if (ret) + pr_warn("failed to setup irq %d\n", irq); + + /* Enable timer0 interrupt */ + val = readl(timer_base + TIMER_CTL_REG); + writel(val | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG); + + sunxi_clockevent.mult = div_sc(rate / TIMER_SCAL, + NSEC_PER_SEC, + sunxi_clockevent.shift); + sunxi_clockevent.max_delta_ns = clockevent_delta2ns(0xff, + &sunxi_clockevent); + sunxi_clockevent.min_delta_ns = clockevent_delta2ns(0x1, + &sunxi_clockevent); + sunxi_clockevent.cpumask = cpumask_of(0); + + clockevents_register_device(&sunxi_clockevent); +} + +struct sys_timer sunxi_timer = { + .init = sunxi_timer_init, +}; diff --git a/include/linux/sunxi_timer.h b/include/linux/sunxi_timer.h new file mode 100644 index 000000000000..b9165bba6e61 --- /dev/null +++ b/include/linux/sunxi_timer.h @@ -0,0 +1,24 @@ +/* + * Copyright 2012 Maxime Ripard + * + * Maxime Ripard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __SUNXI_TIMER_H +#define __SUNXI_TIMER_H + +#include + +extern struct sys_timer sunxi_timer; + +#endif -- cgit v1.2.3 From afd24e146826cec0f46929263a0c874406a19cd8 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 14 Nov 2012 09:59:14 +0100 Subject: irqchip: sunxi: Add irq controller driver Signed-off-by: Maxime Ripard CC: Thomas Gleixner --- .../interrupt-controller/allwinner,sunxi-ic.txt | 104 ++++++++++++++ drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-sunxi.c | 150 +++++++++++++++++++++ include/linux/irqchip/sunxi.h | 27 ++++ 4 files changed, 282 insertions(+) create mode 100644 Documentation/devicetree/bindings/interrupt-controller/allwinner,sunxi-ic.txt create mode 100644 drivers/irqchip/irq-sunxi.c create mode 100644 include/linux/irqchip/sunxi.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sunxi-ic.txt b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sunxi-ic.txt new file mode 100644 index 000000000000..7f9fb85f5456 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sunxi-ic.txt @@ -0,0 +1,104 @@ +Allwinner Sunxi Interrupt Controller + +Required properties: + +- compatible : should be "allwinner,sunxi-ic" +- reg : Specifies base physical address and size of the registers. +- interrupt-controller : Identifies the node as an interrupt controller +- #interrupt-cells : Specifies the number of cells needed to encode an + interrupt source. The value shall be 1. + +The interrupt sources are as follows: + +0: ENMI +1: UART0 +2: UART1 +3: UART2 +4: UART3 +5: IR0 +6: IR1 +7: I2C0 +8: I2C1 +9: I2C2 +10: SPI0 +11: SPI1 +12: SPI2 +13: SPDIF +14: AC97 +15: TS +16: I2S +17: UART4 +18: UART5 +19: UART6 +20: UART7 +21: KEYPAD +22: TIMER0 +23: TIMER1 +24: TIMER2 +25: TIMER3 +26: CAN +27: DMA +28: PIO +29: TOUCH_PANEL +30: AUDIO_CODEC +31: LRADC +32: SDMC0 +33: SDMC1 +34: SDMC2 +35: SDMC3 +36: MEMSTICK +37: NAND +38: USB0 +39: USB1 +40: USB2 +41: SCR +42: CSI0 +43: CSI1 +44: LCDCTRL0 +45: LCDCTRL1 +46: MP +47: DEFEBE0 +48: DEFEBE1 +49: PMU +50: SPI3 +51: TZASC +52: PATA +53: VE +54: SS +55: EMAC +56: SATA +57: GPS +58: HDMI +59: TVE +60: ACE +61: TVD +62: PS2_0 +63: PS2_1 +64: USB3 +65: USB4 +66: PLE_PFM +67: TIMER4 +68: TIMER5 +69: GPU_GP +70: GPU_GPMMU +71: GPU_PP0 +72: GPU_PPMMU0 +73: GPU_PMU +74: GPU_RSV0 +75: GPU_RSV1 +76: GPU_RSV2 +77: GPU_RSV3 +78: GPU_RSV4 +79: GPU_RSV5 +80: GPU_RSV6 +82: SYNC_TIMER0 +83: SYNC_TIMER1 + +Example: + +intc: interrupt-controller { + compatible = "allwinner,sunxi-ic"; + reg = <0x01c20400 0x400>; + interrupt-controller; + #interrupt-cells = <2>; +}; diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 054321db4350..2444d07544cd 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o +obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o diff --git a/drivers/irqchip/irq-sunxi.c b/drivers/irqchip/irq-sunxi.c new file mode 100644 index 000000000000..eef41a49acae --- /dev/null +++ b/drivers/irqchip/irq-sunxi.c @@ -0,0 +1,150 @@ +/* + * Allwinner A1X SoCs IRQ chip driver. + * + * Copyright (C) 2012 Maxime Ripard + * + * Maxime Ripard + * + * Based on code from + * Allwinner Technology Co., Ltd. + * Benn Huang + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include + +#include + +#define SUNXI_IRQ_VECTOR_REG 0x00 +#define SUNXI_IRQ_PROTECTION_REG 0x08 +#define SUNXI_IRQ_NMI_CTRL_REG 0x0c +#define SUNXI_IRQ_PENDING_REG(x) (0x10 + 0x4 * x) +#define SUNXI_IRQ_FIQ_PENDING_REG(x) (0x20 + 0x4 * x) +#define SUNXI_IRQ_ENABLE_REG(x) (0x40 + 0x4 * x) +#define SUNXI_IRQ_MASK_REG(x) (0x50 + 0x4 * x) + +static void __iomem *sunxi_irq_base; +static struct irq_domain *sunxi_irq_domain; + +void sunxi_irq_ack(struct irq_data *irqd) +{ + unsigned int irq = irqd_to_hwirq(irqd); + unsigned int irq_off = irq % 32; + int reg = irq / 32; + u32 val; + + val = readl(sunxi_irq_base + SUNXI_IRQ_PENDING_REG(reg)); + writel(val | (1 << irq_off), + sunxi_irq_base + SUNXI_IRQ_PENDING_REG(reg)); +} + +static void sunxi_irq_mask(struct irq_data *irqd) +{ + unsigned int irq = irqd_to_hwirq(irqd); + unsigned int irq_off = irq % 32; + int reg = irq / 32; + u32 val; + + val = readl(sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg)); + writel(val & ~(1 << irq_off), + sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg)); +} + +static void sunxi_irq_unmask(struct irq_data *irqd) +{ + unsigned int irq = irqd_to_hwirq(irqd); + unsigned int irq_off = irq % 32; + int reg = irq / 32; + u32 val; + + val = readl(sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg)); + writel(val | (1 << irq_off), + sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg)); +} + +static struct irq_chip sunxi_irq_chip = { + .name = "sunxi_irq", + .irq_ack = sunxi_irq_ack, + .irq_mask = sunxi_irq_mask, + .irq_unmask = sunxi_irq_unmask, +}; + +static int sunxi_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(virq, &sunxi_irq_chip, + handle_level_irq); + set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); + + return 0; +} + +static struct irq_domain_ops sunxi_irq_ops = { + .map = sunxi_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int __init sunxi_of_init(struct device_node *node, + struct device_node *parent) +{ + sunxi_irq_base = of_iomap(node, 0); + if (!sunxi_irq_base) + panic("%s: unable to map IC registers\n", + node->full_name); + + /* Disable all interrupts */ + writel(0, sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(0)); + writel(0, sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(1)); + writel(0, sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(2)); + + /* Mask all the interrupts */ + writel(0, sunxi_irq_base + SUNXI_IRQ_MASK_REG(0)); + writel(0, sunxi_irq_base + SUNXI_IRQ_MASK_REG(1)); + writel(0, sunxi_irq_base + SUNXI_IRQ_MASK_REG(2)); + + /* Clear all the pending interrupts */ + writel(0xffffffff, sunxi_irq_base + SUNXI_IRQ_PENDING_REG(0)); + writel(0xffffffff, sunxi_irq_base + SUNXI_IRQ_PENDING_REG(1)); + writel(0xffffffff, sunxi_irq_base + SUNXI_IRQ_PENDING_REG(2)); + + /* Enable protection mode */ + writel(0x01, sunxi_irq_base + SUNXI_IRQ_PROTECTION_REG); + + /* Configure the external interrupt source type */ + writel(0x00, sunxi_irq_base + SUNXI_IRQ_NMI_CTRL_REG); + + sunxi_irq_domain = irq_domain_add_linear(node, 3 * 32, + &sunxi_irq_ops, NULL); + if (!sunxi_irq_domain) + panic("%s: unable to create IRQ domain\n", node->full_name); + + return 0; +} + +static struct of_device_id sunxi_irq_dt_ids[] __initconst = { + { .compatible = "allwinner,sunxi-ic", .data = sunxi_of_init } +}; + +void __init sunxi_init_irq(void) +{ + of_irq_init(sunxi_irq_dt_ids); +} + +asmlinkage void __exception_irq_entry sunxi_handle_irq(struct pt_regs *regs) +{ + u32 irq, hwirq; + + hwirq = readl(sunxi_irq_base + SUNXI_IRQ_VECTOR_REG) >> 2; + while (hwirq != 0) { + irq = irq_find_mapping(sunxi_irq_domain, hwirq); + handle_IRQ(irq, regs); + hwirq = readl(sunxi_irq_base + SUNXI_IRQ_VECTOR_REG) >> 2; + } +} diff --git a/include/linux/irqchip/sunxi.h b/include/linux/irqchip/sunxi.h new file mode 100644 index 000000000000..1fe2c2260e2b --- /dev/null +++ b/include/linux/irqchip/sunxi.h @@ -0,0 +1,27 @@ +/* + * Copyright 2012 Maxime Ripard + * + * Maxime Ripard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __LINUX_IRQCHIP_SUNXI_H +#define __LINUX_IRQCHIP_SUNXI_H + +#include + +extern void sunxi_init_irq(void); + +extern asmlinkage void __exception_irq_entry sunxi_handle_irq( + struct pt_regs *regs); + +#endif -- cgit v1.2.3 From 64100a03ad0b94a45ed2753632d9dd68379ad3ac Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 2 Nov 2012 01:20:43 +0100 Subject: ARM: integrator: hook the CP into the SoC bus This hooks the Integrator/CP into the SoC bus when booting from device tree, by mapping the CP controller registers first, then registering the SoC device, and then populating the device tree with the SoC device as parent. Cc: Lee Jones Acked-by: Arnd Bergmann Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/arm/arm-boards | 4 ++ arch/arm/boot/dts/integratorcp.dts | 5 +++ arch/arm/mach-integrator/Kconfig | 1 + arch/arm/mach-integrator/integrator_cp.c | 57 +++++++++++++++++++++++- 4 files changed, 65 insertions(+), 2 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/arm-boards b/Documentation/devicetree/bindings/arm/arm-boards index fc81a7d6b0f1..db5858e32d3f 100644 --- a/Documentation/devicetree/bindings/arm/arm-boards +++ b/Documentation/devicetree/bindings/arm/arm-boards @@ -9,6 +9,10 @@ Required properties (in root node): FPGA type interrupt controllers, see the versatile-fpga-irq binding doc. +In the root node the Integrator/CP must have a /cpcon node pointing +to the CP control registers, and the Integrator/AP must have a +/syscon node pointing to the Integrator/AP system controller. + ARM Versatile Application and Platform Baseboards ------------------------------------------------- diff --git a/arch/arm/boot/dts/integratorcp.dts b/arch/arm/boot/dts/integratorcp.dts index 2dd5e4e48481..8b119399025a 100644 --- a/arch/arm/boot/dts/integratorcp.dts +++ b/arch/arm/boot/dts/integratorcp.dts @@ -18,6 +18,11 @@ bootargs = "root=/dev/ram0 console=ttyAMA0,38400n8 earlyprintk"; }; + cpcon { + /* CP controller registers */ + reg = <0xcb000000 0x100>; + }; + timer0: timer@13000000 { compatible = "arm,sp804", "arm,primecell"; }; diff --git a/arch/arm/mach-integrator/Kconfig b/arch/arm/mach-integrator/Kconfig index 3961942c9e11..abeff25532ab 100644 --- a/arch/arm/mach-integrator/Kconfig +++ b/arch/arm/mach-integrator/Kconfig @@ -20,6 +20,7 @@ config ARCH_INTEGRATOR_CP select PLAT_VERSATILE_CLCD select SERIAL_AMBA_PL011 select SERIAL_AMBA_PL011_CONSOLE + select SOC_BUS help Include support for the ARM(R) Integrator CP platform. diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c index 5b08e8e4cc83..9194a4f3339c 100644 --- a/arch/arm/mach-integrator/integrator_cp.c +++ b/arch/arm/mach-integrator/integrator_cp.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -336,10 +337,62 @@ static struct of_dev_auxdata intcp_auxdata_lookup[] __initdata = { { /* sentinel */ }, }; +/* Base address to the CP controller */ +static void __iomem *intcp_con_base; + static void __init intcp_init_of(void) { - of_platform_populate(NULL, of_default_bus_match_table, - intcp_auxdata_lookup, NULL); + struct device_node *root; + struct device_node *cpcon; + struct device *parent; + struct soc_device *soc_dev; + struct soc_device_attribute *soc_dev_attr; + u32 intcp_sc_id; + int err; + + /* Here we create an SoC device for the root node */ + root = of_find_node_by_path("/"); + if (!root) + return; + cpcon = of_find_node_by_path("/cpcon"); + if (!cpcon) + return; + + intcp_con_base = of_iomap(cpcon, 0); + if (!intcp_con_base) + return; + + intcp_sc_id = readl(intcp_con_base); + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return; + + err = of_property_read_string(root, "compatible", + &soc_dev_attr->soc_id); + if (err) + return; + err = of_property_read_string(root, "model", &soc_dev_attr->machine); + if (err) + return; + soc_dev_attr->family = "Integrator"; + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%c", + 'A' + (intcp_sc_id & 0x0f)); + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR_OR_NULL(soc_dev)) { + kfree(soc_dev_attr->revision); + kfree(soc_dev_attr); + return; + } + + parent = soc_device_to_device(soc_dev); + + if (!IS_ERR_OR_NULL(parent)) + integrator_init_sysfs(parent, intcp_sc_id); + + of_platform_populate(root, of_default_bus_match_table, + intcp_auxdata_lookup, parent); } static const char * intcp_dt_board_compat[] = { -- cgit v1.2.3 From b53bc2819a71099ecfc3d61ba0796b3dcc6be321 Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Fri, 16 Nov 2012 10:45:25 +0530 Subject: gpio: SPEAr: add spi chipselect control driver SPEAr platform provides a provision to control chipselects of ARM PL022 Prime Cell spi controller through its system registers, which otherwise remains under PL022 control which some protocols do not want. This commit intends to provide the spi chipselect control in software over gpiolib interface. spi chip drivers can use the exported gpiolib interface to define their chipselect through DT or platform data. Cc: Grant Likely Signed-off-by: Shiraz Hashim Reviewed-by: Vipin Kumar Acked-by: Arnd Bergmann Signed-off-by: Viresh Kumar Signed-off-by: Linus Walleij --- .../devicetree/bindings/gpio/spear_spics.txt | 50 +++++ arch/arm/plat-spear/Kconfig | 1 + drivers/gpio/Kconfig | 7 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-spear-spics.c | 217 +++++++++++++++++++++ 5 files changed, 276 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/spear_spics.txt create mode 100644 drivers/gpio/gpio-spear-spics.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/gpio/spear_spics.txt b/Documentation/devicetree/bindings/gpio/spear_spics.txt new file mode 100644 index 000000000000..96c37eb15075 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/spear_spics.txt @@ -0,0 +1,50 @@ +=== ST Microelectronics SPEAr SPI CS Driver === + +SPEAr platform provides a provision to control chipselects of ARM PL022 Prime +Cell spi controller through its system registers, which otherwise remains under +PL022 control. If chipselect remain under PL022 control then they would be +released as soon as transfer is over and TxFIFO becomes empty. This is not +desired by some of the device protocols above spi which expect (multiple) +transfers without releasing their chipselects. + +Chipselects can be controlled by software by turning them as GPIOs. SPEAr +provides another interface through system registers through which software can +directly control each PL022 chipselect. Hence, it is natural for SPEAr to export +the control of this interface as gpio. + +Required properties: + + * compatible: should be defined as "st,spear-spics-gpio" + * reg: mentioning address range of spics controller + * st-spics,peripcfg-reg: peripheral configuration register offset + * st-spics,sw-enable-bit: bit offset to enable sw control + * st-spics,cs-value-bit: bit offset to drive chipselect low or high + * st-spics,cs-enable-mask: chip select number bit mask + * st-spics,cs-enable-shift: chip select number program offset + * gpio-controller: Marks the device node as gpio controller + * #gpio-cells: should be 1 and will mention chip select number + +All the above bit offsets are within peripcfg register. + +Example: +------- +spics: spics@e0700000{ + compatible = "st,spear-spics-gpio"; + reg = <0xe0700000 0x1000>; + st-spics,peripcfg-reg = <0x3b0>; + st-spics,sw-enable-bit = <12>; + st-spics,cs-value-bit = <11>; + st-spics,cs-enable-mask = <3>; + st-spics,cs-enable-shift = <8>; + gpio-controller; + #gpio-cells = <2>; +}; + + +spi0: spi@e0100000 { + status = "okay"; + num-cs = <3>; + cs-gpios = <&gpio1 7 0>, <&spics 0>, + <&spics 1>; + ... +} diff --git a/arch/arm/plat-spear/Kconfig b/arch/arm/plat-spear/Kconfig index f8db7b2deb36..87dbd81bdf51 100644 --- a/arch/arm/plat-spear/Kconfig +++ b/arch/arm/plat-spear/Kconfig @@ -12,6 +12,7 @@ config ARCH_SPEAR13XX bool "ST SPEAr13xx with Device Tree" select ARM_GIC select CPU_V7 + select GPIO_SPEAR_SPICS select HAVE_SMP select MIGHT_HAVE_CACHE_L2X0 select PINCTRL diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 9e3fb3438718..4f592c615990 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -196,6 +196,13 @@ config GPIO_PXA help Say yes here to support the PXA GPIO device +config GPIO_SPEAR_SPICS + bool "ST SPEAr13xx SPI Chip Select as GPIO support" + depends on PLAT_SPEAR + select GENERIC_IRQ_CHIP + help + Say yes here to support ST SPEAr SPI Chip Select as GPIO device + config GPIO_STA2X11 bool "STA2x11/ConneXt GPIO support" depends on MFD_STA2X11 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 1c1b63fcaeb3..a268d99f4e43 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o obj-$(CONFIG_GPIO_SCH) += gpio-sch.o obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o +obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o obj-$(CONFIG_GPIO_STA2X11) += gpio-sta2x11.o obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o obj-$(CONFIG_GPIO_STP_XWAY) += gpio-stp-xway.o diff --git a/drivers/gpio/gpio-spear-spics.c b/drivers/gpio/gpio-spear-spics.c new file mode 100644 index 000000000000..5f45fc4ed5d1 --- /dev/null +++ b/drivers/gpio/gpio-spear-spics.c @@ -0,0 +1,217 @@ +/* + * SPEAr platform SPI chipselect abstraction over gpiolib + * + * Copyright (C) 2012 ST Microelectronics + * Shiraz Hashim + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* maximum chipselects */ +#define NUM_OF_GPIO 4 + +/* + * Provision is available on some SPEAr SoCs to control ARM PL022 spi cs + * through system registers. This register lies outside spi (pl022) + * address space into system registers. + * + * It provides control for spi chip select lines so that any chipselect + * (out of 4 possible chipselects in pl022) can be made low to select + * the particular slave. + */ + +/** + * struct spear_spics - represents spi chip select control + * @base: base address + * @perip_cfg: configuration register + * @sw_enable_bit: bit to enable s/w control over chipselects + * @cs_value_bit: bit to program high or low chipselect + * @cs_enable_mask: mask to select bits required to select chipselect + * @cs_enable_shift: bit pos of cs_enable_mask + * @use_count: use count of a spi controller cs lines + * @last_off: stores last offset caller of set_value() + * @chip: gpio_chip abstraction + */ +struct spear_spics { + void __iomem *base; + u32 perip_cfg; + u32 sw_enable_bit; + u32 cs_value_bit; + u32 cs_enable_mask; + u32 cs_enable_shift; + unsigned long use_count; + int last_off; + struct gpio_chip chip; +}; + +/* gpio framework specific routines */ +static int spics_get_value(struct gpio_chip *chip, unsigned offset) +{ + return -ENXIO; +} + +static void spics_set_value(struct gpio_chip *chip, unsigned offset, int value) +{ + struct spear_spics *spics = container_of(chip, struct spear_spics, + chip); + u32 tmp; + + /* select chip select from register */ + tmp = readl_relaxed(spics->base + spics->perip_cfg); + if (spics->last_off != offset) { + spics->last_off = offset; + tmp &= ~(spics->cs_enable_mask << spics->cs_enable_shift); + tmp |= offset << spics->cs_enable_shift; + } + + /* toggle chip select line */ + tmp &= ~(0x1 << spics->cs_value_bit); + tmp |= value << spics->cs_value_bit; + writel_relaxed(tmp, spics->base + spics->perip_cfg); +} + +static int spics_direction_input(struct gpio_chip *chip, unsigned offset) +{ + return -ENXIO; +} + +static int spics_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + spics_set_value(chip, offset, value); + return 0; +} + +static int spics_request(struct gpio_chip *chip, unsigned offset) +{ + struct spear_spics *spics = container_of(chip, struct spear_spics, + chip); + u32 tmp; + + if (!spics->use_count++) { + tmp = readl_relaxed(spics->base + spics->perip_cfg); + tmp |= 0x1 << spics->sw_enable_bit; + tmp |= 0x1 << spics->cs_value_bit; + writel_relaxed(tmp, spics->base + spics->perip_cfg); + } + + return 0; +} + +static void spics_free(struct gpio_chip *chip, unsigned offset) +{ + struct spear_spics *spics = container_of(chip, struct spear_spics, + chip); + u32 tmp; + + if (!--spics->use_count) { + tmp = readl_relaxed(spics->base + spics->perip_cfg); + tmp &= ~(0x1 << spics->sw_enable_bit); + writel_relaxed(tmp, spics->base + spics->perip_cfg); + } +} + +static int spics_gpio_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct spear_spics *spics; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "invalid IORESOURCE_MEM\n"); + return -EBUSY; + } + + spics = devm_kzalloc(&pdev->dev, sizeof(*spics), GFP_KERNEL); + if (!spics) { + dev_err(&pdev->dev, "memory allocation fail\n"); + return -ENOMEM; + } + + spics->base = devm_request_and_ioremap(&pdev->dev, res); + if (!spics->base) { + dev_err(&pdev->dev, "request and ioremap fail\n"); + return -ENOMEM; + } + + if (of_property_read_u32(np, "st-spics,peripcfg-reg", + &spics->perip_cfg)) + goto err_dt_data; + if (of_property_read_u32(np, "st-spics,sw-enable-bit", + &spics->sw_enable_bit)) + goto err_dt_data; + if (of_property_read_u32(np, "st-spics,cs-value-bit", + &spics->cs_value_bit)) + goto err_dt_data; + if (of_property_read_u32(np, "st-spics,cs-enable-mask", + &spics->cs_enable_mask)) + goto err_dt_data; + if (of_property_read_u32(np, "st-spics,cs-enable-shift", + &spics->cs_enable_shift)) + goto err_dt_data; + + platform_set_drvdata(pdev, spics); + + spics->chip.ngpio = NUM_OF_GPIO; + spics->chip.base = -1; + spics->chip.request = spics_request; + spics->chip.free = spics_free; + spics->chip.direction_input = spics_direction_input; + spics->chip.direction_output = spics_direction_output; + spics->chip.get = spics_get_value; + spics->chip.set = spics_set_value; + spics->chip.label = dev_name(&pdev->dev); + spics->chip.dev = &pdev->dev; + spics->chip.owner = THIS_MODULE; + spics->last_off = -1; + + ret = gpiochip_add(&spics->chip); + if (ret) { + dev_err(&pdev->dev, "unable to add gpio chip\n"); + return ret; + } + + dev_info(&pdev->dev, "spear spics registered\n"); + return 0; + +err_dt_data: + dev_err(&pdev->dev, "DT probe failed\n"); + return -EINVAL; +} + +static const struct of_device_id spics_gpio_of_match[] = { + { .compatible = "st,spear-spics-gpio" }, + {} +}; +MODULE_DEVICE_TABLE(of, spics_gpio_of_match); + +static struct platform_driver spics_gpio_driver = { + .probe = spics_gpio_probe, + .driver = { + .owner = THIS_MODULE, + .name = "spear-spics-gpio", + .of_match_table = spics_gpio_of_match, + }, +}; + +static int __init spics_gpio_init(void) +{ + return platform_driver_register(&spics_gpio_driver); +} +subsys_initcall(spics_gpio_init); + +MODULE_AUTHOR("Shiraz Hashim "); +MODULE_DESCRIPTION("ST Microlectronics SPEAr SPI Chip Select Abstraction"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 7ebd7a3ae0dd6e826767df504f7850d935fc3ee9 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Wed, 26 Sep 2012 14:57:45 +0800 Subject: pinctrl: at91 add deglitch, debounce, pull down and schmitt trigger mux option support add : set_deglitch: enable/disable deglitch set_debounce: enable/disable debounce set_pulldown: enable/disable pulldown disable_schmitt_trig: disable schmitt trigger Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD --- .../bindings/pinctrl/atmel,at91-pinctrl.txt | 9 +- drivers/pinctrl/pinctrl-at91.c | 111 +++++++++++++++++++++ 2 files changed, 118 insertions(+), 2 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt index 20a987e55a28..3a268127b054 100644 --- a/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt @@ -84,8 +84,13 @@ Required properties for pin configuration node: The PERIPH 0 means gpio. Bits used for CONFIG: -PULL_UP(1 << 0): indicate this pin need a pull up. -MULTIDRIVE(1 << 1): indicate this pin need to be configured as multidrive. +PULL_UP (1 << 0): indicate this pin need a pull up. +MULTIDRIVE (1 << 1): indicate this pin need to be configured as multidrive. +DEGLITCH (1 << 2): indicate this pin need deglitch. +PULL_DOWN (1 << 3): indicate this pin need a pull down. +DIS_SCHMIT (1 << 4): indicate this pin need to disable schmit trigger. +DEBOUNCE (1 << 16): indicate this pin need debounce. +DEBOUNCE_VAL (0x3fff << 17): debounce val. NOTE: Some requirements for using atmel,at91rm9200-pinctrl binding: diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 401fc96f577e..0d7e6c3c33a7 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -59,6 +59,12 @@ static int gpio_banks; #define PULL_UP (1 << 0) #define MULTI_DRIVE (1 << 1) +#define DEGLITCH (1 << 2) +#define PULL_DOWN (1 << 3) +#define DIS_SCHMIT (1 << 4) +#define DEBOUNCE (1 << 16) +#define DEBOUNCE_VAL_SHIFT 17 +#define DEBOUNCE_VAL (0x3fff << DEBOUNCE_VAL_SHIFT) /** * struct at91_pmx_func - describes AT91 pinmux functions @@ -122,6 +128,14 @@ struct at91_pin_group { * @mux_B_periph: mux as periph B * @mux_C_periph: mux as periph C * @mux_D_periph: mux as periph D + * @get_deglitch: get deglitch status + * @set_deglitch: enable/disable deglitch + * @get_debounce: get debounce status + * @set_debounce: enable/disable debounce + * @get_pulldown: get pulldown status + * @set_pulldown: enable/disable pulldown + * @get_schmitt_trig: get schmitt trigger status + * @disable_schmitt_trig: disable schmitt trigger * @irq_type: return irq type */ struct at91_pinctrl_mux_ops { @@ -130,6 +144,14 @@ struct at91_pinctrl_mux_ops { void (*mux_B_periph)(void __iomem *pio, unsigned mask); void (*mux_C_periph)(void __iomem *pio, unsigned mask); void (*mux_D_periph)(void __iomem *pio, unsigned mask); + bool (*get_deglitch)(void __iomem *pio, unsigned pin); + void (*set_deglitch)(void __iomem *pio, unsigned mask, bool in_on); + bool (*get_debounce)(void __iomem *pio, unsigned pin, u32 *div); + void (*set_debounce)(void __iomem *pio, unsigned mask, bool in_on, u32 div); + bool (*get_pulldown)(void __iomem *pio, unsigned pin); + void (*set_pulldown)(void __iomem *pio, unsigned mask, bool in_on); + bool (*get_schmitt_trig)(void __iomem *pio, unsigned pin); + void (*disable_schmitt_trig)(void __iomem *pio, unsigned mask); /* irq */ int (*irq_type)(struct irq_data *d, unsigned type); }; @@ -386,10 +408,68 @@ static enum at91_mux at91_mux_get_periph(void __iomem *pio, unsigned mask) return select + 1; } +static bool at91_mux_get_deglitch(void __iomem *pio, unsigned pin) +{ + return (__raw_readl(pio + PIO_IFSR) >> pin) & 0x1; +} + +static void at91_mux_set_deglitch(void __iomem *pio, unsigned mask, bool is_on) +{ + __raw_writel(mask, pio + (is_on ? PIO_IFER : PIO_IFDR)); +} + +static void at91_mux_pio3_set_deglitch(void __iomem *pio, unsigned mask, bool is_on) +{ + if (is_on) + __raw_writel(mask, pio + PIO_IFSCDR); + at91_mux_set_deglitch(pio, mask, is_on); +} + +static bool at91_mux_pio3_get_debounce(void __iomem *pio, unsigned pin, u32 *div) +{ + *div = __raw_readl(pio + PIO_SCDR); + + return (__raw_readl(pio + PIO_IFSCSR) >> pin) & 0x1; +} + +static void at91_mux_pio3_set_debounce(void __iomem *pio, unsigned mask, + bool is_on, u32 div) +{ + if (is_on) { + __raw_writel(mask, pio + PIO_IFSCER); + __raw_writel(div & PIO_SCDR_DIV, pio + PIO_SCDR); + __raw_writel(mask, pio + PIO_IFER); + } else { + __raw_writel(mask, pio + PIO_IFDR); + } +} + +static bool at91_mux_pio3_get_pulldown(void __iomem *pio, unsigned pin) +{ + return (__raw_readl(pio + PIO_PPDSR) >> pin) & 0x1; +} + +static void at91_mux_pio3_set_pulldown(void __iomem *pio, unsigned mask, bool is_on) +{ + __raw_writel(mask, pio + (is_on ? PIO_PPDER : PIO_PPDDR)); +} + +static void at91_mux_pio3_disable_schmitt_trig(void __iomem *pio, unsigned mask) +{ + __raw_writel(__raw_readl(pio + PIO_SCHMITT) | mask, pio + PIO_SCHMITT); +} + +static bool at91_mux_pio3_get_schmitt_trig(void __iomem *pio, unsigned pin) +{ + return (__raw_readl(pio + PIO_SCHMITT) >> pin) & 0x1; +} + static struct at91_pinctrl_mux_ops at91rm9200_ops = { .get_periph = at91_mux_get_periph, .mux_A_periph = at91_mux_set_A_periph, .mux_B_periph = at91_mux_set_B_periph, + .get_deglitch = at91_mux_get_deglitch, + .set_deglitch = at91_mux_set_deglitch, .irq_type = gpio_irq_type, }; @@ -399,6 +479,14 @@ static struct at91_pinctrl_mux_ops at91sam9x5_ops = { .mux_B_periph = at91_mux_pio3_set_B_periph, .mux_C_periph = at91_mux_pio3_set_C_periph, .mux_D_periph = at91_mux_pio3_set_D_periph, + .get_deglitch = at91_mux_get_deglitch, + .set_deglitch = at91_mux_pio3_set_deglitch, + .get_debounce = at91_mux_pio3_get_debounce, + .set_debounce = at91_mux_pio3_set_debounce, + .get_pulldown = at91_mux_pio3_get_pulldown, + .set_pulldown = at91_mux_pio3_set_pulldown, + .get_schmitt_trig = at91_mux_pio3_get_schmitt_trig, + .disable_schmitt_trig = at91_mux_pio3_disable_schmitt_trig, .irq_type = alt_gpio_irq_type, }; @@ -624,6 +712,7 @@ static int at91_pinconf_get(struct pinctrl_dev *pctldev, struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); void __iomem *pio; unsigned pin; + int div; dev_dbg(info->dev, "%s:%d, pin_id=%d, config=0x%lx", __func__, __LINE__, pin_id, *config); pio = pin_to_controller(info, pin_to_bank(pin_id)); @@ -635,6 +724,15 @@ static int at91_pinconf_get(struct pinctrl_dev *pctldev, if (at91_mux_get_pullup(pio, pin)) *config |= PULL_UP; + if (info->ops->get_deglitch && info->ops->get_deglitch(pio, pin)) + *config |= DEGLITCH; + if (info->ops->get_debounce && info->ops->get_debounce(pio, pin, &div)) + *config |= DEBOUNCE | (div << DEBOUNCE_VAL_SHIFT); + if (info->ops->get_pulldown && info->ops->get_pulldown(pio, pin)) + *config |= PULL_DOWN; + if (info->ops->get_schmitt_trig && info->ops->get_schmitt_trig(pio, pin)) + *config |= DIS_SCHMIT; + return 0; } @@ -649,8 +747,21 @@ static int at91_pinconf_set(struct pinctrl_dev *pctldev, pio = pin_to_controller(info, pin_to_bank(pin_id)); mask = pin_to_mask(pin_id % MAX_NB_GPIO_PER_BANK); + if (config & PULL_UP && config & PULL_DOWN) + return -EINVAL; + at91_mux_set_pullup(pio, mask, config & PULL_UP); at91_mux_set_multidrive(pio, mask, config & MULTI_DRIVE); + if (info->ops->set_deglitch) + info->ops->set_deglitch(pio, mask, config & DEGLITCH); + if (info->ops->set_debounce) + info->ops->set_debounce(pio, mask, config & DEBOUNCE, + (config & DEBOUNCE_VAL) >> DEBOUNCE_VAL_SHIFT); + if (info->ops->set_pulldown) + info->ops->set_pulldown(pio, mask, config & PULL_DOWN); + if (info->ops->disable_schmitt_trig && config & DIS_SCHMIT) + info->ops->disable_schmitt_trig(pio, mask); + return 0; } -- cgit v1.2.3 From 454c46df83945602f1cbc86efcfd063c6945d869 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Sun, 28 Oct 2012 18:31:07 +0000 Subject: ARM: AT91: Add DT support to AT91RM9200 System Timer Based on AT91 PIT DT patch from Jean-Christophe PLAGNIOL-VILLARD. Signed-off-by: Joachim Eastwood Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD --- .../devicetree/bindings/arm/atmel-at91.txt | 6 +++ arch/arm/mach-at91/at91rm9200_time.c | 63 +++++++++++++++++++++- 2 files changed, 67 insertions(+), 2 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/atmel-at91.txt b/Documentation/devicetree/bindings/arm/atmel-at91.txt index ecc81e368715..8adc9a8d6be9 100644 --- a/Documentation/devicetree/bindings/arm/atmel-at91.txt +++ b/Documentation/devicetree/bindings/arm/atmel-at91.txt @@ -7,6 +7,12 @@ PIT Timer required properties: - interrupts: Should contain interrupt for the PIT which is the IRQ line shared across all System Controller members. +System Timer (ST) required properties: +- compatible: Should be "atmel,at91rm9200-st" +- reg: Should contain registers location and length +- interrupts: Should contain interrupt for the ST which is the IRQ line + shared across all System Controller members. + TC/TCLIB Timer required properties: - compatible: Should be "atmel,-pit". can be "at91rm9200" or "at91sam9x5" diff --git a/arch/arm/mach-at91/at91rm9200_time.c b/arch/arm/mach-at91/at91rm9200_time.c index aaa443b48c91..cafe98836c8a 100644 --- a/arch/arm/mach-at91/at91rm9200_time.c +++ b/arch/arm/mach-at91/at91rm9200_time.c @@ -24,6 +24,9 @@ #include #include #include +#include +#include +#include #include @@ -91,7 +94,8 @@ static irqreturn_t at91rm9200_timer_interrupt(int irq, void *dev_id) static struct irqaction at91rm9200_timer_irq = { .name = "at91_tick", .flags = IRQF_SHARED | IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, - .handler = at91rm9200_timer_interrupt + .handler = at91rm9200_timer_interrupt, + .irq = NR_IRQS_LEGACY + AT91_ID_SYS, }; static cycle_t read_clk32k(struct clocksource *cs) @@ -179,8 +183,60 @@ static struct clock_event_device clkevt = { void __iomem *at91_st_base; EXPORT_SYMBOL_GPL(at91_st_base); +#ifdef CONFIG_OF +static struct of_device_id at91rm9200_st_timer_ids[] = { + { .compatible = "atmel,at91rm9200-st" }, + { /* sentinel */ } +}; + +static int __init of_at91rm9200_st_init(void) +{ + struct device_node *np; + int ret; + + np = of_find_matching_node(NULL, at91rm9200_st_timer_ids); + if (!np) + goto err; + + at91_st_base = of_iomap(np, 0); + if (!at91_st_base) + goto node_err; + + /* Get the interrupts property */ + ret = irq_of_parse_and_map(np, 0); + if (!ret) + goto ioremap_err; + at91rm9200_timer_irq.irq = ret; + + of_node_put(np); + + return 0; + +ioremap_err: + iounmap(at91_st_base); +node_err: + of_node_put(np); +err: + return -EINVAL; +} +#else +static int __init of_at91rm9200_st_init(void) +{ + return -EINVAL; +} +#endif + void __init at91rm9200_ioremap_st(u32 addr) { +#ifdef CONFIG_OF + struct device_node *np; + + np = of_find_matching_node(NULL, at91rm9200_st_timer_ids); + if (np) { + of_node_put(np); + return; + } +#endif at91_st_base = ioremap(addr, 256); if (!at91_st_base) panic("Impossible to ioremap ST\n"); @@ -191,13 +247,16 @@ void __init at91rm9200_ioremap_st(u32 addr) */ void __init at91rm9200_timer_init(void) { + /* For device tree enabled device: initialize here */ + of_at91rm9200_st_init(); + /* Disable all timer interrupts, and clear any pending ones */ at91_st_write(AT91_ST_IDR, AT91_ST_PITS | AT91_ST_WDOVF | AT91_ST_RTTINC | AT91_ST_ALMS); at91_st_read(AT91_ST_SR); /* Make IRQs happen for the system timer */ - setup_irq(NR_IRQS_LEGACY + AT91_ID_SYS, &at91rm9200_timer_irq); + setup_irq(at91rm9200_timer_irq.irq, &at91rm9200_timer_irq); /* The 32KiHz "Slow Clock" (tick every 30517.58 nanoseconds) is used * directly for the clocksource and all clockevents, after adjusting -- cgit v1.2.3 From 6edc794a5ff245faf60488d32e9fdbeb0aad2223 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Wed, 7 Nov 2012 08:44:59 +0900 Subject: pinctrl: samsung: Add support for EXYNOS4X12 This patch extends the driver with any necessary SoC-specific definitions to support EXYNOS4X12 SoCs. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Acked-by: Thomas Abraham Acked-by: Linus Walleij Signed-off-by: Kukjin Kim --- .../bindings/pinctrl/samsung-pinctrl.txt | 1 + drivers/pinctrl/pinctrl-exynos.c | 110 +++++++++++++++++++++ drivers/pinctrl/pinctrl-samsung.c | 2 + drivers/pinctrl/pinctrl-samsung.h | 1 + 4 files changed, 114 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt index 63806e2d49c2..e97a27856b21 100644 --- a/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt @@ -8,6 +8,7 @@ on-chip controllers onto these pads. Required Properties: - compatible: should be one of the following. - "samsung,pinctrl-exynos4210": for Exynos4210 compatible pin-controller. + - "samsung,pinctrl-exynos4x12": for Exynos4x12 compatible pin-controller. - "samsung,pinctrl-exynos5250": for Exynos5250 compatible pin-controller. - reg: Base address of the pin controller hardware module and length of diff --git a/drivers/pinctrl/pinctrl-exynos.c b/drivers/pinctrl/pinctrl-exynos.c index 73a0aa27cd56..19fab68a9fbf 100644 --- a/drivers/pinctrl/pinctrl-exynos.c +++ b/drivers/pinctrl/pinctrl-exynos.c @@ -566,3 +566,113 @@ struct samsung_pin_ctrl exynos4210_pin_ctrl[] = { .label = "exynos4210-gpio-ctrl2", }, }; + +/* pin banks of exynos4x12 pin-controller 0 */ +static struct samsung_pin_bank exynos4x12_pin_banks0[] = { + EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpa0", 0x00), + EXYNOS_PIN_BANK_EINTG(6, 0x020, "gpa1", 0x04), + EXYNOS_PIN_BANK_EINTG(8, 0x040, "gpb", 0x08), + EXYNOS_PIN_BANK_EINTG(5, 0x060, "gpc0", 0x0c), + EXYNOS_PIN_BANK_EINTG(5, 0x080, "gpc1", 0x10), + EXYNOS_PIN_BANK_EINTG(4, 0x0A0, "gpd0", 0x14), + EXYNOS_PIN_BANK_EINTG(4, 0x0C0, "gpd1", 0x18), + EXYNOS_PIN_BANK_EINTG(8, 0x180, "gpf0", 0x30), + EXYNOS_PIN_BANK_EINTG(8, 0x1A0, "gpf1", 0x34), + EXYNOS_PIN_BANK_EINTG(8, 0x1C0, "gpf2", 0x38), + EXYNOS_PIN_BANK_EINTG(6, 0x1E0, "gpf3", 0x3c), + EXYNOS_PIN_BANK_EINTG(8, 0x240, "gpj0", 0x40), + EXYNOS_PIN_BANK_EINTG(5, 0x260, "gpj1", 0x44), +}; + +/* pin banks of exynos4x12 pin-controller 1 */ +static struct samsung_pin_bank exynos4x12_pin_banks1[] = { + EXYNOS_PIN_BANK_EINTG(7, 0x040, "gpk0", 0x08), + EXYNOS_PIN_BANK_EINTG(7, 0x060, "gpk1", 0x0c), + EXYNOS_PIN_BANK_EINTG(7, 0x080, "gpk2", 0x10), + EXYNOS_PIN_BANK_EINTG(7, 0x0A0, "gpk3", 0x14), + EXYNOS_PIN_BANK_EINTG(7, 0x0C0, "gpl0", 0x18), + EXYNOS_PIN_BANK_EINTG(2, 0x0E0, "gpl1", 0x1c), + EXYNOS_PIN_BANK_EINTG(8, 0x100, "gpl2", 0x20), + EXYNOS_PIN_BANK_EINTG(8, 0x260, "gpm0", 0x24), + EXYNOS_PIN_BANK_EINTG(7, 0x280, "gpm1", 0x28), + EXYNOS_PIN_BANK_EINTG(5, 0x2A0, "gpm2", 0x2c), + EXYNOS_PIN_BANK_EINTG(8, 0x2C0, "gpm3", 0x30), + EXYNOS_PIN_BANK_EINTG(8, 0x2E0, "gpm4", 0x34), + EXYNOS_PIN_BANK_EINTN(6, 0x120, "gpy0"), + EXYNOS_PIN_BANK_EINTN(4, 0x140, "gpy1"), + EXYNOS_PIN_BANK_EINTN(6, 0x160, "gpy2"), + EXYNOS_PIN_BANK_EINTN(8, 0x180, "gpy3"), + EXYNOS_PIN_BANK_EINTN(8, 0x1A0, "gpy4"), + EXYNOS_PIN_BANK_EINTN(8, 0x1C0, "gpy5"), + EXYNOS_PIN_BANK_EINTN(8, 0x1E0, "gpy6"), + EXYNOS_PIN_BANK_EINTW(8, 0xC00, "gpx0", 0x00), + EXYNOS_PIN_BANK_EINTW(8, 0xC20, "gpx1", 0x04), + EXYNOS_PIN_BANK_EINTW(8, 0xC40, "gpx2", 0x08), + EXYNOS_PIN_BANK_EINTW(8, 0xC60, "gpx3", 0x0c), +}; + +/* pin banks of exynos4x12 pin-controller 2 */ +static struct samsung_pin_bank exynos4x12_pin_banks2[] = { + EXYNOS_PIN_BANK_EINTG(7, 0x000, "gpz", 0x00), +}; + +/* pin banks of exynos4x12 pin-controller 3 */ +static struct samsung_pin_bank exynos4x12_pin_banks3[] = { + EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpv0", 0x00), + EXYNOS_PIN_BANK_EINTG(8, 0x020, "gpv1", 0x04), + EXYNOS_PIN_BANK_EINTG(8, 0x040, "gpv2", 0x08), + EXYNOS_PIN_BANK_EINTG(8, 0x060, "gpv3", 0x0c), + EXYNOS_PIN_BANK_EINTG(2, 0x080, "gpv4", 0x10), +}; + +/* + * Samsung pinctrl driver data for Exynos4x12 SoC. Exynos4x12 SoC includes + * four gpio/pin-mux/pinconfig controllers. + */ +struct samsung_pin_ctrl exynos4x12_pin_ctrl[] = { + { + /* pin-controller instance 0 data */ + .pin_banks = exynos4x12_pin_banks0, + .nr_banks = ARRAY_SIZE(exynos4x12_pin_banks0), + .geint_con = EXYNOS_GPIO_ECON_OFFSET, + .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, + .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, + .svc = EXYNOS_SVC_OFFSET, + .eint_gpio_init = exynos_eint_gpio_init, + .label = "exynos4x12-gpio-ctrl0", + }, { + /* pin-controller instance 1 data */ + .pin_banks = exynos4x12_pin_banks1, + .nr_banks = ARRAY_SIZE(exynos4x12_pin_banks1), + .geint_con = EXYNOS_GPIO_ECON_OFFSET, + .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, + .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, + .weint_con = EXYNOS_WKUP_ECON_OFFSET, + .weint_mask = EXYNOS_WKUP_EMASK_OFFSET, + .weint_pend = EXYNOS_WKUP_EPEND_OFFSET, + .svc = EXYNOS_SVC_OFFSET, + .eint_gpio_init = exynos_eint_gpio_init, + .eint_wkup_init = exynos_eint_wkup_init, + .label = "exynos4x12-gpio-ctrl1", + }, { + /* pin-controller instance 2 data */ + .pin_banks = exynos4x12_pin_banks2, + .nr_banks = ARRAY_SIZE(exynos4x12_pin_banks2), + .geint_con = EXYNOS_GPIO_ECON_OFFSET, + .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, + .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, + .svc = EXYNOS_SVC_OFFSET, + .eint_gpio_init = exynos_eint_gpio_init, + .label = "exynos4x12-gpio-ctrl2", + }, { + /* pin-controller instance 3 data */ + .pin_banks = exynos4x12_pin_banks3, + .nr_banks = ARRAY_SIZE(exynos4x12_pin_banks3), + .geint_con = EXYNOS_GPIO_ECON_OFFSET, + .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, + .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, + .svc = EXYNOS_SVC_OFFSET, + .eint_gpio_init = exynos_eint_gpio_init, + .label = "exynos4x12-gpio-ctrl3", + }, +}; diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index fc34cac8a1b0..81c9896d4f64 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -947,6 +947,8 @@ static int __devinit samsung_pinctrl_probe(struct platform_device *pdev) static const struct of_device_id samsung_pinctrl_dt_match[] = { { .compatible = "samsung,pinctrl-exynos4210", .data = (void *)exynos4210_pin_ctrl }, + { .compatible = "samsung,pinctrl-exynos4x12", + .data = (void *)exynos4x12_pin_ctrl }, {}, }; MODULE_DEVICE_TABLE(of, samsung_pinctrl_dt_match); diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h index 0670d9ea43fa..5addfd16e3cc 100644 --- a/drivers/pinctrl/pinctrl-samsung.h +++ b/drivers/pinctrl/pinctrl-samsung.h @@ -236,5 +236,6 @@ struct samsung_pmx_func { /* list of all exported SoC specific data */ extern struct samsung_pin_ctrl exynos4210_pin_ctrl[]; +extern struct samsung_pin_ctrl exynos4x12_pin_ctrl[]; #endif /* __PINCTRL_SAMSUNG_H */ -- cgit v1.2.3 From e0f1abeba5c2d8a2183566717d99294fd1a29c2e Mon Sep 17 00:00:00 2001 From: "Rajanikanth H.V" Date: Sun, 18 Nov 2012 18:45:41 -0800 Subject: ab8500: Add devicetree support for fuelgauge - This patch adds device tree support for fuelgauge driver - optimize bm devices platform_data usage and of_probe(...) Note: of_probe() routine for battery managed devices is made common across all bm drivers. - test status: - interrupt numbers assigned differs between legacy and FDT mode. Signed-off-by: Rajanikanth H.V Signed-off-by: Anton Vorontsov --- Documentation/devicetree/bindings/mfd/ab8500.txt | 7 +- .../devicetree/bindings/power_supply/ab8500/fg.txt | 58 +++ arch/arm/boot/dts/dbx5x0.dtsi | 12 +- drivers/mfd/ab8500-core.c | 5 + drivers/power/Makefile | 2 +- drivers/power/ab8500_bmdata.c | 521 +++++++++++++++++++++ drivers/power/ab8500_btemp.c | 16 +- drivers/power/ab8500_charger.c | 16 +- drivers/power/ab8500_fg.c | 82 ++-- drivers/power/abx500_chargalg.c | 8 +- include/linux/mfd/abx500.h | 36 +- 11 files changed, 667 insertions(+), 96 deletions(-) create mode 100644 Documentation/devicetree/bindings/power_supply/ab8500/fg.txt create mode 100644 drivers/power/ab8500_bmdata.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mfd/ab8500.txt b/Documentation/devicetree/bindings/mfd/ab8500.txt index ce83c8d3c00e..6ca8d817ef92 100644 --- a/Documentation/devicetree/bindings/mfd/ab8500.txt +++ b/Documentation/devicetree/bindings/mfd/ab8500.txt @@ -24,7 +24,12 @@ ab8500-bm : : : Battery Manager ab8500-btemp : : : Battery Temperature ab8500-charger : : : Battery Charger ab8500-codec : : : Audio Codec -ab8500-fg : : : Fuel Gauge +ab8500-fg : : vddadc : Fuel Gauge + : NCONV_ACCU : : Accumulate N Sample Conversion + : BATT_OVV : : Battery Over Voltage + : LOW_BAT_F : : LOW threshold battery voltage + : CC_INT_CALIB : : Coulomb Counter Internal Calibration + : CCEOC : : Coulomb Counter End of Conversion ab8500-gpadc : HW_CONV_END : vddadc : Analogue to Digital Converter SW_CONV_END : : ab8500-gpio : : : GPIO Controller diff --git a/Documentation/devicetree/bindings/power_supply/ab8500/fg.txt b/Documentation/devicetree/bindings/power_supply/ab8500/fg.txt new file mode 100644 index 000000000000..ccafcb9112fb --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/ab8500/fg.txt @@ -0,0 +1,58 @@ +=== AB8500 Fuel Gauge Driver === + +AB8500 is a mixed signal multimedia and power management +device comprising: power and energy-management-module, +wall-charger, usb-charger, audio codec, general purpose adc, +tvout, clock management and sim card interface. + +Fuelgauge support is part of energy-management-modules, other +components of this module are: +main-charger, usb-combo-charger and battery-temperature-monitoring. + +The properties below describes the node for fuelgauge driver. + +Required Properties: +- compatible = This shall be: "stericsson,ab8500-fg" +- battery = Shall be battery specific information + Example: + ab8500_fg { + compatible = "stericsson,ab8500-fg"; + battery = <&ab8500_battery>; + }; + +dependent node: + ab8500_battery: ab8500_battery { + }; + This node will provide information on 'thermistor interface' and + 'battery technology type' used. + +Properties of this node are: +thermistor-on-batctrl: + A boolean value indicating thermistor interface to battery + + Note: + 'btemp' and 'batctrl' are the pins interfaced for battery temperature + measurement, 'btemp' signal is used when NTC(negative temperature + coefficient) resister is interfaced external to battery whereas + 'batctrl' pin is used when NTC resister is internal to battery. + + Example: + ab8500_battery: ab8500_battery { + thermistor-on-batctrl; + }; + indicates: NTC resister is internal to battery, 'batctrl' is used + for thermal measurement. + + The absence of property 'thermal-on-batctrl' indicates + NTC resister is external to battery and 'btemp' signal is used + for thermal measurement. + +battery-type: + This shall be the battery manufacturing technology type, + allowed types are: + "UNKNOWN" "NiMH" "LION" "LIPO" "LiFe" "NiCd" "LiMn" + Example: + ab8500_battery: ab8500_battery { + stericsson,battery-type = "LIPO"; + } + diff --git a/arch/arm/boot/dts/dbx5x0.dtsi b/arch/arm/boot/dts/dbx5x0.dtsi index 4b0e0ca08f40..0c81986904c5 100644 --- a/arch/arm/boot/dts/dbx5x0.dtsi +++ b/arch/arm/boot/dts/dbx5x0.dtsi @@ -352,7 +352,17 @@ vddadc-supply = <&ab8500_ldo_tvout_reg>; }; - ab8500-usb { + ab8500_battery: ab8500_battery { + stericsson,battery-type = "LIPO"; + thermistor-on-batctrl; + }; + + ab8500_fg { + compatible = "stericsson,ab8500-fg"; + battery = <&ab8500_battery>; + }; + + ab8500_usb { compatible = "stericsson,ab8500-usb"; interrupts = < 90 0x4 96 0x4 diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 1667c77b5cde..7c3017ba73e4 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1051,8 +1051,13 @@ static struct mfd_cell __devinitdata ab8500_bm_devs[] = { }, { .name = "ab8500-fg", + .of_compatible = "stericsson,ab8500-fg", .num_resources = ARRAY_SIZE(ab8500_fg_resources), .resources = ab8500_fg_resources, +#ifndef CONFIG_OF + .platform_data = &ab8500_bm_data, + .pdata_size = sizeof(ab8500_bm_data), +#endif }, { .name = "ab8500-chargalg", diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 74dfd9570154..696e3a960b3e 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -38,7 +38,7 @@ obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o -obj-$(CONFIG_AB8500_BM) += ab8500_charger.o ab8500_btemp.o ab8500_fg.o abx500_chargalg.o +obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_btemp.o ab8500_fg.o abx500_chargalg.o obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o diff --git a/drivers/power/ab8500_bmdata.c b/drivers/power/ab8500_bmdata.c new file mode 100644 index 000000000000..e7639b6659f7 --- /dev/null +++ b/drivers/power/ab8500_bmdata.c @@ -0,0 +1,521 @@ +#include +#include +#include +#include +#include +#include + +/* + * These are the defined batteries that uses a NTC and ID resistor placed + * inside of the battery pack. + * Note that the res_to_temp table must be strictly sorted by falling resistance + * values to work. + */ +static struct abx500_res_to_temp temp_tbl_A_thermistor[] = { + {-5, 53407}, + { 0, 48594}, + { 5, 43804}, + {10, 39188}, + {15, 34870}, + {20, 30933}, + {25, 27422}, + {30, 24347}, + {35, 21694}, + {40, 19431}, + {45, 17517}, + {50, 15908}, + {55, 14561}, + {60, 13437}, + {65, 12500}, +}; + +static struct abx500_res_to_temp temp_tbl_B_thermistor[] = { + {-5, 165418}, + { 0, 159024}, + { 5, 151921}, + {10, 144300}, + {15, 136424}, + {20, 128565}, + {25, 120978}, + {30, 113875}, + {35, 107397}, + {40, 101629}, + {45, 96592}, + {50, 92253}, + {55, 88569}, + {60, 85461}, + {65, 82869}, +}; + +static struct abx500_v_to_cap cap_tbl_A_thermistor[] = { + {4171, 100}, + {4114, 95}, + {4009, 83}, + {3947, 74}, + {3907, 67}, + {3863, 59}, + {3830, 56}, + {3813, 53}, + {3791, 46}, + {3771, 33}, + {3754, 25}, + {3735, 20}, + {3717, 17}, + {3681, 13}, + {3664, 8}, + {3651, 6}, + {3635, 5}, + {3560, 3}, + {3408, 1}, + {3247, 0}, +}; + +static struct abx500_v_to_cap cap_tbl_B_thermistor[] = { + {4161, 100}, + {4124, 98}, + {4044, 90}, + {4003, 85}, + {3966, 80}, + {3933, 75}, + {3888, 67}, + {3849, 60}, + {3813, 55}, + {3787, 47}, + {3772, 30}, + {3751, 25}, + {3718, 20}, + {3681, 16}, + {3660, 14}, + {3589, 10}, + {3546, 7}, + {3495, 4}, + {3404, 2}, + {3250, 0}, +}; + +static struct abx500_v_to_cap cap_tbl[] = { + {4186, 100}, + {4163, 99}, + {4114, 95}, + {4068, 90}, + {3990, 80}, + {3926, 70}, + {3898, 65}, + {3866, 60}, + {3833, 55}, + {3812, 50}, + {3787, 40}, + {3768, 30}, + {3747, 25}, + {3730, 20}, + {3705, 15}, + {3699, 14}, + {3684, 12}, + {3672, 9}, + {3657, 7}, + {3638, 6}, + {3556, 4}, + {3424, 2}, + {3317, 1}, + {3094, 0}, +}; + +/* + * Note that the res_to_temp table must be strictly sorted by falling + * resistance values to work. + */ +static struct abx500_res_to_temp temp_tbl[] = { + {-5, 214834}, + { 0, 162943}, + { 5, 124820}, + {10, 96520}, + {15, 75306}, + {20, 59254}, + {25, 47000}, + {30, 37566}, + {35, 30245}, + {40, 24520}, + {45, 20010}, + {50, 16432}, + {55, 13576}, + {60, 11280}, + {65, 9425}, +}; + +/* + * Note that the batres_vs_temp table must be strictly sorted by falling + * temperature values to work. + */ +static struct batres_vs_temp temp_to_batres_tbl_thermistor[] = { + { 40, 120}, + { 30, 135}, + { 20, 165}, + { 10, 230}, + { 00, 325}, + {-10, 445}, + {-20, 595}, +}; + +/* + * Note that the batres_vs_temp table must be strictly sorted by falling + * temperature values to work. + */ +static struct batres_vs_temp temp_to_batres_tbl_ext_thermistor[] = { + { 60, 300}, + { 30, 300}, + { 20, 300}, + { 10, 300}, + { 00, 300}, + {-10, 300}, + {-20, 300}, +}; + +/* battery resistance table for LI ION 9100 battery */ +static struct batres_vs_temp temp_to_batres_tbl_9100[] = { + { 60, 180}, + { 30, 180}, + { 20, 180}, + { 10, 180}, + { 00, 180}, + {-10, 180}, + {-20, 180}, +}; + +static struct abx500_battery_type bat_type_thermistor[] = { +[BATTERY_UNKNOWN] = { + /* First element always represent the UNKNOWN battery */ + .name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN, + .resis_high = 0, + .resis_low = 0, + .battery_resistance = 300, + .charge_full_design = 612, + .nominal_voltage = 3700, + .termination_vol = 4050, + .termination_curr = 200, + .recharge_vol = 3990, + .normal_cur_lvl = 400, + .normal_vol_lvl = 4100, + .maint_a_cur_lvl = 400, + .maint_a_vol_lvl = 4050, + .maint_a_chg_timer_h = 60, + .maint_b_cur_lvl = 400, + .maint_b_vol_lvl = 4000, + .maint_b_chg_timer_h = 200, + .low_high_cur_lvl = 300, + .low_high_vol_lvl = 4000, + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl), + .r_to_t_tbl = temp_tbl, + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl), + .v_to_cap_tbl = cap_tbl, + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), + .batres_tbl = temp_to_batres_tbl_thermistor, +}, +{ + .name = POWER_SUPPLY_TECHNOLOGY_LIPO, + .resis_high = 53407, + .resis_low = 12500, + .battery_resistance = 300, + .charge_full_design = 900, + .nominal_voltage = 3600, + .termination_vol = 4150, + .termination_curr = 80, + .recharge_vol = 4130, + .normal_cur_lvl = 700, + .normal_vol_lvl = 4200, + .maint_a_cur_lvl = 600, + .maint_a_vol_lvl = 4150, + .maint_a_chg_timer_h = 60, + .maint_b_cur_lvl = 600, + .maint_b_vol_lvl = 4100, + .maint_b_chg_timer_h = 200, + .low_high_cur_lvl = 300, + .low_high_vol_lvl = 4000, + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_A_thermistor), + .r_to_t_tbl = temp_tbl_A_thermistor, + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_A_thermistor), + .v_to_cap_tbl = cap_tbl_A_thermistor, + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), + .batres_tbl = temp_to_batres_tbl_thermistor, + +}, +{ + .name = POWER_SUPPLY_TECHNOLOGY_LIPO, + .resis_high = 165418, + .resis_low = 82869, + .battery_resistance = 300, + .charge_full_design = 900, + .nominal_voltage = 3600, + .termination_vol = 4150, + .termination_curr = 80, + .recharge_vol = 4130, + .normal_cur_lvl = 700, + .normal_vol_lvl = 4200, + .maint_a_cur_lvl = 600, + .maint_a_vol_lvl = 4150, + .maint_a_chg_timer_h = 60, + .maint_b_cur_lvl = 600, + .maint_b_vol_lvl = 4100, + .maint_b_chg_timer_h = 200, + .low_high_cur_lvl = 300, + .low_high_vol_lvl = 4000, + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_B_thermistor), + .r_to_t_tbl = temp_tbl_B_thermistor, + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_B_thermistor), + .v_to_cap_tbl = cap_tbl_B_thermistor, + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), + .batres_tbl = temp_to_batres_tbl_thermistor, +}, +}; + +static struct abx500_battery_type bat_type_ext_thermistor[] = { +[BATTERY_UNKNOWN] = { + /* First element always represent the UNKNOWN battery */ + .name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN, + .resis_high = 0, + .resis_low = 0, + .battery_resistance = 300, + .charge_full_design = 612, + .nominal_voltage = 3700, + .termination_vol = 4050, + .termination_curr = 200, + .recharge_vol = 3990, + .normal_cur_lvl = 400, + .normal_vol_lvl = 4100, + .maint_a_cur_lvl = 400, + .maint_a_vol_lvl = 4050, + .maint_a_chg_timer_h = 60, + .maint_b_cur_lvl = 400, + .maint_b_vol_lvl = 4000, + .maint_b_chg_timer_h = 200, + .low_high_cur_lvl = 300, + .low_high_vol_lvl = 4000, + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl), + .r_to_t_tbl = temp_tbl, + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl), + .v_to_cap_tbl = cap_tbl, + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), + .batres_tbl = temp_to_batres_tbl_thermistor, +}, +/* + * These are the batteries that doesn't have an internal NTC resistor to measure + * its temperature. The temperature in this case is measure with a NTC placed + * near the battery but on the PCB. + */ +{ + .name = POWER_SUPPLY_TECHNOLOGY_LIPO, + .resis_high = 76000, + .resis_low = 53000, + .battery_resistance = 300, + .charge_full_design = 900, + .nominal_voltage = 3700, + .termination_vol = 4150, + .termination_curr = 100, + .recharge_vol = 4130, + .normal_cur_lvl = 700, + .normal_vol_lvl = 4200, + .maint_a_cur_lvl = 600, + .maint_a_vol_lvl = 4150, + .maint_a_chg_timer_h = 60, + .maint_b_cur_lvl = 600, + .maint_b_vol_lvl = 4100, + .maint_b_chg_timer_h = 200, + .low_high_cur_lvl = 300, + .low_high_vol_lvl = 4000, + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl), + .r_to_t_tbl = temp_tbl, + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl), + .v_to_cap_tbl = cap_tbl, + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), + .batres_tbl = temp_to_batres_tbl_thermistor, +}, +{ + .name = POWER_SUPPLY_TECHNOLOGY_LION, + .resis_high = 30000, + .resis_low = 10000, + .battery_resistance = 300, + .charge_full_design = 950, + .nominal_voltage = 3700, + .termination_vol = 4150, + .termination_curr = 100, + .recharge_vol = 4130, + .normal_cur_lvl = 700, + .normal_vol_lvl = 4200, + .maint_a_cur_lvl = 600, + .maint_a_vol_lvl = 4150, + .maint_a_chg_timer_h = 60, + .maint_b_cur_lvl = 600, + .maint_b_vol_lvl = 4100, + .maint_b_chg_timer_h = 200, + .low_high_cur_lvl = 300, + .low_high_vol_lvl = 4000, + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl), + .r_to_t_tbl = temp_tbl, + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl), + .v_to_cap_tbl = cap_tbl, + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), + .batres_tbl = temp_to_batres_tbl_thermistor, +}, +{ + .name = POWER_SUPPLY_TECHNOLOGY_LION, + .resis_high = 95000, + .resis_low = 76001, + .battery_resistance = 300, + .charge_full_design = 950, + .nominal_voltage = 3700, + .termination_vol = 4150, + .termination_curr = 100, + .recharge_vol = 4130, + .normal_cur_lvl = 700, + .normal_vol_lvl = 4200, + .maint_a_cur_lvl = 600, + .maint_a_vol_lvl = 4150, + .maint_a_chg_timer_h = 60, + .maint_b_cur_lvl = 600, + .maint_b_vol_lvl = 4100, + .maint_b_chg_timer_h = 200, + .low_high_cur_lvl = 300, + .low_high_vol_lvl = 4000, + .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl), + .r_to_t_tbl = temp_tbl, + .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl), + .v_to_cap_tbl = cap_tbl, + .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl_thermistor), + .batres_tbl = temp_to_batres_tbl_thermistor, +}, +}; + +static const struct abx500_bm_capacity_levels cap_levels = { + .critical = 2, + .low = 10, + .normal = 70, + .high = 95, + .full = 100, +}; + +static const struct abx500_fg_parameters fg = { + .recovery_sleep_timer = 10, + .recovery_total_time = 100, + .init_timer = 1, + .init_discard_time = 5, + .init_total_time = 40, + .high_curr_time = 60, + .accu_charging = 30, + .accu_high_curr = 30, + .high_curr_threshold = 50, + .lowbat_threshold = 3100, + .battok_falling_th_sel0 = 2860, + .battok_raising_th_sel1 = 2860, + .user_cap_limit = 15, + .maint_thres = 97, +}; + +static const struct abx500_maxim_parameters maxi_params = { + .ena_maxi = true, + .chg_curr = 910, + .wait_cycles = 10, + .charger_curr_step = 100, +}; + +static const struct abx500_bm_charger_parameters chg = { + .usb_volt_max = 5500, + .usb_curr_max = 1500, + .ac_volt_max = 7500, + .ac_curr_max = 1500, +}; + +struct abx500_bm_data ab8500_bm_data = { + .temp_under = 3, + .temp_low = 8, + .temp_high = 43, + .temp_over = 48, + .main_safety_tmr_h = 4, + .temp_interval_chg = 20, + .temp_interval_nochg = 120, + .usb_safety_tmr_h = 4, + .bkup_bat_v = BUP_VCH_SEL_2P6V, + .bkup_bat_i = BUP_ICH_SEL_150UA, + .no_maintenance = false, + .adc_therm = ABx500_ADC_THERM_BATCTRL, + .chg_unknown_bat = false, + .enable_overshoot = false, + .fg_res = 100, + .cap_levels = &cap_levels, + .bat_type = bat_type_thermistor, + .n_btypes = 3, + .batt_id = 0, + .interval_charging = 5, + .interval_not_charging = 120, + .temp_hysteresis = 3, + .gnd_lift_resistance = 34, + .maxi = &maxi_params, + .chg_params = &chg, + .fg_params = &fg, +}; + +int __devinit +bmdevs_of_probe(struct device *dev, + struct device_node *np, + struct abx500_bm_data **battery) +{ + struct abx500_battery_type *btype; + struct device_node *np_bat_supply; + struct abx500_bm_data *bat; + const char *btech; + char bat_tech[8]; + int i, thermistor; + + *battery = &ab8500_bm_data; + + /* get phandle to 'battery-info' node */ + np_bat_supply = of_parse_phandle(np, "battery", 0); + if (!np_bat_supply) { + dev_err(dev, "missing property battery\n"); + return -EINVAL; + } + if (of_property_read_bool(np_bat_supply, + "thermistor-on-batctrl")) + thermistor = NTC_INTERNAL; + else + thermistor = NTC_EXTERNAL; + + bat = *battery; + if (thermistor == NTC_EXTERNAL) { + bat->n_btypes = 4; + bat->bat_type = bat_type_ext_thermistor; + bat->adc_therm = ABx500_ADC_THERM_BATTEMP; + } + btech = of_get_property(np_bat_supply, + "stericsson,battery-type", NULL); + if (!btech) { + dev_warn(dev, "missing property battery-name/type\n"); + strcpy(bat_tech, "UNKNOWN"); + } else { + strcpy(bat_tech, btech); + } + + if (strncmp(bat_tech, "LION", 4) == 0) { + bat->no_maintenance = true; + bat->chg_unknown_bat = true; + bat->bat_type[BATTERY_UNKNOWN].charge_full_design = 2600; + bat->bat_type[BATTERY_UNKNOWN].termination_vol = 4150; + bat->bat_type[BATTERY_UNKNOWN].recharge_vol = 4130; + bat->bat_type[BATTERY_UNKNOWN].normal_cur_lvl = 520; + bat->bat_type[BATTERY_UNKNOWN].normal_vol_lvl = 4200; + } + /* select the battery resolution table */ + for (i = 0; i < bat->n_btypes; ++i) { + btype = (bat->bat_type + i); + if (thermistor == NTC_EXTERNAL) { + btype->batres_tbl = + temp_to_batres_tbl_ext_thermistor; + } else if (strncmp(bat_tech, "LION", 4) == 0) { + btype->batres_tbl = + temp_to_batres_tbl_9100; + } else { + btype->batres_tbl = + temp_to_batres_tbl_thermistor; + } + } + of_node_put(np_bat_supply); + return 0; +} diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c index e3b6395b20dd..abc2abc16a5d 100644 --- a/drivers/power/ab8500_btemp.c +++ b/drivers/power/ab8500_btemp.c @@ -93,7 +93,7 @@ struct ab8500_btemp { struct ab8500 *parent; struct ab8500_gpadc *gpadc; struct ab8500_fg *fg; - struct abx500_btemp_platform_data *pdata; + struct abx500_bmdevs_plat_data *pdata; struct abx500_bm_data *bat; struct power_supply btemp_psy; struct ab8500_btemp_events events; @@ -962,10 +962,10 @@ static int __devexit ab8500_btemp_remove(struct platform_device *pdev) static int __devinit ab8500_btemp_probe(struct platform_device *pdev) { + struct abx500_bmdevs_plat_data *plat_data = pdev->dev.platform_data; + struct ab8500_btemp *di; int irq, i, ret = 0; u8 val; - struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data; - struct ab8500_btemp *di; if (!plat_data) { dev_err(&pdev->dev, "No platform data\n"); @@ -982,21 +982,13 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev) di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); /* get btemp specific platform data */ - di->pdata = plat_data->btemp; + di->pdata = plat_data; if (!di->pdata) { dev_err(di->dev, "no btemp platform data supplied\n"); ret = -EINVAL; goto free_device_info; } - /* get battery specific platform data */ - di->bat = plat_data->battery; - if (!di->bat) { - dev_err(di->dev, "no battery platform data supplied\n"); - ret = -EINVAL; - goto free_device_info; - } - /* BTEMP supply */ di->btemp_psy.name = "ab8500_btemp"; di->btemp_psy.type = POWER_SUPPLY_TYPE_BATTERY; diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index 26ff759e2220..723edb47b1d8 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -220,7 +220,7 @@ struct ab8500_charger { bool autopower; struct ab8500 *parent; struct ab8500_gpadc *gpadc; - struct abx500_charger_platform_data *pdata; + struct abx500_bmdevs_plat_data *pdata; struct abx500_bm_data *bat; struct ab8500_charger_event_flags flags; struct ab8500_charger_usb_state usb_state; @@ -2533,9 +2533,9 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev) static int __devinit ab8500_charger_probe(struct platform_device *pdev) { - int irq, i, charger_status, ret = 0; - struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data; + struct abx500_bmdevs_plat_data *plat_data = pdev->dev.platform_data; struct ab8500_charger *di; + int irq, i, charger_status, ret = 0; if (!plat_data) { dev_err(&pdev->dev, "No platform data\n"); @@ -2555,21 +2555,13 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) spin_lock_init(&di->usb_state.usb_lock); /* get charger specific platform data */ - di->pdata = plat_data->charger; + di->pdata = plat_data; if (!di->pdata) { dev_err(di->dev, "no charger platform data supplied\n"); ret = -EINVAL; goto free_device_info; } - /* get battery specific platform data */ - di->bat = plat_data->battery; - if (!di->bat) { - dev_err(di->dev, "no battery platform data supplied\n"); - ret = -EINVAL; - goto free_device_info; - } - di->autopower = false; /* AC supply */ diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 2db8cc254399..ed62ef788eb5 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -22,15 +22,16 @@ #include #include #include -#include -#include #include -#include #include -#include -#include #include +#include #include +#include +#include +#include +#include +#include #define MILLI_TO_MICRO 1000 #define FG_LSB_IN_MA 1627 @@ -172,7 +173,6 @@ struct inst_curr_result_list { * @avg_cap: Average capacity filter * @parent: Pointer to the struct ab8500 * @gpadc: Pointer to the struct gpadc - * @pdata: Pointer to the abx500_fg platform data * @bat: Pointer to the abx500_bm platform data * @fg_psy: Structure that holds the FG specific battery properties * @fg_wq: Work queue for running the FG algorithm @@ -212,7 +212,6 @@ struct ab8500_fg { struct ab8500_fg_avg_cap avg_cap; struct ab8500 *parent; struct ab8500_gpadc *gpadc; - struct abx500_fg_platform_data *pdata; struct abx500_bm_data *bat; struct power_supply fg_psy; struct workqueue_struct *fg_wq; @@ -2429,7 +2428,6 @@ static int __devexit ab8500_fg_remove(struct platform_device *pdev) flush_scheduled_work(); power_supply_unregister(&di->fg_psy); platform_set_drvdata(pdev, NULL); - kfree(di); return ret; } @@ -2442,21 +2440,39 @@ static struct ab8500_fg_interrupts ab8500_fg_irq[] = { {"CCEOC", ab8500_fg_cc_data_end_handler}, }; +static char *supply_interface[] = { + "ab8500_chargalg", + "ab8500_usb", +}; + static int __devinit ab8500_fg_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; + struct ab8500_fg *di; int i, irq; int ret = 0; - struct abx500_bm_plat_data *plat_data = pdev->dev.platform_data; - struct ab8500_fg *di; - - if (!plat_data) { - dev_err(&pdev->dev, "No platform data\n"); - return -EINVAL; - } - di = kzalloc(sizeof(*di), GFP_KERNEL); - if (!di) + di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); + if (!di) { + dev_err(&pdev->dev, "%s no mem for ab8500_fg\n", __func__); return -ENOMEM; + } + di->bat = pdev->mfd_cell->platform_data; + if (!di->bat) { + if (np) { + ret = bmdevs_of_probe(&pdev->dev, np, &di->bat); + if (ret) { + dev_err(&pdev->dev, + "failed to get battery information\n"); + return ret; + } + } else { + dev_err(&pdev->dev, "missing dt node for ab8500_fg\n"); + return -EINVAL; + } + } else { + dev_info(&pdev->dev, "falling back to legacy platform data\n"); + } mutex_init(&di->cc_lock); @@ -2465,29 +2481,13 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev) di->parent = dev_get_drvdata(pdev->dev.parent); di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - /* get fg specific platform data */ - di->pdata = plat_data->fg; - if (!di->pdata) { - dev_err(di->dev, "no fg platform data supplied\n"); - ret = -EINVAL; - goto free_device_info; - } - - /* get battery specific platform data */ - di->bat = plat_data->battery; - if (!di->bat) { - dev_err(di->dev, "no battery platform data supplied\n"); - ret = -EINVAL; - goto free_device_info; - } - di->fg_psy.name = "ab8500_fg"; di->fg_psy.type = POWER_SUPPLY_TYPE_BATTERY; di->fg_psy.properties = ab8500_fg_props; di->fg_psy.num_properties = ARRAY_SIZE(ab8500_fg_props); di->fg_psy.get_property = ab8500_fg_get_property; - di->fg_psy.supplied_to = di->pdata->supplied_to; - di->fg_psy.num_supplicants = di->pdata->num_supplicants; + di->fg_psy.supplied_to = supply_interface; + di->fg_psy.num_supplicants = ARRAY_SIZE(supply_interface), di->fg_psy.external_power_changed = ab8500_fg_external_power_changed; di->bat_cap.max_mah_design = MILLI_TO_MICRO * @@ -2506,8 +2506,7 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev) di->fg_wq = create_singlethread_workqueue("ab8500_fg_wq"); if (di->fg_wq == NULL) { dev_err(di->dev, "failed to create work queue\n"); - ret = -ENOMEM; - goto free_device_info; + return -ENOMEM; } /* Init work for running the fg algorithm instantly */ @@ -2606,12 +2605,14 @@ free_irq: } free_inst_curr_wq: destroy_workqueue(di->fg_wq); -free_device_info: - kfree(di); - return ret; } +static const struct of_device_id ab8500_fg_match[] = { + { .compatible = "stericsson,ab8500-fg", }, + { }, +}; + static struct platform_driver ab8500_fg_driver = { .probe = ab8500_fg_probe, .remove = __devexit_p(ab8500_fg_remove), @@ -2620,6 +2621,7 @@ static struct platform_driver ab8500_fg_driver = { .driver = { .name = "ab8500-fg", .owner = THIS_MODULE, + .of_match_table = ab8500_fg_match, }, }; diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c index 4d302803ffcc..758ea76de2f8 100644 --- a/drivers/power/abx500_chargalg.c +++ b/drivers/power/abx500_chargalg.c @@ -231,7 +231,7 @@ struct abx500_chargalg { struct abx500_chargalg_charger_info chg_info; struct abx500_chargalg_battery_data batt_data; struct abx500_chargalg_suspension_status susp_status; - struct abx500_chargalg_platform_data *pdata; + struct abx500_bmdevs_plat_data *pdata; struct abx500_bm_data *bat; struct power_supply chargalg_psy; struct ux500_charger *ac_chg; @@ -1802,7 +1802,7 @@ static int __devexit abx500_chargalg_remove(struct platform_device *pdev) static int __devinit abx500_chargalg_probe(struct platform_device *pdev) { - struct abx500_bm_plat_data *plat_data; + struct abx500_bmdevs_plat_data *plat_data; int ret = 0; struct abx500_chargalg *di = @@ -1812,10 +1812,8 @@ static int __devinit abx500_chargalg_probe(struct platform_device *pdev) /* get device struct */ di->dev = &pdev->dev; - plat_data = pdev->dev.platform_data; - di->pdata = plat_data->chargalg; - di->bat = plat_data->battery; + di->pdata = plat_data; /* chargalg supply */ di->chargalg_psy.name = "abx500_chargalg"; diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 5d5298d56026..33f2c58554f4 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -267,39 +267,27 @@ struct abx500_bm_data { int gnd_lift_resistance; const struct abx500_maxim_parameters *maxi; const struct abx500_bm_capacity_levels *cap_levels; - const struct abx500_battery_type *bat_type; + struct abx500_battery_type *bat_type; const struct abx500_bm_charger_parameters *chg_params; const struct abx500_fg_parameters *fg_params; }; -struct abx500_chargalg_platform_data { - char **supplied_to; - size_t num_supplicants; -}; - -struct abx500_charger_platform_data { - char **supplied_to; - size_t num_supplicants; - bool autopower_cfg; -}; +extern struct abx500_bm_data ab8500_bm_data; -struct abx500_btemp_platform_data { - char **supplied_to; - size_t num_supplicants; +struct abx500_bmdevs_plat_data { + char **supplied_to; + size_t num_supplicants; + bool autopower_cfg; }; -struct abx500_fg_platform_data { - char **supplied_to; - size_t num_supplicants; +enum { + NTC_EXTERNAL = 0, + NTC_INTERNAL, }; -struct abx500_bm_plat_data { - struct abx500_bm_data *battery; - struct abx500_charger_platform_data *charger; - struct abx500_btemp_platform_data *btemp; - struct abx500_fg_platform_data *fg; - struct abx500_chargalg_platform_data *chargalg; -}; +int bmdevs_of_probe(struct device *dev, + struct device_node *np, + struct abx500_bm_data **battery); int abx500_set_register_interruptible(struct device *dev, u8 bank, u8 reg, u8 value); -- cgit v1.2.3 From bd9e8ab2d58d2183aa723bf0eb8e07d4362b4975 Mon Sep 17 00:00:00 2001 From: "Rajanikanth H.V" Date: Sun, 18 Nov 2012 19:16:58 -0800 Subject: ab8500: Add devicetree support for btemp This patch adds device tree support for battery-temperature-monitor driver Signed-off-by: Rajanikanth H.V Signed-off-by: Anton Vorontsov --- Documentation/devicetree/bindings/mfd/ab8500.txt | 6 ++ .../bindings/power_supply/ab8500/btemp.txt | 16 ++++++ arch/arm/boot/dts/dbx5x0.dtsi | 5 ++ drivers/mfd/ab8500-core.c | 5 ++ drivers/power/Kconfig | 6 -- drivers/power/ab8500_bmdata.c | 4 +- drivers/power/ab8500_btemp.c | 67 +++++++++++++--------- 7 files changed, 73 insertions(+), 36 deletions(-) create mode 100644 Documentation/devicetree/bindings/power_supply/ab8500/btemp.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mfd/ab8500.txt b/Documentation/devicetree/bindings/mfd/ab8500.txt index 6ca8d817ef92..f2ee0e7a5da3 100644 --- a/Documentation/devicetree/bindings/mfd/ab8500.txt +++ b/Documentation/devicetree/bindings/mfd/ab8500.txt @@ -30,6 +30,12 @@ ab8500-fg : : vddadc : Fuel Gauge : LOW_BAT_F : : LOW threshold battery voltage : CC_INT_CALIB : : Coulomb Counter Internal Calibration : CCEOC : : Coulomb Counter End of Conversion +ab8500-btemp : : vtvout : Battery Temperature + : BAT_CTRL_INDB : : Battery Removal Indicator + : BTEMP_LOW : : Btemp < BtempLow, if battery temperature is lower than -10°C + : BTEMP_LOW_MEDIUM : : BtempLow < Btemp < BtempMedium,if battery temperature is between -10 and 0°C + : BTEMP_MEDIUM_HIGH : : BtempMedium < Btemp < BtempHigh,if battery temperature is between 0°C and“MaxTemp + : BTEMP_HIGH : : Btemp > BtempHigh, if battery temperature is higher than “MaxTemp ab8500-gpadc : HW_CONV_END : vddadc : Analogue to Digital Converter SW_CONV_END : : ab8500-gpio : : : GPIO Controller diff --git a/Documentation/devicetree/bindings/power_supply/ab8500/btemp.txt b/Documentation/devicetree/bindings/power_supply/ab8500/btemp.txt new file mode 100644 index 000000000000..0ba1bcc7f33a --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/ab8500/btemp.txt @@ -0,0 +1,16 @@ +=== AB8500 Battery Temperature Monitor Driver === + +The properties below describes the node for btemp driver. + +Required Properties: +- compatible = Shall be: "stericsson,ab8500-btemp" +- battery = Shall be battery specific information + + Example: + ab8500_btemp { + compatible = "stericsson,ab8500-btemp"; + battery = <&ab8500_battery>; + }; + +For information on battery specific node, Ref: +Documentation/devicetree/bindings/power_supply/ab8500/fg.txt diff --git a/arch/arm/boot/dts/dbx5x0.dtsi b/arch/arm/boot/dts/dbx5x0.dtsi index 0c81986904c5..d170fadd7ddb 100644 --- a/arch/arm/boot/dts/dbx5x0.dtsi +++ b/arch/arm/boot/dts/dbx5x0.dtsi @@ -362,6 +362,11 @@ battery = <&ab8500_battery>; }; + ab8500_btemp { + compatible = "stericsson,ab8500-btemp"; + battery = <&ab8500_battery>; + }; + ab8500_usb { compatible = "stericsson,ab8500-usb"; interrupts = < 90 0x4 diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 7c3017ba73e4..94d45be5c1b4 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1046,8 +1046,13 @@ static struct mfd_cell __devinitdata ab8500_bm_devs[] = { }, { .name = "ab8500-btemp", + .of_compatible = "stericsson,ab8500-btemp", .num_resources = ARRAY_SIZE(ab8500_btemp_resources), .resources = ab8500_btemp_resources, +#ifndef CONFIG_OF + .platform_data = &ab8500_bm_data, + .pdata_size = sizeof(ab8500_bm_data), +#endif }, { .name = "ab8500-fg", diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 523a58014ed5..263499f8709a 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -346,12 +346,6 @@ config AB8500_BM help Say Y to include support for AB8500 battery management. -config AB8500_BATTERY_THERM_ON_BATCTRL - bool "Thermistor connected on BATCTRL ADC" - depends on AB8500_BM - help - Say Y to enable battery temperature measurements using - thermistor connected on BATCTRL ADC. endif # POWER_SUPPLY source "drivers/power/avs/Kconfig" diff --git a/drivers/power/ab8500_bmdata.c b/drivers/power/ab8500_bmdata.c index e7639b6659f7..03cc528425cb 100644 --- a/drivers/power/ab8500_bmdata.c +++ b/drivers/power/ab8500_bmdata.c @@ -30,7 +30,7 @@ static struct abx500_res_to_temp temp_tbl_A_thermistor[] = { }; static struct abx500_res_to_temp temp_tbl_B_thermistor[] = { - {-5, 165418}, + {-5, 200000}, { 0, 159024}, { 5, 151921}, {10, 144300}, @@ -240,7 +240,7 @@ static struct abx500_battery_type bat_type_thermistor[] = { }, { .name = POWER_SUPPLY_TECHNOLOGY_LIPO, - .resis_high = 165418, + .resis_high = 200000, .resis_low = 82869, .battery_resistance = 300, .charge_full_design = 900, diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c index abc2abc16a5d..8135542ee746 100644 --- a/drivers/power/ab8500_btemp.c +++ b/drivers/power/ab8500_btemp.c @@ -20,11 +20,13 @@ #include #include #include -#include +#include +#include +#include #include +#include #include #include -#include #define VTVOUT_V 1800 @@ -76,7 +78,6 @@ struct ab8500_btemp_ranges { * @parent: Pointer to the struct ab8500 * @gpadc: Pointer to the struct gpadc * @fg: Pointer to the struct fg - * @pdata: Pointer to the abx500_btemp platform data * @bat: Pointer to the abx500_bm platform data * @btemp_psy: Structure for BTEMP specific battery properties * @events: Structure for information about events triggered @@ -93,7 +94,6 @@ struct ab8500_btemp { struct ab8500 *parent; struct ab8500_gpadc *gpadc; struct ab8500_fg *fg; - struct abx500_bmdevs_plat_data *pdata; struct abx500_bm_data *bat; struct power_supply btemp_psy; struct ab8500_btemp_events events; @@ -955,48 +955,57 @@ static int __devexit ab8500_btemp_remove(struct platform_device *pdev) flush_scheduled_work(); power_supply_unregister(&di->btemp_psy); platform_set_drvdata(pdev, NULL); - kfree(di); return 0; } +static char *supply_interface[] = { + "ab8500_chargalg", + "ab8500_fg", +}; + static int __devinit ab8500_btemp_probe(struct platform_device *pdev) { - struct abx500_bmdevs_plat_data *plat_data = pdev->dev.platform_data; + struct device_node *np = pdev->dev.of_node; struct ab8500_btemp *di; int irq, i, ret = 0; u8 val; - if (!plat_data) { - dev_err(&pdev->dev, "No platform data\n"); - return -EINVAL; - } - - di = kzalloc(sizeof(*di), GFP_KERNEL); - if (!di) + di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); + if (!di) { + dev_err(&pdev->dev, "%s no mem for ab8500_btemp\n", __func__); return -ENOMEM; + } + di->bat = pdev->mfd_cell->platform_data; + if (!di->bat) { + if (np) { + ret = bmdevs_of_probe(&pdev->dev, np, &di->bat); + if (ret) { + dev_err(&pdev->dev, + "failed to get battery information\n"); + return ret; + } + } else { + dev_err(&pdev->dev, "missing dt node for ab8500_btemp\n"); + return -EINVAL; + } + } else { + dev_info(&pdev->dev, "falling back to legacy platform data\n"); + } /* get parent data */ di->dev = &pdev->dev; di->parent = dev_get_drvdata(pdev->dev.parent); di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); - /* get btemp specific platform data */ - di->pdata = plat_data; - if (!di->pdata) { - dev_err(di->dev, "no btemp platform data supplied\n"); - ret = -EINVAL; - goto free_device_info; - } - /* BTEMP supply */ di->btemp_psy.name = "ab8500_btemp"; di->btemp_psy.type = POWER_SUPPLY_TYPE_BATTERY; di->btemp_psy.properties = ab8500_btemp_props; di->btemp_psy.num_properties = ARRAY_SIZE(ab8500_btemp_props); di->btemp_psy.get_property = ab8500_btemp_get_property; - di->btemp_psy.supplied_to = di->pdata->supplied_to; - di->btemp_psy.num_supplicants = di->pdata->num_supplicants; + di->btemp_psy.supplied_to = supply_interface; + di->btemp_psy.num_supplicants = ARRAY_SIZE(supply_interface); di->btemp_psy.external_power_changed = ab8500_btemp_external_power_changed; @@ -1006,8 +1015,7 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev) create_singlethread_workqueue("ab8500_btemp_wq"); if (di->btemp_wq == NULL) { dev_err(di->dev, "failed to create work queue\n"); - ret = -ENOMEM; - goto free_device_info; + return -ENOMEM; } /* Init work for measuring temperature periodically */ @@ -1085,12 +1093,14 @@ free_irq: } free_btemp_wq: destroy_workqueue(di->btemp_wq); -free_device_info: - kfree(di); - return ret; } +static const struct of_device_id ab8500_btemp_match[] = { + { .compatible = "stericsson,ab8500-btemp", }, + { }, +}; + static struct platform_driver ab8500_btemp_driver = { .probe = ab8500_btemp_probe, .remove = __devexit_p(ab8500_btemp_remove), @@ -1099,6 +1109,7 @@ static struct platform_driver ab8500_btemp_driver = { .driver = { .name = "ab8500-btemp", .owner = THIS_MODULE, + .of_match_table = ab8500_btemp_match, }, }; -- cgit v1.2.3 From 4aef72dbb2e8997e627dd94ae2b9109dc09fffaa Mon Sep 17 00:00:00 2001 From: "Rajanikanth H.V" Date: Sun, 18 Nov 2012 19:17:47 -0800 Subject: ab8500: Add devicetree support for charger This patch adds device tree support for ab8500-charger driver Signed-off-by: Rajanikanth H.V Signed-off-by: Anton Vorontsov --- Documentation/devicetree/bindings/mfd/ab8500.txt | 14 ++++ .../bindings/power_supply/ab8500/charger.txt | 25 ++++++++ arch/arm/boot/dts/dbx5x0.dtsi | 6 ++ drivers/mfd/ab8500-core.c | 5 ++ drivers/power/ab8500_charger.c | 74 +++++++++++++--------- 5 files changed, 95 insertions(+), 29 deletions(-) create mode 100644 Documentation/devicetree/bindings/power_supply/ab8500/charger.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mfd/ab8500.txt b/Documentation/devicetree/bindings/mfd/ab8500.txt index f2ee0e7a5da3..13b707b7355c 100644 --- a/Documentation/devicetree/bindings/mfd/ab8500.txt +++ b/Documentation/devicetree/bindings/mfd/ab8500.txt @@ -36,6 +36,20 @@ ab8500-btemp : : vtvout : Battery Temperature : BTEMP_LOW_MEDIUM : : BtempLow < Btemp < BtempMedium,if battery temperature is between -10 and 0°C : BTEMP_MEDIUM_HIGH : : BtempMedium < Btemp < BtempHigh,if battery temperature is between 0°C and“MaxTemp : BTEMP_HIGH : : Btemp > BtempHigh, if battery temperature is higher than “MaxTemp +ab8500-charger : : vddadc : Charger interface + : MAIN_CH_UNPLUG_DET : : main charger unplug detection management (not in 8505) + : MAIN_CHARGE_PLUG_DET : : main charger plug detection management (not in 8505) + : MAIN_EXT_CH_NOT_OK : : main charger not OK + : MAIN_CH_TH_PROT_R : : Die temp is above main charger + : MAIN_CH_TH_PROT_F : : Die temp is below main charger + : VBUS_DET_F : : VBUS falling detected + : VBUS_DET_R : : VBUS rising detected + : USB_LINK_STATUS : : USB link status has changed + : USB_CH_TH_PROT_R : : Die temp is above usb charger + : USB_CH_TH_PROT_F : : Die temp is below usb charger + : USB_CHARGER_NOT_OKR : : allowed USB charger not ok detection + : VBUS_OVV : : Overvoltage on Vbus ball detected (USB charge is stopped) + : CH_WD_EXP : : Charger watchdog detected ab8500-gpadc : HW_CONV_END : vddadc : Analogue to Digital Converter SW_CONV_END : : ab8500-gpio : : : GPIO Controller diff --git a/Documentation/devicetree/bindings/power_supply/ab8500/charger.txt b/Documentation/devicetree/bindings/power_supply/ab8500/charger.txt new file mode 100644 index 000000000000..6bdbb08ea9e0 --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/ab8500/charger.txt @@ -0,0 +1,25 @@ +=== AB8500 Charger Driver === + +Required Properties: +- compatible = Shall be "stericsson,ab8500-charger" +- battery = Shall be battery specific information + Example: + ab8500_charger { + compatible = "stericsson,ab8500-charger"; + battery = <&ab8500_battery>; + }; + +- vddadc-supply: Supply for USB and Main charger + Example: + ab8500-charger { + vddadc-supply = <&ab8500_ldo_tvout_reg>; + } +- autopower_cfg: + Boolean value depicting the presence of 'automatic poweron after powerloss' + Example: + ab8500-charger { + autopower_cfg; + }; + +For information on battery specific node, Ref: +Documentation/devicetree/bindings/power_supply/ab8500/fg.txt diff --git a/arch/arm/boot/dts/dbx5x0.dtsi b/arch/arm/boot/dts/dbx5x0.dtsi index d170fadd7ddb..1d4ad3098aeb 100644 --- a/arch/arm/boot/dts/dbx5x0.dtsi +++ b/arch/arm/boot/dts/dbx5x0.dtsi @@ -367,6 +367,12 @@ battery = <&ab8500_battery>; }; + ab8500_charger { + compatible = "stericsson,ab8500-charger"; + battery = <&ab8500_battery>; + vddadc-supply = <&ab8500_ldo_tvout_reg>; + }; + ab8500_usb { compatible = "stericsson,ab8500-usb"; interrupts = < 90 0x4 diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 94d45be5c1b4..c7a120bfd50a 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1041,8 +1041,13 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = { static struct mfd_cell __devinitdata ab8500_bm_devs[] = { { .name = "ab8500-charger", + .of_compatible = "stericsson,ab8500-charger", .num_resources = ARRAY_SIZE(ab8500_charger_resources), .resources = ab8500_charger_resources, +#ifndef CONFIG_OF + .platform_data = &ab8500_bm_data, + .pdata_size = sizeof(ab8500_bm_data), +#endif }, { .name = "ab8500-btemp", diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index 723edb47b1d8..dddc9473f72a 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include #include @@ -181,9 +183,9 @@ struct ab8500_charger_usb_state { * @vbat Battery voltage * @old_vbat Previously measured battery voltage * @autopower Indicate if we should have automatic pwron after pwrloss + * @autopower_cfg platform specific power config support for "pwron after pwrloss" * @parent: Pointer to the struct ab8500 * @gpadc: Pointer to the struct gpadc - * @pdata: Pointer to the abx500_charger platform data * @bat: Pointer to the abx500_bm platform data * @flags: Structure for information about events triggered * @usb_state: Structure for usb stack information @@ -218,9 +220,9 @@ struct ab8500_charger { int vbat; int old_vbat; bool autopower; + bool autopower_cfg; struct ab8500 *parent; struct ab8500_gpadc *gpadc; - struct abx500_bmdevs_plat_data *pdata; struct abx500_bm_data *bat; struct ab8500_charger_event_flags flags; struct ab8500_charger_usb_state usb_state; @@ -322,7 +324,7 @@ static void ab8500_power_loss_handling(struct ab8500_charger *di) static void ab8500_power_supply_changed(struct ab8500_charger *di, struct power_supply *psy) { - if (di->pdata->autopower_cfg) { + if (di->autopower_cfg) { if (!di->usb.charger_connected && !di->ac.charger_connected && di->autopower) { @@ -2526,25 +2528,45 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev) power_supply_unregister(&di->usb_chg.psy); power_supply_unregister(&di->ac_chg.psy); platform_set_drvdata(pdev, NULL); - kfree(di); return 0; } +static char *supply_interface[] = { + "ab8500_chargalg", + "ab8500_fg", + "ab8500_btemp", +}; + static int __devinit ab8500_charger_probe(struct platform_device *pdev) { - struct abx500_bmdevs_plat_data *plat_data = pdev->dev.platform_data; + struct device_node *np = pdev->dev.of_node; struct ab8500_charger *di; int irq, i, charger_status, ret = 0; - if (!plat_data) { - dev_err(&pdev->dev, "No platform data\n"); - return -EINVAL; - } - - di = kzalloc(sizeof(*di), GFP_KERNEL); - if (!di) + di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); + if (!di) { + dev_err(&pdev->dev, "%s no mem for ab8500_charger\n", __func__); return -ENOMEM; + } + di->bat = pdev->mfd_cell->platform_data; + if (!di->bat) { + if (np) { + ret = bmdevs_of_probe(&pdev->dev, np, &di->bat); + if (ret) { + dev_err(&pdev->dev, + "failed to get battery information\n"); + return ret; + } + di->autopower_cfg = of_property_read_bool(np, "autopower_cfg"); + } else { + dev_err(&pdev->dev, "missing dt node for ab8500_charger\n"); + return -EINVAL; + } + } else { + dev_info(&pdev->dev, "falling back to legacy platform data\n"); + di->autopower_cfg = false; + } /* get parent data */ di->dev = &pdev->dev; @@ -2554,14 +2576,6 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) /* initialize lock */ spin_lock_init(&di->usb_state.usb_lock); - /* get charger specific platform data */ - di->pdata = plat_data; - if (!di->pdata) { - dev_err(di->dev, "no charger platform data supplied\n"); - ret = -EINVAL; - goto free_device_info; - } - di->autopower = false; /* AC supply */ @@ -2571,8 +2585,8 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) di->ac_chg.psy.properties = ab8500_charger_ac_props; di->ac_chg.psy.num_properties = ARRAY_SIZE(ab8500_charger_ac_props); di->ac_chg.psy.get_property = ab8500_charger_ac_get_property; - di->ac_chg.psy.supplied_to = di->pdata->supplied_to; - di->ac_chg.psy.num_supplicants = di->pdata->num_supplicants; + di->ac_chg.psy.supplied_to = supply_interface; + di->ac_chg.psy.num_supplicants = ARRAY_SIZE(supply_interface), /* ux500_charger sub-class */ di->ac_chg.ops.enable = &ab8500_charger_ac_en; di->ac_chg.ops.kick_wd = &ab8500_charger_watchdog_kick; @@ -2589,8 +2603,8 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) di->usb_chg.psy.properties = ab8500_charger_usb_props; di->usb_chg.psy.num_properties = ARRAY_SIZE(ab8500_charger_usb_props); di->usb_chg.psy.get_property = ab8500_charger_usb_get_property; - di->usb_chg.psy.supplied_to = di->pdata->supplied_to; - di->usb_chg.psy.num_supplicants = di->pdata->num_supplicants; + di->usb_chg.psy.supplied_to = supply_interface; + di->usb_chg.psy.num_supplicants = ARRAY_SIZE(supply_interface), /* ux500_charger sub-class */ di->usb_chg.ops.enable = &ab8500_charger_usb_en; di->usb_chg.ops.kick_wd = &ab8500_charger_watchdog_kick; @@ -2606,8 +2620,7 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev) create_singlethread_workqueue("ab8500_charger_wq"); if (di->charger_wq == NULL) { dev_err(di->dev, "failed to create work queue\n"); - ret = -ENOMEM; - goto free_device_info; + return -ENOMEM; } /* Init work for HW failure check */ @@ -2749,12 +2762,14 @@ free_regulator: regulator_put(di->regu); free_charger_wq: destroy_workqueue(di->charger_wq); -free_device_info: - kfree(di); - return ret; } +static const struct of_device_id ab8500_charger_match[] = { + { .compatible = "stericsson,ab8500-charger", }, + { }, +}; + static struct platform_driver ab8500_charger_driver = { .probe = ab8500_charger_probe, .remove = __devexit_p(ab8500_charger_remove), @@ -2763,6 +2778,7 @@ static struct platform_driver ab8500_charger_driver = { .driver = { .name = "ab8500-charger", .owner = THIS_MODULE, + .of_match_table = ab8500_charger_match, }, }; -- cgit v1.2.3 From a12810ab9fcf0c9fd5e50b5e350a3ffbeaa571be Mon Sep 17 00:00:00 2001 From: "Rajanikanth H.V" Date: Wed, 31 Oct 2012 15:40:33 +0000 Subject: ab8500: Add devicetree support for chargalg This patch adds device tree support for charging algorithm driver Signed-off-by: Rajanikanth H.V Signed-off-by: Anton Vorontsov --- .../bindings/power_supply/ab8500/chargalg.txt | 16 +++++++ arch/arm/boot/dts/dbx5x0.dtsi | 5 ++ drivers/mfd/ab8500-core.c | 5 ++ drivers/power/abx500_chargalg.c | 54 +++++++++++++++------- include/linux/mfd/abx500.h | 6 --- 5 files changed, 64 insertions(+), 22 deletions(-) create mode 100644 Documentation/devicetree/bindings/power_supply/ab8500/chargalg.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/power_supply/ab8500/chargalg.txt b/Documentation/devicetree/bindings/power_supply/ab8500/chargalg.txt new file mode 100644 index 000000000000..ef5328371122 --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/ab8500/chargalg.txt @@ -0,0 +1,16 @@ +=== AB8500 Charging Algorithm Driver === + +The properties below describes the node for chargalg driver. + +Required Properties: +- compatible = Shall be: "stericsson,ab8500-chargalg" +- battery = Shall be battery specific information + +Example: +ab8500_chargalg { + compatible = "stericsson,ab8500-chargalg"; + battery = <&ab8500_battery>; +}; + +For information on battery specific node, Ref: +Documentation/devicetree/bindings/power_supply/ab8500/fg.txt diff --git a/arch/arm/boot/dts/dbx5x0.dtsi b/arch/arm/boot/dts/dbx5x0.dtsi index 1d4ad3098aeb..12a68af44903 100644 --- a/arch/arm/boot/dts/dbx5x0.dtsi +++ b/arch/arm/boot/dts/dbx5x0.dtsi @@ -373,6 +373,11 @@ vddadc-supply = <&ab8500_ldo_tvout_reg>; }; + ab8500_chargalg { + compatible = "stericsson,ab8500-chargalg"; + battery = <&ab8500_battery>; + }; + ab8500_usb { compatible = "stericsson,ab8500-usb"; interrupts = < 90 0x4 diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index c7a120bfd50a..5ec70f26b9d5 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1071,8 +1071,13 @@ static struct mfd_cell __devinitdata ab8500_bm_devs[] = { }, { .name = "ab8500-chargalg", + .of_compatible = "stericsson,ab8500-chargalg", .num_resources = ARRAY_SIZE(ab8500_chargalg_resources), .resources = ab8500_chargalg_resources, +#ifndef CONFIG_OF + .platform_data = &ab8500_bm_data, + .pdata_size = sizeof(ab8500_bm_data), +#endif }, }; diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c index 758ea76de2f8..dcdc4393b9e7 100644 --- a/drivers/power/abx500_chargalg.c +++ b/drivers/power/abx500_chargalg.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -205,7 +207,6 @@ enum maxim_ret { * @chg_info: information about connected charger types * @batt_data: data of the battery * @susp_status: current charger suspension status - * @pdata: pointer to the abx500_chargalg platform data * @bat: pointer to the abx500_bm platform data * @chargalg_psy: structure that holds the battery properties exposed by * the charging algorithm @@ -231,7 +232,6 @@ struct abx500_chargalg { struct abx500_chargalg_charger_info chg_info; struct abx500_chargalg_battery_data batt_data; struct abx500_chargalg_suspension_status susp_status; - struct abx500_bmdevs_plat_data *pdata; struct abx500_bm_data *bat; struct power_supply chargalg_psy; struct ux500_charger *ac_chg; @@ -1795,25 +1795,44 @@ static int __devexit abx500_chargalg_remove(struct platform_device *pdev) flush_scheduled_work(); power_supply_unregister(&di->chargalg_psy); platform_set_drvdata(pdev, NULL); - kfree(di); return 0; } +static char *supply_interface[] = { + "ab8500_fg", +}; + static int __devinit abx500_chargalg_probe(struct platform_device *pdev) { - struct abx500_bmdevs_plat_data *plat_data; + struct device_node *np = pdev->dev.of_node; + struct abx500_chargalg *di; int ret = 0; - struct abx500_chargalg *di = - kzalloc(sizeof(struct abx500_chargalg), GFP_KERNEL); - if (!di) + di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); + if (!di) { + dev_err(&pdev->dev, "%s no mem for ab8500_chargalg\n", __func__); return -ENOMEM; + } + di->bat = pdev->mfd_cell->platform_data; + if (!di->bat) { + if (np) { + ret = bmdevs_of_probe(&pdev->dev, np, &di->bat); + if (ret) { + dev_err(&pdev->dev, + "failed to get battery information\n"); + return ret; + } + } else { + dev_err(&pdev->dev, "missing dt node for ab8500_chargalg\n"); + return -EINVAL; + } + } else { + dev_info(&pdev->dev, "falling back to legacy platform data\n"); + } /* get device struct */ di->dev = &pdev->dev; - plat_data = pdev->dev.platform_data; - di->pdata = plat_data; /* chargalg supply */ di->chargalg_psy.name = "abx500_chargalg"; @@ -1821,8 +1840,8 @@ static int __devinit abx500_chargalg_probe(struct platform_device *pdev) di->chargalg_psy.properties = abx500_chargalg_props; di->chargalg_psy.num_properties = ARRAY_SIZE(abx500_chargalg_props); di->chargalg_psy.get_property = abx500_chargalg_get_property; - di->chargalg_psy.supplied_to = di->pdata->supplied_to; - di->chargalg_psy.num_supplicants = di->pdata->num_supplicants; + di->chargalg_psy.supplied_to = supply_interface; + di->chargalg_psy.num_supplicants = ARRAY_SIZE(supply_interface), di->chargalg_psy.external_power_changed = abx500_chargalg_external_power_changed; @@ -1842,7 +1861,7 @@ static int __devinit abx500_chargalg_probe(struct platform_device *pdev) create_singlethread_workqueue("abx500_chargalg_wq"); if (di->chargalg_wq == NULL) { dev_err(di->dev, "failed to create work queue\n"); - goto free_device_info; + return -ENOMEM; } /* Init work for chargalg */ @@ -1883,20 +1902,23 @@ free_psy: power_supply_unregister(&di->chargalg_psy); free_chargalg_wq: destroy_workqueue(di->chargalg_wq); -free_device_info: - kfree(di); - return ret; } +static const struct of_device_id ab8500_chargalg_match[] = { + { .compatible = "stericsson,ab8500-chargalg", }, + { }, +}; + static struct platform_driver abx500_chargalg_driver = { .probe = abx500_chargalg_probe, .remove = __devexit_p(abx500_chargalg_remove), .suspend = abx500_chargalg_suspend, .resume = abx500_chargalg_resume, .driver = { - .name = "abx500-chargalg", + .name = "ab8500-chargalg", .owner = THIS_MODULE, + .of_match_table = ab8500_chargalg_match, }, }; diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 33f2c58554f4..2138bd33021a 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -274,12 +274,6 @@ struct abx500_bm_data { extern struct abx500_bm_data ab8500_bm_data; -struct abx500_bmdevs_plat_data { - char **supplied_to; - size_t num_supplicants; - bool autopower_cfg; -}; - enum { NTC_EXTERNAL = 0, NTC_INTERNAL, -- cgit v1.2.3 From 0857ba3c24c308f42a242fe8a1894772750230ce Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Sun, 18 Nov 2012 18:36:19 +0200 Subject: i2c: i2c-cbus-gpio: introduce driver Add i2c driver to enable access to devices behind CBUS on Nokia Internet Tablets. The patch also adds CBUS I2C configuration for N8x0 which is one of the users of this driver. Acked-by: Felipe Balbi Acked-by: Tony Lindgren Signed-off-by: Aaro Koskinen Signed-off-by: Wolfram Sang --- .../devicetree/bindings/i2c/i2c-cbus-gpio.txt | 27 ++ arch/arm/mach-omap2/board-n8x0.c | 42 +++ drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-cbus-gpio.c | 300 +++++++++++++++++++++ include/linux/platform_data/i2c-cbus-gpio.h | 27 ++ 6 files changed, 407 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/i2c-cbus-gpio.txt create mode 100644 drivers/i2c/busses/i2c-cbus-gpio.c create mode 100644 include/linux/platform_data/i2c-cbus-gpio.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/i2c/i2c-cbus-gpio.txt b/Documentation/devicetree/bindings/i2c/i2c-cbus-gpio.txt new file mode 100644 index 000000000000..8ce9cd2855b5 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-cbus-gpio.txt @@ -0,0 +1,27 @@ +Device tree bindings for i2c-cbus-gpio driver + +Required properties: + - compatible = "i2c-cbus-gpio"; + - gpios: clk, dat, sel + - #address-cells = <1>; + - #size-cells = <0>; + +Optional properties: + - child nodes conforming to i2c bus binding + +Example: + +i2c@0 { + compatible = "i2c-cbus-gpio"; + gpios = <&gpio 66 0 /* clk */ + &gpio 65 0 /* dat */ + &gpio 64 0 /* sel */ + >; + #address-cells = <1>; + #size-cells = <0>; + + retu-mfd: retu@1 { + compatible = "retu-mfd"; + reg = <0x1>; + }; +}; diff --git a/arch/arm/mach-omap2/board-n8x0.c b/arch/arm/mach-omap2/board-n8x0.c index d95f727ca39a..bbfd74263c42 100644 --- a/arch/arm/mach-omap2/board-n8x0.c +++ b/arch/arm/mach-omap2/board-n8x0.c @@ -16,10 +16,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -39,6 +41,45 @@ #define TUSB6010_GPIO_ENABLE 0 #define TUSB6010_DMACHAN 0x3f +#if defined(CONFIG_I2C_CBUS_GPIO) || defined(CONFIG_I2C_CBUS_GPIO_MODULE) +static struct i2c_cbus_platform_data n8x0_cbus_data = { + .clk_gpio = 66, + .dat_gpio = 65, + .sel_gpio = 64, +}; + +static struct platform_device n8x0_cbus_device = { + .name = "i2c-cbus-gpio", + .id = 3, + .dev = { + .platform_data = &n8x0_cbus_data, + }, +}; + +static struct i2c_board_info n8x0_i2c_board_info_3[] __initdata = { + { + I2C_BOARD_INFO("retu-mfd", 0x01), + }, +}; + +static void __init n8x0_cbus_init(void) +{ + const int retu_irq_gpio = 108; + + if (gpio_request_one(retu_irq_gpio, GPIOF_IN, "Retu IRQ")) + return; + irq_set_irq_type(gpio_to_irq(retu_irq_gpio), IRQ_TYPE_EDGE_RISING); + n8x0_i2c_board_info_3[0].irq = gpio_to_irq(retu_irq_gpio); + i2c_register_board_info(3, n8x0_i2c_board_info_3, + ARRAY_SIZE(n8x0_i2c_board_info_3)); + platform_device_register(&n8x0_cbus_device); +} +#else /* CONFIG_I2C_CBUS_GPIO */ +static void __init n8x0_cbus_init(void) +{ +} +#endif /* CONFIG_I2C_CBUS_GPIO */ + #if defined(CONFIG_USB_MUSB_TUSB6010) || defined(CONFIG_USB_MUSB_TUSB6010_MODULE) /* * Enable or disable power to TUSB6010. When enabling, turn on 3.3 V and @@ -677,6 +718,7 @@ static void __init n8x0_init_machine(void) gpmc_onenand_init(board_onenand_data); n8x0_mmc_init(); n8x0_usb_init(); + n8x0_cbus_init(); } MACHINE_START(NOKIA_N800, "Nokia N800") diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index e9df4612b7eb..e949edf644d4 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -337,6 +337,16 @@ config I2C_BLACKFIN_TWI_CLK_KHZ help The unit of the TWI clock is kHz. +config I2C_CBUS_GPIO + tristate "CBUS I2C driver" + depends on GENERIC_GPIO + help + Support for CBUS access using I2C API. Mostly relevant for Nokia + Internet Tablets (770, N800 and N810). + + This driver can also be built as a module. If so, the module + will be called i2c-cbus-gpio. + config I2C_CPM tristate "Freescale CPM1 or CPM2 (MPC8xx/826x)" depends on (CPM1 || CPM2) && OF_I2C diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 395b516ffa08..f9e3e0b5c827 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o +obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o obj-$(CONFIG_I2C_CPM) += i2c-cpm.o obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o obj-$(CONFIG_I2C_DESIGNWARE_CORE) += i2c-designware-core.o diff --git a/drivers/i2c/busses/i2c-cbus-gpio.c b/drivers/i2c/busses/i2c-cbus-gpio.c new file mode 100644 index 000000000000..98386d659318 --- /dev/null +++ b/drivers/i2c/busses/i2c-cbus-gpio.c @@ -0,0 +1,300 @@ +/* + * CBUS I2C driver for Nokia Internet Tablets. + * + * Copyright (C) 2004-2010 Nokia Corporation + * + * Based on code written by Juha Yrjölä, David Weinehall, Mikko Ylinen and + * Felipe Balbi. Converted to I2C driver by Aaro Koskinen. + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Bit counts are derived from Nokia implementation. These should be checked + * if other CBUS implementations appear. + */ +#define CBUS_ADDR_BITS 3 +#define CBUS_REG_BITS 5 + +struct cbus_host { + spinlock_t lock; /* host lock */ + struct device *dev; + int clk_gpio; + int dat_gpio; + int sel_gpio; +}; + +/** + * cbus_send_bit - sends one bit over the bus + * @host: the host we're using + * @bit: one bit of information to send + */ +static void cbus_send_bit(struct cbus_host *host, unsigned bit) +{ + gpio_set_value(host->dat_gpio, bit ? 1 : 0); + gpio_set_value(host->clk_gpio, 1); + gpio_set_value(host->clk_gpio, 0); +} + +/** + * cbus_send_data - sends @len amount of data over the bus + * @host: the host we're using + * @data: the data to send + * @len: size of the transfer + */ +static void cbus_send_data(struct cbus_host *host, unsigned data, unsigned len) +{ + int i; + + for (i = len; i > 0; i--) + cbus_send_bit(host, data & (1 << (i - 1))); +} + +/** + * cbus_receive_bit - receives one bit from the bus + * @host: the host we're using + */ +static int cbus_receive_bit(struct cbus_host *host) +{ + int ret; + + gpio_set_value(host->clk_gpio, 1); + ret = gpio_get_value(host->dat_gpio); + gpio_set_value(host->clk_gpio, 0); + return ret; +} + +/** + * cbus_receive_word - receives 16-bit word from the bus + * @host: the host we're using + */ +static int cbus_receive_word(struct cbus_host *host) +{ + int ret = 0; + int i; + + for (i = 16; i > 0; i--) { + int bit = cbus_receive_bit(host); + + if (bit < 0) + return bit; + + if (bit) + ret |= 1 << (i - 1); + } + return ret; +} + +/** + * cbus_transfer - transfers data over the bus + * @host: the host we're using + * @rw: read/write flag + * @dev: device address + * @reg: register address + * @data: if @rw == I2C_SBUS_WRITE data to send otherwise 0 + */ +static int cbus_transfer(struct cbus_host *host, char rw, unsigned dev, + unsigned reg, unsigned data) +{ + unsigned long flags; + int ret; + + /* We don't want interrupts disturbing our transfer */ + spin_lock_irqsave(&host->lock, flags); + + /* Reset state and start of transfer, SEL stays down during transfer */ + gpio_set_value(host->sel_gpio, 0); + + /* Set the DAT pin to output */ + gpio_direction_output(host->dat_gpio, 1); + + /* Send the device address */ + cbus_send_data(host, dev, CBUS_ADDR_BITS); + + /* Send the rw flag */ + cbus_send_bit(host, rw == I2C_SMBUS_READ); + + /* Send the register address */ + cbus_send_data(host, reg, CBUS_REG_BITS); + + if (rw == I2C_SMBUS_WRITE) { + cbus_send_data(host, data, 16); + ret = 0; + } else { + ret = gpio_direction_input(host->dat_gpio); + if (ret) { + dev_dbg(host->dev, "failed setting direction\n"); + goto out; + } + gpio_set_value(host->clk_gpio, 1); + + ret = cbus_receive_word(host); + if (ret < 0) { + dev_dbg(host->dev, "failed receiving data\n"); + goto out; + } + } + + /* Indicate end of transfer, SEL goes up until next transfer */ + gpio_set_value(host->sel_gpio, 1); + gpio_set_value(host->clk_gpio, 1); + gpio_set_value(host->clk_gpio, 0); + +out: + spin_unlock_irqrestore(&host->lock, flags); + + return ret; +} + +static int cbus_i2c_smbus_xfer(struct i2c_adapter *adapter, + u16 addr, + unsigned short flags, + char read_write, + u8 command, + int size, + union i2c_smbus_data *data) +{ + struct cbus_host *chost = i2c_get_adapdata(adapter); + int ret; + + if (size != I2C_SMBUS_WORD_DATA) + return -EINVAL; + + ret = cbus_transfer(chost, read_write == I2C_SMBUS_READ, addr, + command, data->word); + if (ret < 0) + return ret; + + if (read_write == I2C_SMBUS_READ) + data->word = ret; + + return 0; +} + +static u32 cbus_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_WRITE_WORD_DATA; +} + +static const struct i2c_algorithm cbus_i2c_algo = { + .smbus_xfer = cbus_i2c_smbus_xfer, + .functionality = cbus_i2c_func, +}; + +static int cbus_i2c_remove(struct platform_device *pdev) +{ + struct i2c_adapter *adapter = platform_get_drvdata(pdev); + + return i2c_del_adapter(adapter); +} + +static int cbus_i2c_probe(struct platform_device *pdev) +{ + struct i2c_adapter *adapter; + struct cbus_host *chost; + int ret; + + adapter = devm_kzalloc(&pdev->dev, sizeof(struct i2c_adapter), + GFP_KERNEL); + if (!adapter) + return -ENOMEM; + + chost = devm_kzalloc(&pdev->dev, sizeof(*chost), GFP_KERNEL); + if (!chost) + return -ENOMEM; + + if (pdev->dev.of_node) { + struct device_node *dnode = pdev->dev.of_node; + if (of_gpio_count(dnode) != 3) + return -ENODEV; + chost->clk_gpio = of_get_gpio(dnode, 0); + chost->dat_gpio = of_get_gpio(dnode, 1); + chost->sel_gpio = of_get_gpio(dnode, 2); + } else if (pdev->dev.platform_data) { + struct i2c_cbus_platform_data *pdata = pdev->dev.platform_data; + chost->clk_gpio = pdata->clk_gpio; + chost->dat_gpio = pdata->dat_gpio; + chost->sel_gpio = pdata->sel_gpio; + } else { + return -ENODEV; + } + + adapter->owner = THIS_MODULE; + adapter->class = I2C_CLASS_HWMON; + adapter->dev.parent = &pdev->dev; + adapter->nr = pdev->id; + adapter->timeout = HZ; + adapter->algo = &cbus_i2c_algo; + strlcpy(adapter->name, "CBUS I2C adapter", sizeof(adapter->name)); + + spin_lock_init(&chost->lock); + chost->dev = &pdev->dev; + + ret = devm_gpio_request_one(&pdev->dev, chost->clk_gpio, + GPIOF_OUT_INIT_LOW, "CBUS clk"); + if (ret) + return ret; + + ret = devm_gpio_request_one(&pdev->dev, chost->dat_gpio, GPIOF_IN, + "CBUS data"); + if (ret) + return ret; + + ret = devm_gpio_request_one(&pdev->dev, chost->sel_gpio, + GPIOF_OUT_INIT_HIGH, "CBUS sel"); + if (ret) + return ret; + + i2c_set_adapdata(adapter, chost); + platform_set_drvdata(pdev, adapter); + + return i2c_add_numbered_adapter(adapter); +} + +#if defined(CONFIG_OF) +static const struct of_device_id i2c_cbus_dt_ids[] = { + { .compatible = "i2c-cbus-gpio", }, + { } +}; +MODULE_DEVICE_TABLE(of, i2c_cbus_dt_ids); +#endif + +static struct platform_driver cbus_i2c_driver = { + .probe = cbus_i2c_probe, + .remove = cbus_i2c_remove, + .driver = { + .owner = THIS_MODULE, + .name = "i2c-cbus-gpio", + }, +}; +module_platform_driver(cbus_i2c_driver); + +MODULE_ALIAS("platform:i2c-cbus-gpio"); +MODULE_DESCRIPTION("CBUS I2C driver"); +MODULE_AUTHOR("Juha Yrjölä"); +MODULE_AUTHOR("David Weinehall"); +MODULE_AUTHOR("Mikko Ylinen"); +MODULE_AUTHOR("Felipe Balbi"); +MODULE_AUTHOR("Aaro Koskinen "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/i2c-cbus-gpio.h b/include/linux/platform_data/i2c-cbus-gpio.h new file mode 100644 index 000000000000..6faa992a9502 --- /dev/null +++ b/include/linux/platform_data/i2c-cbus-gpio.h @@ -0,0 +1,27 @@ +/* + * i2c-cbus-gpio.h - CBUS I2C platform_data definition + * + * Copyright (C) 2004-2009 Nokia Corporation + * + * Written by Felipe Balbi and Aaro Koskinen. + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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 __INCLUDE_LINUX_I2C_CBUS_GPIO_H +#define __INCLUDE_LINUX_I2C_CBUS_GPIO_H + +struct i2c_cbus_platform_data { + int dat_gpio; + int clk_gpio; + int sel_gpio; +}; + +#endif /* __INCLUDE_LINUX_I2C_CBUS_GPIO_H */ -- cgit v1.2.3 From a0ae02405076ac32bd17ece976e914b5b6075bb0 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Thu, 17 Nov 2011 17:31:51 +0000 Subject: ARM: kernel: add device tree init map function When booting through a device tree, the kernel cpu logical id map can be initialized using device tree data passed by FW or through an embedded blob. This patch adds a function that parses device tree "cpu" nodes and retrieves the corresponding CPUs hardware identifiers (MPIDR). It sets the possible cpus and the cpu logical map values according to the number of CPUs defined in the device tree and respective properties. The device tree HW identifiers are considered valid if all CPU nodes contain a "reg" property, there are no duplicate "reg" entries and the DT defines a CPU node whose "reg" property matches the MPIDR[23:0] of the boot CPU. The primary CPU is assigned cpu logical number 0 to keep the current convention valid. Current bindings documentation is included in the patch: Documentation/devicetree/bindings/arm/cpus.txt Signed-off-by: Lorenzo Pieralisi Acked-by: Nicolas Pitre --- Documentation/devicetree/bindings/arm/cpus.txt | 77 +++++++++++++++++++ arch/arm/include/asm/prom.h | 2 + arch/arm/kernel/devtree.c | 100 +++++++++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/cpus.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt new file mode 100644 index 000000000000..f32494dbfe19 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/cpus.txt @@ -0,0 +1,77 @@ +* ARM CPUs binding description + +The device tree allows to describe the layout of CPUs in a system through +the "cpus" node, which in turn contains a number of subnodes (ie "cpu") +defining properties for every cpu. + +Bindings for CPU nodes follow the ePAPR standard, available from: + +http://devicetree.org + +For the ARM architecture every CPU node must contain the following properties: + +- device_type: must be "cpu" +- reg: property matching the CPU MPIDR[23:0] register bits + reg[31:24] bits must be set to 0 +- compatible: should be one of: + "arm,arm1020" + "arm,arm1020e" + "arm,arm1022" + "arm,arm1026" + "arm,arm720" + "arm,arm740" + "arm,arm7tdmi" + "arm,arm920" + "arm,arm922" + "arm,arm925" + "arm,arm926" + "arm,arm940" + "arm,arm946" + "arm,arm9tdmi" + "arm,cortex-a5" + "arm,cortex-a7" + "arm,cortex-a8" + "arm,cortex-a9" + "arm,cortex-a15" + "arm,arm1136" + "arm,arm1156" + "arm,arm1176" + "arm,arm11mpcore" + "faraday,fa526" + "intel,sa110" + "intel,sa1100" + "marvell,feroceon" + "marvell,mohawk" + "marvell,xsc3" + "marvell,xscale" + +Example: + + cpus { + #size-cells = <0>; + #address-cells = <1>; + + CPU0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <0x0>; + }; + + CPU1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <0x1>; + }; + + CPU2: cpu@100 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x100>; + }; + + CPU3: cpu@101 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x101>; + }; + }; diff --git a/arch/arm/include/asm/prom.h b/arch/arm/include/asm/prom.h index aeae9c609df4..8dd51dc1a367 100644 --- a/arch/arm/include/asm/prom.h +++ b/arch/arm/include/asm/prom.h @@ -15,6 +15,7 @@ extern struct machine_desc *setup_machine_fdt(unsigned int dt_phys); extern void arm_dt_memblock_reserve(void); +extern void __init arm_dt_init_cpu_maps(void); #else /* CONFIG_OF */ @@ -24,6 +25,7 @@ static inline struct machine_desc *setup_machine_fdt(unsigned int dt_phys) } static inline void arm_dt_memblock_reserve(void) { } +static inline void arm_dt_init_cpu_maps(void) { } #endif /* CONFIG_OF */ #endif /* ASMARM_PROM_H */ diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c index bee7f9d47f02..aaf9add497fe 100644 --- a/arch/arm/kernel/devtree.c +++ b/arch/arm/kernel/devtree.c @@ -19,8 +19,10 @@ #include #include +#include #include #include +#include #include #include @@ -61,6 +63,104 @@ void __init arm_dt_memblock_reserve(void) } } +/* + * arm_dt_init_cpu_maps - Function retrieves cpu nodes from the device tree + * and builds the cpu logical map array containing MPIDR values related to + * logical cpus + * + * Updates the cpu possible mask with the number of parsed cpu nodes + */ +void __init arm_dt_init_cpu_maps(void) +{ + /* + * Temp logical map is initialized with UINT_MAX values that are + * considered invalid logical map entries since the logical map must + * contain a list of MPIDR[23:0] values where MPIDR[31:24] must + * read as 0. + */ + struct device_node *cpu, *cpus; + u32 i, j, cpuidx = 1; + u32 mpidr = is_smp() ? read_cpuid_mpidr() & MPIDR_HWID_BITMASK : 0; + + u32 tmp_map[NR_CPUS] = { [0 ... NR_CPUS-1] = UINT_MAX }; + bool bootcpu_valid = false; + cpus = of_find_node_by_path("/cpus"); + + if (!cpus) + return; + + for_each_child_of_node(cpus, cpu) { + u32 hwid; + + pr_debug(" * %s...\n", cpu->full_name); + /* + * A device tree containing CPU nodes with missing "reg" + * properties is considered invalid to build the + * cpu_logical_map. + */ + if (of_property_read_u32(cpu, "reg", &hwid)) { + pr_debug(" * %s missing reg property\n", + cpu->full_name); + return; + } + + /* + * 8 MSBs must be set to 0 in the DT since the reg property + * defines the MPIDR[23:0]. + */ + if (hwid & ~MPIDR_HWID_BITMASK) + return; + + /* + * Duplicate MPIDRs are a recipe for disaster. + * Scan all initialized entries and check for + * duplicates. If any is found just bail out. + * temp values were initialized to UINT_MAX + * to avoid matching valid MPIDR[23:0] values. + */ + for (j = 0; j < cpuidx; j++) + if (WARN(tmp_map[j] == hwid, "Duplicate /cpu reg " + "properties in the DT\n")) + return; + + /* + * Build a stashed array of MPIDR values. Numbering scheme + * requires that if detected the boot CPU must be assigned + * logical id 0. Other CPUs get sequential indexes starting + * from 1. If a CPU node with a reg property matching the + * boot CPU MPIDR is detected, this is recorded so that the + * logical map built from DT is validated and can be used + * to override the map created in smp_setup_processor_id(). + */ + if (hwid == mpidr) { + i = 0; + bootcpu_valid = true; + } else { + i = cpuidx++; + } + + tmp_map[i] = hwid; + + if (cpuidx > nr_cpu_ids) + break; + } + + if (WARN(!bootcpu_valid, "DT missing boot CPU MPIDR[23:0], " + "fall back to default cpu_logical_map\n")) + return; + + /* + * Since the boot CPU node contains proper data, and all nodes have + * a reg property, the DT CPU list can be considered valid and the + * logical map created in smp_setup_processor_id() can be overridden + */ + for (i = 0; i < cpuidx; i++) { + set_cpu_possible(i, true); + cpu_logical_map(i) = tmp_map[i]; + pr_debug("cpu logical map 0x%x\n", cpu_logical_map(i)); + } +} + /** * setup_machine_fdt - Machine setup when an dtb was passed to the kernel * @dt_phys: physical address of dt blob -- cgit v1.2.3 From 3272dd9b0fe4bc09321219ab65dc5eda3e82445c Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 16 Nov 2012 00:33:59 +0000 Subject: of/net/mdio-gpio: Fix pdev->id issue when using devicetrees. When the mdio-gpio driver is probed via device trees, the platform device id is set as -1, However the pdev->id is re-used as bus-id for while creating mdio gpio bus. So For device tree case the mdio-gpio bus name appears as "gpio-ffffffff" where as for non-device tree case the bus name appears as "gpio-" Which means the bus_id is fixed in device tree case, so we can't have two mdio gpio buses via device trees. Assigning a logical bus number via device tree solves the problem and the bus name is much consistent with non-device tree bus name. Without this patch 1. we can't support two mdio-gpio buses via device trees. 2. we should always pass gpio-ffffffff as bus name to phy_connect, very different to non-device tree bus name. So, setting up the bus_id via aliases from device tree is the right solution and other drivers do similar thing. Signed-off-by: Srinivas Kandagatla Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/mdio-gpio.txt | 9 ++++++++- drivers/net/phy/mdio-gpio.c | 11 +++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/net/mdio-gpio.txt b/Documentation/devicetree/bindings/net/mdio-gpio.txt index bc9549529014..c79bab025369 100644 --- a/Documentation/devicetree/bindings/net/mdio-gpio.txt +++ b/Documentation/devicetree/bindings/net/mdio-gpio.txt @@ -8,9 +8,16 @@ gpios property as described in section VIII.1 in the following order: MDC, MDIO. +Note: Each gpio-mdio bus should have an alias correctly numbered in "aliases" +node. + Example: -mdio { +aliases { + mdio-gpio0 = <&mdio0>; +}; + +mdio0: mdio { compatible = "virtual,mdio-gpio"; #address-cells = <1>; #size-cells = <0>; diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 899274f2f9b1..2ed1140df3e9 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -185,17 +185,20 @@ static int __devinit mdio_gpio_probe(struct platform_device *pdev) { struct mdio_gpio_platform_data *pdata; struct mii_bus *new_bus; - int ret; + int ret, bus_id; - if (pdev->dev.of_node) + if (pdev->dev.of_node) { pdata = mdio_gpio_of_get_data(pdev); - else + bus_id = of_alias_get_id(pdev->dev.of_node, "mdio-gpio"); + } else { pdata = pdev->dev.platform_data; + bus_id = pdev->id; + } if (!pdata) return -ENODEV; - new_bus = mdio_gpio_bus_init(&pdev->dev, pdata, pdev->id); + new_bus = mdio_gpio_bus_init(&pdev->dev, pdata, bus_id); if (!new_bus) return -ENODEV; -- cgit v1.2.3 From d8f4a9eda006788d8054b8500d9eb5b6efcd8755 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 15 Nov 2012 21:28:22 +0000 Subject: drm: Add NVIDIA Tegra20 support This commit adds a KMS driver for the Tegra20 SoC. This includes basic support for host1x and the two display controllers found on the Tegra20 SoC. Each display controller can drive a separate RGB/LVDS output. Signed-off-by: Thierry Reding Tested-by: Stephen Warren Acked-by: Mark Zhang Reviewed-by: Mark Zhang Tested-by: Mark Zhang Tested-and-acked-by: Alexandre Courbot Acked-by: Terje Bergstrom Tested-by: Terje Bergstrom Signed-off-by: Dave Airlie --- .../bindings/gpu/nvidia,tegra20-host1x.txt | 191 +++++ drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/tegra/Kconfig | 23 + drivers/gpu/drm/tegra/Makefile | 7 + drivers/gpu/drm/tegra/dc.c | 833 +++++++++++++++++++++ drivers/gpu/drm/tegra/dc.h | 388 ++++++++++ drivers/gpu/drm/tegra/drm.c | 115 +++ drivers/gpu/drm/tegra/drm.h | 232 ++++++ drivers/gpu/drm/tegra/fb.c | 56 ++ drivers/gpu/drm/tegra/host1x.c | 314 ++++++++ drivers/gpu/drm/tegra/output.c | 267 +++++++ drivers/gpu/drm/tegra/rgb.c | 228 ++++++ 13 files changed, 2657 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt create mode 100644 drivers/gpu/drm/tegra/Kconfig create mode 100644 drivers/gpu/drm/tegra/Makefile create mode 100644 drivers/gpu/drm/tegra/dc.c create mode 100644 drivers/gpu/drm/tegra/dc.h create mode 100644 drivers/gpu/drm/tegra/drm.c create mode 100644 drivers/gpu/drm/tegra/drm.h create mode 100644 drivers/gpu/drm/tegra/fb.c create mode 100644 drivers/gpu/drm/tegra/host1x.c create mode 100644 drivers/gpu/drm/tegra/output.c create mode 100644 drivers/gpu/drm/tegra/rgb.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt b/Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt new file mode 100644 index 000000000000..b4fa934ae3a2 --- /dev/null +++ b/Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt @@ -0,0 +1,191 @@ +NVIDIA Tegra host1x + +Required properties: +- compatible: "nvidia,tegra-host1x" +- reg: Physical base address and length of the controller's registers. +- interrupts: The interrupt outputs from the controller. +- #address-cells: The number of cells used to represent physical base addresses + in the host1x address space. Should be 1. +- #size-cells: The number of cells used to represent the size of an address + range in the host1x address space. Should be 1. +- ranges: The mapping of the host1x address space to the CPU address space. + +The host1x top-level node defines a number of children, each representing one +of the following host1x client modules: + +- mpe: video encoder + + Required properties: + - compatible: "nvidia,tegra-mpe" + - reg: Physical base address and length of the controller's registers. + - interrupts: The interrupt outputs from the controller. + +- vi: video input + + Required properties: + - compatible: "nvidia,tegra-vi" + - reg: Physical base address and length of the controller's registers. + - interrupts: The interrupt outputs from the controller. + +- epp: encoder pre-processor + + Required properties: + - compatible: "nvidia,tegra-epp" + - reg: Physical base address and length of the controller's registers. + - interrupts: The interrupt outputs from the controller. + +- isp: image signal processor + + Required properties: + - compatible: "nvidia,tegra-isp" + - reg: Physical base address and length of the controller's registers. + - interrupts: The interrupt outputs from the controller. + +- gr2d: 2D graphics engine + + Required properties: + - compatible: "nvidia,tegra-gr2d" + - reg: Physical base address and length of the controller's registers. + - interrupts: The interrupt outputs from the controller. + +- gr3d: 3D graphics engine + + Required properties: + - compatible: "nvidia,tegra-gr3d" + - reg: Physical base address and length of the controller's registers. + +- dc: display controller + + Required properties: + - compatible: "nvidia,tegra-dc" + - reg: Physical base address and length of the controller's registers. + - interrupts: The interrupt outputs from the controller. + + Each display controller node has a child node, named "rgb", that represents + the RGB output associated with the controller. It can take the following + optional properties: + - nvidia,ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing + - nvidia,hpd-gpio: specifies a GPIO used for hotplug detection + - nvidia,edid: supplies a binary EDID blob + +- hdmi: High Definition Multimedia Interface + + Required properties: + - compatible: "nvidia,tegra-hdmi" + - reg: Physical base address and length of the controller's registers. + - interrupts: The interrupt outputs from the controller. + - vdd-supply: regulator for supply voltage + - pll-supply: regulator for PLL + + Optional properties: + - nvidia,ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing + - nvidia,hpd-gpio: specifies a GPIO used for hotplug detection + - nvidia,edid: supplies a binary EDID blob + +- tvo: TV encoder output + + Required properties: + - compatible: "nvidia,tegra-tvo" + - reg: Physical base address and length of the controller's registers. + - interrupts: The interrupt outputs from the controller. + +- dsi: display serial interface + + Required properties: + - compatible: "nvidia,tegra-dsi" + - reg: Physical base address and length of the controller's registers. + +Example: + +/ { + ... + + host1x { + compatible = "nvidia,tegra20-host1x", "simple-bus"; + reg = <0x50000000 0x00024000>; + interrupts = <0 65 0x04 /* mpcore syncpt */ + 0 67 0x04>; /* mpcore general */ + + #address-cells = <1>; + #size-cells = <1>; + + ranges = <0x54000000 0x54000000 0x04000000>; + + mpe { + compatible = "nvidia,tegra20-mpe"; + reg = <0x54040000 0x00040000>; + interrupts = <0 68 0x04>; + }; + + vi { + compatible = "nvidia,tegra20-vi"; + reg = <0x54080000 0x00040000>; + interrupts = <0 69 0x04>; + }; + + epp { + compatible = "nvidia,tegra20-epp"; + reg = <0x540c0000 0x00040000>; + interrupts = <0 70 0x04>; + }; + + isp { + compatible = "nvidia,tegra20-isp"; + reg = <0x54100000 0x00040000>; + interrupts = <0 71 0x04>; + }; + + gr2d { + compatible = "nvidia,tegra20-gr2d"; + reg = <0x54140000 0x00040000>; + interrupts = <0 72 0x04>; + }; + + gr3d { + compatible = "nvidia,tegra20-gr3d"; + reg = <0x54180000 0x00040000>; + }; + + dc@54200000 { + compatible = "nvidia,tegra20-dc"; + reg = <0x54200000 0x00040000>; + interrupts = <0 73 0x04>; + + rgb { + status = "disabled"; + }; + }; + + dc@54240000 { + compatible = "nvidia,tegra20-dc"; + reg = <0x54240000 0x00040000>; + interrupts = <0 74 0x04>; + + rgb { + status = "disabled"; + }; + }; + + hdmi { + compatible = "nvidia,tegra20-hdmi"; + reg = <0x54280000 0x00040000>; + interrupts = <0 75 0x04>; + status = "disabled"; + }; + + tvo { + compatible = "nvidia,tegra20-tvo"; + reg = <0x542c0000 0x00040000>; + interrupts = <0 76 0x04>; + status = "disabled"; + }; + + dsi { + compatible = "nvidia,tegra20-dsi"; + reg = <0x54300000 0x00040000>; + status = "disabled"; + }; + }; + + ... +}; diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 18321b68b880..983201b450f1 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -210,3 +210,5 @@ source "drivers/gpu/drm/mgag200/Kconfig" source "drivers/gpu/drm/cirrus/Kconfig" source "drivers/gpu/drm/shmobile/Kconfig" + +source "drivers/gpu/drm/tegra/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index dc4e88f9fb11..ac91a339b042 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -48,4 +48,5 @@ obj-$(CONFIG_DRM_GMA500) += gma500/ obj-$(CONFIG_DRM_UDL) += udl/ obj-$(CONFIG_DRM_AST) += ast/ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ +obj-$(CONFIG_DRM_TEGRA) += tegra/ obj-y += i2c/ diff --git a/drivers/gpu/drm/tegra/Kconfig b/drivers/gpu/drm/tegra/Kconfig new file mode 100644 index 000000000000..be1daf7344d3 --- /dev/null +++ b/drivers/gpu/drm/tegra/Kconfig @@ -0,0 +1,23 @@ +config DRM_TEGRA + tristate "NVIDIA Tegra DRM" + depends on DRM && OF && ARCH_TEGRA + select DRM_KMS_HELPER + select DRM_GEM_CMA_HELPER + select DRM_KMS_CMA_HELPER + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + Choose this option if you have an NVIDIA Tegra SoC. + + To compile this driver as a module, choose M here: the module + will be called tegra-drm. + +if DRM_TEGRA + +config DRM_TEGRA_DEBUG + bool "NVIDIA Tegra DRM debug support" + help + Say yes here to enable debugging support. + +endif diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile new file mode 100644 index 000000000000..624a807aedbe --- /dev/null +++ b/drivers/gpu/drm/tegra/Makefile @@ -0,0 +1,7 @@ +ccflags-y := -Iinclude/drm +ccflags-$(CONFIG_DRM_TEGRA_DEBUG) += -DDEBUG + +tegra-drm-y := drm.o fb.o dc.o host1x.o +tegra-drm-y += output.o rgb.o + +obj-$(CONFIG_DRM_TEGRA) += tegra-drm.o diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c new file mode 100644 index 000000000000..53b98520ebd6 --- /dev/null +++ b/drivers/gpu/drm/tegra/dc.c @@ -0,0 +1,833 @@ +/* + * Copyright (C) 2012 Avionic Design GmbH + * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include + +#include "drm.h" +#include "dc.h" + +struct tegra_dc_window { + fixed20_12 x; + fixed20_12 y; + fixed20_12 w; + fixed20_12 h; + unsigned int outx; + unsigned int outy; + unsigned int outw; + unsigned int outh; + unsigned int stride; + unsigned int fmt; +}; + +static const struct drm_crtc_funcs tegra_crtc_funcs = { + .set_config = drm_crtc_helper_set_config, + .destroy = drm_crtc_cleanup, +}; + +static void tegra_crtc_dpms(struct drm_crtc *crtc, int mode) +{ +} + +static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted) +{ + return true; +} + +static inline u32 compute_dda_inc(fixed20_12 inf, unsigned int out, bool v, + unsigned int bpp) +{ + fixed20_12 outf = dfixed_init(out); + u32 dda_inc; + int max; + + if (v) + max = 15; + else { + switch (bpp) { + case 2: + max = 8; + break; + + default: + WARN_ON_ONCE(1); + /* fallthrough */ + case 4: + max = 4; + break; + } + } + + outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1)); + inf.full -= dfixed_const(1); + + dda_inc = dfixed_div(inf, outf); + dda_inc = min_t(u32, dda_inc, dfixed_const(max)); + + return dda_inc; +} + +static inline u32 compute_initial_dda(fixed20_12 in) +{ + return dfixed_frac(in); +} + +static int tegra_dc_set_timings(struct tegra_dc *dc, + struct drm_display_mode *mode) +{ + /* TODO: For HDMI compliance, h & v ref_to_sync should be set to 1 */ + unsigned int h_ref_to_sync = 0; + unsigned int v_ref_to_sync = 0; + unsigned long value; + + tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS); + + value = (v_ref_to_sync << 16) | h_ref_to_sync; + tegra_dc_writel(dc, value, DC_DISP_REF_TO_SYNC); + + value = ((mode->vsync_end - mode->vsync_start) << 16) | + ((mode->hsync_end - mode->hsync_start) << 0); + tegra_dc_writel(dc, value, DC_DISP_SYNC_WIDTH); + + value = ((mode->vsync_start - mode->vdisplay) << 16) | + ((mode->hsync_start - mode->hdisplay) << 0); + tegra_dc_writel(dc, value, DC_DISP_BACK_PORCH); + + value = ((mode->vtotal - mode->vsync_end) << 16) | + ((mode->htotal - mode->hsync_end) << 0); + tegra_dc_writel(dc, value, DC_DISP_FRONT_PORCH); + + value = (mode->vdisplay << 16) | mode->hdisplay; + tegra_dc_writel(dc, value, DC_DISP_ACTIVE); + + return 0; +} + +static int tegra_crtc_setup_clk(struct drm_crtc *crtc, + struct drm_display_mode *mode, + unsigned long *div) +{ + unsigned long pclk = mode->clock * 1000, rate; + struct tegra_dc *dc = to_tegra_dc(crtc); + struct tegra_output *output = NULL; + struct drm_encoder *encoder; + long err; + + list_for_each_entry(encoder, &crtc->dev->mode_config.encoder_list, head) + if (encoder->crtc == crtc) { + output = encoder_to_output(encoder); + break; + } + + if (!output) + return -ENODEV; + + /* + * This assumes that the display controller will divide its parent + * clock by 2 to generate the pixel clock. + */ + err = tegra_output_setup_clock(output, dc->clk, pclk * 2); + if (err < 0) { + dev_err(dc->dev, "failed to setup clock: %ld\n", err); + return err; + } + + rate = clk_get_rate(dc->clk); + *div = (rate * 2 / pclk) - 2; + + DRM_DEBUG_KMS("rate: %lu, div: %lu\n", rate, *div); + + return 0; +} + +static int tegra_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted, + int x, int y, struct drm_framebuffer *old_fb) +{ + struct tegra_framebuffer *fb = to_tegra_fb(crtc->fb); + struct tegra_dc *dc = to_tegra_dc(crtc); + unsigned int h_dda, v_dda, bpp; + struct tegra_dc_window win; + unsigned long div, value; + int err; + + err = tegra_crtc_setup_clk(crtc, mode, &div); + if (err) { + dev_err(dc->dev, "failed to setup clock for CRTC: %d\n", err); + return err; + } + + /* program display mode */ + tegra_dc_set_timings(dc, mode); + + value = DE_SELECT_ACTIVE | DE_CONTROL_NORMAL; + tegra_dc_writel(dc, value, DC_DISP_DATA_ENABLE_OPTIONS); + + value = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_POLARITY(1)); + value &= ~LVS_OUTPUT_POLARITY_LOW; + value &= ~LHS_OUTPUT_POLARITY_LOW; + tegra_dc_writel(dc, value, DC_COM_PIN_OUTPUT_POLARITY(1)); + + value = DISP_DATA_FORMAT_DF1P1C | DISP_ALIGNMENT_MSB | + DISP_ORDER_RED_BLUE; + tegra_dc_writel(dc, value, DC_DISP_DISP_INTERFACE_CONTROL); + + tegra_dc_writel(dc, 0x00010001, DC_DISP_SHIFT_CLOCK_OPTIONS); + + value = SHIFT_CLK_DIVIDER(div) | PIXEL_CLK_DIVIDER_PCD1; + tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL); + + /* setup window parameters */ + memset(&win, 0, sizeof(win)); + win.x.full = dfixed_const(0); + win.y.full = dfixed_const(0); + win.w.full = dfixed_const(mode->hdisplay); + win.h.full = dfixed_const(mode->vdisplay); + win.outx = 0; + win.outy = 0; + win.outw = mode->hdisplay; + win.outh = mode->vdisplay; + + switch (crtc->fb->pixel_format) { + case DRM_FORMAT_XRGB8888: + win.fmt = WIN_COLOR_DEPTH_B8G8R8A8; + break; + + case DRM_FORMAT_RGB565: + win.fmt = WIN_COLOR_DEPTH_B5G6R5; + break; + + default: + win.fmt = WIN_COLOR_DEPTH_B8G8R8A8; + WARN_ON(1); + break; + } + + bpp = crtc->fb->bits_per_pixel / 8; + win.stride = win.outw * bpp; + + /* program window registers */ + value = tegra_dc_readl(dc, DC_CMD_DISPLAY_WINDOW_HEADER); + value |= WINDOW_A_SELECT; + tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); + + tegra_dc_writel(dc, win.fmt, DC_WIN_COLOR_DEPTH); + tegra_dc_writel(dc, 0, DC_WIN_BYTE_SWAP); + + value = V_POSITION(win.outy) | H_POSITION(win.outx); + tegra_dc_writel(dc, value, DC_WIN_POSITION); + + value = V_SIZE(win.outh) | H_SIZE(win.outw); + tegra_dc_writel(dc, value, DC_WIN_SIZE); + + value = V_PRESCALED_SIZE(dfixed_trunc(win.h)) | + H_PRESCALED_SIZE(dfixed_trunc(win.w) * bpp); + tegra_dc_writel(dc, value, DC_WIN_PRESCALED_SIZE); + + h_dda = compute_dda_inc(win.w, win.outw, false, bpp); + v_dda = compute_dda_inc(win.h, win.outh, true, bpp); + + value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda); + tegra_dc_writel(dc, value, DC_WIN_DDA_INC); + + h_dda = compute_initial_dda(win.x); + v_dda = compute_initial_dda(win.y); + + tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA); + tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA); + + tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE); + tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE); + + tegra_dc_writel(dc, fb->obj->paddr, DC_WINBUF_START_ADDR); + tegra_dc_writel(dc, win.stride, DC_WIN_LINE_STRIDE); + tegra_dc_writel(dc, dfixed_trunc(win.x) * bpp, + DC_WINBUF_ADDR_H_OFFSET); + tegra_dc_writel(dc, dfixed_trunc(win.y), DC_WINBUF_ADDR_V_OFFSET); + + value = WIN_ENABLE; + + if (bpp < 24) + value |= COLOR_EXPAND; + + tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); + + tegra_dc_writel(dc, 0xff00, DC_WIN_BLEND_NOKEY); + tegra_dc_writel(dc, 0xff00, DC_WIN_BLEND_1WIN); + + return 0; +} + +static void tegra_crtc_prepare(struct drm_crtc *crtc) +{ + struct tegra_dc *dc = to_tegra_dc(crtc); + unsigned int syncpt; + unsigned long value; + + /* hardware initialization */ + tegra_periph_reset_deassert(dc->clk); + usleep_range(10000, 20000); + + if (dc->pipe) + syncpt = SYNCPT_VBLANK1; + else + syncpt = SYNCPT_VBLANK0; + + /* initialize display controller */ + tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); + tegra_dc_writel(dc, 0x100 | syncpt, DC_CMD_CONT_SYNCPT_VSYNC); + + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | WIN_A_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); + + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); + + value = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | + PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; + tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); + + value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); + value |= DISP_CTRL_MODE_C_DISPLAY; + tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); + + /* initialize timer */ + value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) | + WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20); + tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY); + + value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) | + WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); + tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); + + value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_MASK); + + value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); +} + +static void tegra_crtc_commit(struct drm_crtc *crtc) +{ + struct tegra_dc *dc = to_tegra_dc(crtc); + unsigned long update_mask; + unsigned long value; + + update_mask = GENERAL_ACT_REQ | WIN_A_ACT_REQ; + + tegra_dc_writel(dc, update_mask << 8, DC_CMD_STATE_CONTROL); + + value = tegra_dc_readl(dc, DC_CMD_INT_ENABLE); + value |= FRAME_END_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); + + value = tegra_dc_readl(dc, DC_CMD_INT_MASK); + value |= FRAME_END_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_MASK); + + tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL); +} + +static void tegra_crtc_load_lut(struct drm_crtc *crtc) +{ +} + +static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { + .dpms = tegra_crtc_dpms, + .mode_fixup = tegra_crtc_mode_fixup, + .mode_set = tegra_crtc_mode_set, + .prepare = tegra_crtc_prepare, + .commit = tegra_crtc_commit, + .load_lut = tegra_crtc_load_lut, +}; + +static irqreturn_t tegra_drm_irq(int irq, void *data) +{ + struct tegra_dc *dc = data; + unsigned long status; + + status = tegra_dc_readl(dc, DC_CMD_INT_STATUS); + tegra_dc_writel(dc, status, DC_CMD_INT_STATUS); + + if (status & FRAME_END_INT) { + /* + dev_dbg(dc->dev, "%s(): frame end\n", __func__); + */ + } + + if (status & VBLANK_INT) { + /* + dev_dbg(dc->dev, "%s(): vertical blank\n", __func__); + */ + drm_handle_vblank(dc->base.dev, dc->pipe); + } + + if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) { + /* + dev_dbg(dc->dev, "%s(): underflow\n", __func__); + */ + } + + return IRQ_HANDLED; +} + +static int tegra_dc_show_regs(struct seq_file *s, void *data) +{ + struct drm_info_node *node = s->private; + struct tegra_dc *dc = node->info_ent->data; + +#define DUMP_REG(name) \ + seq_printf(s, "%-40s %#05x %08lx\n", #name, name, \ + tegra_dc_readl(dc, name)) + + DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT); + DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); + DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT_ERROR); + DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT); + DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL); + DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT_ERROR); + DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT); + DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL); + DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT_ERROR); + DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT); + DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL); + DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT_ERROR); + DUMP_REG(DC_CMD_CONT_SYNCPT_VSYNC); + DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0); + DUMP_REG(DC_CMD_DISPLAY_COMMAND); + DUMP_REG(DC_CMD_SIGNAL_RAISE); + DUMP_REG(DC_CMD_DISPLAY_POWER_CONTROL); + DUMP_REG(DC_CMD_INT_STATUS); + DUMP_REG(DC_CMD_INT_MASK); + DUMP_REG(DC_CMD_INT_ENABLE); + DUMP_REG(DC_CMD_INT_TYPE); + DUMP_REG(DC_CMD_INT_POLARITY); + DUMP_REG(DC_CMD_SIGNAL_RAISE1); + DUMP_REG(DC_CMD_SIGNAL_RAISE2); + DUMP_REG(DC_CMD_SIGNAL_RAISE3); + DUMP_REG(DC_CMD_STATE_ACCESS); + DUMP_REG(DC_CMD_STATE_CONTROL); + DUMP_REG(DC_CMD_DISPLAY_WINDOW_HEADER); + DUMP_REG(DC_CMD_REG_ACT_CONTROL); + DUMP_REG(DC_COM_CRC_CONTROL); + DUMP_REG(DC_COM_CRC_CHECKSUM); + DUMP_REG(DC_COM_PIN_OUTPUT_ENABLE(0)); + DUMP_REG(DC_COM_PIN_OUTPUT_ENABLE(1)); + DUMP_REG(DC_COM_PIN_OUTPUT_ENABLE(2)); + DUMP_REG(DC_COM_PIN_OUTPUT_ENABLE(3)); + DUMP_REG(DC_COM_PIN_OUTPUT_POLARITY(0)); + DUMP_REG(DC_COM_PIN_OUTPUT_POLARITY(1)); + DUMP_REG(DC_COM_PIN_OUTPUT_POLARITY(2)); + DUMP_REG(DC_COM_PIN_OUTPUT_POLARITY(3)); + DUMP_REG(DC_COM_PIN_OUTPUT_DATA(0)); + DUMP_REG(DC_COM_PIN_OUTPUT_DATA(1)); + DUMP_REG(DC_COM_PIN_OUTPUT_DATA(2)); + DUMP_REG(DC_COM_PIN_OUTPUT_DATA(3)); + DUMP_REG(DC_COM_PIN_INPUT_ENABLE(0)); + DUMP_REG(DC_COM_PIN_INPUT_ENABLE(1)); + DUMP_REG(DC_COM_PIN_INPUT_ENABLE(2)); + DUMP_REG(DC_COM_PIN_INPUT_ENABLE(3)); + DUMP_REG(DC_COM_PIN_INPUT_DATA(0)); + DUMP_REG(DC_COM_PIN_INPUT_DATA(1)); + DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(0)); + DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(1)); + DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(2)); + DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(3)); + DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(4)); + DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(5)); + DUMP_REG(DC_COM_PIN_OUTPUT_SELECT(6)); + DUMP_REG(DC_COM_PIN_MISC_CONTROL); + DUMP_REG(DC_COM_PIN_PM0_CONTROL); + DUMP_REG(DC_COM_PIN_PM0_DUTY_CYCLE); + DUMP_REG(DC_COM_PIN_PM1_CONTROL); + DUMP_REG(DC_COM_PIN_PM1_DUTY_CYCLE); + DUMP_REG(DC_COM_SPI_CONTROL); + DUMP_REG(DC_COM_SPI_START_BYTE); + DUMP_REG(DC_COM_HSPI_WRITE_DATA_AB); + DUMP_REG(DC_COM_HSPI_WRITE_DATA_CD); + DUMP_REG(DC_COM_HSPI_CS_DC); + DUMP_REG(DC_COM_SCRATCH_REGISTER_A); + DUMP_REG(DC_COM_SCRATCH_REGISTER_B); + DUMP_REG(DC_COM_GPIO_CTRL); + DUMP_REG(DC_COM_GPIO_DEBOUNCE_COUNTER); + DUMP_REG(DC_COM_CRC_CHECKSUM_LATCHED); + DUMP_REG(DC_DISP_DISP_SIGNAL_OPTIONS0); + DUMP_REG(DC_DISP_DISP_SIGNAL_OPTIONS1); + DUMP_REG(DC_DISP_DISP_WIN_OPTIONS); + DUMP_REG(DC_DISP_DISP_MEM_HIGH_PRIORITY); + DUMP_REG(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); + DUMP_REG(DC_DISP_DISP_TIMING_OPTIONS); + DUMP_REG(DC_DISP_REF_TO_SYNC); + DUMP_REG(DC_DISP_SYNC_WIDTH); + DUMP_REG(DC_DISP_BACK_PORCH); + DUMP_REG(DC_DISP_ACTIVE); + DUMP_REG(DC_DISP_FRONT_PORCH); + DUMP_REG(DC_DISP_H_PULSE0_CONTROL); + DUMP_REG(DC_DISP_H_PULSE0_POSITION_A); + DUMP_REG(DC_DISP_H_PULSE0_POSITION_B); + DUMP_REG(DC_DISP_H_PULSE0_POSITION_C); + DUMP_REG(DC_DISP_H_PULSE0_POSITION_D); + DUMP_REG(DC_DISP_H_PULSE1_CONTROL); + DUMP_REG(DC_DISP_H_PULSE1_POSITION_A); + DUMP_REG(DC_DISP_H_PULSE1_POSITION_B); + DUMP_REG(DC_DISP_H_PULSE1_POSITION_C); + DUMP_REG(DC_DISP_H_PULSE1_POSITION_D); + DUMP_REG(DC_DISP_H_PULSE2_CONTROL); + DUMP_REG(DC_DISP_H_PULSE2_POSITION_A); + DUMP_REG(DC_DISP_H_PULSE2_POSITION_B); + DUMP_REG(DC_DISP_H_PULSE2_POSITION_C); + DUMP_REG(DC_DISP_H_PULSE2_POSITION_D); + DUMP_REG(DC_DISP_V_PULSE0_CONTROL); + DUMP_REG(DC_DISP_V_PULSE0_POSITION_A); + DUMP_REG(DC_DISP_V_PULSE0_POSITION_B); + DUMP_REG(DC_DISP_V_PULSE0_POSITION_C); + DUMP_REG(DC_DISP_V_PULSE1_CONTROL); + DUMP_REG(DC_DISP_V_PULSE1_POSITION_A); + DUMP_REG(DC_DISP_V_PULSE1_POSITION_B); + DUMP_REG(DC_DISP_V_PULSE1_POSITION_C); + DUMP_REG(DC_DISP_V_PULSE2_CONTROL); + DUMP_REG(DC_DISP_V_PULSE2_POSITION_A); + DUMP_REG(DC_DISP_V_PULSE3_CONTROL); + DUMP_REG(DC_DISP_V_PULSE3_POSITION_A); + DUMP_REG(DC_DISP_M0_CONTROL); + DUMP_REG(DC_DISP_M1_CONTROL); + DUMP_REG(DC_DISP_DI_CONTROL); + DUMP_REG(DC_DISP_PP_CONTROL); + DUMP_REG(DC_DISP_PP_SELECT_A); + DUMP_REG(DC_DISP_PP_SELECT_B); + DUMP_REG(DC_DISP_PP_SELECT_C); + DUMP_REG(DC_DISP_PP_SELECT_D); + DUMP_REG(DC_DISP_DISP_CLOCK_CONTROL); + DUMP_REG(DC_DISP_DISP_INTERFACE_CONTROL); + DUMP_REG(DC_DISP_DISP_COLOR_CONTROL); + DUMP_REG(DC_DISP_SHIFT_CLOCK_OPTIONS); + DUMP_REG(DC_DISP_DATA_ENABLE_OPTIONS); + DUMP_REG(DC_DISP_SERIAL_INTERFACE_OPTIONS); + DUMP_REG(DC_DISP_LCD_SPI_OPTIONS); + DUMP_REG(DC_DISP_BORDER_COLOR); + DUMP_REG(DC_DISP_COLOR_KEY0_LOWER); + DUMP_REG(DC_DISP_COLOR_KEY0_UPPER); + DUMP_REG(DC_DISP_COLOR_KEY1_LOWER); + DUMP_REG(DC_DISP_COLOR_KEY1_UPPER); + DUMP_REG(DC_DISP_CURSOR_FOREGROUND); + DUMP_REG(DC_DISP_CURSOR_BACKGROUND); + DUMP_REG(DC_DISP_CURSOR_START_ADDR); + DUMP_REG(DC_DISP_CURSOR_START_ADDR_NS); + DUMP_REG(DC_DISP_CURSOR_POSITION); + DUMP_REG(DC_DISP_CURSOR_POSITION_NS); + DUMP_REG(DC_DISP_INIT_SEQ_CONTROL); + DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_A); + DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_B); + DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_C); + DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_D); + DUMP_REG(DC_DISP_DC_MCCIF_FIFOCTRL); + DUMP_REG(DC_DISP_MCCIF_DISPLAY0A_HYST); + DUMP_REG(DC_DISP_MCCIF_DISPLAY0B_HYST); + DUMP_REG(DC_DISP_MCCIF_DISPLAY1A_HYST); + DUMP_REG(DC_DISP_MCCIF_DISPLAY1B_HYST); + DUMP_REG(DC_DISP_DAC_CRT_CTRL); + DUMP_REG(DC_DISP_DISP_MISC_CONTROL); + DUMP_REG(DC_DISP_SD_CONTROL); + DUMP_REG(DC_DISP_SD_CSC_COEFF); + DUMP_REG(DC_DISP_SD_LUT(0)); + DUMP_REG(DC_DISP_SD_LUT(1)); + DUMP_REG(DC_DISP_SD_LUT(2)); + DUMP_REG(DC_DISP_SD_LUT(3)); + DUMP_REG(DC_DISP_SD_LUT(4)); + DUMP_REG(DC_DISP_SD_LUT(5)); + DUMP_REG(DC_DISP_SD_LUT(6)); + DUMP_REG(DC_DISP_SD_LUT(7)); + DUMP_REG(DC_DISP_SD_LUT(8)); + DUMP_REG(DC_DISP_SD_FLICKER_CONTROL); + DUMP_REG(DC_DISP_DC_PIXEL_COUNT); + DUMP_REG(DC_DISP_SD_HISTOGRAM(0)); + DUMP_REG(DC_DISP_SD_HISTOGRAM(1)); + DUMP_REG(DC_DISP_SD_HISTOGRAM(2)); + DUMP_REG(DC_DISP_SD_HISTOGRAM(3)); + DUMP_REG(DC_DISP_SD_HISTOGRAM(4)); + DUMP_REG(DC_DISP_SD_HISTOGRAM(5)); + DUMP_REG(DC_DISP_SD_HISTOGRAM(6)); + DUMP_REG(DC_DISP_SD_HISTOGRAM(7)); + DUMP_REG(DC_DISP_SD_BL_TF(0)); + DUMP_REG(DC_DISP_SD_BL_TF(1)); + DUMP_REG(DC_DISP_SD_BL_TF(2)); + DUMP_REG(DC_DISP_SD_BL_TF(3)); + DUMP_REG(DC_DISP_SD_BL_CONTROL); + DUMP_REG(DC_DISP_SD_HW_K_VALUES); + DUMP_REG(DC_DISP_SD_MAN_K_VALUES); + DUMP_REG(DC_WIN_WIN_OPTIONS); + DUMP_REG(DC_WIN_BYTE_SWAP); + DUMP_REG(DC_WIN_BUFFER_CONTROL); + DUMP_REG(DC_WIN_COLOR_DEPTH); + DUMP_REG(DC_WIN_POSITION); + DUMP_REG(DC_WIN_SIZE); + DUMP_REG(DC_WIN_PRESCALED_SIZE); + DUMP_REG(DC_WIN_H_INITIAL_DDA); + DUMP_REG(DC_WIN_V_INITIAL_DDA); + DUMP_REG(DC_WIN_DDA_INC); + DUMP_REG(DC_WIN_LINE_STRIDE); + DUMP_REG(DC_WIN_BUF_STRIDE); + DUMP_REG(DC_WIN_UV_BUF_STRIDE); + DUMP_REG(DC_WIN_BUFFER_ADDR_MODE); + DUMP_REG(DC_WIN_DV_CONTROL); + DUMP_REG(DC_WIN_BLEND_NOKEY); + DUMP_REG(DC_WIN_BLEND_1WIN); + DUMP_REG(DC_WIN_BLEND_2WIN_X); + DUMP_REG(DC_WIN_BLEND_2WIN_Y); + DUMP_REG(DC_WIN_BLEND32WIN_XY); + DUMP_REG(DC_WIN_HP_FETCH_CONTROL); + DUMP_REG(DC_WINBUF_START_ADDR); + DUMP_REG(DC_WINBUF_START_ADDR_NS); + DUMP_REG(DC_WINBUF_START_ADDR_U); + DUMP_REG(DC_WINBUF_START_ADDR_U_NS); + DUMP_REG(DC_WINBUF_START_ADDR_V); + DUMP_REG(DC_WINBUF_START_ADDR_V_NS); + DUMP_REG(DC_WINBUF_ADDR_H_OFFSET); + DUMP_REG(DC_WINBUF_ADDR_H_OFFSET_NS); + DUMP_REG(DC_WINBUF_ADDR_V_OFFSET); + DUMP_REG(DC_WINBUF_ADDR_V_OFFSET_NS); + DUMP_REG(DC_WINBUF_UFLOW_STATUS); + DUMP_REG(DC_WINBUF_AD_UFLOW_STATUS); + DUMP_REG(DC_WINBUF_BD_UFLOW_STATUS); + DUMP_REG(DC_WINBUF_CD_UFLOW_STATUS); + +#undef DUMP_REG + + return 0; +} + +static struct drm_info_list debugfs_files[] = { + { "regs", tegra_dc_show_regs, 0, NULL }, +}; + +static int tegra_dc_debugfs_init(struct tegra_dc *dc, struct drm_minor *minor) +{ + unsigned int i; + char *name; + int err; + + name = kasprintf(GFP_KERNEL, "dc.%d", dc->pipe); + dc->debugfs = debugfs_create_dir(name, minor->debugfs_root); + kfree(name); + + if (!dc->debugfs) + return -ENOMEM; + + dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), + GFP_KERNEL); + if (!dc->debugfs_files) { + err = -ENOMEM; + goto remove; + } + + for (i = 0; i < ARRAY_SIZE(debugfs_files); i++) + dc->debugfs_files[i].data = dc; + + err = drm_debugfs_create_files(dc->debugfs_files, + ARRAY_SIZE(debugfs_files), + dc->debugfs, minor); + if (err < 0) + goto free; + + dc->minor = minor; + + return 0; + +free: + kfree(dc->debugfs_files); + dc->debugfs_files = NULL; +remove: + debugfs_remove(dc->debugfs); + dc->debugfs = NULL; + + return err; +} + +static int tegra_dc_debugfs_exit(struct tegra_dc *dc) +{ + drm_debugfs_remove_files(dc->debugfs_files, ARRAY_SIZE(debugfs_files), + dc->minor); + dc->minor = NULL; + + kfree(dc->debugfs_files); + dc->debugfs_files = NULL; + + debugfs_remove(dc->debugfs); + dc->debugfs = NULL; + + return 0; +} + +static int tegra_dc_drm_init(struct host1x_client *client, + struct drm_device *drm) +{ + struct tegra_dc *dc = host1x_client_to_dc(client); + int err; + + dc->pipe = drm->mode_config.num_crtc; + + drm_crtc_init(drm, &dc->base, &tegra_crtc_funcs); + drm_mode_crtc_set_gamma_size(&dc->base, 256); + drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs); + + err = tegra_dc_rgb_init(drm, dc); + if (err < 0 && err != -ENODEV) { + dev_err(dc->dev, "failed to initialize RGB output: %d\n", err); + return err; + } + + if (IS_ENABLED(CONFIG_DEBUG_FS)) { + err = tegra_dc_debugfs_init(dc, drm->primary); + if (err < 0) + dev_err(dc->dev, "debugfs setup failed: %d\n", err); + } + + err = devm_request_irq(dc->dev, dc->irq, tegra_drm_irq, 0, + dev_name(dc->dev), dc); + if (err < 0) { + dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq, + err); + return err; + } + + return 0; +} + +static int tegra_dc_drm_exit(struct host1x_client *client) +{ + struct tegra_dc *dc = host1x_client_to_dc(client); + int err; + + devm_free_irq(dc->dev, dc->irq, dc); + + if (IS_ENABLED(CONFIG_DEBUG_FS)) { + err = tegra_dc_debugfs_exit(dc); + if (err < 0) + dev_err(dc->dev, "debugfs cleanup failed: %d\n", err); + } + + err = tegra_dc_rgb_exit(dc); + if (err) { + dev_err(dc->dev, "failed to shutdown RGB output: %d\n", err); + return err; + } + + return 0; +} + +static const struct host1x_client_ops dc_client_ops = { + .drm_init = tegra_dc_drm_init, + .drm_exit = tegra_dc_drm_exit, +}; + +static int tegra_dc_probe(struct platform_device *pdev) +{ + struct host1x *host1x = dev_get_drvdata(pdev->dev.parent); + struct resource *regs; + struct tegra_dc *dc; + int err; + + dc = devm_kzalloc(&pdev->dev, sizeof(*dc), GFP_KERNEL); + if (!dc) + return -ENOMEM; + + INIT_LIST_HEAD(&dc->list); + dc->dev = &pdev->dev; + + dc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(dc->clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + return PTR_ERR(dc->clk); + } + + err = clk_prepare_enable(dc->clk); + if (err < 0) + return err; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(&pdev->dev, "failed to get registers\n"); + return -ENXIO; + } + + dc->regs = devm_request_and_ioremap(&pdev->dev, regs); + if (!dc->regs) { + dev_err(&pdev->dev, "failed to remap registers\n"); + return -ENXIO; + } + + dc->irq = platform_get_irq(pdev, 0); + if (dc->irq < 0) { + dev_err(&pdev->dev, "failed to get IRQ\n"); + return -ENXIO; + } + + INIT_LIST_HEAD(&dc->client.list); + dc->client.ops = &dc_client_ops; + dc->client.dev = &pdev->dev; + + err = tegra_dc_rgb_probe(dc); + if (err < 0 && err != -ENODEV) { + dev_err(&pdev->dev, "failed to probe RGB output: %d\n", err); + return err; + } + + err = host1x_register_client(host1x, &dc->client); + if (err < 0) { + dev_err(&pdev->dev, "failed to register host1x client: %d\n", + err); + return err; + } + + platform_set_drvdata(pdev, dc); + + return 0; +} + +static int tegra_dc_remove(struct platform_device *pdev) +{ + struct host1x *host1x = dev_get_drvdata(pdev->dev.parent); + struct tegra_dc *dc = platform_get_drvdata(pdev); + int err; + + err = host1x_unregister_client(host1x, &dc->client); + if (err < 0) { + dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", + err); + return err; + } + + clk_disable_unprepare(dc->clk); + + return 0; +} + +static struct of_device_id tegra_dc_of_match[] = { + { .compatible = "nvidia,tegra20-dc", }, + { }, +}; + +struct platform_driver tegra_dc_driver = { + .driver = { + .name = "tegra-dc", + .owner = THIS_MODULE, + .of_match_table = tegra_dc_of_match, + }, + .probe = tegra_dc_probe, + .remove = tegra_dc_remove, +}; diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h new file mode 100644 index 000000000000..99977b5d5c36 --- /dev/null +++ b/drivers/gpu/drm/tegra/dc.h @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2012 Avionic Design GmbH + * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef TEGRA_DC_H +#define TEGRA_DC_H 1 + +#define DC_CMD_GENERAL_INCR_SYNCPT 0x000 +#define DC_CMD_GENERAL_INCR_SYNCPT_CNTRL 0x001 +#define DC_CMD_GENERAL_INCR_SYNCPT_ERROR 0x002 +#define DC_CMD_WIN_A_INCR_SYNCPT 0x008 +#define DC_CMD_WIN_A_INCR_SYNCPT_CNTRL 0x009 +#define DC_CMD_WIN_A_INCR_SYNCPT_ERROR 0x00a +#define DC_CMD_WIN_B_INCR_SYNCPT 0x010 +#define DC_CMD_WIN_B_INCR_SYNCPT_CNTRL 0x011 +#define DC_CMD_WIN_B_INCR_SYNCPT_ERROR 0x012 +#define DC_CMD_WIN_C_INCR_SYNCPT 0x018 +#define DC_CMD_WIN_C_INCR_SYNCPT_CNTRL 0x019 +#define DC_CMD_WIN_C_INCR_SYNCPT_ERROR 0x01a +#define DC_CMD_CONT_SYNCPT_VSYNC 0x028 +#define DC_CMD_DISPLAY_COMMAND_OPTION0 0x031 +#define DC_CMD_DISPLAY_COMMAND 0x032 +#define DISP_CTRL_MODE_STOP (0 << 5) +#define DISP_CTRL_MODE_C_DISPLAY (1 << 5) +#define DISP_CTRL_MODE_NC_DISPLAY (2 << 5) +#define DC_CMD_SIGNAL_RAISE 0x033 +#define DC_CMD_DISPLAY_POWER_CONTROL 0x036 +#define PW0_ENABLE (1 << 0) +#define PW1_ENABLE (1 << 2) +#define PW2_ENABLE (1 << 4) +#define PW3_ENABLE (1 << 6) +#define PW4_ENABLE (1 << 8) +#define PM0_ENABLE (1 << 16) +#define PM1_ENABLE (1 << 18) + +#define DC_CMD_INT_STATUS 0x037 +#define DC_CMD_INT_MASK 0x038 +#define DC_CMD_INT_ENABLE 0x039 +#define DC_CMD_INT_TYPE 0x03a +#define DC_CMD_INT_POLARITY 0x03b +#define CTXSW_INT (1 << 0) +#define FRAME_END_INT (1 << 1) +#define VBLANK_INT (1 << 2) +#define WIN_A_UF_INT (1 << 8) +#define WIN_B_UF_INT (1 << 9) +#define WIN_C_UF_INT (1 << 10) +#define WIN_A_OF_INT (1 << 14) +#define WIN_B_OF_INT (1 << 15) +#define WIN_C_OF_INT (1 << 16) + +#define DC_CMD_SIGNAL_RAISE1 0x03c +#define DC_CMD_SIGNAL_RAISE2 0x03d +#define DC_CMD_SIGNAL_RAISE3 0x03e + +#define DC_CMD_STATE_ACCESS 0x040 + +#define DC_CMD_STATE_CONTROL 0x041 +#define GENERAL_ACT_REQ (1 << 0) +#define WIN_A_ACT_REQ (1 << 1) +#define WIN_B_ACT_REQ (1 << 2) +#define WIN_C_ACT_REQ (1 << 3) +#define GENERAL_UPDATE (1 << 8) +#define WIN_A_UPDATE (1 << 9) +#define WIN_B_UPDATE (1 << 10) +#define WIN_C_UPDATE (1 << 11) +#define NC_HOST_TRIG (1 << 24) + +#define DC_CMD_DISPLAY_WINDOW_HEADER 0x042 +#define WINDOW_A_SELECT (1 << 4) +#define WINDOW_B_SELECT (1 << 5) +#define WINDOW_C_SELECT (1 << 6) + +#define DC_CMD_REG_ACT_CONTROL 0x043 + +#define DC_COM_CRC_CONTROL 0x300 +#define DC_COM_CRC_CHECKSUM 0x301 +#define DC_COM_PIN_OUTPUT_ENABLE(x) (0x302 + (x)) +#define DC_COM_PIN_OUTPUT_POLARITY(x) (0x306 + (x)) +#define LVS_OUTPUT_POLARITY_LOW (1 << 28) +#define LHS_OUTPUT_POLARITY_LOW (1 << 30) +#define DC_COM_PIN_OUTPUT_DATA(x) (0x30a + (x)) +#define DC_COM_PIN_INPUT_ENABLE(x) (0x30e + (x)) +#define DC_COM_PIN_INPUT_DATA(x) (0x312 + (x)) +#define DC_COM_PIN_OUTPUT_SELECT(x) (0x314 + (x)) + +#define DC_COM_PIN_MISC_CONTROL 0x31b +#define DC_COM_PIN_PM0_CONTROL 0x31c +#define DC_COM_PIN_PM0_DUTY_CYCLE 0x31d +#define DC_COM_PIN_PM1_CONTROL 0x31e +#define DC_COM_PIN_PM1_DUTY_CYCLE 0x31f + +#define DC_COM_SPI_CONTROL 0x320 +#define DC_COM_SPI_START_BYTE 0x321 +#define DC_COM_HSPI_WRITE_DATA_AB 0x322 +#define DC_COM_HSPI_WRITE_DATA_CD 0x323 +#define DC_COM_HSPI_CS_DC 0x324 +#define DC_COM_SCRATCH_REGISTER_A 0x325 +#define DC_COM_SCRATCH_REGISTER_B 0x326 +#define DC_COM_GPIO_CTRL 0x327 +#define DC_COM_GPIO_DEBOUNCE_COUNTER 0x328 +#define DC_COM_CRC_CHECKSUM_LATCHED 0x329 + +#define DC_DISP_DISP_SIGNAL_OPTIONS0 0x400 +#define H_PULSE_0_ENABLE (1 << 8) +#define H_PULSE_1_ENABLE (1 << 10) +#define H_PULSE_2_ENABLE (1 << 12) + +#define DC_DISP_DISP_SIGNAL_OPTIONS1 0x401 + +#define DC_DISP_DISP_WIN_OPTIONS 0x402 +#define HDMI_ENABLE (1 << 30) + +#define DC_DISP_DISP_MEM_HIGH_PRIORITY 0x403 +#define CURSOR_THRESHOLD(x) (((x) & 0x03) << 24) +#define WINDOW_A_THRESHOLD(x) (((x) & 0x7f) << 16) +#define WINDOW_B_THRESHOLD(x) (((x) & 0x7f) << 8) +#define WINDOW_C_THRESHOLD(x) (((x) & 0xff) << 0) + +#define DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER 0x404 +#define CURSOR_DELAY(x) (((x) & 0x3f) << 24) +#define WINDOW_A_DELAY(x) (((x) & 0x3f) << 16) +#define WINDOW_B_DELAY(x) (((x) & 0x3f) << 8) +#define WINDOW_C_DELAY(x) (((x) & 0x3f) << 0) + +#define DC_DISP_DISP_TIMING_OPTIONS 0x405 +#define VSYNC_H_POSITION(x) ((x) & 0xfff) + +#define DC_DISP_REF_TO_SYNC 0x406 +#define DC_DISP_SYNC_WIDTH 0x407 +#define DC_DISP_BACK_PORCH 0x408 +#define DC_DISP_ACTIVE 0x409 +#define DC_DISP_FRONT_PORCH 0x40a +#define DC_DISP_H_PULSE0_CONTROL 0x40b +#define DC_DISP_H_PULSE0_POSITION_A 0x40c +#define DC_DISP_H_PULSE0_POSITION_B 0x40d +#define DC_DISP_H_PULSE0_POSITION_C 0x40e +#define DC_DISP_H_PULSE0_POSITION_D 0x40f +#define DC_DISP_H_PULSE1_CONTROL 0x410 +#define DC_DISP_H_PULSE1_POSITION_A 0x411 +#define DC_DISP_H_PULSE1_POSITION_B 0x412 +#define DC_DISP_H_PULSE1_POSITION_C 0x413 +#define DC_DISP_H_PULSE1_POSITION_D 0x414 +#define DC_DISP_H_PULSE2_CONTROL 0x415 +#define DC_DISP_H_PULSE2_POSITION_A 0x416 +#define DC_DISP_H_PULSE2_POSITION_B 0x417 +#define DC_DISP_H_PULSE2_POSITION_C 0x418 +#define DC_DISP_H_PULSE2_POSITION_D 0x419 +#define DC_DISP_V_PULSE0_CONTROL 0x41a +#define DC_DISP_V_PULSE0_POSITION_A 0x41b +#define DC_DISP_V_PULSE0_POSITION_B 0x41c +#define DC_DISP_V_PULSE0_POSITION_C 0x41d +#define DC_DISP_V_PULSE1_CONTROL 0x41e +#define DC_DISP_V_PULSE1_POSITION_A 0x41f +#define DC_DISP_V_PULSE1_POSITION_B 0x420 +#define DC_DISP_V_PULSE1_POSITION_C 0x421 +#define DC_DISP_V_PULSE2_CONTROL 0x422 +#define DC_DISP_V_PULSE2_POSITION_A 0x423 +#define DC_DISP_V_PULSE3_CONTROL 0x424 +#define DC_DISP_V_PULSE3_POSITION_A 0x425 +#define DC_DISP_M0_CONTROL 0x426 +#define DC_DISP_M1_CONTROL 0x427 +#define DC_DISP_DI_CONTROL 0x428 +#define DC_DISP_PP_CONTROL 0x429 +#define DC_DISP_PP_SELECT_A 0x42a +#define DC_DISP_PP_SELECT_B 0x42b +#define DC_DISP_PP_SELECT_C 0x42c +#define DC_DISP_PP_SELECT_D 0x42d + +#define PULSE_MODE_NORMAL (0 << 3) +#define PULSE_MODE_ONE_CLOCK (1 << 3) +#define PULSE_POLARITY_HIGH (0 << 4) +#define PULSE_POLARITY_LOW (1 << 4) +#define PULSE_QUAL_ALWAYS (0 << 6) +#define PULSE_QUAL_VACTIVE (2 << 6) +#define PULSE_QUAL_VACTIVE1 (3 << 6) +#define PULSE_LAST_START_A (0 << 8) +#define PULSE_LAST_END_A (1 << 8) +#define PULSE_LAST_START_B (2 << 8) +#define PULSE_LAST_END_B (3 << 8) +#define PULSE_LAST_START_C (4 << 8) +#define PULSE_LAST_END_C (5 << 8) +#define PULSE_LAST_START_D (6 << 8) +#define PULSE_LAST_END_D (7 << 8) + +#define PULSE_START(x) (((x) & 0xfff) << 0) +#define PULSE_END(x) (((x) & 0xfff) << 16) + +#define DC_DISP_DISP_CLOCK_CONTROL 0x42e +#define PIXEL_CLK_DIVIDER_PCD1 (0 << 8) +#define PIXEL_CLK_DIVIDER_PCD1H (1 << 8) +#define PIXEL_CLK_DIVIDER_PCD2 (2 << 8) +#define PIXEL_CLK_DIVIDER_PCD3 (3 << 8) +#define PIXEL_CLK_DIVIDER_PCD4 (4 << 8) +#define PIXEL_CLK_DIVIDER_PCD6 (5 << 8) +#define PIXEL_CLK_DIVIDER_PCD8 (6 << 8) +#define PIXEL_CLK_DIVIDER_PCD9 (7 << 8) +#define PIXEL_CLK_DIVIDER_PCD12 (8 << 8) +#define PIXEL_CLK_DIVIDER_PCD16 (9 << 8) +#define PIXEL_CLK_DIVIDER_PCD18 (10 << 8) +#define PIXEL_CLK_DIVIDER_PCD24 (11 << 8) +#define PIXEL_CLK_DIVIDER_PCD13 (12 << 8) +#define SHIFT_CLK_DIVIDER(x) ((x) & 0xff) + +#define DC_DISP_DISP_INTERFACE_CONTROL 0x42f +#define DISP_DATA_FORMAT_DF1P1C (0 << 0) +#define DISP_DATA_FORMAT_DF1P2C24B (1 << 0) +#define DISP_DATA_FORMAT_DF1P2C18B (2 << 0) +#define DISP_DATA_FORMAT_DF1P2C16B (3 << 0) +#define DISP_DATA_FORMAT_DF2S (4 << 0) +#define DISP_DATA_FORMAT_DF3S (5 << 0) +#define DISP_DATA_FORMAT_DFSPI (6 << 0) +#define DISP_DATA_FORMAT_DF1P3C24B (7 << 0) +#define DISP_DATA_FORMAT_DF1P3C18B (8 << 0) +#define DISP_ALIGNMENT_MSB (0 << 8) +#define DISP_ALIGNMENT_LSB (1 << 8) +#define DISP_ORDER_RED_BLUE (0 << 9) +#define DISP_ORDER_BLUE_RED (1 << 9) + +#define DC_DISP_DISP_COLOR_CONTROL 0x430 +#define BASE_COLOR_SIZE666 (0 << 0) +#define BASE_COLOR_SIZE111 (1 << 0) +#define BASE_COLOR_SIZE222 (2 << 0) +#define BASE_COLOR_SIZE333 (3 << 0) +#define BASE_COLOR_SIZE444 (4 << 0) +#define BASE_COLOR_SIZE555 (5 << 0) +#define BASE_COLOR_SIZE565 (6 << 0) +#define BASE_COLOR_SIZE332 (7 << 0) +#define BASE_COLOR_SIZE888 (8 << 0) +#define DITHER_CONTROL_DISABLE (0 << 8) +#define DITHER_CONTROL_ORDERED (2 << 8) +#define DITHER_CONTROL_ERRDIFF (3 << 8) + +#define DC_DISP_SHIFT_CLOCK_OPTIONS 0x431 + +#define DC_DISP_DATA_ENABLE_OPTIONS 0x432 +#define DE_SELECT_ACTIVE_BLANK (0 << 0) +#define DE_SELECT_ACTIVE (1 << 0) +#define DE_SELECT_ACTIVE_IS (2 << 0) +#define DE_CONTROL_ONECLK (0 << 2) +#define DE_CONTROL_NORMAL (1 << 2) +#define DE_CONTROL_EARLY_EXT (2 << 2) +#define DE_CONTROL_EARLY (3 << 2) +#define DE_CONTROL_ACTIVE_BLANK (4 << 2) + +#define DC_DISP_SERIAL_INTERFACE_OPTIONS 0x433 +#define DC_DISP_LCD_SPI_OPTIONS 0x434 +#define DC_DISP_BORDER_COLOR 0x435 +#define DC_DISP_COLOR_KEY0_LOWER 0x436 +#define DC_DISP_COLOR_KEY0_UPPER 0x437 +#define DC_DISP_COLOR_KEY1_LOWER 0x438 +#define DC_DISP_COLOR_KEY1_UPPER 0x439 + +#define DC_DISP_CURSOR_FOREGROUND 0x43c +#define DC_DISP_CURSOR_BACKGROUND 0x43d + +#define DC_DISP_CURSOR_START_ADDR 0x43e +#define DC_DISP_CURSOR_START_ADDR_NS 0x43f + +#define DC_DISP_CURSOR_POSITION 0x440 +#define DC_DISP_CURSOR_POSITION_NS 0x441 + +#define DC_DISP_INIT_SEQ_CONTROL 0x442 +#define DC_DISP_SPI_INIT_SEQ_DATA_A 0x443 +#define DC_DISP_SPI_INIT_SEQ_DATA_B 0x444 +#define DC_DISP_SPI_INIT_SEQ_DATA_C 0x445 +#define DC_DISP_SPI_INIT_SEQ_DATA_D 0x446 + +#define DC_DISP_DC_MCCIF_FIFOCTRL 0x480 +#define DC_DISP_MCCIF_DISPLAY0A_HYST 0x481 +#define DC_DISP_MCCIF_DISPLAY0B_HYST 0x482 +#define DC_DISP_MCCIF_DISPLAY1A_HYST 0x483 +#define DC_DISP_MCCIF_DISPLAY1B_HYST 0x484 + +#define DC_DISP_DAC_CRT_CTRL 0x4c0 +#define DC_DISP_DISP_MISC_CONTROL 0x4c1 +#define DC_DISP_SD_CONTROL 0x4c2 +#define DC_DISP_SD_CSC_COEFF 0x4c3 +#define DC_DISP_SD_LUT(x) (0x4c4 + (x)) +#define DC_DISP_SD_FLICKER_CONTROL 0x4cd +#define DC_DISP_DC_PIXEL_COUNT 0x4ce +#define DC_DISP_SD_HISTOGRAM(x) (0x4cf + (x)) +#define DC_DISP_SD_BL_PARAMETERS 0x4d7 +#define DC_DISP_SD_BL_TF(x) (0x4d8 + (x)) +#define DC_DISP_SD_BL_CONTROL 0x4dc +#define DC_DISP_SD_HW_K_VALUES 0x4dd +#define DC_DISP_SD_MAN_K_VALUES 0x4de + +#define DC_WIN_WIN_OPTIONS 0x700 +#define COLOR_EXPAND (1 << 6) +#define WIN_ENABLE (1 << 30) + +#define DC_WIN_BYTE_SWAP 0x701 +#define BYTE_SWAP_NOSWAP (0 << 0) +#define BYTE_SWAP_SWAP2 (1 << 0) +#define BYTE_SWAP_SWAP4 (2 << 0) +#define BYTE_SWAP_SWAP4HW (3 << 0) + +#define DC_WIN_BUFFER_CONTROL 0x702 +#define BUFFER_CONTROL_HOST (0 << 0) +#define BUFFER_CONTROL_VI (1 << 0) +#define BUFFER_CONTROL_EPP (2 << 0) +#define BUFFER_CONTROL_MPEGE (3 << 0) +#define BUFFER_CONTROL_SB2D (4 << 0) + +#define DC_WIN_COLOR_DEPTH 0x703 +#define WIN_COLOR_DEPTH_P1 0 +#define WIN_COLOR_DEPTH_P2 1 +#define WIN_COLOR_DEPTH_P4 2 +#define WIN_COLOR_DEPTH_P8 3 +#define WIN_COLOR_DEPTH_B4G4R4A4 4 +#define WIN_COLOR_DEPTH_B5G5R5A 5 +#define WIN_COLOR_DEPTH_B5G6R5 6 +#define WIN_COLOR_DEPTH_AB5G5R5 7 +#define WIN_COLOR_DEPTH_B8G8R8A8 12 +#define WIN_COLOR_DEPTH_R8G8B8A8 13 +#define WIN_COLOR_DEPTH_B6x2G6x2R6x2A8 14 +#define WIN_COLOR_DEPTH_R6x2G6x2B6x2A8 15 +#define WIN_COLOR_DEPTH_YCbCr422 16 +#define WIN_COLOR_DEPTH_YUV422 17 +#define WIN_COLOR_DEPTH_YCbCr420P 18 +#define WIN_COLOR_DEPTH_YUV420P 19 +#define WIN_COLOR_DEPTH_YCbCr422P 20 +#define WIN_COLOR_DEPTH_YUV422P 21 +#define WIN_COLOR_DEPTH_YCbCr422R 22 +#define WIN_COLOR_DEPTH_YUV422R 23 +#define WIN_COLOR_DEPTH_YCbCr422RA 24 +#define WIN_COLOR_DEPTH_YUV422RA 25 + +#define DC_WIN_POSITION 0x704 +#define H_POSITION(x) (((x) & 0x1fff) << 0) +#define V_POSITION(x) (((x) & 0x1fff) << 16) + +#define DC_WIN_SIZE 0x705 +#define H_SIZE(x) (((x) & 0x1fff) << 0) +#define V_SIZE(x) (((x) & 0x1fff) << 16) + +#define DC_WIN_PRESCALED_SIZE 0x706 +#define H_PRESCALED_SIZE(x) (((x) & 0x7fff) << 0) +#define V_PRESCALED_SIZE(x) (((x) & 0x1fff) << 16) + +#define DC_WIN_H_INITIAL_DDA 0x707 +#define DC_WIN_V_INITIAL_DDA 0x708 +#define DC_WIN_DDA_INC 0x709 +#define H_DDA_INC(x) (((x) & 0xffff) << 0) +#define V_DDA_INC(x) (((x) & 0xffff) << 16) + +#define DC_WIN_LINE_STRIDE 0x70a +#define DC_WIN_BUF_STRIDE 0x70b +#define DC_WIN_UV_BUF_STRIDE 0x70c +#define DC_WIN_BUFFER_ADDR_MODE 0x70d +#define DC_WIN_DV_CONTROL 0x70e + +#define DC_WIN_BLEND_NOKEY 0x70f +#define DC_WIN_BLEND_1WIN 0x710 +#define DC_WIN_BLEND_2WIN_X 0x711 +#define DC_WIN_BLEND_2WIN_Y 0x712 +#define DC_WIN_BLEND32WIN_XY 0x713 + +#define DC_WIN_HP_FETCH_CONTROL 0x714 + +#define DC_WINBUF_START_ADDR 0x800 +#define DC_WINBUF_START_ADDR_NS 0x801 +#define DC_WINBUF_START_ADDR_U 0x802 +#define DC_WINBUF_START_ADDR_U_NS 0x803 +#define DC_WINBUF_START_ADDR_V 0x804 +#define DC_WINBUF_START_ADDR_V_NS 0x805 + +#define DC_WINBUF_ADDR_H_OFFSET 0x806 +#define DC_WINBUF_ADDR_H_OFFSET_NS 0x807 +#define DC_WINBUF_ADDR_V_OFFSET 0x808 +#define DC_WINBUF_ADDR_V_OFFSET_NS 0x809 + +#define DC_WINBUF_UFLOW_STATUS 0x80a + +#define DC_WINBUF_AD_UFLOW_STATUS 0xbca +#define DC_WINBUF_BD_UFLOW_STATUS 0xdca +#define DC_WINBUF_CD_UFLOW_STATUS 0xfca + +/* synchronization points */ +#define SYNCPT_VBLANK0 26 +#define SYNCPT_VBLANK1 27 + +#endif /* TEGRA_DC_H */ diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c new file mode 100644 index 000000000000..3a503c9e4686 --- /dev/null +++ b/drivers/gpu/drm/tegra/drm.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2012 Avionic Design GmbH + * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include +#include +#include + +#include "drm.h" + +#define DRIVER_NAME "tegra" +#define DRIVER_DESC "NVIDIA Tegra graphics" +#define DRIVER_DATE "20120330" +#define DRIVER_MAJOR 0 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 0 + +static int tegra_drm_load(struct drm_device *drm, unsigned long flags) +{ + struct device *dev = drm->dev; + struct host1x *host1x; + int err; + + host1x = dev_get_drvdata(dev); + drm->dev_private = host1x; + host1x->drm = drm; + + drm_mode_config_init(drm); + + err = host1x_drm_init(host1x, drm); + if (err < 0) + return err; + + err = tegra_drm_fb_init(drm); + if (err < 0) + return err; + + drm_kms_helper_poll_init(drm); + + return 0; +} + +static int tegra_drm_unload(struct drm_device *drm) +{ + drm_kms_helper_poll_fini(drm); + tegra_drm_fb_exit(drm); + + drm_mode_config_cleanup(drm); + + return 0; +} + +static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp) +{ + return 0; +} + +static void tegra_drm_lastclose(struct drm_device *drm) +{ + struct host1x *host1x = drm->dev_private; + + drm_fbdev_cma_restore_mode(host1x->fbdev); +} + +static struct drm_ioctl_desc tegra_drm_ioctls[] = { +}; + +static const struct file_operations tegra_drm_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .mmap = drm_gem_cma_mmap, + .poll = drm_poll, + .fasync = drm_fasync, + .read = drm_read, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .llseek = noop_llseek, +}; + +struct drm_driver tegra_drm_driver = { + .driver_features = DRIVER_BUS_PLATFORM | DRIVER_MODESET | DRIVER_GEM, + .load = tegra_drm_load, + .unload = tegra_drm_unload, + .open = tegra_drm_open, + .lastclose = tegra_drm_lastclose, + + .gem_free_object = drm_gem_cma_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + .dumb_create = drm_gem_cma_dumb_create, + .dumb_map_offset = drm_gem_cma_dumb_map_offset, + .dumb_destroy = drm_gem_cma_dumb_destroy, + + .ioctls = tegra_drm_ioctls, + .num_ioctls = ARRAY_SIZE(tegra_drm_ioctls), + .fops = &tegra_drm_fops, + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, +}; diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h new file mode 100644 index 000000000000..d502a0395190 --- /dev/null +++ b/drivers/gpu/drm/tegra/drm.h @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2012 Avionic Design GmbH + * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef TEGRA_DRM_H +#define TEGRA_DRM_H 1 + +#include +#include +#include +#include +#include +#include +#include + +struct tegra_framebuffer { + struct drm_framebuffer base; + struct drm_gem_cma_object *obj; +}; + +static inline struct tegra_framebuffer *to_tegra_fb(struct drm_framebuffer *fb) +{ + return container_of(fb, struct tegra_framebuffer, base); +} + +struct host1x { + struct drm_device *drm; + struct device *dev; + void __iomem *regs; + struct clk *clk; + int syncpt; + int irq; + + struct mutex drm_clients_lock; + struct list_head drm_clients; + struct list_head drm_active; + + struct mutex clients_lock; + struct list_head clients; + + struct drm_fbdev_cma *fbdev; + struct tegra_framebuffer fb; +}; + +struct host1x_client; + +struct host1x_client_ops { + int (*drm_init)(struct host1x_client *client, struct drm_device *drm); + int (*drm_exit)(struct host1x_client *client); +}; + +struct host1x_client { + struct host1x *host1x; + struct device *dev; + + const struct host1x_client_ops *ops; + + struct list_head list; +}; + +extern int host1x_drm_init(struct host1x *host1x, struct drm_device *drm); +extern int host1x_drm_exit(struct host1x *host1x); + +extern int host1x_register_client(struct host1x *host1x, + struct host1x_client *client); +extern int host1x_unregister_client(struct host1x *host1x, + struct host1x_client *client); + +struct tegra_output; + +struct tegra_dc { + struct host1x_client client; + + struct host1x *host1x; + struct device *dev; + + struct drm_crtc base; + int pipe; + + struct clk *clk; + + void __iomem *regs; + int irq; + + struct tegra_output *rgb; + + struct list_head list; + + struct drm_info_list *debugfs_files; + struct drm_minor *minor; + struct dentry *debugfs; +}; + +static inline struct tegra_dc *host1x_client_to_dc(struct host1x_client *client) +{ + return container_of(client, struct tegra_dc, client); +} + +static inline struct tegra_dc *to_tegra_dc(struct drm_crtc *crtc) +{ + return container_of(crtc, struct tegra_dc, base); +} + +static inline void tegra_dc_writel(struct tegra_dc *dc, unsigned long value, + unsigned long reg) +{ + writel(value, dc->regs + (reg << 2)); +} + +static inline unsigned long tegra_dc_readl(struct tegra_dc *dc, + unsigned long reg) +{ + return readl(dc->regs + (reg << 2)); +} + +struct tegra_output_ops { + int (*enable)(struct tegra_output *output); + int (*disable)(struct tegra_output *output); + int (*setup_clock)(struct tegra_output *output, struct clk *clk, + unsigned long pclk); + int (*check_mode)(struct tegra_output *output, + struct drm_display_mode *mode, + enum drm_mode_status *status); +}; + +enum tegra_output_type { + TEGRA_OUTPUT_RGB, +}; + +struct tegra_output { + struct device_node *of_node; + struct device *dev; + + const struct tegra_output_ops *ops; + enum tegra_output_type type; + + struct i2c_adapter *ddc; + const struct edid *edid; + unsigned int hpd_irq; + int hpd_gpio; + + struct drm_encoder encoder; + struct drm_connector connector; +}; + +static inline struct tegra_output *encoder_to_output(struct drm_encoder *e) +{ + return container_of(e, struct tegra_output, encoder); +} + +static inline struct tegra_output *connector_to_output(struct drm_connector *c) +{ + return container_of(c, struct tegra_output, connector); +} + +static inline int tegra_output_enable(struct tegra_output *output) +{ + if (output && output->ops && output->ops->enable) + return output->ops->enable(output); + + return output ? -ENOSYS : -EINVAL; +} + +static inline int tegra_output_disable(struct tegra_output *output) +{ + if (output && output->ops && output->ops->disable) + return output->ops->disable(output); + + return output ? -ENOSYS : -EINVAL; +} + +static inline int tegra_output_setup_clock(struct tegra_output *output, + struct clk *clk, unsigned long pclk) +{ + if (output && output->ops && output->ops->setup_clock) + return output->ops->setup_clock(output, clk, pclk); + + return output ? -ENOSYS : -EINVAL; +} + +static inline int tegra_output_check_mode(struct tegra_output *output, + struct drm_display_mode *mode, + enum drm_mode_status *status) +{ + if (output && output->ops && output->ops->check_mode) + return output->ops->check_mode(output, mode, status); + + return output ? -ENOSYS : -EINVAL; +} + +/* from rgb.c */ +extern int tegra_dc_rgb_probe(struct tegra_dc *dc); +extern int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc); +extern int tegra_dc_rgb_exit(struct tegra_dc *dc); + +/* from output.c */ +extern int tegra_output_parse_dt(struct tegra_output *output); +extern int tegra_output_init(struct drm_device *drm, struct tegra_output *output); +extern int tegra_output_exit(struct tegra_output *output); + +/* from gem.c */ +extern struct tegra_gem_object *tegra_gem_alloc(struct drm_device *drm, + size_t size); +extern int tegra_gem_handle_create(struct drm_device *drm, + struct drm_file *file, size_t size, + unsigned long flags, uint32_t *handle); +extern int tegra_gem_dumb_create(struct drm_file *file, struct drm_device *drm, + struct drm_mode_create_dumb *args); +extern int tegra_gem_dumb_map_offset(struct drm_file *file, + struct drm_device *drm, uint32_t handle, + uint64_t *offset); +extern int tegra_gem_dumb_destroy(struct drm_file *file, + struct drm_device *drm, uint32_t handle); +extern int tegra_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); +extern int tegra_gem_init_object(struct drm_gem_object *obj); +extern void tegra_gem_free_object(struct drm_gem_object *obj); +extern struct vm_operations_struct tegra_gem_vm_ops; + +/* from fb.c */ +extern int tegra_drm_fb_init(struct drm_device *drm); +extern void tegra_drm_fb_exit(struct drm_device *drm); + +extern struct platform_driver tegra_host1x_driver; +extern struct platform_driver tegra_dc_driver; +extern struct drm_driver tegra_drm_driver; + +#endif /* TEGRA_DRM_H */ diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c new file mode 100644 index 000000000000..97993c6835fd --- /dev/null +++ b/drivers/gpu/drm/tegra/fb.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2012 Avionic Design GmbH + * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "drm.h" + +static void tegra_drm_fb_output_poll_changed(struct drm_device *drm) +{ + struct host1x *host1x = drm->dev_private; + + drm_fbdev_cma_hotplug_event(host1x->fbdev); +} + +static const struct drm_mode_config_funcs tegra_drm_mode_funcs = { + .fb_create = drm_fb_cma_create, + .output_poll_changed = tegra_drm_fb_output_poll_changed, +}; + +int tegra_drm_fb_init(struct drm_device *drm) +{ + struct host1x *host1x = drm->dev_private; + struct drm_fbdev_cma *fbdev; + + drm->mode_config.min_width = 0; + drm->mode_config.min_height = 0; + + drm->mode_config.max_width = 4096; + drm->mode_config.max_height = 4096; + + drm->mode_config.funcs = &tegra_drm_mode_funcs; + + fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc, + drm->mode_config.num_connector); + if (IS_ERR(fbdev)) + return PTR_ERR(fbdev); + +#ifndef CONFIG_FRAMEBUFFER_CONSOLE + drm_fbdev_cma_restore_mode(fbdev); +#endif + + host1x->fbdev = fbdev; + + return 0; +} + +void tegra_drm_fb_exit(struct drm_device *drm) +{ + struct host1x *host1x = drm->dev_private; + + drm_fbdev_cma_fini(host1x->fbdev); +} diff --git a/drivers/gpu/drm/tegra/host1x.c b/drivers/gpu/drm/tegra/host1x.c new file mode 100644 index 000000000000..9fbed4765e60 --- /dev/null +++ b/drivers/gpu/drm/tegra/host1x.c @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2012 Avionic Design GmbH + * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include "drm.h" + +struct host1x_drm_client { + struct host1x_client *client; + struct device_node *np; + struct list_head list; +}; + +static int host1x_add_drm_client(struct host1x *host1x, struct device_node *np) +{ + struct host1x_drm_client *client; + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return -ENOMEM; + + INIT_LIST_HEAD(&client->list); + client->np = of_node_get(np); + + list_add_tail(&client->list, &host1x->drm_clients); + + return 0; +} + +static int host1x_activate_drm_client(struct host1x *host1x, + struct host1x_drm_client *drm, + struct host1x_client *client) +{ + mutex_lock(&host1x->drm_clients_lock); + list_del_init(&drm->list); + list_add_tail(&drm->list, &host1x->drm_active); + drm->client = client; + mutex_unlock(&host1x->drm_clients_lock); + + return 0; +} + +static int host1x_remove_drm_client(struct host1x *host1x, + struct host1x_drm_client *client) +{ + mutex_lock(&host1x->drm_clients_lock); + list_del_init(&client->list); + mutex_unlock(&host1x->drm_clients_lock); + + of_node_put(client->np); + kfree(client); + + return 0; +} + +static int host1x_parse_dt(struct host1x *host1x) +{ + static const char * const compat[] = { + "nvidia,tegra20-dc", + }; + unsigned int i; + int err; + + for (i = 0; i < ARRAY_SIZE(compat); i++) { + struct device_node *np; + + for_each_child_of_node(host1x->dev->of_node, np) { + if (of_device_is_compatible(np, compat[i]) && + of_device_is_available(np)) { + err = host1x_add_drm_client(host1x, np); + if (err < 0) + return err; + } + } + } + + return 0; +} + +static int tegra_host1x_probe(struct platform_device *pdev) +{ + struct host1x *host1x; + struct resource *regs; + int err; + + host1x = devm_kzalloc(&pdev->dev, sizeof(*host1x), GFP_KERNEL); + if (!host1x) + return -ENOMEM; + + mutex_init(&host1x->drm_clients_lock); + INIT_LIST_HEAD(&host1x->drm_clients); + INIT_LIST_HEAD(&host1x->drm_active); + mutex_init(&host1x->clients_lock); + INIT_LIST_HEAD(&host1x->clients); + host1x->dev = &pdev->dev; + + err = host1x_parse_dt(host1x); + if (err < 0) { + dev_err(&pdev->dev, "failed to parse DT: %d\n", err); + return err; + } + + host1x->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(host1x->clk)) + return PTR_ERR(host1x->clk); + + err = clk_prepare_enable(host1x->clk); + if (err < 0) + return err; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + err = -ENXIO; + goto err; + } + + err = platform_get_irq(pdev, 0); + if (err < 0) + goto err; + + host1x->syncpt = err; + + err = platform_get_irq(pdev, 1); + if (err < 0) + goto err; + + host1x->irq = err; + + host1x->regs = devm_request_and_ioremap(&pdev->dev, regs); + if (!host1x->regs) { + err = -EADDRNOTAVAIL; + goto err; + } + + platform_set_drvdata(pdev, host1x); + + return 0; + +err: + clk_disable_unprepare(host1x->clk); + return err; +} + +static int tegra_host1x_remove(struct platform_device *pdev) +{ + struct host1x *host1x = platform_get_drvdata(pdev); + + clk_disable_unprepare(host1x->clk); + + return 0; +} + +int host1x_drm_init(struct host1x *host1x, struct drm_device *drm) +{ + struct host1x_client *client; + + mutex_lock(&host1x->clients_lock); + + list_for_each_entry(client, &host1x->clients, list) { + if (client->ops && client->ops->drm_init) { + int err = client->ops->drm_init(client, drm); + if (err < 0) { + dev_err(host1x->dev, + "DRM setup failed for %s: %d\n", + dev_name(client->dev), err); + return err; + } + } + } + + mutex_unlock(&host1x->clients_lock); + + return 0; +} + +int host1x_drm_exit(struct host1x *host1x) +{ + struct platform_device *pdev = to_platform_device(host1x->dev); + struct host1x_client *client; + + if (!host1x->drm) + return 0; + + mutex_lock(&host1x->clients_lock); + + list_for_each_entry_reverse(client, &host1x->clients, list) { + if (client->ops && client->ops->drm_exit) { + int err = client->ops->drm_exit(client); + if (err < 0) { + dev_err(host1x->dev, + "DRM cleanup failed for %s: %d\n", + dev_name(client->dev), err); + return err; + } + } + } + + mutex_unlock(&host1x->clients_lock); + + drm_platform_exit(&tegra_drm_driver, pdev); + host1x->drm = NULL; + + return 0; +} + +int host1x_register_client(struct host1x *host1x, struct host1x_client *client) +{ + struct host1x_drm_client *drm, *tmp; + int err; + + mutex_lock(&host1x->clients_lock); + list_add_tail(&client->list, &host1x->clients); + mutex_unlock(&host1x->clients_lock); + + list_for_each_entry_safe(drm, tmp, &host1x->drm_clients, list) + if (drm->np == client->dev->of_node) + host1x_activate_drm_client(host1x, drm, client); + + if (list_empty(&host1x->drm_clients)) { + struct platform_device *pdev = to_platform_device(host1x->dev); + + err = drm_platform_init(&tegra_drm_driver, pdev); + if (err < 0) { + dev_err(host1x->dev, "drm_platform_init(): %d\n", err); + return err; + } + } + + return 0; +} + +int host1x_unregister_client(struct host1x *host1x, + struct host1x_client *client) +{ + struct host1x_drm_client *drm, *tmp; + int err; + + list_for_each_entry_safe(drm, tmp, &host1x->drm_active, list) { + if (drm->client == client) { + err = host1x_drm_exit(host1x); + if (err < 0) { + dev_err(host1x->dev, "host1x_drm_exit(): %d\n", + err); + return err; + } + + host1x_remove_drm_client(host1x, drm); + break; + } + } + + mutex_lock(&host1x->clients_lock); + list_del_init(&client->list); + mutex_unlock(&host1x->clients_lock); + + return 0; +} + +static struct of_device_id tegra_host1x_of_match[] = { + { .compatible = "nvidia,tegra20-host1x", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra_host1x_of_match); + +struct platform_driver tegra_host1x_driver = { + .driver = { + .name = "tegra-host1x", + .owner = THIS_MODULE, + .of_match_table = tegra_host1x_of_match, + }, + .probe = tegra_host1x_probe, + .remove = tegra_host1x_remove, +}; + +static int __init tegra_host1x_init(void) +{ + int err; + + err = platform_driver_register(&tegra_host1x_driver); + if (err < 0) + return err; + + err = platform_driver_register(&tegra_dc_driver); + if (err < 0) + goto unregister_host1x; + + return 0; + +unregister_host1x: + platform_driver_unregister(&tegra_host1x_driver); + return err; +} +module_init(tegra_host1x_init); + +static void __exit tegra_host1x_exit(void) +{ + platform_driver_unregister(&tegra_dc_driver); + platform_driver_unregister(&tegra_host1x_driver); +} +module_exit(tegra_host1x_exit); + +MODULE_AUTHOR("Thierry Reding "); +MODULE_DESCRIPTION("NVIDIA Tegra DRM driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c new file mode 100644 index 000000000000..4e824044a57c --- /dev/null +++ b/drivers/gpu/drm/tegra/output.c @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2012 Avionic Design GmbH + * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "drm.h" + +static int tegra_connector_get_modes(struct drm_connector *connector) +{ + struct tegra_output *output = connector_to_output(connector); + struct edid *edid = NULL; + int err = 0; + + if (output->edid) + edid = kmemdup(output->edid, sizeof(*edid), GFP_KERNEL); + else if (output->ddc) + edid = drm_get_edid(connector, output->ddc); + + drm_mode_connector_update_edid_property(connector, edid); + + if (edid) { + err = drm_add_edid_modes(connector, edid); + kfree(edid); + } + + return err; +} + +static int tegra_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct tegra_output *output = connector_to_output(connector); + enum drm_mode_status status = MODE_OK; + int err; + + err = tegra_output_check_mode(output, mode, &status); + if (err < 0) + return MODE_ERROR; + + return status; +} + +static struct drm_encoder * +tegra_connector_best_encoder(struct drm_connector *connector) +{ + struct tegra_output *output = connector_to_output(connector); + + return &output->encoder; +} + +static const struct drm_connector_helper_funcs connector_helper_funcs = { + .get_modes = tegra_connector_get_modes, + .mode_valid = tegra_connector_mode_valid, + .best_encoder = tegra_connector_best_encoder, +}; + +static enum drm_connector_status +tegra_connector_detect(struct drm_connector *connector, bool force) +{ + struct tegra_output *output = connector_to_output(connector); + enum drm_connector_status status = connector_status_unknown; + + if (gpio_is_valid(output->hpd_gpio)) { + if (gpio_get_value(output->hpd_gpio) == 0) + status = connector_status_disconnected; + else + status = connector_status_connected; + } else { + if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) + status = connector_status_connected; + } + + return status; +} + +static void tegra_connector_destroy(struct drm_connector *connector) +{ + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); +} + +static const struct drm_connector_funcs connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = tegra_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = tegra_connector_destroy, +}; + +static void tegra_encoder_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs encoder_funcs = { + .destroy = tegra_encoder_destroy, +}; + +static void tegra_encoder_dpms(struct drm_encoder *encoder, int mode) +{ +} + +static bool tegra_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted) +{ + return true; +} + +static void tegra_encoder_prepare(struct drm_encoder *encoder) +{ +} + +static void tegra_encoder_commit(struct drm_encoder *encoder) +{ +} + +static void tegra_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted) +{ + struct tegra_output *output = encoder_to_output(encoder); + int err; + + err = tegra_output_enable(output); + if (err < 0) + dev_err(encoder->dev->dev, "tegra_output_enable(): %d\n", err); +} + +static const struct drm_encoder_helper_funcs encoder_helper_funcs = { + .dpms = tegra_encoder_dpms, + .mode_fixup = tegra_encoder_mode_fixup, + .prepare = tegra_encoder_prepare, + .commit = tegra_encoder_commit, + .mode_set = tegra_encoder_mode_set, +}; + +static irqreturn_t hpd_irq(int irq, void *data) +{ + struct tegra_output *output = data; + + drm_helper_hpd_irq_event(output->connector.dev); + + return IRQ_HANDLED; +} + +int tegra_output_parse_dt(struct tegra_output *output) +{ + enum of_gpio_flags flags; + struct device_node *ddc; + size_t size; + int err; + + if (!output->of_node) + output->of_node = output->dev->of_node; + + output->edid = of_get_property(output->of_node, "nvidia,edid", &size); + + ddc = of_parse_phandle(output->of_node, "nvidia,ddc-i2c-bus", 0); + if (ddc) { + output->ddc = of_find_i2c_adapter_by_node(ddc); + if (!output->ddc) { + err = -EPROBE_DEFER; + of_node_put(ddc); + return err; + } + + of_node_put(ddc); + } + + if (!output->edid && !output->ddc) + return -ENODEV; + + output->hpd_gpio = of_get_named_gpio_flags(output->of_node, + "nvidia,hpd-gpio", 0, + &flags); + + return 0; +} + +int tegra_output_init(struct drm_device *drm, struct tegra_output *output) +{ + int connector, encoder, err; + + if (gpio_is_valid(output->hpd_gpio)) { + unsigned long flags; + + err = gpio_request_one(output->hpd_gpio, GPIOF_DIR_IN, + "HDMI hotplug detect"); + if (err < 0) { + dev_err(output->dev, "gpio_request_one(): %d\n", err); + return err; + } + + err = gpio_to_irq(output->hpd_gpio); + if (err < 0) { + dev_err(output->dev, "gpio_to_irq(): %d\n", err); + goto free_hpd; + } + + output->hpd_irq = err; + + flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT; + + err = request_threaded_irq(output->hpd_irq, NULL, hpd_irq, + flags, "hpd", output); + if (err < 0) { + dev_err(output->dev, "failed to request IRQ#%u: %d\n", + output->hpd_irq, err); + goto free_hpd; + } + + output->connector.polled = DRM_CONNECTOR_POLL_HPD; + } + + switch (output->type) { + case TEGRA_OUTPUT_RGB: + connector = DRM_MODE_CONNECTOR_LVDS; + encoder = DRM_MODE_ENCODER_LVDS; + break; + + default: + connector = DRM_MODE_CONNECTOR_Unknown; + encoder = DRM_MODE_ENCODER_NONE; + break; + } + + drm_connector_init(drm, &output->connector, &connector_funcs, + connector); + drm_connector_helper_add(&output->connector, &connector_helper_funcs); + + drm_encoder_init(drm, &output->encoder, &encoder_funcs, encoder); + drm_encoder_helper_add(&output->encoder, &encoder_helper_funcs); + + drm_mode_connector_attach_encoder(&output->connector, &output->encoder); + drm_sysfs_connector_add(&output->connector); + + output->encoder.possible_crtcs = 0x3; + + return 0; + +free_hpd: + gpio_free(output->hpd_gpio); + + return err; +} + +int tegra_output_exit(struct tegra_output *output) +{ + if (gpio_is_valid(output->hpd_gpio)) { + free_irq(output->hpd_irq, output); + gpio_free(output->hpd_gpio); + } + + if (output->ddc) + put_device(&output->ddc->dev); + + return 0; +} diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c new file mode 100644 index 000000000000..ed4416f20260 --- /dev/null +++ b/drivers/gpu/drm/tegra/rgb.c @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2012 Avionic Design GmbH + * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include "drm.h" +#include "dc.h" + +struct tegra_rgb { + struct tegra_output output; + struct clk *clk_parent; + struct clk *clk; +}; + +static inline struct tegra_rgb *to_rgb(struct tegra_output *output) +{ + return container_of(output, struct tegra_rgb, output); +} + +struct reg_entry { + unsigned long offset; + unsigned long value; +}; + +static const struct reg_entry rgb_enable[] = { + { DC_COM_PIN_OUTPUT_ENABLE(0), 0x00000000 }, + { DC_COM_PIN_OUTPUT_ENABLE(1), 0x00000000 }, + { DC_COM_PIN_OUTPUT_ENABLE(2), 0x00000000 }, + { DC_COM_PIN_OUTPUT_ENABLE(3), 0x00000000 }, + { DC_COM_PIN_OUTPUT_POLARITY(0), 0x00000000 }, + { DC_COM_PIN_OUTPUT_POLARITY(1), 0x01000000 }, + { DC_COM_PIN_OUTPUT_POLARITY(2), 0x00000000 }, + { DC_COM_PIN_OUTPUT_POLARITY(3), 0x00000000 }, + { DC_COM_PIN_OUTPUT_DATA(0), 0x00000000 }, + { DC_COM_PIN_OUTPUT_DATA(1), 0x00000000 }, + { DC_COM_PIN_OUTPUT_DATA(2), 0x00000000 }, + { DC_COM_PIN_OUTPUT_DATA(3), 0x00000000 }, + { DC_COM_PIN_OUTPUT_SELECT(0), 0x00000000 }, + { DC_COM_PIN_OUTPUT_SELECT(1), 0x00000000 }, + { DC_COM_PIN_OUTPUT_SELECT(2), 0x00000000 }, + { DC_COM_PIN_OUTPUT_SELECT(3), 0x00000000 }, + { DC_COM_PIN_OUTPUT_SELECT(4), 0x00210222 }, + { DC_COM_PIN_OUTPUT_SELECT(5), 0x00002200 }, + { DC_COM_PIN_OUTPUT_SELECT(6), 0x00020000 }, +}; + +static const struct reg_entry rgb_disable[] = { + { DC_COM_PIN_OUTPUT_SELECT(6), 0x00000000 }, + { DC_COM_PIN_OUTPUT_SELECT(5), 0x00000000 }, + { DC_COM_PIN_OUTPUT_SELECT(4), 0x00000000 }, + { DC_COM_PIN_OUTPUT_SELECT(3), 0x00000000 }, + { DC_COM_PIN_OUTPUT_SELECT(2), 0x00000000 }, + { DC_COM_PIN_OUTPUT_SELECT(1), 0x00000000 }, + { DC_COM_PIN_OUTPUT_SELECT(0), 0x00000000 }, + { DC_COM_PIN_OUTPUT_DATA(3), 0xaaaaaaaa }, + { DC_COM_PIN_OUTPUT_DATA(2), 0xaaaaaaaa }, + { DC_COM_PIN_OUTPUT_DATA(1), 0xaaaaaaaa }, + { DC_COM_PIN_OUTPUT_DATA(0), 0xaaaaaaaa }, + { DC_COM_PIN_OUTPUT_POLARITY(3), 0x00000000 }, + { DC_COM_PIN_OUTPUT_POLARITY(2), 0x00000000 }, + { DC_COM_PIN_OUTPUT_POLARITY(1), 0x00000000 }, + { DC_COM_PIN_OUTPUT_POLARITY(0), 0x00000000 }, + { DC_COM_PIN_OUTPUT_ENABLE(3), 0x55555555 }, + { DC_COM_PIN_OUTPUT_ENABLE(2), 0x55555555 }, + { DC_COM_PIN_OUTPUT_ENABLE(1), 0x55150005 }, + { DC_COM_PIN_OUTPUT_ENABLE(0), 0x55555555 }, +}; + +static void tegra_dc_write_regs(struct tegra_dc *dc, + const struct reg_entry *table, + unsigned int num) +{ + unsigned int i; + + for (i = 0; i < num; i++) + tegra_dc_writel(dc, table[i].value, table[i].offset); +} + +static int tegra_output_rgb_enable(struct tegra_output *output) +{ + struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); + + tegra_dc_write_regs(dc, rgb_enable, ARRAY_SIZE(rgb_enable)); + + return 0; +} + +static int tegra_output_rgb_disable(struct tegra_output *output) +{ + struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); + + tegra_dc_write_regs(dc, rgb_disable, ARRAY_SIZE(rgb_disable)); + + return 0; +} + +static int tegra_output_rgb_setup_clock(struct tegra_output *output, + struct clk *clk, unsigned long pclk) +{ + struct tegra_rgb *rgb = to_rgb(output); + + return clk_set_parent(clk, rgb->clk_parent); +} + +static int tegra_output_rgb_check_mode(struct tegra_output *output, + struct drm_display_mode *mode, + enum drm_mode_status *status) +{ + /* + * FIXME: For now, always assume that the mode is okay. There are + * unresolved issues with clk_round_rate(), which doesn't always + * reliably report whether a frequency can be set or not. + */ + + *status = MODE_OK; + + return 0; +} + +static const struct tegra_output_ops rgb_ops = { + .enable = tegra_output_rgb_enable, + .disable = tegra_output_rgb_disable, + .setup_clock = tegra_output_rgb_setup_clock, + .check_mode = tegra_output_rgb_check_mode, +}; + +int tegra_dc_rgb_probe(struct tegra_dc *dc) +{ + struct device_node *np; + struct tegra_rgb *rgb; + int err; + + np = of_get_child_by_name(dc->dev->of_node, "rgb"); + if (!np || !of_device_is_available(np)) + return -ENODEV; + + rgb = devm_kzalloc(dc->dev, sizeof(*rgb), GFP_KERNEL); + if (!rgb) + return -ENOMEM; + + rgb->clk = devm_clk_get(dc->dev, NULL); + if (IS_ERR(rgb->clk)) { + dev_err(dc->dev, "failed to get clock\n"); + return PTR_ERR(rgb->clk); + } + + rgb->clk_parent = devm_clk_get(dc->dev, "parent"); + if (IS_ERR(rgb->clk_parent)) { + dev_err(dc->dev, "failed to get parent clock\n"); + return PTR_ERR(rgb->clk_parent); + } + + err = clk_set_parent(rgb->clk, rgb->clk_parent); + if (err < 0) { + dev_err(dc->dev, "failed to set parent clock: %d\n", err); + return err; + } + + rgb->output.dev = dc->dev; + rgb->output.of_node = np; + + err = tegra_output_parse_dt(&rgb->output); + if (err < 0) + return err; + + dc->rgb = &rgb->output; + + return 0; +} + +int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc) +{ + struct tegra_rgb *rgb = to_rgb(dc->rgb); + int err; + + if (!dc->rgb) + return -ENODEV; + + rgb->output.type = TEGRA_OUTPUT_RGB; + rgb->output.ops = &rgb_ops; + + err = tegra_output_init(dc->base.dev, &rgb->output); + if (err < 0) { + dev_err(dc->dev, "output setup failed: %d\n", err); + return err; + } + + /* + * By default, outputs can be associated with each display controller. + * RGB outputs are an exception, so we make sure they can be attached + * to only their parent display controller. + */ + rgb->output.encoder.possible_crtcs = 1 << dc->pipe; + + return 0; +} + +int tegra_dc_rgb_exit(struct tegra_dc *dc) +{ + if (dc->rgb) { + int err; + + err = tegra_output_disable(dc->rgb); + if (err < 0) { + dev_err(dc->dev, "output failed to disable: %d\n", err); + return err; + } + + err = tegra_output_exit(dc->rgb); + if (err < 0) { + dev_err(dc->dev, "output cleanup failed: %d\n", err); + return err; + } + + dc->rgb = NULL; + } + + return 0; +} -- cgit v1.2.3 From 8ac49e0485bb79223a111b366a3b1f5ec9148729 Mon Sep 17 00:00:00 2001 From: Christian Daudt Date: Mon, 19 Nov 2012 09:46:10 -0800 Subject: Add support for generic BCM SoC chipsets In order to start upstreaming Broadcom SoC support, create a starting hierarchy, arch and dts files. The first support SoC family that is planned is the BCM281XX (BCM11130/11140/11351/28145/28155) family of dual A9 mobile SoC cores. This code is just the skeleton code for get the machine upstreamed. It has been made MULTIPLATFORM compatible. Next steps ---------- Upstream a basic set of drivers - sufficient for a console boot to ramdisk. These will includer timer, gpio, i2c drivers. After this basic set, we will proceed with a more comprehensive set of drivers for the 281XX SoC family. v2 patch mods -------- - Remove l2x0_of_init call as there were problems with the code. A separate patch will be submitted with cache init code - Rename capri files and refs to bcm281xx-based names - Add bcm281xx binding doc - various misc cleanups v3 patch mods ------------- - Remove extra #include lines - Remove remaining references to capri - dt uart chipset string added - cleaned up chip # references v4 patch mods ------------- - swap order of compatible definitions for uart - fix typo v5 patch mods ------------- - Rename bcm281xx to bcm11351 in dts+code, leaving references to bcm281xx only in help+comments. v6 patch mods ------------- - fix typo in uart 'compatible' string Signed-off-by: Christian Daudt Reviewed-by: Stephen Warren Signed-off-by: Olof Johansson --- .../devicetree/bindings/arm/bcm/bcm11351.txt | 9 ++ arch/arm/Kconfig | 2 + arch/arm/Makefile | 1 + arch/arm/boot/dts/Makefile | 1 + arch/arm/boot/dts/bcm11351-brt.dts | 30 ++++++ arch/arm/boot/dts/bcm11351.dtsi | 50 +++++++++ arch/arm/configs/bcm_defconfig | 114 +++++++++++++++++++++ arch/arm/mach-bcm/Kconfig | 19 ++++ arch/arm/mach-bcm/Makefile | 13 +++ arch/arm/mach-bcm/board_bcm.c | 57 +++++++++++ 10 files changed, 296 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/bcm/bcm11351.txt create mode 100644 arch/arm/boot/dts/bcm11351-brt.dts create mode 100644 arch/arm/boot/dts/bcm11351.dtsi create mode 100644 arch/arm/configs/bcm_defconfig create mode 100644 arch/arm/mach-bcm/Kconfig create mode 100644 arch/arm/mach-bcm/Makefile create mode 100644 arch/arm/mach-bcm/board_bcm.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/bcm/bcm11351.txt b/Documentation/devicetree/bindings/arm/bcm/bcm11351.txt new file mode 100644 index 000000000000..fb7b5cd2652f --- /dev/null +++ b/Documentation/devicetree/bindings/arm/bcm/bcm11351.txt @@ -0,0 +1,9 @@ +Broadcom BCM11351 device tree bindings +------------------------------------------- + +Boards with the bcm281xx SoC family (which includes bcm11130, bcm11140, +bcm11351, bcm28145, bcm28155 SoCs) shall have the following properties: + +Required root node property: + +compatible = "bcm,bcm11351"; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index e9b6b9e5e779..c1cc4f79e4fe 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1009,6 +1009,8 @@ source "arch/arm/mach-mvebu/Kconfig" source "arch/arm/mach-at91/Kconfig" +source "arch/arm/mach-bcm/Kconfig" + source "arch/arm/mach-clps711x/Kconfig" source "arch/arm/mach-cns3xxx/Kconfig" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 3353b7613208..208912167823 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -137,6 +137,7 @@ textofs-$(CONFIG_ARCH_MSM8960) := 0x00208000 # Machine directory name. This list is sorted alphanumerically # by CONFIG_* macro name. machine-$(CONFIG_ARCH_AT91) += at91 +machine-$(CONFIG_ARCH_BCM) += bcm machine-$(CONFIG_ARCH_BCM2835) += bcm2835 machine-$(CONFIG_ARCH_CLPS711X) += clps711x machine-$(CONFIG_ARCH_CNS3XXX) += cns3xxx diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 5cc95667fa3b..cc803c92a72c 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -17,6 +17,7 @@ dtb-$(CONFIG_ARCH_AT91) += aks-cdu.dtb \ usb_a9263.dtb \ usb_a9g20.dtb dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb +dtb-$(CONFIG_ARCH_BCM) += bcm11351-brt.dtb dtb-$(CONFIG_ARCH_DOVE) += dove-cm-a510.dtb \ dove-cubox.dtb \ dove-dove-db.dtb diff --git a/arch/arm/boot/dts/bcm11351-brt.dts b/arch/arm/boot/dts/bcm11351-brt.dts new file mode 100644 index 000000000000..248067cf7069 --- /dev/null +++ b/arch/arm/boot/dts/bcm11351-brt.dts @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2012 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/dts-v1/; + +/include/ "bcm11351.dtsi" + +/ { + model = "BCM11351 BRT board"; + compatible = "bcm,bcm11351-brt", "bcm,bcm11351"; + + memory { + reg = <0x80000000 0x40000000>; /* 1 GB */ + }; + + uart@3e000000 { + status = "okay"; + }; + +}; diff --git a/arch/arm/boot/dts/bcm11351.dtsi b/arch/arm/boot/dts/bcm11351.dtsi new file mode 100644 index 000000000000..ad135885bd2a --- /dev/null +++ b/arch/arm/boot/dts/bcm11351.dtsi @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/include/ "skeleton.dtsi" + +/ { + model = "BCM11351 SoC"; + compatible = "bcm,bcm11351"; + interrupt-parent = <&gic>; + + chosen { + bootargs = "console=ttyS0,115200n8"; + }; + + gic: interrupt-controller@3ff00100 { + compatible = "arm,cortex-a9-gic"; + #interrupt-cells = <3>; + #address-cells = <0>; + interrupt-controller; + reg = <0x3ff01000 0x1000>, + <0x3ff00100 0x100>; + }; + + uart@3e000000 { + compatible = "bcm,bcm11351-dw-apb-uart", "snps,dw-apb-uart"; + status = "disabled"; + reg = <0x3e000000 0x1000>; + clock-frequency = <13000000>; + interrupts = <0x0 67 0x4>; + reg-shift = <2>; + reg-io-width = <4>; + }; + + L2: l2-cache { + compatible = "arm,pl310-cache"; + reg = <0x3ff20000 0x1000>; + cache-unified; + cache-level = <2>; + }; +}; diff --git a/arch/arm/configs/bcm_defconfig b/arch/arm/configs/bcm_defconfig new file mode 100644 index 000000000000..e3bf2d65618e --- /dev/null +++ b/arch/arm/configs/bcm_defconfig @@ -0,0 +1,114 @@ +CONFIG_EXPERIMENTAL=y +# CONFIG_LOCALVERSION_AUTO is not set +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=19 +CONFIG_CGROUPS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_SCHED=y +CONFIG_BLK_CGROUP=y +CONFIG_NAMESPACES=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_PARTITION_ADVANCED=y +CONFIG_EFI_PARTITION=y +CONFIG_ARCH_BCM=y +CONFIG_ARM_THUMBEE=y +CONFIG_ARM_ERRATA_743622=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_COMPACTION is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_ARM_APPENDED_DTB=y +CONFIG_CMDLINE="console=ttyS0,115200n8 mem=128M" +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_PM_RUNTIME=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_PROC_DEVICETREE=y +# CONFIG_BLK_DEV is not set +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_INPUT_FF_MEMLESS=y +CONFIG_INPUT_JOYDEV=y +CONFIG_INPUT_EVDEV=y +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=y +# CONFIG_SERIO is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_RSA=y +CONFIG_SERIAL_8250_DW=y +CONFIG_HW_RANDOM=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +# CONFIG_HWMON is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +# CONFIG_USB_SUPPORT is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_CONFIGFS_FS=y +# CONFIG_MISC_FILESYSTEMS is not set +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=110 +CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y +CONFIG_DEBUG_INFO=y +# CONFIG_FTRACE is not set +CONFIG_DEBUG_LL=y +CONFIG_CRC_CCITT=y +CONFIG_CRC_T10DIF=y +CONFIG_CRC_ITU_T=y +CONFIG_CRC7=y +CONFIG_XZ_DEC=y +CONFIG_AVERAGE=y diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig new file mode 100644 index 000000000000..48705c10a0fe --- /dev/null +++ b/arch/arm/mach-bcm/Kconfig @@ -0,0 +1,19 @@ +config ARCH_BCM + bool "Broadcom SoC" if ARCH_MULTI_V7 + depends on MMU + select ARCH_REQUIRE_GPIOLIB + select ARM_ERRATA_754322 + select ARM_ERRATA_764369 if SMP + select ARM_GIC + select CPU_V7 + select GENERIC_CLOCKEVENTS + select GENERIC_GPIO + select GENERIC_TIME + select GPIO_BCM + select SPARSE_IRQ + select TICK_ONESHOT + help + This enables support for system based on Broadcom SoCs. + It currently supports the 'BCM281XX' family, which includes + BCM11130, BCM11140, BCM11351, BCM28145 and + BCM28155 variants. diff --git a/arch/arm/mach-bcm/Makefile b/arch/arm/mach-bcm/Makefile new file mode 100644 index 000000000000..bbf412261e5e --- /dev/null +++ b/arch/arm/mach-bcm/Makefile @@ -0,0 +1,13 @@ +# +# Copyright (C) 2012 Broadcom Corporation +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation version 2. +# +# This program is distributed "as is" WITHOUT ANY WARRANTY of any +# kind, whether express or implied; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +obj-$(CONFIG_ARCH_BCM) := board_bcm.o diff --git a/arch/arm/mach-bcm/board_bcm.c b/arch/arm/mach-bcm/board_bcm.c new file mode 100644 index 000000000000..3a62f1b1cabc --- /dev/null +++ b/arch/arm/mach-bcm/board_bcm.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2012 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include + +static const struct of_device_id irq_match[] = { + {.compatible = "arm,cortex-a9-gic", .data = gic_of_init, }, + {} +}; + +static void timer_init(void) +{ +} + +static struct sys_timer timer = { + .init = timer_init, +}; + +static void __init init_irq(void) +{ + of_irq_init(irq_match); +} + +static void __init board_init(void) +{ + of_platform_populate(NULL, of_default_bus_match_table, NULL, + &platform_bus); +} + +static const char * const bcm11351_dt_compat[] = { "bcm,bcm11351", NULL, }; + +DT_MACHINE_START(BCM11351_DT, "Broadcom Application Processor") + .init_irq = init_irq, + .timer = &timer, + .init_machine = board_init, + .dt_compat = bcm11351_dt_compat, + .handle_irq = gic_handle_irq, +MACHINE_END -- cgit v1.2.3 From c47d244a646d08e2161b7fa22c5512e7988762ae Mon Sep 17 00:00:00 2001 From: Vasanth Ananthan Date: Tue, 20 Nov 2012 21:02:11 +0900 Subject: ARM: EXYNOS: DT Support for SATA and SATA PHY This patch adds Device Nodes for SATA and SATA PHY device. Signed-off-by: Vasanth Ananthan [kgene.kim@samsung.com: removed address definitions as per comments] Signed-off-by: Kukjin Kim --- .../devicetree/bindings/ata/exynos-sata-phy.txt | 14 ++++++++++++++ Documentation/devicetree/bindings/ata/exynos-sata.txt | 17 +++++++++++++++++ arch/arm/boot/dts/exynos5250-smdk5250.dts | 15 +++++++++++++++ arch/arm/boot/dts/exynos5250.dtsi | 18 ++++++++++++++++++ arch/arm/mach-exynos/mach-exynos5-dt.c | 6 ++++++ 5 files changed, 70 insertions(+) create mode 100644 Documentation/devicetree/bindings/ata/exynos-sata-phy.txt create mode 100644 Documentation/devicetree/bindings/ata/exynos-sata.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/ata/exynos-sata-phy.txt b/Documentation/devicetree/bindings/ata/exynos-sata-phy.txt new file mode 100644 index 000000000000..37824fac688e --- /dev/null +++ b/Documentation/devicetree/bindings/ata/exynos-sata-phy.txt @@ -0,0 +1,14 @@ +* Samsung SATA PHY Controller + +SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers. +Each SATA PHY controller should have its own node. + +Required properties: +- compatible : compatible list, contains "samsung,exynos5-sata-phy" +- reg : + +Example: + sata@ffe07000 { + compatible = "samsung,exynos5-sata-phy"; + reg = <0xffe07000 0x1000>; + }; diff --git a/Documentation/devicetree/bindings/ata/exynos-sata.txt b/Documentation/devicetree/bindings/ata/exynos-sata.txt new file mode 100644 index 000000000000..0849f1025e34 --- /dev/null +++ b/Documentation/devicetree/bindings/ata/exynos-sata.txt @@ -0,0 +1,17 @@ +* Samsung AHCI SATA Controller + +SATA nodes are defined to describe on-chip Serial ATA controllers. +Each SATA controller should have its own node. + +Required properties: +- compatible : compatible list, contains "samsung,exynos5-sata" +- interrupts : +- reg : +- samsung,sata-freq : + +Example: + sata@ffe08000 { + compatible = "samsung,exynos5-sata"; + reg = <0xffe08000 0x1000>; + interrupts = <115>; + }; diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts index 21d4ccdb0a5a..3f8c2ab1d038 100644 --- a/arch/arm/boot/dts/exynos5250-smdk5250.dts +++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts @@ -55,6 +55,21 @@ }; }; + i2c@121D0000 { + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <40000>; + samsung,i2c-slave-addr = <0x38>; + + sata-phy { + compatible = "samsung,sata-phy"; + reg = <0x38>; + }; + }; + + sata@122F0000 { + samsung,sata-freq = <66>; + }; + i2c@12C80000 { status = "disabled"; }; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index 670dfdb01a09..e913525a4814 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -104,6 +104,17 @@ interrupts = <0 54 0>; }; + sata@122F0000 { + compatible = "samsung,exynos5-sata-ahci"; + reg = <0x122F0000 0x1ff>; + interrupts = <0 115 0>; + }; + + sata-phy@12170000 { + compatible = "samsung,exynos5-sata-phy"; + reg = <0x12170000 0x1ff>; + }; + i2c@12C60000 { compatible = "samsung,s3c2440-i2c"; reg = <0x12C60000 0x100>; @@ -168,6 +179,13 @@ #size-cells = <0>; }; + i2c@121D0000 { + compatible = "samsung,exynos5-sata-phy-i2c"; + reg = <0x121D0000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + }; + spi_0: spi@12d20000 { compatible = "samsung,exynos4210-spi"; reg = <0x12d20000 0x100>; diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c index 4c0f3369abdf..a88c19f5efcc 100644 --- a/arch/arm/mach-exynos/mach-exynos5-dt.c +++ b/arch/arm/mach-exynos/mach-exynos5-dt.c @@ -64,6 +64,12 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = { "exynos4210-spi.1", NULL), OF_DEV_AUXDATA("samsung,exynos4210-spi", EXYNOS5_PA_SPI2, "exynos4210-spi.2", NULL), + OF_DEV_AUXDATA("samsung,exynos5-sata-ahci", 0x122F0000, + "exynos5-sata", NULL), + OF_DEV_AUXDATA("samsung,exynos5-sata-phy", 0x12170000, + "exynos5-sata-phy", NULL), + OF_DEV_AUXDATA("samsung,exynos5-sata-phy-i2c", 0x121D0000, + "exynos5-sata-phy-i2c", NULL), OF_DEV_AUXDATA("arm,pl330", EXYNOS5_PA_PDMA0, "dma-pl330.0", NULL), OF_DEV_AUXDATA("arm,pl330", EXYNOS5_PA_PDMA1, "dma-pl330.1", NULL), OF_DEV_AUXDATA("arm,pl330", EXYNOS5_PA_MDMA1, "dma-pl330.2", NULL), -- cgit v1.2.3 From 97fa4cf442ff2872000d9110686371775795a32b Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Sat, 17 Nov 2012 15:22:22 +0100 Subject: clk: mvebu: add mvebu core clocks. This driver allows to provide DT clocks for core clocks found on Marvell Kirkwood, Dove & 370/XP SoCs. The core clock frequencies and ratios are determined by decoding the Sample-At-Reset registers. Although technically correct, using a divider of 0 will lead to div_by_zero panic. Let's use a ratio of 0/1 instead to fail later with a zero clock. Signed-off-by: Gregory CLEMENT Signed-off-by: Sebastian Hesselbarth Signed-off-by: Andrew Lunn Tested-by Gregory CLEMENT --- .../devicetree/bindings/clock/mvebu-core-clock.txt | 47 ++ drivers/clk/Kconfig | 2 + drivers/clk/Makefile | 1 + drivers/clk/mvebu/Kconfig | 3 + drivers/clk/mvebu/Makefile | 1 + drivers/clk/mvebu/clk-core.c | 675 +++++++++++++++++++++ drivers/clk/mvebu/clk-core.h | 18 + drivers/clk/mvebu/clk.c | 23 + include/linux/clk/mvebu.h | 22 + 9 files changed, 792 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/mvebu-core-clock.txt create mode 100644 drivers/clk/mvebu/Kconfig create mode 100644 drivers/clk/mvebu/Makefile create mode 100644 drivers/clk/mvebu/clk-core.c create mode 100644 drivers/clk/mvebu/clk-core.h create mode 100644 drivers/clk/mvebu/clk.c create mode 100644 include/linux/clk/mvebu.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt new file mode 100644 index 000000000000..1e662948661e --- /dev/null +++ b/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt @@ -0,0 +1,47 @@ +* Core Clock bindings for Marvell MVEBU SoCs + +Marvell MVEBU SoCs usually allow to determine core clock frequencies by +reading the Sample-At-Reset (SAR) register. The core clock consumer should +specify the desired clock by having the clock ID in its "clocks" phandle cell. + +The following is a list of provided IDs and clock names on Armada 370/XP: + 0 = tclk (Internal Bus clock) + 1 = cpuclk (CPU clock) + 2 = nbclk (L2 Cache clock) + 3 = hclk (DRAM control clock) + 4 = dramclk (DDR clock) + +The following is a list of provided IDs and clock names on Kirkwood and Dove: + 0 = tclk (Internal Bus clock) + 1 = cpuclk (CPU0 clock) + 2 = l2clk (L2 Cache clock derived from CPU0 clock) + 3 = ddrclk (DDR controller clock derived from CPU0 clock) + +Required properties: +- compatible : shall be one of the following: + "marvell,armada-370-core-clock" - For Armada 370 SoC core clocks + "marvell,armada-xp-core-clock" - For Armada XP SoC core clocks + "marvell,dove-core-clock" - for Dove SoC core clocks + "marvell,kirkwood-core-clock" - for Kirkwood SoC (except mv88f6180) + "marvell,mv88f6180-core-clock" - for Kirkwood MV88f6180 SoC +- reg : shall be the register address of the Sample-At-Reset (SAR) register +- #clock-cells : from common clock binding; shall be set to 1 + +Optional properties: +- clock-output-names : from common clock binding; allows overwrite default clock + output names ("tclk", "cpuclk", "l2clk", "ddrclk") + +Example: + +core_clk: core-clocks@d0214 { + compatible = "marvell,dove-core-clock"; + reg = <0xd0214 0x4>; + #clock-cells = <1>; +}; + +spi0: spi@10600 { + compatible = "marvell,orion-spi"; + /* ... */ + /* get tclk from core clock provider */ + clocks = <&core_clk 0>; +}; diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index bace9e98f75d..60427c0d23e6 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -54,3 +54,5 @@ config COMMON_CLK_MAX77686 This driver supports Maxim 77686 crystal oscillator clock. endmenu + +source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 71a25b91de00..d0a14ae8d49c 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_PLAT_SPEAR) += spear/ obj-$(CONFIG_ARCH_U300) += clk-u300.o obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/ obj-$(CONFIG_ARCH_PRIMA2) += clk-prima2.o +obj-$(CONFIG_PLAT_ORION) += mvebu/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig new file mode 100644 index 000000000000..fd7bf97ff74d --- /dev/null +++ b/drivers/clk/mvebu/Kconfig @@ -0,0 +1,3 @@ +config MVEBU_CLK_CORE + bool + diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile new file mode 100644 index 000000000000..de1d9617f75a --- /dev/null +++ b/drivers/clk/mvebu/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MVEBU_CLK_CORE) += clk.o clk-core.o diff --git a/drivers/clk/mvebu/clk-core.c b/drivers/clk/mvebu/clk-core.c new file mode 100644 index 000000000000..69056a7479e8 --- /dev/null +++ b/drivers/clk/mvebu/clk-core.c @@ -0,0 +1,675 @@ +/* + * Marvell EBU clock core handling defined at reset + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT + * Sebastian Hesselbarth + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include +#include +#include +#include +#include "clk-core.h" + +struct core_ratio { + int id; + const char *name; +}; + +struct core_clocks { + u32 (*get_tclk_freq)(void __iomem *sar); + u32 (*get_cpu_freq)(void __iomem *sar); + void (*get_clk_ratio)(void __iomem *sar, int id, int *mult, int *div); + const struct core_ratio *ratios; + int num_ratios; +}; + +static struct clk_onecell_data clk_data; + +static void __init mvebu_clk_core_setup(struct device_node *np, + struct core_clocks *coreclk) +{ + const char *tclk_name = "tclk"; + const char *cpuclk_name = "cpuclk"; + void __iomem *base; + unsigned long rate; + int n; + + base = of_iomap(np, 0); + if (WARN_ON(!base)) + return; + + /* + * Allocate struct for TCLK, cpu clk, and core ratio clocks + */ + clk_data.clk_num = 2 + coreclk->num_ratios; + clk_data.clks = kzalloc(clk_data.clk_num * sizeof(struct clk *), + GFP_KERNEL); + if (WARN_ON(!clk_data.clks)) + return; + + /* + * Register TCLK + */ + of_property_read_string_index(np, "clock-output-names", 0, + &tclk_name); + rate = coreclk->get_tclk_freq(base); + clk_data.clks[0] = clk_register_fixed_rate(NULL, tclk_name, NULL, + CLK_IS_ROOT, rate); + WARN_ON(IS_ERR(clk_data.clks[0])); + + /* + * Register CPU clock + */ + of_property_read_string_index(np, "clock-output-names", 1, + &cpuclk_name); + rate = coreclk->get_cpu_freq(base); + clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL, + CLK_IS_ROOT, rate); + WARN_ON(IS_ERR(clk_data.clks[1])); + + /* + * Register fixed-factor clocks derived from CPU clock + */ + for (n = 0; n < coreclk->num_ratios; n++) { + const char *rclk_name = coreclk->ratios[n].name; + int mult, div; + + of_property_read_string_index(np, "clock-output-names", + 2+n, &rclk_name); + coreclk->get_clk_ratio(base, coreclk->ratios[n].id, + &mult, &div); + clk_data.clks[2+n] = clk_register_fixed_factor(NULL, rclk_name, + cpuclk_name, 0, mult, div); + WARN_ON(IS_ERR(clk_data.clks[2+n])); + }; + + /* + * SAR register isn't needed anymore + */ + iounmap(base); + + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +} + +#ifdef CONFIG_MACH_ARMADA_370_XP +/* + * Armada 370/XP Sample At Reset is a 64 bit bitfiled split in two + * register of 32 bits + */ + +#define SARL 0 /* Low part [0:31] */ +#define SARL_AXP_PCLK_FREQ_OPT 21 +#define SARL_AXP_PCLK_FREQ_OPT_MASK 0x7 +#define SARL_A370_PCLK_FREQ_OPT 11 +#define SARL_A370_PCLK_FREQ_OPT_MASK 0xF +#define SARL_AXP_FAB_FREQ_OPT 24 +#define SARL_AXP_FAB_FREQ_OPT_MASK 0xF +#define SARL_A370_FAB_FREQ_OPT 15 +#define SARL_A370_FAB_FREQ_OPT_MASK 0x1F +#define SARL_A370_TCLK_FREQ_OPT 20 +#define SARL_A370_TCLK_FREQ_OPT_MASK 0x1 +#define SARH 4 /* High part [32:63] */ +#define SARH_AXP_PCLK_FREQ_OPT (52-32) +#define SARH_AXP_PCLK_FREQ_OPT_MASK 0x1 +#define SARH_AXP_PCLK_FREQ_OPT_SHIFT 3 +#define SARH_AXP_FAB_FREQ_OPT (51-32) +#define SARH_AXP_FAB_FREQ_OPT_MASK 0x1 +#define SARH_AXP_FAB_FREQ_OPT_SHIFT 4 + +static const u32 __initconst armada_370_tclk_frequencies[] = { + 16600000, + 20000000, +}; + +static u32 __init armada_370_get_tclk_freq(void __iomem *sar) +{ + u8 tclk_freq_select = 0; + + tclk_freq_select = ((readl(sar) >> SARL_A370_TCLK_FREQ_OPT) & + SARL_A370_TCLK_FREQ_OPT_MASK); + return armada_370_tclk_frequencies[tclk_freq_select]; +} + +static const u32 __initconst armada_370_cpu_frequencies[] = { + 400000000, + 533000000, + 667000000, + 800000000, + 1000000000, + 1067000000, + 1200000000, +}; + +static u32 __init armada_370_get_cpu_freq(void __iomem *sar) +{ + u32 cpu_freq; + u8 cpu_freq_select = 0; + + cpu_freq_select = ((readl(sar) >> SARL_A370_PCLK_FREQ_OPT) & + SARL_A370_PCLK_FREQ_OPT_MASK); + if (cpu_freq_select > ARRAY_SIZE(armada_370_cpu_frequencies)) { + pr_err("CPU freq select unsuported %d\n", cpu_freq_select); + cpu_freq = 0; + } else + cpu_freq = armada_370_cpu_frequencies[cpu_freq_select]; + + return cpu_freq; +} + +enum { A370_XP_NBCLK, A370_XP_HCLK, A370_XP_DRAMCLK }; + +static const struct core_ratio __initconst armada_370_xp_core_ratios[] = { + { .id = A370_XP_NBCLK, .name = "nbclk" }, + { .id = A370_XP_HCLK, .name = "hclk" }, + { .id = A370_XP_DRAMCLK, .name = "dramclk" }, +}; + +static const int __initconst armada_370_xp_nbclk_ratios[32][2] = { + {0, 1}, {1, 2}, {2, 2}, {2, 2}, + {1, 2}, {1, 2}, {1, 1}, {2, 3}, + {0, 1}, {1, 2}, {2, 4}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {2, 2}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {2, 3}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static const int __initconst armada_370_xp_hclk_ratios[32][2] = { + {0, 1}, {1, 2}, {2, 6}, {2, 3}, + {1, 3}, {1, 4}, {1, 2}, {2, 6}, + {0, 1}, {1, 6}, {2, 10}, {0, 1}, + {1, 4}, {0, 1}, {0, 1}, {2, 5}, + {0, 1}, {0, 1}, {0, 1}, {1, 2}, + {2, 6}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static const int __initconst armada_370_xp_dramclk_ratios[32][2] = { + {0, 1}, {1, 2}, {2, 3}, {2, 3}, + {1, 3}, {1, 2}, {1, 2}, {2, 6}, + {0, 1}, {1, 3}, {2, 5}, {0, 1}, + {1, 4}, {0, 1}, {0, 1}, {2, 5}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {2, 3}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {1, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static void __init armada_370_xp_get_clk_ratio(u32 opt, + void __iomem *sar, int id, int *mult, int *div) +{ + switch (id) { + case A370_XP_NBCLK: + *mult = armada_370_xp_nbclk_ratios[opt][0]; + *div = armada_370_xp_nbclk_ratios[opt][1]; + break; + case A370_XP_HCLK: + *mult = armada_370_xp_hclk_ratios[opt][0]; + *div = armada_370_xp_hclk_ratios[opt][1]; + break; + case A370_XP_DRAMCLK: + *mult = armada_370_xp_dramclk_ratios[opt][0]; + *div = armada_370_xp_dramclk_ratios[opt][1]; + break; + } +} + +static void __init armada_370_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + u32 opt = ((readl(sar) >> SARL_A370_FAB_FREQ_OPT) & + SARL_A370_FAB_FREQ_OPT_MASK); + + armada_370_xp_get_clk_ratio(opt, sar, id, mult, div); +} + + +static const struct core_clocks armada_370_core_clocks = { + .get_tclk_freq = armada_370_get_tclk_freq, + .get_cpu_freq = armada_370_get_cpu_freq, + .get_clk_ratio = armada_370_get_clk_ratio, + .ratios = armada_370_xp_core_ratios, + .num_ratios = ARRAY_SIZE(armada_370_xp_core_ratios), +}; + +static const u32 __initconst armada_xp_cpu_frequencies[] = { + 1000000000, + 1066000000, + 1200000000, + 1333000000, + 1500000000, + 1666000000, + 1800000000, + 2000000000, + 667000000, + 0, + 800000000, + 1600000000, +}; + +/* For Armada XP TCLK frequency is fix: 250MHz */ +static u32 __init armada_xp_get_tclk_freq(void __iomem *sar) +{ + return 250 * 1000 * 1000; +} + +static u32 __init armada_xp_get_cpu_freq(void __iomem *sar) +{ + u32 cpu_freq; + u8 cpu_freq_select = 0; + + cpu_freq_select = ((readl(sar) >> SARL_AXP_PCLK_FREQ_OPT) & + SARL_AXP_PCLK_FREQ_OPT_MASK); + /* + * The upper bit is not contiguous to the other ones and + * located in the high part of the SAR registers + */ + cpu_freq_select |= (((readl(sar+4) >> SARH_AXP_PCLK_FREQ_OPT) & + SARH_AXP_PCLK_FREQ_OPT_MASK) + << SARH_AXP_PCLK_FREQ_OPT_SHIFT); + if (cpu_freq_select > ARRAY_SIZE(armada_xp_cpu_frequencies)) { + pr_err("CPU freq select unsuported: %d\n", cpu_freq_select); + cpu_freq = 0; + } else + cpu_freq = armada_xp_cpu_frequencies[cpu_freq_select]; + + return cpu_freq; +} + +static void __init armada_xp_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + + u32 opt = ((readl(sar) >> SARL_AXP_FAB_FREQ_OPT) & + SARL_AXP_FAB_FREQ_OPT_MASK); + /* + * The upper bit is not contiguous to the other ones and + * located in the high part of the SAR registers + */ + opt |= (((readl(sar+4) >> SARH_AXP_FAB_FREQ_OPT) & + SARH_AXP_FAB_FREQ_OPT_MASK) + << SARH_AXP_FAB_FREQ_OPT_SHIFT); + + armada_370_xp_get_clk_ratio(opt, sar, id, mult, div); +} + +static const struct core_clocks armada_xp_core_clocks = { + .get_tclk_freq = armada_xp_get_tclk_freq, + .get_cpu_freq = armada_xp_get_cpu_freq, + .get_clk_ratio = armada_xp_get_clk_ratio, + .ratios = armada_370_xp_core_ratios, + .num_ratios = ARRAY_SIZE(armada_370_xp_core_ratios), +}; + +#endif /* CONFIG_MACH_ARMADA_370_XP */ + +/* + * Dove PLL sample-at-reset configuration + * + * SAR0[8:5] : CPU frequency + * 5 = 1000 MHz + * 6 = 933 MHz + * 7 = 933 MHz + * 8 = 800 MHz + * 9 = 800 MHz + * 10 = 800 MHz + * 11 = 1067 MHz + * 12 = 667 MHz + * 13 = 533 MHz + * 14 = 400 MHz + * 15 = 333 MHz + * others reserved. + * + * SAR0[11:9] : CPU to L2 Clock divider ratio + * 0 = (1/1) * CPU + * 2 = (1/2) * CPU + * 4 = (1/3) * CPU + * 6 = (1/4) * CPU + * others reserved. + * + * SAR0[15:12] : CPU to DDR DRAM Clock divider ratio + * 0 = (1/1) * CPU + * 2 = (1/2) * CPU + * 3 = (2/5) * CPU + * 4 = (1/3) * CPU + * 6 = (1/4) * CPU + * 8 = (1/5) * CPU + * 10 = (1/6) * CPU + * 12 = (1/7) * CPU + * 14 = (1/8) * CPU + * 15 = (1/10) * CPU + * others reserved. + * + * SAR0[24:23] : TCLK frequency + * 0 = 166 MHz + * 1 = 125 MHz + * others reserved. + */ +#ifdef CONFIG_ARCH_DOVE +#define SAR_DOVE_CPU_FREQ 5 +#define SAR_DOVE_CPU_FREQ_MASK 0xf +#define SAR_DOVE_L2_RATIO 9 +#define SAR_DOVE_L2_RATIO_MASK 0x7 +#define SAR_DOVE_DDR_RATIO 12 +#define SAR_DOVE_DDR_RATIO_MASK 0xf +#define SAR_DOVE_TCLK_FREQ 23 +#define SAR_DOVE_TCLK_FREQ_MASK 0x3 + +static const u32 __initconst dove_tclk_frequencies[] = { + 166666667, + 125000000, + 0, 0 +}; + +static u32 __init dove_get_tclk_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_DOVE_TCLK_FREQ) & + SAR_DOVE_TCLK_FREQ_MASK; + return dove_tclk_frequencies[opt]; +} + +static const u32 __initconst dove_cpu_frequencies[] = { + 0, 0, 0, 0, 0, + 1000000000, + 933333333, 933333333, + 800000000, 800000000, 800000000, + 1066666667, + 666666667, + 533333333, + 400000000, + 333333333 +}; + +static u32 __init dove_get_cpu_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_DOVE_CPU_FREQ) & + SAR_DOVE_CPU_FREQ_MASK; + return dove_cpu_frequencies[opt]; +} + +enum { DOVE_CPU_TO_L2, DOVE_CPU_TO_DDR }; + +static const struct core_ratio __initconst dove_core_ratios[] = { + { .id = DOVE_CPU_TO_L2, .name = "l2clk", }, + { .id = DOVE_CPU_TO_DDR, .name = "ddrclk", } +}; + +static const int __initconst dove_cpu_l2_ratios[8][2] = { + { 1, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 }, + { 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 } +}; + +static const int __initconst dove_cpu_ddr_ratios[16][2] = { + { 1, 1 }, { 0, 1 }, { 1, 2 }, { 2, 5 }, + { 1, 3 }, { 0, 1 }, { 1, 4 }, { 0, 1 }, + { 1, 5 }, { 0, 1 }, { 1, 6 }, { 0, 1 }, + { 1, 7 }, { 0, 1 }, { 1, 8 }, { 1, 10 } +}; + +static void __init dove_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + switch (id) { + case DOVE_CPU_TO_L2: + { + u32 opt = (readl(sar) >> SAR_DOVE_L2_RATIO) & + SAR_DOVE_L2_RATIO_MASK; + *mult = dove_cpu_l2_ratios[opt][0]; + *div = dove_cpu_l2_ratios[opt][1]; + break; + } + case DOVE_CPU_TO_DDR: + { + u32 opt = (readl(sar) >> SAR_DOVE_DDR_RATIO) & + SAR_DOVE_DDR_RATIO_MASK; + *mult = dove_cpu_ddr_ratios[opt][0]; + *div = dove_cpu_ddr_ratios[opt][1]; + break; + } + } +} + +static const struct core_clocks dove_core_clocks = { + .get_tclk_freq = dove_get_tclk_freq, + .get_cpu_freq = dove_get_cpu_freq, + .get_clk_ratio = dove_get_clk_ratio, + .ratios = dove_core_ratios, + .num_ratios = ARRAY_SIZE(dove_core_ratios), +}; +#endif /* CONFIG_ARCH_DOVE */ + +/* + * Kirkwood PLL sample-at-reset configuration + * (6180 has different SAR layout than other Kirkwood SoCs) + * + * SAR0[4:3,22,1] : CPU frequency (6281,6292,6282) + * 4 = 600 MHz + * 6 = 800 MHz + * 7 = 1000 MHz + * 9 = 1200 MHz + * 12 = 1500 MHz + * 13 = 1600 MHz + * 14 = 1800 MHz + * 15 = 2000 MHz + * others reserved. + * + * SAR0[19,10:9] : CPU to L2 Clock divider ratio (6281,6292,6282) + * 1 = (1/2) * CPU + * 3 = (1/3) * CPU + * 5 = (1/4) * CPU + * others reserved. + * + * SAR0[8:5] : CPU to DDR DRAM Clock divider ratio (6281,6292,6282) + * 2 = (1/2) * CPU + * 4 = (1/3) * CPU + * 6 = (1/4) * CPU + * 7 = (2/9) * CPU + * 8 = (1/5) * CPU + * 9 = (1/6) * CPU + * others reserved. + * + * SAR0[4:2] : Kirkwood 6180 cpu/l2/ddr clock configuration (6180 only) + * 5 = [CPU = 600 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/3) * CPU] + * 6 = [CPU = 800 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/4) * CPU] + * 7 = [CPU = 1000 MHz, L2 = (1/2) * CPU, DDR = 200 MHz = (1/5) * CPU] + * others reserved. + * + * SAR0[21] : TCLK frequency + * 0 = 200 MHz + * 1 = 166 MHz + * others reserved. + */ +#ifdef CONFIG_ARCH_KIRKWOOD +#define SAR_KIRKWOOD_CPU_FREQ(x) \ + (((x & (1 << 1)) >> 1) | \ + ((x & (1 << 22)) >> 21) | \ + ((x & (3 << 3)) >> 1)) +#define SAR_KIRKWOOD_L2_RATIO(x) \ + (((x & (3 << 9)) >> 9) | \ + (((x & (1 << 19)) >> 17))) +#define SAR_KIRKWOOD_DDR_RATIO 5 +#define SAR_KIRKWOOD_DDR_RATIO_MASK 0xf +#define SAR_MV88F6180_CLK 2 +#define SAR_MV88F6180_CLK_MASK 0x7 +#define SAR_KIRKWOOD_TCLK_FREQ 21 +#define SAR_KIRKWOOD_TCLK_FREQ_MASK 0x1 + +enum { KIRKWOOD_CPU_TO_L2, KIRKWOOD_CPU_TO_DDR }; + +static const struct core_ratio __initconst kirkwood_core_ratios[] = { + { .id = KIRKWOOD_CPU_TO_L2, .name = "l2clk", }, + { .id = KIRKWOOD_CPU_TO_DDR, .name = "ddrclk", } +}; + +static u32 __init kirkwood_get_tclk_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_KIRKWOOD_TCLK_FREQ) & + SAR_KIRKWOOD_TCLK_FREQ_MASK; + return (opt) ? 166666667 : 200000000; +} + +static const u32 __initconst kirkwood_cpu_frequencies[] = { + 0, 0, 0, 0, + 600000000, + 0, + 800000000, + 1000000000, + 0, + 1200000000, + 0, 0, + 1500000000, + 1600000000, + 1800000000, + 2000000000 +}; + +static u32 __init kirkwood_get_cpu_freq(void __iomem *sar) +{ + u32 opt = SAR_KIRKWOOD_CPU_FREQ(readl(sar)); + return kirkwood_cpu_frequencies[opt]; +} + +static const int __initconst kirkwood_cpu_l2_ratios[8][2] = { + { 0, 1 }, { 1, 2 }, { 0, 1 }, { 1, 3 }, + { 0, 1 }, { 1, 4 }, { 0, 1 }, { 0, 1 } +}; + +static const int __initconst kirkwood_cpu_ddr_ratios[16][2] = { + { 0, 1 }, { 0, 1 }, { 1, 2 }, { 0, 1 }, + { 1, 3 }, { 0, 1 }, { 1, 4 }, { 2, 9 }, + { 1, 5 }, { 1, 6 }, { 0, 1 }, { 0, 1 }, + { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 } +}; + +static void __init kirkwood_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + switch (id) { + case KIRKWOOD_CPU_TO_L2: + { + u32 opt = SAR_KIRKWOOD_L2_RATIO(readl(sar)); + *mult = kirkwood_cpu_l2_ratios[opt][0]; + *div = kirkwood_cpu_l2_ratios[opt][1]; + break; + } + case KIRKWOOD_CPU_TO_DDR: + { + u32 opt = (readl(sar) >> SAR_KIRKWOOD_DDR_RATIO) & + SAR_KIRKWOOD_DDR_RATIO_MASK; + *mult = kirkwood_cpu_ddr_ratios[opt][0]; + *div = kirkwood_cpu_ddr_ratios[opt][1]; + break; + } + } +} + +static const struct core_clocks kirkwood_core_clocks = { + .get_tclk_freq = kirkwood_get_tclk_freq, + .get_cpu_freq = kirkwood_get_cpu_freq, + .get_clk_ratio = kirkwood_get_clk_ratio, + .ratios = kirkwood_core_ratios, + .num_ratios = ARRAY_SIZE(kirkwood_core_ratios), +}; + +static const u32 __initconst mv88f6180_cpu_frequencies[] = { + 0, 0, 0, 0, 0, + 600000000, + 800000000, + 1000000000 +}; + +static u32 __init mv88f6180_get_cpu_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) & SAR_MV88F6180_CLK_MASK; + return mv88f6180_cpu_frequencies[opt]; +} + +static const int __initconst mv88f6180_cpu_ddr_ratios[8][2] = { + { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, + { 0, 1 }, { 1, 3 }, { 1, 4 }, { 1, 5 } +}; + +static void __init mv88f6180_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + switch (id) { + case KIRKWOOD_CPU_TO_L2: + { + /* mv88f6180 has a fixed 1:2 CPU-to-L2 ratio */ + *mult = 1; + *div = 2; + break; + } + case KIRKWOOD_CPU_TO_DDR: + { + u32 opt = (readl(sar) >> SAR_MV88F6180_CLK) & + SAR_MV88F6180_CLK_MASK; + *mult = mv88f6180_cpu_ddr_ratios[opt][0]; + *div = mv88f6180_cpu_ddr_ratios[opt][1]; + break; + } + } +} + +static const struct core_clocks mv88f6180_core_clocks = { + .get_tclk_freq = kirkwood_get_tclk_freq, + .get_cpu_freq = mv88f6180_get_cpu_freq, + .get_clk_ratio = mv88f6180_get_clk_ratio, + .ratios = kirkwood_core_ratios, + .num_ratios = ARRAY_SIZE(kirkwood_core_ratios), +}; +#endif /* CONFIG_ARCH_KIRKWOOD */ + +static const __initdata struct of_device_id clk_core_match[] = { +#ifdef CONFIG_MACH_ARMADA_370_XP + { + .compatible = "marvell,armada-370-core-clock", + .data = &armada_370_core_clocks, + }, + { + .compatible = "marvell,armada-xp-core-clock", + .data = &armada_xp_core_clocks, + }, +#endif +#ifdef CONFIG_ARCH_DOVE + { + .compatible = "marvell,dove-core-clock", + .data = &dove_core_clocks, + }, +#endif + +#ifdef CONFIG_ARCH_KIRKWOOD + { + .compatible = "marvell,kirkwood-core-clock", + .data = &kirkwood_core_clocks, + }, + { + .compatible = "marvell,mv88f6180-core-clock", + .data = &mv88f6180_core_clocks, + }, +#endif + + { } +}; + +void __init mvebu_core_clk_init(void) +{ + struct device_node *np; + + for_each_matching_node(np, clk_core_match) { + const struct of_device_id *match = + of_match_node(clk_core_match, np); + mvebu_clk_core_setup(np, (struct core_clocks *)match->data); + } +} diff --git a/drivers/clk/mvebu/clk-core.h b/drivers/clk/mvebu/clk-core.h new file mode 100644 index 000000000000..28b5e02e9885 --- /dev/null +++ b/drivers/clk/mvebu/clk-core.h @@ -0,0 +1,18 @@ +/* + * * Marvell EBU clock core handling defined at reset + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __MVEBU_CLK_CORE_H +#define __MVEBU_CLK_CORE_H + +void __init mvebu_core_clk_init(void); + +#endif diff --git a/drivers/clk/mvebu/clk.c b/drivers/clk/mvebu/clk.c new file mode 100644 index 000000000000..e6742acf9880 --- /dev/null +++ b/drivers/clk/mvebu/clk.c @@ -0,0 +1,23 @@ +/* + * Marvell EBU SoC clock handling. + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include +#include +#include +#include "clk-core.h" + +void __init mvebu_clocks_init(void) +{ + mvebu_core_clk_init(); +} diff --git a/include/linux/clk/mvebu.h b/include/linux/clk/mvebu.h new file mode 100644 index 000000000000..8c4ae713b063 --- /dev/null +++ b/include/linux/clk/mvebu.h @@ -0,0 +1,22 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __CLK_MVEBU_H_ +#define __CLK_MVEBU_H_ + +void __init mvebu_clocks_init(void); + +#endif -- cgit v1.2.3 From ab8ba01b3fe5e0b81bd2da0afe66f7f6968e017b Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Sat, 17 Nov 2012 15:22:23 +0100 Subject: clk: mvebu: add armada-370-xp CPU specific clocks Add Armada 370/XP specific CPU clocks Signed-off-by: Gregory CLEMENT Signed-off-by: Thomas Petazzoni Tested-by: Gregory CLEMENT --- .../devicetree/bindings/clock/mvebu-cpu-clock.txt | 21 +++ drivers/clk/mvebu/Kconfig | 3 + drivers/clk/mvebu/Makefile | 1 + drivers/clk/mvebu/clk-cpu.c | 186 +++++++++++++++++++++ drivers/clk/mvebu/clk-cpu.h | 22 +++ drivers/clk/mvebu/clk.c | 2 + 6 files changed, 235 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt create mode 100644 drivers/clk/mvebu/clk-cpu.c create mode 100644 drivers/clk/mvebu/clk-cpu.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt new file mode 100644 index 000000000000..feb830130714 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt @@ -0,0 +1,21 @@ +Device Tree Clock bindings for cpu clock of Marvell EBU platforms + +Required properties: +- compatible : shall be one of the following: + "marvell,armada-xp-cpu-clock" - cpu clocks for Armada XP +- reg : Address and length of the clock complex register set +- #clock-cells : should be set to 1. +- clocks : shall be the input parent clock phandle for the clock. + +cpuclk: clock-complex@d0018700 { + #clock-cells = <1>; + compatible = "marvell,armada-xp-cpu-clock"; + reg = <0xd0018700 0xA0>; + clocks = <&coreclk 1>; +} + +cpu@0 { + compatible = "marvell,sheeva-v7"; + reg = <0>; + clocks = <&cpuclk 0>; +}; diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig index fd7bf97ff74d..1dd93ada9cb3 100644 --- a/drivers/clk/mvebu/Kconfig +++ b/drivers/clk/mvebu/Kconfig @@ -1,3 +1,6 @@ config MVEBU_CLK_CORE bool +config MVEBU_CLK_CPU + bool + diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile index de1d9617f75a..93da083b77e3 100644 --- a/drivers/clk/mvebu/Makefile +++ b/drivers/clk/mvebu/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_MVEBU_CLK_CORE) += clk.o clk-core.o +obj-$(CONFIG_MVEBU_CLK_CPU) += clk-cpu.o diff --git a/drivers/clk/mvebu/clk-cpu.c b/drivers/clk/mvebu/clk-cpu.c new file mode 100644 index 000000000000..ff004578a119 --- /dev/null +++ b/drivers/clk/mvebu/clk-cpu.c @@ -0,0 +1,186 @@ +/* + * Marvell MVEBU CPU clock handling. + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include +#include +#include +#include +#include "clk-cpu.h" + +#define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET 0x0 +#define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET 0xC +#define SYS_CTRL_CLK_DIVIDER_MASK 0x3F + +#define MAX_CPU 4 +struct cpu_clk { + struct clk_hw hw; + int cpu; + const char *clk_name; + const char *parent_name; + void __iomem *reg_base; +}; + +static struct clk **clks; + +static struct clk_onecell_data clk_data; + +#define to_cpu_clk(p) container_of(p, struct cpu_clk, hw) + +static unsigned long clk_cpu_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct cpu_clk *cpuclk = to_cpu_clk(hwclk); + u32 reg, div; + + reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET); + div = (reg >> (cpuclk->cpu * 8)) & SYS_CTRL_CLK_DIVIDER_MASK; + return parent_rate / div; +} + +static long clk_cpu_round_rate(struct clk_hw *hwclk, unsigned long rate, + unsigned long *parent_rate) +{ + /* Valid ratio are 1:1, 1:2 and 1:3 */ + u32 div; + + div = *parent_rate / rate; + if (div == 0) + div = 1; + else if (div > 3) + div = 3; + + return *parent_rate / div; +} + +static int clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate, + unsigned long parent_rate) +{ + struct cpu_clk *cpuclk = to_cpu_clk(hwclk); + u32 reg, div; + u32 reload_mask; + + div = parent_rate / rate; + reg = (readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET) + & (~(SYS_CTRL_CLK_DIVIDER_MASK << (cpuclk->cpu * 8)))) + | (div << (cpuclk->cpu * 8)); + writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET); + /* Set clock divider reload smooth bit mask */ + reload_mask = 1 << (20 + cpuclk->cpu); + + reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET) + | reload_mask; + writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET); + + /* Now trigger the clock update */ + reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET) + | 1 << 24; + writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET); + + /* Wait for clocks to settle down then clear reload request */ + udelay(1000); + reg &= ~(reload_mask | 1 << 24); + writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET); + udelay(1000); + + return 0; +} + +static const struct clk_ops cpu_ops = { + .recalc_rate = clk_cpu_recalc_rate, + .round_rate = clk_cpu_round_rate, + .set_rate = clk_cpu_set_rate, +}; + +void __init of_cpu_clk_setup(struct device_node *node) +{ + struct cpu_clk *cpuclk; + void __iomem *clock_complex_base = of_iomap(node, 0); + int ncpus = 0; + struct device_node *dn; + + if (clock_complex_base == NULL) { + pr_err("%s: clock-complex base register not set\n", + __func__); + return; + } + + for_each_node_by_type(dn, "cpu") + ncpus++; + + cpuclk = kzalloc(ncpus * sizeof(*cpuclk), GFP_KERNEL); + if (WARN_ON(!cpuclk)) + return; + + clks = kzalloc(ncpus * sizeof(*clks), GFP_KERNEL); + if (WARN_ON(!clks)) + return; + + for_each_node_by_type(dn, "cpu") { + struct clk_init_data init; + struct clk *clk; + struct clk *parent_clk; + char *clk_name = kzalloc(5, GFP_KERNEL); + int cpu, err; + + if (WARN_ON(!clk_name)) + return; + + err = of_property_read_u32(dn, "reg", &cpu); + if (WARN_ON(err)) + return; + + sprintf(clk_name, "cpu%d", cpu); + parent_clk = of_clk_get(node, 0); + + cpuclk[cpu].parent_name = __clk_get_name(parent_clk); + cpuclk[cpu].clk_name = clk_name; + cpuclk[cpu].cpu = cpu; + cpuclk[cpu].reg_base = clock_complex_base; + cpuclk[cpu].hw.init = &init; + + init.name = cpuclk[cpu].clk_name; + init.ops = &cpu_ops; + init.flags = 0; + init.parent_names = &cpuclk[cpu].parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &cpuclk[cpu].hw); + if (WARN_ON(IS_ERR(clk))) + goto bail_out; + clks[cpu] = clk; + } + clk_data.clk_num = MAX_CPU; + clk_data.clks = clks; + of_clk_add_provider(node, of_clk_src_onecell_get, &clk_data); + + return; +bail_out: + kfree(clks); + kfree(cpuclk); +} + +static const __initconst struct of_device_id clk_cpu_match[] = { + { + .compatible = "marvell,armada-xp-cpu-clock", + .data = of_cpu_clk_setup, + }, + { + /* sentinel */ + }, +}; + +void __init mvebu_cpu_clk_init(void) +{ + of_clk_init(clk_cpu_match); +} diff --git a/drivers/clk/mvebu/clk-cpu.h b/drivers/clk/mvebu/clk-cpu.h new file mode 100644 index 000000000000..08e2affba4e6 --- /dev/null +++ b/drivers/clk/mvebu/clk-cpu.h @@ -0,0 +1,22 @@ +/* + * Marvell MVEBU CPU clock handling. + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __MVEBU_CLK_CPU_H +#define __MVEBU_CLK_CPU_H + +#ifdef CONFIG_MVEBU_CLK_CPU +void __init mvebu_cpu_clk_init(void); +#else +static inline void mvebu_cpu_clk_init(void) {} +#endif + +#endif diff --git a/drivers/clk/mvebu/clk.c b/drivers/clk/mvebu/clk.c index e6742acf9880..e3d4d7a9de6a 100644 --- a/drivers/clk/mvebu/clk.c +++ b/drivers/clk/mvebu/clk.c @@ -16,8 +16,10 @@ #include #include #include "clk-core.h" +#include "clk-cpu.h" void __init mvebu_clocks_init(void) { mvebu_core_clk_init(); + mvebu_cpu_clk_init(); } -- cgit v1.2.3 From f97d0d7aa8f8cec29a24d65afa12a777c6d2a2f1 Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Sat, 17 Nov 2012 15:22:26 +0100 Subject: clk: mvebu: add clock gating control provider for DT This driver allows to provide DT clocks for clock gates found on Marvell Dove and Kirkwood SoCs. The clock gates are referenced by the phandle index of the corresponding bit in the clock gating control register to ease lookup in the datasheet. Signed-off-by: Sebastian Hesselbarth --- .../bindings/clock/mvebu-gated-clock.txt | 76 +++++++++ drivers/clk/mvebu/Kconfig | 2 + drivers/clk/mvebu/Makefile | 1 + drivers/clk/mvebu/clk-gating-ctrl.c | 177 +++++++++++++++++++++ drivers/clk/mvebu/clk-gating-ctrl.h | 22 +++ drivers/clk/mvebu/clk.c | 2 + 6 files changed, 280 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt create mode 100644 drivers/clk/mvebu/clk-gating-ctrl.c create mode 100644 drivers/clk/mvebu/clk-gating-ctrl.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt new file mode 100644 index 000000000000..4ad8ccd15e67 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt @@ -0,0 +1,76 @@ +* Gated Clock bindings for Marvell Orion SoCs + +Marvell Dove and Kirkwood allow some peripheral clocks to be gated to save +some power. The clock consumer should specify the desired clock by having +the clock ID in its "clocks" phandle cell. The clock ID is directly mapped to +the corresponding clock gating control bit in HW to ease manual clock lookup +in datasheet. + +The following is a list of provided IDs for Dove: +ID Clock Peripheral +----------------------------------- +0 usb0 USB Host 0 +1 usb1 USB Host 1 +2 ge Gigabit Ethernet +3 sata SATA Host +4 pex0 PCIe Cntrl 0 +5 pex1 PCIe Cntrl 1 +8 sdio0 SDHCI Host 0 +9 sdio1 SDHCI Host 1 +10 nand NAND Cntrl +11 camera Camera Cntrl +12 i2s0 I2S Cntrl 0 +13 i2s1 I2S Cntrl 1 +15 crypto CESA engine +21 ac97 AC97 Cntrl +22 pdma Peripheral DMA +23 xor0 XOR DMA 0 +24 xor1 XOR DMA 1 +30 gephy Gigabit Ethernel PHY +Note: gephy(30) is implemented as a parent clock of ge(2) + +The following is a list of provided IDs for Kirkwood: +ID Clock Peripheral +----------------------------------- +0 ge0 Gigabit Ethernet 0 +2 pex0 PCIe Cntrl 0 +3 usb0 USB Host 0 +4 sdio SDIO Cntrl +5 tsu Transp. Stream Unit +6 dunit SDRAM Cntrl +7 runit Runit +8 xor0 XOR DMA 0 +9 audio I2S Cntrl 0 +14 sata0 SATA Host 0 +15 sata1 SATA Host 1 +16 xor1 XOR DMA 1 +17 crypto CESA engine +18 pex1 PCIe Cntrl 1 +19 ge1 Gigabit Ethernet 0 +20 tdm Time Division Mplx + +Required properties: +- compatible : shall be one of the following: + "marvell,dove-gating-clock" - for Dove SoC clock gating + "marvell,kirkwood-gating-clock" - for Kirkwood SoC clock gating +- reg : shall be the register address of the Clock Gating Control register +- #clock-cells : from common clock binding; shall be set to 1 + +Optional properties: +- clocks : default parent clock phandle (e.g. tclk) + +Example: + +gate_clk: clock-gating-control@d0038 { + compatible = "marvell,dove-gating-clock"; + reg = <0xd0038 0x4>; + /* default parent clock is tclk */ + clocks = <&core_clk 0>; + #clock-cells = <1>; +}; + +sdio0: sdio@92000 { + compatible = "marvell,dove-sdhci"; + /* get clk gate bit 8 (sdio0) */ + clocks = <&gate_clk 8>; +}; diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig index 1dd93ada9cb3..57323fd15ec9 100644 --- a/drivers/clk/mvebu/Kconfig +++ b/drivers/clk/mvebu/Kconfig @@ -4,3 +4,5 @@ config MVEBU_CLK_CORE config MVEBU_CLK_CPU bool +config MVEBU_CLK_GATING + bool diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile index 93da083b77e3..58df3dc49363 100644 --- a/drivers/clk/mvebu/Makefile +++ b/drivers/clk/mvebu/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_MVEBU_CLK_CORE) += clk.o clk-core.o obj-$(CONFIG_MVEBU_CLK_CPU) += clk-cpu.o +obj-$(CONFIG_MVEBU_CLK_GATING) += clk-gating-ctrl.o diff --git a/drivers/clk/mvebu/clk-gating-ctrl.c b/drivers/clk/mvebu/clk-gating-ctrl.c new file mode 100644 index 000000000000..fa69f87c5797 --- /dev/null +++ b/drivers/clk/mvebu/clk-gating-ctrl.c @@ -0,0 +1,177 @@ +/* + * Marvell MVEBU clock gating control. + * + * Sebastian Hesselbarth + * Andrew Lunn + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct mvebu_gating_ctrl { + spinlock_t lock; + struct clk **gates; + int num_gates; +}; + +struct mvebu_soc_descr { + const char *name; + const char *parent; + int bit_idx; +}; + +#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) + +static struct clk __init *mvebu_clk_gating_get_src( + struct of_phandle_args *clkspec, void *data) +{ + struct mvebu_gating_ctrl *ctrl = (struct mvebu_gating_ctrl *)data; + int n; + + if (clkspec->args_count < 1) + return ERR_PTR(-EINVAL); + + for (n = 0; n < ctrl->num_gates; n++) { + struct clk_gate *gate = + to_clk_gate(__clk_get_hw(ctrl->gates[n])); + if (clkspec->args[0] == gate->bit_idx) + return ctrl->gates[n]; + } + return ERR_PTR(-ENODEV); +} + +static void __init mvebu_clk_gating_setup( + struct device_node *np, const struct mvebu_soc_descr *descr) +{ + struct mvebu_gating_ctrl *ctrl; + struct clk *clk; + void __iomem *base; + const char *default_parent = NULL; + int n; + + base = of_iomap(np, 0); + + clk = of_clk_get(np, 0); + if (!IS_ERR(clk)) { + default_parent = __clk_get_name(clk); + clk_put(clk); + } + + ctrl = kzalloc(sizeof(struct mvebu_gating_ctrl), GFP_KERNEL); + if (WARN_ON(!ctrl)) + return; + + spin_lock_init(&ctrl->lock); + + /* + * Count, allocate, and register clock gates + */ + for (n = 0; descr[n].name;) + n++; + + ctrl->num_gates = n; + ctrl->gates = kzalloc(ctrl->num_gates * sizeof(struct clk *), + GFP_KERNEL); + if (WARN_ON(!ctrl->gates)) { + kfree(ctrl); + return; + } + + for (n = 0; n < ctrl->num_gates; n++) { + const char *parent = + (descr[n].parent) ? descr[n].parent : default_parent; + ctrl->gates[n] = clk_register_gate(NULL, descr[n].name, parent, + 0, base, descr[n].bit_idx, 0, &ctrl->lock); + WARN_ON(IS_ERR(ctrl->gates[n])); + } + of_clk_add_provider(np, mvebu_clk_gating_get_src, ctrl); +} + +/* + * SoC specific clock gating control + */ + +#ifdef CONFIG_ARCH_DOVE +static const struct mvebu_soc_descr __initconst dove_gating_descr[] = { + { "usb0", NULL, 0 }, + { "usb1", NULL, 1 }, + { "ge", "gephy", 2 }, + { "sata", NULL, 3 }, + { "pex0", NULL, 4 }, + { "pex1", NULL, 5 }, + { "sdio0", NULL, 8 }, + { "sdio1", NULL, 9 }, + { "nand", NULL, 10 }, + { "camera", NULL, 11 }, + { "i2s0", NULL, 12 }, + { "i2s1", NULL, 13 }, + { "crypto", NULL, 15 }, + { "ac97", NULL, 21 }, + { "pdma", NULL, 22 }, + { "xor0", NULL, 23 }, + { "xor1", NULL, 24 }, + { "gephy", NULL, 30 }, + { } +}; +#endif + +#ifdef CONFIG_ARCH_KIRKWOOD +static const struct mvebu_soc_descr __initconst kirkwood_gating_descr[] = { + { "ge0", NULL, 0 }, + { "pex0", NULL, 2 }, + { "usb0", NULL, 3 }, + { "sdio", NULL, 4 }, + { "tsu", NULL, 5 }, + { "runit", NULL, 7 }, + { "xor0", NULL, 8 }, + { "audio", NULL, 9 }, + { "sata0", NULL, 14 }, + { "sata1", NULL, 15 }, + { "xor1", NULL, 16 }, + { "crypto", NULL, 17 }, + { "pex1", NULL, 18 }, + { "ge1", NULL, 19 }, + { "tdm", NULL, 20 }, + { } +}; +#endif + +static const __initdata struct of_device_id clk_gating_match[] = { +#ifdef CONFIG_ARCH_DOVE + { + .compatible = "marvell,dove-gating-clock", + .data = dove_gating_descr, + }, +#endif + +#ifdef CONFIG_ARCH_KIRKWOOD + { + .compatible = "marvell,kirkwood-gating-clock", + .data = kirkwood_gating_descr, + }, +#endif + + { } +}; + +void __init mvebu_gating_clk_init(void) +{ + struct device_node *np; + + for_each_matching_node(np, clk_gating_match) { + const struct of_device_id *match = + of_match_node(clk_gating_match, np); + mvebu_clk_gating_setup(np, + (const struct mvebu_soc_descr *)match->data); + } +} diff --git a/drivers/clk/mvebu/clk-gating-ctrl.h b/drivers/clk/mvebu/clk-gating-ctrl.h new file mode 100644 index 000000000000..9275d1e51f1b --- /dev/null +++ b/drivers/clk/mvebu/clk-gating-ctrl.h @@ -0,0 +1,22 @@ +/* + * Marvell EBU gating clock handling + * + * Copyright (C) 2012 Marvell + * + * Thomas Petazzoni + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __MVEBU_CLK_GATING_H +#define __MVEBU_CLK_GATING_H + +#ifdef CONFIG_MVEBU_CLK_GATING +void __init mvebu_gating_clk_init(void); +#else +void mvebu_gating_clk_init(void) {} +#endif + +#endif diff --git a/drivers/clk/mvebu/clk.c b/drivers/clk/mvebu/clk.c index e3d4d7a9de6a..855681b8a9dc 100644 --- a/drivers/clk/mvebu/clk.c +++ b/drivers/clk/mvebu/clk.c @@ -17,9 +17,11 @@ #include #include "clk-core.h" #include "clk-cpu.h" +#include "clk-gating-ctrl.h" void __init mvebu_clocks_init(void) { mvebu_core_clk_init(); + mvebu_gating_clk_init(); mvebu_cpu_clk_init(); } -- cgit v1.2.3 From c4c34d608482b48c1c007fecea5a7a5c65168fa2 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Sat, 17 Nov 2012 15:22:29 +0100 Subject: clk: mvebu: armada 370/XP add clock gating control provider for DT Signed-off-by: Gregory CLEMENT Signed-off-by: Andrew Lunn Signed-off-by: Thomas Petazzoni --- .../bindings/clock/mvebu-gated-clock.txt | 43 +++++++++++++ drivers/clk/mvebu/clk-gating-ctrl.c | 74 +++++++++++++++++++++- 2 files changed, 116 insertions(+), 1 deletion(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt index 4ad8ccd15e67..7337005ef5e1 100644 --- a/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt +++ b/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt @@ -6,6 +6,49 @@ the clock ID in its "clocks" phandle cell. The clock ID is directly mapped to the corresponding clock gating control bit in HW to ease manual clock lookup in datasheet. +The following is a list of provided IDs for Armada 370: +ID Clock Peripheral +----------------------------------- +0 Audio AC97 Cntrl +1 pex0_en PCIe 0 Clock out +2 pex1_en PCIe 1 Clock out +3 ge1 Gigabit Ethernet 1 +4 ge0 Gigabit Ethernet 0 +5 pex0 PCIe Cntrl 0 +9 pex1 PCIe Cntrl 1 +15 sata0 SATA Host 0 +17 sdio SDHCI Host +25 tdm Time Division Mplx +28 ddr DDR Cntrl +30 sata1 SATA Host 0 + +The following is a list of provided IDs for Armada XP: +ID Clock Peripheral +----------------------------------- +0 audio Audio Cntrl +1 ge3 Gigabit Ethernet 3 +2 ge2 Gigabit Ethernet 2 +3 ge1 Gigabit Ethernet 1 +4 ge0 Gigabit Ethernet 0 +5 pex0 PCIe Cntrl 0 +6 pex1 PCIe Cntrl 1 +7 pex2 PCIe Cntrl 2 +8 pex3 PCIe Cntrl 3 +13 bp +14 sata0lnk +15 sata0 SATA Host 0 +16 lcd LCD Cntrl +17 sdio SDHCI Host +18 usb0 USB Host 0 +19 usb1 USB Host 1 +20 usb2 USB Host 2 +22 xor0 XOR DMA 0 +23 crypto CESA engine +25 tdm Time Division Mplx +28 xor1 XOR DMA 1 +29 sata1lnk +30 sata1 SATA Host 0 + The following is a list of provided IDs for Dove: ID Clock Peripheral ----------------------------------- diff --git a/drivers/clk/mvebu/clk-gating-ctrl.c b/drivers/clk/mvebu/clk-gating-ctrl.c index fa69f87c5797..c6d3c263b070 100644 --- a/drivers/clk/mvebu/clk-gating-ctrl.c +++ b/drivers/clk/mvebu/clk-gating-ctrl.c @@ -88,10 +88,21 @@ static void __init mvebu_clk_gating_setup( } for (n = 0; n < ctrl->num_gates; n++) { + u8 flags = 0; const char *parent = (descr[n].parent) ? descr[n].parent : default_parent; + + /* + * On Armada 370, the DDR clock is a special case: it + * isn't taken by any driver, but should anyway be + * kept enabled, so we mark it as IGNORE_UNUSED for + * now. + */ + if (!strcmp(descr[n].name, "ddr")) + flags |= CLK_IGNORE_UNUSED; + ctrl->gates[n] = clk_register_gate(NULL, descr[n].name, parent, - 0, base, descr[n].bit_idx, 0, &ctrl->lock); + flags, base, descr[n].bit_idx, 0, &ctrl->lock); WARN_ON(IS_ERR(ctrl->gates[n])); } of_clk_add_provider(np, mvebu_clk_gating_get_src, ctrl); @@ -101,6 +112,53 @@ static void __init mvebu_clk_gating_setup( * SoC specific clock gating control */ +#ifdef CONFIG_MACH_ARMADA_370 +static const struct mvebu_soc_descr __initconst armada_370_gating_descr[] = { + { "audio", NULL, 0 }, + { "pex0_en", NULL, 1 }, + { "pex1_en", NULL, 2 }, + { "ge1", NULL, 3 }, + { "ge0", NULL, 4 }, + { "pex0", NULL, 5 }, + { "pex1", NULL, 9 }, + { "sata0", NULL, 15 }, + { "sdio", NULL, 17 }, + { "tdm", NULL, 25 }, + { "ddr", NULL, 28 }, + { "sata1", NULL, 30 }, + { } +}; +#endif + +#ifdef CONFIG_MACH_ARMADA_XP +static const struct mvebu_soc_descr __initconst armada_xp_gating_descr[] = { + { "audio", NULL, 0 }, + { "ge3", NULL, 1 }, + { "ge2", NULL, 2 }, + { "ge1", NULL, 3 }, + { "ge0", NULL, 4 }, + { "pex0", NULL, 5 }, + { "pex1", NULL, 6 }, + { "pex2", NULL, 7 }, + { "pex3", NULL, 8 }, + { "bp", NULL, 13 }, + { "sata0lnk", NULL, 14 }, + { "sata0", "sata0lnk", 15 }, + { "lcd", NULL, 16 }, + { "sdio", NULL, 17 }, + { "usb0", NULL, 18 }, + { "usb1", NULL, 19 }, + { "usb2", NULL, 20 }, + { "xor0", NULL, 22 }, + { "crypto", NULL, 23 }, + { "tdm", NULL, 25 }, + { "xor1", NULL, 28 }, + { "sata1lnk", NULL, 29 }, + { "sata1", "sata1lnk", 30 }, + { } +}; +#endif + #ifdef CONFIG_ARCH_DOVE static const struct mvebu_soc_descr __initconst dove_gating_descr[] = { { "usb0", NULL, 0 }, @@ -147,6 +205,20 @@ static const struct mvebu_soc_descr __initconst kirkwood_gating_descr[] = { #endif static const __initdata struct of_device_id clk_gating_match[] = { +#ifdef CONFIG_MACH_ARMADA_370 + { + .compatible = "marvell,armada-370-gating-clock", + .data = armada_370_gating_descr, + }, +#endif + +#ifdef CONFIG_MACH_ARMADA_XP + { + .compatible = "marvell,armada-xp-gating-clock", + .data = armada_xp_gating_descr, + }, +#endif + #ifdef CONFIG_ARCH_DOVE { .compatible = "marvell,dove-gating-clock", -- cgit v1.2.3 From 307c2bf467e3682c6df1b8186365224fd2d581d3 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Sat, 17 Nov 2012 15:22:25 +0100 Subject: clocksource: convert time-armada-370-xp to clk framework Signed-off-by: Gregory CLEMENT Tested-by Gregory CLEMENT --- Documentation/devicetree/bindings/arm/armada-370-xp-timer.txt | 1 + arch/arm/boot/dts/armada-370-db.dts | 4 ---- arch/arm/boot/dts/armada-370-xp.dtsi | 1 + drivers/clocksource/time-armada-370-xp.c | 11 ++++++----- 4 files changed, 8 insertions(+), 9 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/armada-370-xp-timer.txt b/Documentation/devicetree/bindings/arm/armada-370-xp-timer.txt index 8b6ea2267c94..64830118b013 100644 --- a/Documentation/devicetree/bindings/arm/armada-370-xp-timer.txt +++ b/Documentation/devicetree/bindings/arm/armada-370-xp-timer.txt @@ -5,6 +5,7 @@ Required properties: - compatible: Should be "marvell,armada-370-xp-timer" - interrupts: Should contain the list of Global Timer interrupts - reg: Should contain the base address of the Global Timer registers +- clocks: clock driving the timer hardware Optional properties: - marvell,timer-25Mhz: Tells whether the Global timer supports the 25 diff --git a/arch/arm/boot/dts/armada-370-db.dts b/arch/arm/boot/dts/armada-370-db.dts index fffd5c2a3041..4a31b0396623 100644 --- a/arch/arm/boot/dts/armada-370-db.dts +++ b/arch/arm/boot/dts/armada-370-db.dts @@ -34,9 +34,5 @@ clock-frequency = <200000000>; status = "okay"; }; - timer@d0020300 { - clock-frequency = <600000000>; - status = "okay"; - }; }; }; diff --git a/arch/arm/boot/dts/armada-370-xp.dtsi b/arch/arm/boot/dts/armada-370-xp.dtsi index 16cc82cdaa81..94b4b9e03571 100644 --- a/arch/arm/boot/dts/armada-370-xp.dtsi +++ b/arch/arm/boot/dts/armada-370-xp.dtsi @@ -62,6 +62,7 @@ compatible = "marvell,armada-370-xp-timer"; reg = <0xd0020300 0x30>; interrupts = <37>, <38>, <39>, <40>; + clocks = <&coreclk 2>; }; addr-decoding@d0020000 { diff --git a/drivers/clocksource/time-armada-370-xp.c b/drivers/clocksource/time-armada-370-xp.c index 4674f94957cd..a4605fd7e303 100644 --- a/drivers/clocksource/time-armada-370-xp.c +++ b/drivers/clocksource/time-armada-370-xp.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -167,7 +168,6 @@ void __init armada_370_xp_timer_init(void) u32 u; struct device_node *np; unsigned int timer_clk; - int ret; np = of_find_compatible_node(NULL, NULL, "marvell,armada-370-xp-timer"); timer_base = of_iomap(np, 0); WARN_ON(!timer_base); @@ -179,13 +179,14 @@ void __init armada_370_xp_timer_init(void) timer_base + TIMER_CTRL_OFF); timer_clk = 25000000; } else { - u32 clk = 0; - ret = of_property_read_u32(np, "clock-frequency", &clk); - WARN_ON(!clk || ret < 0); + unsigned long rate = 0; + struct clk *clk = of_clk_get(np, 0); + WARN_ON(IS_ERR(clk)); + rate = clk_get_rate(clk); u = readl(timer_base + TIMER_CTRL_OFF); writel(u & ~(TIMER0_25MHZ | TIMER1_25MHZ), timer_base + TIMER_CTRL_OFF); - timer_clk = clk / TIMER_DIVIDER; + timer_clk = rate / TIMER_DIVIDER; } /* We use timer 0 as clocksource, and timer 1 for -- cgit v1.2.3 From f7d12ef53ddfd8175933af42bfc477376d19e19e Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 15 Nov 2012 16:47:58 +0100 Subject: dma: mv_xor: add Device Tree binding This patch finally adds a Device Tree binding to the mv_xor driver. Thanks to the previous cleanup patches, the Device Tree binding is relatively simply: one DT node per XOR engine, with sub-nodes for each XOR channel of the XOR engine. The binding obviously comes with the necessary documentation. Signed-off-by: Thomas Petazzoni Cc: devicetree-discuss@lists.ozlabs.org --- Documentation/devicetree/bindings/dma/mv-xor.txt | 40 ++++++++++++++++ drivers/dma/mv_xor.c | 58 ++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 Documentation/devicetree/bindings/dma/mv-xor.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/dma/mv-xor.txt b/Documentation/devicetree/bindings/dma/mv-xor.txt new file mode 100644 index 000000000000..7c6cb7fcecd2 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/mv-xor.txt @@ -0,0 +1,40 @@ +* Marvell XOR engines + +Required properties: +- compatible: Should be "marvell,orion-xor" +- reg: Should contain registers location and length (two sets) + the first set is the low registers, the second set the high + registers for the XOR engine. +- clocks: pointer to the reference clock + +The DT node must also contains sub-nodes for each XOR channel that the +XOR engine has. Those sub-nodes have the following required +properties: +- interrupts: interrupt of the XOR channel + +And the following optional properties: +- dmacap,memcpy to indicate that the XOR channel is capable of memcpy operations +- dmacap,memset to indicate that the XOR channel is capable of memset operations +- dmacap,xor to indicate that the XOR channel is capable of xor operations + +Example: + +xor@d0060900 { + compatible = "marvell,orion-xor"; + reg = <0xd0060900 0x100 + 0xd0060b00 0x100>; + clocks = <&coreclk 0>; + status = "okay"; + + xor00 { + interrupts = <51>; + dmacap,memcpy; + dmacap,xor; + }; + xor01 { + interrupts = <52>; + dmacap,memcpy; + dmacap,xor; + dmacap,memset; + }; +}; diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index d48245c0b0b0..b1c4edd56ebc 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #include #include "dmaengine.h" @@ -1278,7 +1281,42 @@ static int mv_xor_probe(struct platform_device *pdev) if (!IS_ERR(xordev->clk)) clk_prepare_enable(xordev->clk); - if (pdata && pdata->channels) { + if (pdev->dev.of_node) { + struct device_node *np; + int i = 0; + + for_each_child_of_node(pdev->dev.of_node, np) { + dma_cap_mask_t cap_mask; + int irq; + + dma_cap_zero(cap_mask); + if (of_property_read_bool(np, "dmacap,memcpy")) + dma_cap_set(DMA_MEMCPY, cap_mask); + if (of_property_read_bool(np, "dmacap,xor")) + dma_cap_set(DMA_XOR, cap_mask); + if (of_property_read_bool(np, "dmacap,memset")) + dma_cap_set(DMA_MEMSET, cap_mask); + if (of_property_read_bool(np, "dmacap,interrupt")) + dma_cap_set(DMA_INTERRUPT, cap_mask); + + irq = irq_of_parse_and_map(np, 0); + if (irq < 0) { + ret = irq; + goto err_channel_add; + } + + xordev->channels[i] = + mv_xor_channel_add(xordev, pdev, i, + cap_mask, irq); + if (IS_ERR(xordev->channels[i])) { + ret = PTR_ERR(xordev->channels[i]); + irq_dispose_mapping(irq); + goto err_channel_add; + } + + i++; + } + } else if (pdata && pdata->channels) { for (i = 0; i < MV_XOR_MAX_CHANNELS; i++) { struct mv_xor_channel_data *cd; int irq; @@ -1309,8 +1347,11 @@ static int mv_xor_probe(struct platform_device *pdev) err_channel_add: for (i = 0; i < MV_XOR_MAX_CHANNELS; i++) - if (xordev->channels[i]) + if (xordev->channels[i]) { + if (pdev->dev.of_node) + irq_dispose_mapping(xordev->channels[i]->irq); mv_xor_channel_remove(xordev->channels[i]); + } clk_disable_unprepare(xordev->clk); clk_put(xordev->clk); @@ -1335,12 +1376,21 @@ static int mv_xor_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static struct of_device_id mv_xor_dt_ids[] __devinitdata = { + { .compatible = "marvell,orion-xor", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mv_xor_dt_ids); +#endif + static struct platform_driver mv_xor_driver = { .probe = mv_xor_probe, .remove = mv_xor_remove, .driver = { - .owner = THIS_MODULE, - .name = MV_XOR_NAME, + .owner = THIS_MODULE, + .name = MV_XOR_NAME, + .of_match_table = of_match_ptr(mv_xor_dt_ids), }, }; -- cgit v1.2.3 From 189dd62642c9819005cf37d3a9e441d203112bd2 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 19 Nov 2012 14:15:25 +0100 Subject: net: mvneta: add clk support Now that the Armada 370/XP platform has gained proper integration with the clock framework, we add clk support in the Marvell Armada 370/XP Ethernet driver. Since the existing Device Tree binding that exposes a 'clock-frequency' property has never been exposed in any stable kernel release, we take the freedom of removing this property to replace it with the standard 'clocks' clock pointer property. The Device Tree binding documentation is updated accordingly. Signed-off-by: Thomas Petazzoni --- .../bindings/net/marvell-armada-370-neta.txt | 4 +-- drivers/net/ethernet/marvell/mvneta.c | 31 ++++++++++++++-------- 2 files changed, 22 insertions(+), 13 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt index c4e87f0e450e..859a6fa7569c 100644 --- a/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt +++ b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt @@ -8,7 +8,7 @@ Required properties: property, a single integer). - phy-mode: The interface between the SoC and the PHY (a string that of_get_phy_mode() can understand) -- clock-frequency: frequency of the peripheral clock of the SoC. +- clocks: a pointer to the reference clock for this device. Example: @@ -16,7 +16,7 @@ ethernet@d0070000 { compatible = "marvell,armada-370-neta"; reg = <0xd0070000 0x2500>; interrupts = <8>; - clock-frequency = <250000000>; + clocks = <&gate_clk 4>; status = "okay"; phy = <&phy0>; phy-mode = "rgmii-id"; diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index d9dadee6ab79..17b0a4198c88 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -29,6 +29,7 @@ #include #include #include +#include /* Registers */ #define MVNETA_RXQ_CONFIG_REG(q) (0x1400 + ((q) << 2)) @@ -242,7 +243,7 @@ struct mvneta_port { int weight; /* Core clock */ - unsigned int clk_rate_hz; + struct clk *clk; u8 mcast_count[256]; u16 tx_ring_size; u16 rx_ring_size; @@ -1029,7 +1030,11 @@ static void mvneta_rx_pkts_coal_set(struct mvneta_port *pp, static void mvneta_rx_time_coal_set(struct mvneta_port *pp, struct mvneta_rx_queue *rxq, u32 value) { - u32 val = (pp->clk_rate_hz / 1000000) * value; + u32 val; + unsigned long clk_rate; + + clk_rate = clk_get_rate(pp->clk); + val = (clk_rate / 1000000) * value; mvreg_write(pp, MVNETA_RXQ_TIME_COAL_REG(rxq->id), val); rxq->time_coal = value; @@ -2671,7 +2676,7 @@ static int __devinit mvneta_probe(struct platform_device *pdev) const struct mbus_dram_target_info *dram_target_info; struct device_node *dn = pdev->dev.of_node; struct device_node *phy_node; - u32 phy_addr, clk_rate_hz; + u32 phy_addr; struct mvneta_port *pp; struct net_device *dev; const char *mac_addr; @@ -2710,12 +2715,6 @@ static int __devinit mvneta_probe(struct platform_device *pdev) goto err_free_irq; } - if (of_property_read_u32(dn, "clock-frequency", &clk_rate_hz) != 0) { - dev_err(&pdev->dev, "could not read clock-frequency\n"); - err = -EINVAL; - goto err_free_irq; - } - mac_addr = of_get_mac_address(dn); if (!mac_addr || !is_valid_ether_addr(mac_addr)) @@ -2736,7 +2735,6 @@ static int __devinit mvneta_probe(struct platform_device *pdev) clear_bit(MVNETA_F_TX_DONE_TIMER_BIT, &pp->flags); pp->weight = MVNETA_RX_POLL_WEIGHT; - pp->clk_rate_hz = clk_rate_hz; pp->phy_node = phy_node; pp->phy_interface = phy_mode; @@ -2746,6 +2744,14 @@ static int __devinit mvneta_probe(struct platform_device *pdev) goto err_free_irq; } + pp->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pp->clk)) { + err = PTR_ERR(pp->clk); + goto err_unmap; + } + + clk_prepare_enable(pp->clk); + pp->tx_done_timer.data = (unsigned long)dev; pp->tx_ring_size = MVNETA_MAX_TXD; @@ -2757,7 +2763,7 @@ static int __devinit mvneta_probe(struct platform_device *pdev) err = mvneta_init(pp, phy_addr); if (err < 0) { dev_err(&pdev->dev, "can't init eth hal\n"); - goto err_unmap; + goto err_clk; } mvneta_port_power_up(pp, phy_mode); @@ -2785,6 +2791,8 @@ static int __devinit mvneta_probe(struct platform_device *pdev) err_deinit: mvneta_deinit(pp); +err_clk: + clk_disable_unprepare(pp->clk); err_unmap: iounmap(pp->base); err_free_irq: @@ -2802,6 +2810,7 @@ static int __devexit mvneta_remove(struct platform_device *pdev) unregister_netdev(dev); mvneta_deinit(pp); + clk_disable_unprepare(pp->clk); iounmap(pp->base); irq_dispose_mapping(dev->irq); free_netdev(dev); -- cgit v1.2.3 From 6872a3173501f16aee5ce160359d1bb513d06a59 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 13 Nov 2012 14:12:05 +0000 Subject: of: Add vendor prefix for Synopsys Inc. This patch adds a device tree vendor prefix for Synopsys Inc. Signed-off-by: Srinivas Kandagatla Signed-off-by: Rob Herring --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index ff41e4019010..bea1e9a0388e 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -46,6 +46,7 @@ schindler Schindler sil Silicon Image simtek sirf SiRF Technology, Inc. +snps Synopsys, Inc. st STMicroelectronics stericsson ST-Ericsson ti Texas Instruments -- cgit v1.2.3 From ae8c4209af2cec065fef15d200a42a04130799f7 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 19 Nov 2012 15:34:43 -0700 Subject: of: Add vendor prefix for Asahi Kasei Corp. Their stock ticker is 3407.T which wouldn't make a good DT vendor prefix. Use the company name initials instead. Signed-off-by: Stephen Warren Signed-off-by: Rob Herring --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index bea1e9a0388e..98271503f006 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -5,6 +5,7 @@ using them to avoid name-space collisions. ad Avionic Design GmbH adi Analog Devices, Inc. +ak Asahi Kasei Corp. amcc Applied Micro Circuits Corporation (APM, formally AMCC) apm Applied Micro Circuits Corporation (APM) arm ARM Ltd. -- cgit v1.2.3 From 67bea88dd842deb3ed8327ad101970dbd615ddcb Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 19 Nov 2012 18:57:08 +0100 Subject: ARM: sunxi: Add sunxi restart function via onchip watchdog Signed-off-by: Stefan Roese Signed-off-by: Maxime Ripard --- .../devicetree/bindings/watchdog/sunxi-wdt.txt | 13 +++++++++ arch/arm/boot/dts/sunxi.dtsi | 7 ++++- arch/arm/mach-sunxi/sunxi.c | 34 ++++++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/watchdog/sunxi-wdt.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/watchdog/sunxi-wdt.txt b/Documentation/devicetree/bindings/watchdog/sunxi-wdt.txt new file mode 100644 index 000000000000..0b2717775600 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/sunxi-wdt.txt @@ -0,0 +1,13 @@ +Allwinner sunXi Watchdog timer + +Required properties: + +- compatible : should be "allwinner,sunxi-wdt" +- reg : Specifies base physical address and size of the registers. + +Example: + +wdt: watchdog@01c20c90 { + compatible = "allwinner,sunxi-wdt"; + reg = <0x01c20c90 0x10>; +}; diff --git a/arch/arm/boot/dts/sunxi.dtsi b/arch/arm/boot/dts/sunxi.dtsi index 8284fbd7ddab..8bbc2bfef221 100644 --- a/arch/arm/boot/dts/sunxi.dtsi +++ b/arch/arm/boot/dts/sunxi.dtsi @@ -42,11 +42,16 @@ timer@01c20c00 { compatible = "allwinner,sunxi-timer"; - reg = <0x01c20c00 0x400>; + reg = <0x01c20c00 0x90>; interrupts = <22>; clocks = <&osc>; }; + wdt: watchdog@01c20c90 { + compatible = "allwinner,sunxi-wdt"; + reg = <0x01c20c90 0x10>; + }; + intc: interrupt-controller@01c20400 { compatible = "allwinner,sunxi-ic"; reg = <0x01c20400 0x400>; diff --git a/arch/arm/mach-sunxi/sunxi.c b/arch/arm/mach-sunxi/sunxi.c index 13d4d96cbc85..dc634ae2081f 100644 --- a/arch/arm/mach-sunxi/sunxi.c +++ b/arch/arm/mach-sunxi/sunxi.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -26,6 +27,36 @@ #include "sunxi.h" +#define WATCHDOG_CTRL_REG 0x00 +#define WATCHDOG_MODE_REG 0x04 + +static void __iomem *wdt_base; + +static void sunxi_setup_restart(void) +{ + struct device_node *np = of_find_compatible_node(NULL, NULL, + "allwinner,sunxi-wdt"); + if (WARN(!np, "unable to setup watchdog restart")) + return; + + wdt_base = of_iomap(np, 0); + WARN(!wdt_base, "failed to map watchdog base address"); +} + +static void sunxi_restart(char mode, const char *cmd) +{ + if (!wdt_base) + return; + + /* Enable timer and set reset bit in the watchdog */ + writel(3, wdt_base + WATCHDOG_MODE_REG); + writel(0xa57 << 1 | 1, wdt_base + WATCHDOG_CTRL_REG); + while(1) { + mdelay(5); + writel(3, wdt_base + WATCHDOG_MODE_REG); + } +} + static struct map_desc sunxi_io_desc[] __initdata = { { .virtual = (unsigned long) SUNXI_REGS_VIRT_BASE, @@ -42,6 +73,8 @@ void __init sunxi_map_io(void) static void __init sunxi_dt_init(void) { + sunxi_setup_restart(); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); } @@ -56,6 +89,7 @@ DT_MACHINE_START(SUNXI_DT, "Allwinner A1X (Device Tree)") .map_io = sunxi_map_io, .init_irq = sunxi_init_irq, .handle_irq = sunxi_handle_irq, + .restart = sunxi_restart, .timer = &sunxi_timer, .dt_compat = sunxi_board_dt_compat, MACHINE_END -- cgit v1.2.3 From eb433dad48b49f757255f2860db5c2bd61dcae0d Mon Sep 17 00:00:00 2001 From: Colin Foe-Parker Date: Tue, 20 Nov 2012 15:18:44 +0530 Subject: mfd: tps65217: Set PMIC to shutdown on PWR_EN toggle Set tps65217 PMIC status to OFF if power enable toggle is supported. By setting this bit to 1 to enter PMIC to OFF state when PWR_EN pin is pulled low. Also adds a DT flag to specify that device pmic supports shutdown control or not. Signed-off-by: Colin Foe-Parker [anilkumar@ti.com: move the additions to tps65217 MFD driver] Signed-off-by: AnilKumar Ch Signed-off-by: Samuel Ortiz --- Documentation/devicetree/bindings/regulator/tps65217.txt | 4 ++++ drivers/mfd/tps65217.c | 12 ++++++++++++ 2 files changed, 16 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/regulator/tps65217.txt b/Documentation/devicetree/bindings/regulator/tps65217.txt index d316fb895daf..4f05d208c95c 100644 --- a/Documentation/devicetree/bindings/regulator/tps65217.txt +++ b/Documentation/devicetree/bindings/regulator/tps65217.txt @@ -11,6 +11,9 @@ Required properties: using the standard binding for regulators found at Documentation/devicetree/bindings/regulator/regulator.txt. +Optional properties: +- ti,pmic-shutdown-controller: Telling the PMIC to shutdown on PWR_EN toggle. + The valid names for regulators are: tps65217: dcdc1, dcdc2, dcdc3, ldo1, ldo2, ldo3 and ldo4 @@ -20,6 +23,7 @@ Example: tps: tps@24 { compatible = "ti,tps65217"; + ti,pmic-shutdown-controller; regulators { dcdc1_reg: dcdc1 { diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index 3fb32e655254..c7f17d86db78 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -160,6 +160,7 @@ static int __devinit tps65217_probe(struct i2c_client *client, unsigned int version; unsigned int chip_id = ids->driver_data; const struct of_device_id *match; + bool status_off = false; int ret; if (client->dev.of_node) { @@ -170,6 +171,8 @@ static int __devinit tps65217_probe(struct i2c_client *client, return -EINVAL; } chip_id = (unsigned int)match->data; + status_off = of_property_read_bool(client->dev.of_node, + "ti,pmic-shutdown-controller"); } if (!chip_id) { @@ -207,6 +210,15 @@ static int __devinit tps65217_probe(struct i2c_client *client, return ret; } + /* Set the PMIC to shutdown on PWR_EN toggle */ + if (status_off) { + ret = tps65217_set_bits(tps, TPS65217_REG_STATUS, + TPS65217_STATUS_OFF, TPS65217_STATUS_OFF, + TPS65217_PROTECT_NONE); + if (ret) + dev_warn(tps->dev, "unable to set the status OFF\n"); + } + dev_info(tps->dev, "TPS65217 ID %#x version 1.%d\n", (version & TPS65217_CHIPID_CHIP_MASK) >> 4, version & TPS65217_CHIPID_REV_MASK); -- cgit v1.2.3 From 2ed5f236716cbd20f51bdc87ddd91bad259c81a6 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 22 Nov 2012 00:21:08 +0900 Subject: ARM: EXYNOS: Detect power domain state on registration from DT Initial state of power domains might vary on different boards and with different bootloaders. This patch adds detection of initial state of power domains when being registered from DT. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Signed-off-by: Kukjin Kim --- Documentation/devicetree/bindings/arm/exynos/power_domain.txt | 4 ---- arch/arm/mach-exynos/pm_domains.c | 8 +++++--- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt index 6528e215c5fe..843b54622313 100644 --- a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt +++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt @@ -9,10 +9,6 @@ Required Properties: - reg: physical base address of the controller and length of memory mapped region. -Optional Properties: -- samsung,exynos4210-pd-off: Specifies that the power domain is in turned-off - state during boot and remains to be turned-off until explicitly turned-on. - Example: lcd0: power-domain-lcd0 { diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c index c0bc83a7663e..d1abc1a94ecd 100644 --- a/arch/arm/mach-exynos/pm_domains.c +++ b/arch/arm/mach-exynos/pm_domains.c @@ -89,6 +89,7 @@ static __init int exynos_pm_dt_parse_domains(void) for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { struct exynos_pm_domain *pd; + int on; pd = kzalloc(sizeof(*pd), GFP_KERNEL); if (!pd) { @@ -97,14 +98,15 @@ static __init int exynos_pm_dt_parse_domains(void) return -ENOMEM; } - if (of_get_property(np, "samsung,exynos4210-pd-off", NULL)) - pd->is_off = true; pd->name = np->name; pd->base = of_iomap(np, 0); pd->pd.power_off = exynos_pd_power_off; pd->pd.power_on = exynos_pd_power_on; pd->pd.of_node = np; - pm_genpd_init(&pd->pd, NULL, false); + + on = __raw_readl(pd->base + 0x4) & S5P_INT_LOCAL_PWR_EN; + + pm_genpd_init(&pd->pd, NULL, !on); } return 0; } -- cgit v1.2.3 From 8a65d2365df0635da13ae30c01e217c477c9ce3e Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 22 Nov 2012 00:21:17 +0900 Subject: ARM: EXYNOS: Bind devices to power domains using DT This patch adds a way to specify bindings between devices and power domains using device tree. A device can be bound to particular power domain by adding a power-domain property containing a phandle to the domain. The device will be bound to the domain before binding a driver to it and unbound after unbinding a driver from it. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Signed-off-by: Kukjin Kim --- .../bindings/arm/exynos/power_domain.txt | 13 +++- arch/arm/mach-exynos/pm_domains.c | 82 ++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt index 843b54622313..5216b419016a 100644 --- a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt +++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt @@ -4,14 +4,25 @@ Exynos processors include support for multiple power domains which are used to gate power to one or more peripherals on the processor. Required Properties: -- compatiable: should be one of the following. +- compatible: should be one of the following. * samsung,exynos4210-pd - for exynos4210 type power domain. - reg: physical base address of the controller and length of memory mapped region. +Node of a device using power domains must have a samsung,power-domain property +defined with a phandle to respective power domain. + Example: lcd0: power-domain-lcd0 { compatible = "samsung,exynos4210-pd"; reg = <0x10023C00 0x10>; }; + +Example of the node using power domain: + + node { + /* ... */ + samsung,power-domain = <&lcd0>; + /* ... */ + }; diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c index 5b7ce7ea6c61..9f1351de52f7 100644 --- a/arch/arm/mach-exynos/pm_domains.c +++ b/arch/arm/mach-exynos/pm_domains.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include @@ -83,14 +85,89 @@ static struct exynos_pm_domain PD = { \ } #ifdef CONFIG_OF +static void exynos_add_device_to_domain(struct exynos_pm_domain *pd, + struct device *dev) +{ + int ret; + + dev_dbg(dev, "adding to power domain %s\n", pd->pd.name); + + while (1) { + ret = pm_genpd_add_device(&pd->pd, dev); + if (ret != -EAGAIN) + break; + cond_resched(); + } + + pm_genpd_dev_need_restore(dev, true); +} + +static void exynos_remove_device_from_domain(struct device *dev) +{ + struct generic_pm_domain *genpd = dev_to_genpd(dev); + int ret; + + dev_dbg(dev, "removing from power domain %s\n", genpd->name); + + while (1) { + ret = pm_genpd_remove_device(genpd, dev); + if (ret != -EAGAIN) + break; + cond_resched(); + } +} + +static void exynos_read_domain_from_dt(struct device *dev) +{ + struct platform_device *pd_pdev; + struct exynos_pm_domain *pd; + struct device_node *node; + + node = of_parse_phandle(dev->of_node, "samsung,power-domain", 0); + if (!node) + return; + pd_pdev = of_find_device_by_node(node); + if (!pd_pdev) + return; + pd = platform_get_drvdata(pd_pdev); + exynos_add_device_to_domain(pd, dev); +} + +static int exynos_pm_notifier_call(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct device *dev = data; + + switch (event) { + case BUS_NOTIFY_BIND_DRIVER: + if (dev->of_node) + exynos_read_domain_from_dt(dev); + + break; + + case BUS_NOTIFY_UNBOUND_DRIVER: + exynos_remove_device_from_domain(dev); + + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block platform_nb = { + .notifier_call = exynos_pm_notifier_call, +}; + static __init int exynos_pm_dt_parse_domains(void) { + struct platform_device *pdev; struct device_node *np; for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { struct exynos_pm_domain *pd; int on; + pdev = of_find_device_by_node(np); + pd = kzalloc(sizeof(*pd), GFP_KERNEL); if (!pd) { pr_err("%s: failed to allocate memory for domain\n", @@ -105,10 +182,15 @@ static __init int exynos_pm_dt_parse_domains(void) pd->pd.power_on = exynos_pd_power_on; pd->pd.of_node = np; + platform_set_drvdata(pdev, pd); + on = __raw_readl(pd->base + 0x4) & S5P_INT_LOCAL_PWR_EN; pm_genpd_init(&pd->pd, NULL, !on); } + + bus_register_notifier(&platform_bus_type, &platform_nb); + return 0; } #else -- cgit v1.2.3 From 009f13159bfdccd6e06fe3b62a39fee6dce26c39 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Thu, 2 Aug 2012 11:16:29 +0300 Subject: arm: mvebu: Add support for coherency fabric in mach-mvebu The Armada 370 and Armada XP SOCs have a coherency fabric unit which is responsible for ensuring hardware coherency between all CPUs and between CPUs and I/O masters. This patch provides the basic support needed for SMP. Signed-off-by: Yehuda Yitschak Signed-off-by: Gregory CLEMENT Reviewed-by: Will Deacon --- .../devicetree/bindings/arm/coherency-fabric.txt | 16 +++++ arch/arm/boot/dts/armada-370-xp.dtsi | 5 ++ arch/arm/mach-mvebu/Makefile | 2 +- arch/arm/mach-mvebu/coherency.c | 82 ++++++++++++++++++++++ arch/arm/mach-mvebu/coherency.h | 24 +++++++ arch/arm/mach-mvebu/coherency_ll.S | 49 +++++++++++++ arch/arm/mach-mvebu/common.h | 1 + 7 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/arm/coherency-fabric.txt create mode 100644 arch/arm/mach-mvebu/coherency.c create mode 100644 arch/arm/mach-mvebu/coherency.h create mode 100644 arch/arm/mach-mvebu/coherency_ll.S (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/coherency-fabric.txt b/Documentation/devicetree/bindings/arm/coherency-fabric.txt new file mode 100644 index 000000000000..2bfbf67dd77e --- /dev/null +++ b/Documentation/devicetree/bindings/arm/coherency-fabric.txt @@ -0,0 +1,16 @@ +Coherency fabric +---------------- +Available on Marvell SOCs: Armada 370 and Armada XP + +Required properties: + +- compatible: "marvell,coherency-fabric" +- reg: Should contain,coherency fabric registers location and length. + +Example: + +coherency-fabric@d0020200 { + compatible = "marvell,coherency-fabric"; + reg = <0xd0020200 0xb0>; +}; + diff --git a/arch/arm/boot/dts/armada-370-xp.dtsi b/arch/arm/boot/dts/armada-370-xp.dtsi index 94b4b9e03571..b0d075b50f29 100644 --- a/arch/arm/boot/dts/armada-370-xp.dtsi +++ b/arch/arm/boot/dts/armada-370-xp.dtsi @@ -36,6 +36,11 @@ interrupt-controller; }; + coherency-fabric@d0020200 { + compatible = "marvell,coherency-fabric"; + reg = <0xd0020200 0xb0>; + }; + soc { #address-cells = <1>; #size-cells = <1>; diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile index 57f996b6aa0e..5ce4b42c2697 100644 --- a/arch/arm/mach-mvebu/Makefile +++ b/arch/arm/mach-mvebu/Makefile @@ -2,4 +2,4 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \ -I$(srctree)/arch/arm/plat-orion/include obj-y += system-controller.o -obj-$(CONFIG_MACH_ARMADA_370_XP) += armada-370-xp.o irq-armada-370-xp.o addr-map.o +obj-$(CONFIG_MACH_ARMADA_370_XP) += armada-370-xp.o irq-armada-370-xp.o addr-map.o coherency.o coherency_ll.o diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c new file mode 100644 index 000000000000..596ee66a9cc4 --- /dev/null +++ b/arch/arm/mach-mvebu/coherency.c @@ -0,0 +1,82 @@ +/* + * Coherency fabric (Aurora) support for Armada 370 and XP platforms. + * + * Copyright (C) 2012 Marvell + * + * Yehuda Yitschak + * Gregory Clement + * Thomas Petazzoni + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * The Armada 370 and Armada XP SOCs have a coherency fabric which is + * responsible for ensuring hardware coherency between all CPUs and between + * CPUs and I/O masters. This file initializes the coherency fabric and + * supplies basic routines for configuring and controlling hardware coherency + */ + +#include +#include +#include +#include +#include +#include +#include "armada-370-xp.h" + +/* + * Some functions in this file are called very early during SMP + * initialization. At that time the device tree framework is not yet + * ready, and it is not possible to get the register address to + * ioremap it. That's why the pointer below is given with an initial + * value matching its virtual mapping + */ +static void __iomem *coherency_base = ARMADA_370_XP_REGS_VIRT_BASE + 0x20200; + +/* Coherency fabric registers */ +#define COHERENCY_FABRIC_CFG_OFFSET 0x4 + +static struct of_device_id of_coherency_table[] = { + {.compatible = "marvell,coherency-fabric"}, + { /* end of list */ }, +}; + +#ifdef CONFIG_SMP +int coherency_get_cpu_count(void) +{ + int reg, cnt; + + reg = readl(coherency_base + COHERENCY_FABRIC_CFG_OFFSET); + cnt = (reg & 0xF) + 1; + + return cnt; +} +#endif + +/* Function defined in coherency_ll.S */ +int ll_set_cpu_coherent(void __iomem *base_addr, unsigned int hw_cpu_id); + +int set_cpu_coherent(unsigned int hw_cpu_id, int smp_group_id) +{ + if (!coherency_base) { + pr_warn("Can't make CPU %d cache coherent.\n", hw_cpu_id); + pr_warn("Coherency fabric is not initialized\n"); + return 1; + } + + return ll_set_cpu_coherent(coherency_base, hw_cpu_id); +} + +int __init coherency_init(void) +{ + struct device_node *np; + + np = of_find_matching_node(NULL, of_coherency_table); + if (np) { + pr_info("Initializing Coherency fabric\n"); + coherency_base = of_iomap(np, 0); + } + + return 0; +} diff --git a/arch/arm/mach-mvebu/coherency.h b/arch/arm/mach-mvebu/coherency.h new file mode 100644 index 000000000000..2f428137f6fe --- /dev/null +++ b/arch/arm/mach-mvebu/coherency.h @@ -0,0 +1,24 @@ +/* + * arch/arm/mach-mvebu/include/mach/coherency.h + * + * + * Coherency fabric (Aurora) support for Armada 370 and XP platforms. + * + * Copyright (C) 2012 Marvell + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __MACH_370_XP_COHERENCY_H +#define __MACH_370_XP_COHERENCY_H + +#ifdef CONFIG_SMP +int coherency_get_cpu_count(void); +#endif + +int set_cpu_coherent(int cpu_id, int smp_group_id); +int coherency_init(void); + +#endif /* __MACH_370_XP_COHERENCY_H */ diff --git a/arch/arm/mach-mvebu/coherency_ll.S b/arch/arm/mach-mvebu/coherency_ll.S new file mode 100644 index 000000000000..53e8391192cd --- /dev/null +++ b/arch/arm/mach-mvebu/coherency_ll.S @@ -0,0 +1,49 @@ +/* + * Coherency fabric: low level functions + * + * Copyright (C) 2012 Marvell + * + * Gregory CLEMENT + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * This file implements the assembly function to add a CPU to the + * coherency fabric. This function is called by each of the secondary + * CPUs during their early boot in an SMP kernel, this why this + * function have to callable from assembly. It can also be called by a + * primary CPU from C code during its boot. + */ + +#include +#define ARMADA_XP_CFB_CTL_REG_OFFSET 0x0 +#define ARMADA_XP_CFB_CFG_REG_OFFSET 0x4 + + .text +/* + * r0: Coherency fabric base register address + * r1: HW CPU id + */ +ENTRY(ll_set_cpu_coherent) + /* Create bit by cpu index */ + mov r3, #(1 << 24) + lsl r1, r3, r1 + + /* Add CPU to SMP group - Atomic */ + add r3, r0, #ARMADA_XP_CFB_CTL_REG_OFFSET + ldr r2, [r3] + orr r2, r2, r1 + str r2, [r3] + + /* Enable coherency on CPU - Atomic */ + add r3, r0, #ARMADA_XP_CFB_CFG_REG_OFFSET + ldr r2, [r3] + orr r2, r2, r1 + str r2, [r3] + + dsb + + mov r0, #0 + mov pc, lr +ENDPROC(ll_set_cpu_coherent) diff --git a/arch/arm/mach-mvebu/common.h b/arch/arm/mach-mvebu/common.h index 02f89eaa25fe..ba6b62a42f52 100644 --- a/arch/arm/mach-mvebu/common.h +++ b/arch/arm/mach-mvebu/common.h @@ -20,4 +20,5 @@ void mvebu_restart(char mode, const char *cmd); void armada_370_xp_init_irq(void); void armada_370_xp_handle_irq(struct pt_regs *regs); +int armada_370_xp_coherency_init(void); #endif -- cgit v1.2.3 From 7444dad2409afd94c08875e961ca61c5999cd606 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Thu, 2 Aug 2012 11:17:51 +0300 Subject: arm: mvebu: Add initial support for power managmement service unit The Armada 370 and Armada XP SOCs have a power management service unit which is responsible for powering down and waking up CPUs and other SOC units. This patch adds support for this unit. Signed-off-by: Yehuda Yitschak Signed-off-by: Gregory CLEMENT --- .../devicetree/bindings/arm/armada-370-xp-pmsu.txt | 20 ++++++ arch/arm/boot/dts/armada-xp.dtsi | 6 ++ arch/arm/mach-mvebu/Makefile | 2 +- arch/arm/mach-mvebu/common.h | 1 + arch/arm/mach-mvebu/pmsu.c | 75 ++++++++++++++++++++++ arch/arm/mach-mvebu/pmsu.h | 16 +++++ 6 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/arm/armada-370-xp-pmsu.txt create mode 100644 arch/arm/mach-mvebu/pmsu.c create mode 100644 arch/arm/mach-mvebu/pmsu.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/armada-370-xp-pmsu.txt b/Documentation/devicetree/bindings/arm/armada-370-xp-pmsu.txt new file mode 100644 index 000000000000..926b4d6aae7e --- /dev/null +++ b/Documentation/devicetree/bindings/arm/armada-370-xp-pmsu.txt @@ -0,0 +1,20 @@ +Power Management Service Unit(PMSU) +----------------------------------- +Available on Marvell SOCs: Armada 370 and Armada XP + +Required properties: + +- compatible: "marvell,armada-370-xp-pmsu" + +- reg: Should contain PMSU registers location and length. First pair + for the per-CPU SW Reset Control registers, second pair for the + Power Management Service Unit. + +Example: + +armada-370-xp-pmsu@d0022000 { + compatible = "marvell,armada-370-xp-pmsu"; + reg = <0xd0022100 0x430>, + <0xd0020800 0x20>; +}; + diff --git a/arch/arm/boot/dts/armada-xp.dtsi b/arch/arm/boot/dts/armada-xp.dtsi index f51554e80009..1f95e227053b 100644 --- a/arch/arm/boot/dts/armada-xp.dtsi +++ b/arch/arm/boot/dts/armada-xp.dtsi @@ -27,6 +27,12 @@ <0xd0021870 0x58>; }; + armada-370-xp-pmsu@d0022000 { + compatible = "marvell,armada-370-xp-pmsu"; + reg = <0xd0022100 0x430>, + <0xd0020800 0x20>; + }; + soc { serial@d0012200 { compatible = "ns16550"; diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile index 5ce4b42c2697..2e3ec11c51e6 100644 --- a/arch/arm/mach-mvebu/Makefile +++ b/arch/arm/mach-mvebu/Makefile @@ -2,4 +2,4 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \ -I$(srctree)/arch/arm/plat-orion/include obj-y += system-controller.o -obj-$(CONFIG_MACH_ARMADA_370_XP) += armada-370-xp.o irq-armada-370-xp.o addr-map.o coherency.o coherency_ll.o +obj-$(CONFIG_MACH_ARMADA_370_XP) += armada-370-xp.o irq-armada-370-xp.o addr-map.o coherency.o coherency_ll.o pmsu.o diff --git a/arch/arm/mach-mvebu/common.h b/arch/arm/mach-mvebu/common.h index ba6b62a42f52..9285d0496651 100644 --- a/arch/arm/mach-mvebu/common.h +++ b/arch/arm/mach-mvebu/common.h @@ -21,4 +21,5 @@ void armada_370_xp_init_irq(void); void armada_370_xp_handle_irq(struct pt_regs *regs); int armada_370_xp_coherency_init(void); +int armada_370_xp_pmsu_init(void); #endif diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c new file mode 100644 index 000000000000..3cc4bef6401c --- /dev/null +++ b/arch/arm/mach-mvebu/pmsu.c @@ -0,0 +1,75 @@ +/* + * Power Management Service Unit(PMSU) support for Armada 370/XP platforms. + * + * Copyright (C) 2012 Marvell + * + * Yehuda Yitschak + * Gregory Clement + * Thomas Petazzoni + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * The Armada 370 and Armada XP SOCs have a power management service + * unit which is responsible for powering down and waking up CPUs and + * other SOC units + */ + +#include +#include +#include +#include +#include +#include + +static void __iomem *pmsu_mp_base; +static void __iomem *pmsu_reset_base; + +#define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x24) +#define PMSU_RESET_CTL_OFFSET(cpu) (cpu * 0x8) + +static struct of_device_id of_pmsu_table[] = { + {.compatible = "marvell,armada-370-xp-pmsu"}, + { /* end of list */ }, +}; + +#ifdef CONFIG_SMP +int armada_xp_boot_cpu(unsigned int cpu_id, void *boot_addr) +{ + int reg, hw_cpu; + + if (!pmsu_mp_base || !pmsu_reset_base) { + pr_warn("Can't boot CPU. PMSU is uninitialized\n"); + return 1; + } + + hw_cpu = cpu_logical_map(cpu_id); + + writel(virt_to_phys(boot_addr), pmsu_mp_base + + PMSU_BOOT_ADDR_REDIRECT_OFFSET(hw_cpu)); + + /* Release CPU from reset by clearing reset bit*/ + reg = readl(pmsu_reset_base + PMSU_RESET_CTL_OFFSET(hw_cpu)); + reg &= (~0x1); + writel(reg, pmsu_reset_base + PMSU_RESET_CTL_OFFSET(hw_cpu)); + + return 0; +} +#endif + +int __init armada_370_xp_pmsu_init(void) +{ + struct device_node *np; + + np = of_find_matching_node(NULL, of_pmsu_table); + if (np) { + pr_info("Initializing Power Management Service Unit\n"); + pmsu_mp_base = of_iomap(np, 0); + pmsu_reset_base = of_iomap(np, 1); + } + + return 0; +} + +early_initcall(armada_370_xp_pmsu_init); diff --git a/arch/arm/mach-mvebu/pmsu.h b/arch/arm/mach-mvebu/pmsu.h new file mode 100644 index 000000000000..07a737c6b95d --- /dev/null +++ b/arch/arm/mach-mvebu/pmsu.h @@ -0,0 +1,16 @@ +/* + * Power Management Service Unit (PMSU) support for Armada 370/XP platforms. + * + * Copyright (C) 2012 Marvell + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __MACH_MVEBU_PMSU_H +#define __MACH_MVEBU_PMSU_H + +int armada_xp_boot_cpu(unsigned int cpu_id, void *phys_addr); + +#endif /* __MACH_370_XP_PMSU_H */ -- cgit v1.2.3 From 344e873e5657e8dc0631e4d1d42b69f7d625b02c Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Thu, 2 Aug 2012 11:19:12 +0300 Subject: arm: mvebu: Add IPI support via doorbells This patch enhances the IRQ controller driver to add support for Inter-Processor-Interrupts that are needed to enable SMP support. Signed-off-by: Yehuda Yitschak Signed-off-by: Gregory CLEMENT --- .../devicetree/bindings/arm/armada-370-xp-mpic.txt | 12 ++- arch/arm/boot/dts/armada-xp.dtsi | 2 +- arch/arm/mach-mvebu/armada-370-xp.h | 7 ++ arch/arm/mach-mvebu/irq-armada-370-xp.c | 92 ++++++++++++++++++++-- 4 files changed, 103 insertions(+), 10 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt b/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt index 70c0dc5f00ed..61df564c0d23 100644 --- a/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt +++ b/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt @@ -6,9 +6,15 @@ Required properties: - interrupt-controller: Identifies the node as an interrupt controller. - #interrupt-cells: The number of cells to define the interrupts. Should be 1. The cell is the IRQ number + - reg: Should contain PMIC registers location and length. First pair for the main interrupt registers, second pair for the per-CPU - interrupt registers + interrupt registers. For this last pair, to be compliant with SMP + support, the "virtual" must be use (For the record, these registers + automatically map to the interrupt controller registers of the + current CPU) + + Example: @@ -18,6 +24,6 @@ Example: #address-cells = <1>; #size-cells = <1>; interrupt-controller; - reg = <0xd0020000 0x1000>, - <0xd0021000 0x1000>; + reg = <0xd0020a00 0x1d0>, + <0xd0021070 0x58>; }; diff --git a/arch/arm/boot/dts/armada-xp.dtsi b/arch/arm/boot/dts/armada-xp.dtsi index 1f95e227053b..e6db2b7e2925 100644 --- a/arch/arm/boot/dts/armada-xp.dtsi +++ b/arch/arm/boot/dts/armada-xp.dtsi @@ -24,7 +24,7 @@ mpic: interrupt-controller@d0020000 { reg = <0xd0020a00 0x1d0>, - <0xd0021870 0x58>; + <0xd0021070 0x58>; }; armada-370-xp-pmsu@d0022000 { diff --git a/arch/arm/mach-mvebu/armada-370-xp.h b/arch/arm/mach-mvebu/armada-370-xp.h index aac9bebc6b03..c6a7d74fddfe 100644 --- a/arch/arm/mach-mvebu/armada-370-xp.h +++ b/arch/arm/mach-mvebu/armada-370-xp.h @@ -19,4 +19,11 @@ #define ARMADA_370_XP_REGS_VIRT_BASE IOMEM(0xfeb00000) #define ARMADA_370_XP_REGS_SIZE SZ_1M +#ifdef CONFIG_SMP +#include + +void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq); +void armada_xp_mpic_smp_cpu_init(void); +#endif + #endif /* __MACH_ARMADA_370_XP_H */ diff --git a/arch/arm/mach-mvebu/irq-armada-370-xp.c b/arch/arm/mach-mvebu/irq-armada-370-xp.c index 5f5f9394b6b2..549b6846f940 100644 --- a/arch/arm/mach-mvebu/irq-armada-370-xp.c +++ b/arch/arm/mach-mvebu/irq-armada-370-xp.c @@ -24,6 +24,7 @@ #include #include #include +#include /* Interrupt Controller Registers Map */ #define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48) @@ -35,6 +36,12 @@ #define ARMADA_370_XP_CPU_INTACK_OFFS (0x44) +#define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x4) +#define ARMADA_370_XP_IN_DRBEL_MSK_OFFS (0xc) +#define ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS (0x8) + +#define ACTIVE_DOORBELLS (8) + static void __iomem *per_cpu_int_base; static void __iomem *main_int_base; static struct irq_domain *armada_370_xp_mpic_domain; @@ -51,11 +58,22 @@ static void armada_370_xp_irq_unmask(struct irq_data *d) per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); } +#ifdef CONFIG_SMP +static int armada_xp_set_affinity(struct irq_data *d, + const struct cpumask *mask_val, bool force) +{ + return 0; +} +#endif + static struct irq_chip armada_370_xp_irq_chip = { .name = "armada_370_xp_irq", .irq_mask = armada_370_xp_irq_mask, .irq_mask_ack = armada_370_xp_irq_mask, .irq_unmask = armada_370_xp_irq_unmask, +#ifdef CONFIG_SMP + .irq_set_affinity = armada_xp_set_affinity, +#endif }; static int armada_370_xp_mpic_irq_map(struct irq_domain *h, @@ -72,6 +90,41 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h, return 0; } +#ifdef CONFIG_SMP +void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq) +{ + int cpu; + unsigned long map = 0; + + /* Convert our logical CPU mask into a physical one. */ + for_each_cpu(cpu, mask) + map |= 1 << cpu_logical_map(cpu); + + /* + * Ensure that stores to Normal memory are visible to the + * other CPUs before issuing the IPI. + */ + dsb(); + + /* submit softirq */ + writel((map << 8) | irq, main_int_base + + ARMADA_370_XP_SW_TRIG_INT_OFFS); +} + +void armada_xp_mpic_smp_cpu_init(void) +{ + /* Clear pending IPIs */ + writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); + + /* Enable first 8 IPIs */ + writel((1 << ACTIVE_DOORBELLS) - 1, per_cpu_int_base + + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); + + /* Unmask IPI interrupt */ + writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); +} +#endif /* CONFIG_SMP */ + static struct irq_domain_ops armada_370_xp_mpic_irq_ops = { .map = armada_370_xp_mpic_irq_map, .xlate = irq_domain_xlate_onecell, @@ -91,13 +144,18 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); armada_370_xp_mpic_domain = - irq_domain_add_linear(node, (control >> 2) & 0x3ff, - &armada_370_xp_mpic_irq_ops, NULL); + irq_domain_add_linear(node, (control >> 2) & 0x3ff, + &armada_370_xp_mpic_irq_ops, NULL); if (!armada_370_xp_mpic_domain) panic("Unable to add Armada_370_Xp MPIC irq domain (DT)\n"); irq_set_default_host(armada_370_xp_mpic_domain); + +#ifdef CONFIG_SMP + armada_xp_mpic_smp_cpu_init(); +#endif + return 0; } @@ -111,14 +169,36 @@ asmlinkage void __exception_irq_entry armada_370_xp_handle_irq(struct pt_regs ARMADA_370_XP_CPU_INTACK_OFFS); irqnr = irqstat & 0x3FF; - if (irqnr < 1023) { - irqnr = - irq_find_mapping(armada_370_xp_mpic_domain, irqnr); + if (irqnr > 1022) + break; + + if (irqnr >= 8) { + irqnr = irq_find_mapping(armada_370_xp_mpic_domain, + irqnr); handle_IRQ(irqnr, regs); continue; } +#ifdef CONFIG_SMP + /* IPI Handling */ + if (irqnr == 0) { + u32 ipimask, ipinr; + + ipimask = readl_relaxed(per_cpu_int_base + + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) + & 0xFF; + + writel(0x0, per_cpu_int_base + + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); + + /* Handle all pending doorbells */ + for (ipinr = 0; ipinr < ACTIVE_DOORBELLS; ipinr++) { + if (ipimask & (0x1 << ipinr)) + handle_IPI(ipinr, regs); + } + continue; + } +#endif - break; } while (1); } -- cgit v1.2.3 From 07c121149cb30405b37ceae3cd29e5b6b6e65cc0 Mon Sep 17 00:00:00 2001 From: Marek Belisko Date: Thu, 15 Nov 2012 22:51:56 +0100 Subject: Documentation: Move common leds properties description to separate file. There are several drivers that use LEDs and depend on exactly the same device tree binding. However, the binding documentation has simply been cut-and-paste into each of the binding documents. Rather than continue to duplicate it, this patch adds a common led binding document that all can reference. Signed-off-by: Marek Belisko Reviewed-by: Stephen Warren Signed-off-by: Grant Likely --- Documentation/devicetree/bindings/gpio/led.txt | 14 ++++---------- Documentation/devicetree/bindings/leds/common.txt | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 Documentation/devicetree/bindings/leds/common.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/gpio/led.txt b/Documentation/devicetree/bindings/gpio/led.txt index edc83c1c0d54..df1b3080f6b8 100644 --- a/Documentation/devicetree/bindings/gpio/led.txt +++ b/Documentation/devicetree/bindings/gpio/led.txt @@ -10,16 +10,10 @@ LED sub-node properties: - gpios : Should specify the LED's GPIO, see "gpios property" in Documentation/devicetree/bindings/gpio/gpio.txt. Active low LEDs should be indicated using flags in the GPIO specifier. -- label : (optional) The label for this LED. If omitted, the label is - taken from the node name (excluding the unit address). -- linux,default-trigger : (optional) This parameter, if present, is a - string defining the trigger assigned to the LED. Current triggers are: - "backlight" - LED will act as a back-light, controlled by the framebuffer - system - "default-on" - LED will turn on, but see "default-state" below - "heartbeat" - LED "double" flashes at a load average based rate - "ide-disk" - LED indicates disk activity - "timer" - LED flashes at a fixed, configurable rate +- label : (optional) + see Documentation/devicetree/bindings/leds/common.txt +- linux,default-trigger : (optional) + see Documentation/devicetree/bindings/leds/common.txt - default-state: (optional) The initial state of the LED. Valid values are "on", "off", and "keep". If the LED is already on or off and the default-state property is set the to same value, then no diff --git a/Documentation/devicetree/bindings/leds/common.txt b/Documentation/devicetree/bindings/leds/common.txt new file mode 100644 index 000000000000..2d88816dd550 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/common.txt @@ -0,0 +1,23 @@ +Common leds properties. + +Optional properties for child nodes: +- label : The label for this LED. If omitted, the label is + taken from the node name (excluding the unit address). + +- linux,default-trigger : This parameter, if present, is a + string defining the trigger assigned to the LED. Current triggers are: + "backlight" - LED will act as a back-light, controlled by the framebuffer + system + "default-on" - LED will turn on (but for leds-gpio see "default-state" + property in Documentation/devicetree/bindings/gpio/led.txt) + "heartbeat" - LED "double" flashes at a load average based rate + "ide-disk" - LED indicates disk activity + "timer" - LED flashes at a fixed, configurable rate + +Examples: + +system-status { + label = "Status"; + linux,default-trigger = "heartbeat"; + ... +}; -- cgit v1.2.3 From 155dd0c2f7c24c1531a90229f1c749cebf3f21f7 Mon Sep 17 00:00:00 2001 From: "Chaiken, Alison" Date: Thu, 15 Nov 2012 23:32:51 +0000 Subject: Documentation: correct of_platform_populate() argument list The documentation doesn't match the actual function prototype. This is a trivial patch to fix it. Signed-off-by: Alison Chaiken Acked-by: Arnd Bergmann Signed-off-by: Grant Likely --- Documentation/devicetree/usage-model.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/usage-model.txt b/Documentation/devicetree/usage-model.txt index dca90fe22a90..ef9d06c9f8fd 100644 --- a/Documentation/devicetree/usage-model.txt +++ b/Documentation/devicetree/usage-model.txt @@ -347,7 +347,7 @@ later), which will happily live at the base of the Linux /sys/devices tree. Therefore, if a DT node is at the root of the tree, then it really probably is best registered as a platform_device. -Linux board support code calls of_platform_populate(NULL, NULL, NULL) +Linux board support code calls of_platform_populate(NULL, NULL, NULL, NULL) to kick off discovery of devices at the root of the tree. The parameters are all NULL because when starting from the root of the tree, there is no need to provide a starting node (the first NULL), a -- cgit v1.2.3 From e60304f8cb7bb545e79fe62d9b9762460c254ec2 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Fri, 12 Oct 2012 19:20:36 +0200 Subject: arm: mvebu: Add hardware I/O Coherency support Armada 370 and XP come with an unit called coherency fabric. This unit allows to use the Armada 370/XP as a nearly coherent architecture. The coherency mechanism uses snoop filters to ensure the coherency between caches, DRAM and devices. This mechanism needs a synchronization barrier which guarantees that all the memory writes initiated by the devices have reached their target and do not reside in intermediate write buffers. That's why the architecture is not totally coherent and we need to provide our own functions for some DMA operations. Beside the use of the coherency fabric, the device units will have to set the attribute flag of the decoding address window to select the accurate coherency process for the memory transaction. This is done each device driver programs the DRAM address windows. The value of the attribute set by the driver is retrieved through the orion_addr_map_cfg struct filled during the early initialization of the platform. Signed-off-by: Gregory CLEMENT Reviewed-by: Yehuda Yitschak Acked-by: Marek Szyprowski --- .../devicetree/bindings/arm/coherency-fabric.txt | 9 ++- arch/arm/boot/dts/armada-370-xp.dtsi | 3 +- arch/arm/mach-mvebu/addr-map.c | 3 + arch/arm/mach-mvebu/coherency.c | 73 ++++++++++++++++++++++ 4 files changed, 85 insertions(+), 3 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/coherency-fabric.txt b/Documentation/devicetree/bindings/arm/coherency-fabric.txt index 2bfbf67dd77e..17d8cd107559 100644 --- a/Documentation/devicetree/bindings/arm/coherency-fabric.txt +++ b/Documentation/devicetree/bindings/arm/coherency-fabric.txt @@ -5,12 +5,17 @@ Available on Marvell SOCs: Armada 370 and Armada XP Required properties: - compatible: "marvell,coherency-fabric" -- reg: Should contain,coherency fabric registers location and length. + +- reg: Should contain coherency fabric registers location and + length. First pair for the coherency fabric registers, second pair + for the per-CPU fabric registers registers. Example: coherency-fabric@d0020200 { compatible = "marvell,coherency-fabric"; - reg = <0xd0020200 0xb0>; + reg = <0xd0020200 0xb0>, + <0xd0021810 0x1c>; + }; diff --git a/arch/arm/boot/dts/armada-370-xp.dtsi b/arch/arm/boot/dts/armada-370-xp.dtsi index b0d075b50f29..98a6b26a7dc8 100644 --- a/arch/arm/boot/dts/armada-370-xp.dtsi +++ b/arch/arm/boot/dts/armada-370-xp.dtsi @@ -38,7 +38,8 @@ coherency-fabric@d0020200 { compatible = "marvell,coherency-fabric"; - reg = <0xd0020200 0xb0>; + reg = <0xd0020200 0xb0>, + <0xd0021810 0x1c>; }; soc { diff --git a/arch/arm/mach-mvebu/addr-map.c b/arch/arm/mach-mvebu/addr-map.c index fe454a4430be..595f6b722a8f 100644 --- a/arch/arm/mach-mvebu/addr-map.c +++ b/arch/arm/mach-mvebu/addr-map.c @@ -108,6 +108,9 @@ static int __init armada_setup_cpu_mbus(void) addr_map_cfg.bridge_virt_base = mbus_unit_addr_decoding_base; + if (of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric")) + addr_map_cfg.hw_io_coherency = 1; + /* * Disable, clear and configure windows. */ diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c index 596ee66a9cc4..8278960066c3 100644 --- a/arch/arm/mach-mvebu/coherency.c +++ b/arch/arm/mach-mvebu/coherency.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include "armada-370-xp.h" @@ -33,10 +35,13 @@ * value matching its virtual mapping */ static void __iomem *coherency_base = ARMADA_370_XP_REGS_VIRT_BASE + 0x20200; +static void __iomem *coherency_cpu_base; /* Coherency fabric registers */ #define COHERENCY_FABRIC_CFG_OFFSET 0x4 +#define IO_SYNC_BARRIER_CTL_OFFSET 0x0 + static struct of_device_id of_coherency_table[] = { {.compatible = "marvell,coherency-fabric"}, { /* end of list */ }, @@ -68,6 +73,70 @@ int set_cpu_coherent(unsigned int hw_cpu_id, int smp_group_id) return ll_set_cpu_coherent(coherency_base, hw_cpu_id); } +static inline void mvebu_hwcc_sync_io_barrier(void) +{ + writel(0x1, coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET); + while (readl(coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET) & 0x1); +} + +static dma_addr_t mvebu_hwcc_dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + if (dir != DMA_TO_DEVICE) + mvebu_hwcc_sync_io_barrier(); + return pfn_to_dma(dev, page_to_pfn(page)) + offset; +} + + +static void mvebu_hwcc_dma_unmap_page(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + if (dir != DMA_TO_DEVICE) + mvebu_hwcc_sync_io_barrier(); +} + +static void mvebu_hwcc_dma_sync(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction dir) +{ + if (dir != DMA_TO_DEVICE) + mvebu_hwcc_sync_io_barrier(); +} + +static struct dma_map_ops mvebu_hwcc_dma_ops = { + .alloc = arm_dma_alloc, + .free = arm_dma_free, + .mmap = arm_dma_mmap, + .map_page = mvebu_hwcc_dma_map_page, + .unmap_page = mvebu_hwcc_dma_unmap_page, + .get_sgtable = arm_dma_get_sgtable, + .map_sg = arm_dma_map_sg, + .unmap_sg = arm_dma_unmap_sg, + .sync_single_for_cpu = mvebu_hwcc_dma_sync, + .sync_single_for_device = mvebu_hwcc_dma_sync, + .sync_sg_for_cpu = arm_dma_sync_sg_for_cpu, + .sync_sg_for_device = arm_dma_sync_sg_for_device, + .set_dma_mask = arm_dma_set_mask, +}; + +static int mvebu_hwcc_platform_notifier(struct notifier_block *nb, + unsigned long event, void *__dev) +{ + struct device *dev = __dev; + + if (event != BUS_NOTIFY_ADD_DEVICE) + return NOTIFY_DONE; + set_dma_ops(dev, &mvebu_hwcc_dma_ops); + + return NOTIFY_OK; +} + +static struct notifier_block mvebu_hwcc_platform_nb = { + .notifier_call = mvebu_hwcc_platform_notifier, +}; + int __init coherency_init(void) { struct device_node *np; @@ -76,6 +145,10 @@ int __init coherency_init(void) if (np) { pr_info("Initializing Coherency fabric\n"); coherency_base = of_iomap(np, 0); + coherency_cpu_base = of_iomap(np, 1); + set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0); + bus_register_notifier(&platform_bus_type, + &mvebu_hwcc_platform_nb); } return 0; -- cgit v1.2.3 From 72052fcc10263817031dd96ea438c1f49eb39dd3 Mon Sep 17 00:00:00 2001 From: Simon Guinot Date: Wed, 17 Oct 2012 12:09:03 +0200 Subject: leds: leds-ns2: add device tree binding Signed-off-by: Simon Guinot Reviewed-by: Andrew Lunn Signed-off-by: Jason Cooper --- .../devicetree/bindings/gpio/leds-ns2.txt | 26 ++++++++ drivers/leds/leds-ns2.c | 78 +++++++++++++++++++++- 2 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/gpio/leds-ns2.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/gpio/leds-ns2.txt b/Documentation/devicetree/bindings/gpio/leds-ns2.txt new file mode 100644 index 000000000000..aef3aca34d2d --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/leds-ns2.txt @@ -0,0 +1,26 @@ +Binding for dual-GPIO LED found on Network Space v2 (and parents). + +Required properties: +- compatible: "lacie,ns2-leds". + +Each LED is represented as a sub-node of the ns2-leds device. + +Required sub-node properties: +- cmd-gpio: Command LED GPIO. See OF device-tree GPIO specification. +- slow-gpio: Slow LED GPIO. See OF device-tree GPIO specification. + +Optional sub-node properties: +- label: Name for this LED. If omitted, the label is taken from the node name. +- linux,default-trigger: Trigger assigned to the LED. + +Example: + +ns2-leds { + compatible = "lacie,ns2-leds"; + + blue-sata { + label = "ns2:blue:sata"; + slow-gpio = <&gpio0 29 0>; + cmd-gpio = <&gpio0 30 0>; + }; +}; diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c index d176ec83f5d9..d64cc2227fd9 100644 --- a/drivers/leds/leds-ns2.c +++ b/drivers/leds/leds-ns2.c @@ -30,6 +30,7 @@ #include #include #include +#include /* * The Network Space v2 dual-GPIO LED is wired to a CPLD and can blink in @@ -263,6 +264,62 @@ static void delete_ns2_led(struct ns2_led_data *led_dat) gpio_free(led_dat->slow); } +#ifdef CONFIG_OF_GPIO +/* + * Translate OpenFirmware node properties into platform_data. + */ +static int __devinit +ns2_leds_get_of_pdata(struct device *dev, struct ns2_led_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + struct device_node *child; + struct ns2_led *leds; + int num_leds = 0; + int i = 0; + + num_leds = of_get_child_count(np); + if (!num_leds) + return -ENODEV; + + leds = devm_kzalloc(dev, num_leds * sizeof(struct ns2_led), + GFP_KERNEL); + if (!leds) + return -ENOMEM; + + for_each_child_of_node(np, child) { + const char *string; + int ret; + + ret = of_get_named_gpio(child, "cmd-gpio", 0); + if (ret < 0) + return ret; + leds[i].cmd = ret; + ret = of_get_named_gpio(child, "slow-gpio", 0); + if (ret < 0) + return ret; + leds[i].slow = ret; + ret = of_property_read_string(child, "label", &string); + leds[i].name = (ret == 0) ? string : child->name; + ret = of_property_read_string(child, "linux,default-trigger", + &string); + if (ret == 0) + leds[i].default_trigger = string; + + i++; + } + + pdata->leds = leds; + pdata->num_leds = num_leds; + + return 0; +} + +static const struct of_device_id of_ns2_leds_match[] = { + { .compatible = "lacie,ns2-leds", }, + {}, +}; +#endif /* CONFIG_OF_GPIO */ + static int __devinit ns2_led_probe(struct platform_device *pdev) { struct ns2_led_platform_data *pdata = pdev->dev.platform_data; @@ -270,11 +327,25 @@ static int __devinit ns2_led_probe(struct platform_device *pdev) int i; int ret; +#ifdef CONFIG_OF_GPIO + if (!pdata) { + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct ns2_led_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + ret = ns2_leds_get_of_pdata(&pdev->dev, pdata); + if (ret) + return ret; + } +#else if (!pdata) return -EINVAL; +#endif /* CONFIG_OF_GPIO */ leds_data = devm_kzalloc(&pdev->dev, sizeof(struct ns2_led_data) * - pdata->num_leds, GFP_KERNEL); + pdata->num_leds, GFP_KERNEL); if (!leds_data) return -ENOMEM; @@ -312,8 +383,9 @@ static struct platform_driver ns2_led_driver = { .probe = ns2_led_probe, .remove = __devexit_p(ns2_led_remove), .driver = { - .name = "leds-ns2", - .owner = THIS_MODULE, + .name = "leds-ns2", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_ns2_leds_match), }, }; -- cgit v1.2.3 From 743179849015dc71bb2ea63d8cd4bfa7fdfb4bc6 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Thu, 15 Nov 2012 20:19:57 +0100 Subject: of_spi: add generic binding support to specify cs gpio This will allow to use gpio for chip select with no modification in the driver binding When use the cs-gpios, the gpio number will be passed via the cs_gpio field and the number of chip select will automatically increased with max(hw cs, gpio cs). So if for example the controller has 2 CS lines, and the cs-gpios property looks like this: cs-gpios = <&gpio1 0 0> <0> <&gpio1 1 0> <&gpio1 2 0>; Then it should be configured so that num_chipselect = 4 with the following mapping: cs0 : &gpio1 0 0 cs1 : native cs2 : &gpio1 1 0 cs3 : &gpio1 2 0 Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Cc: devicetree-discuss@lists.ozlabs.org Cc: spi-devel-general@lists.sourceforge.net Signed-off-by: Richard Genoud [grant.likely: fixed up type of cs count so min() can do type checking] Signed-off-by: Grant Likely --- Documentation/devicetree/bindings/spi/spi-bus.txt | 20 +++++++++ drivers/spi/spi.c | 54 +++++++++++++++++++++-- include/linux/spi/spi.h | 3 ++ 3 files changed, 74 insertions(+), 3 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/spi/spi-bus.txt b/Documentation/devicetree/bindings/spi/spi-bus.txt index d2c33d0f533e..77a8b0d39b54 100644 --- a/Documentation/devicetree/bindings/spi/spi-bus.txt +++ b/Documentation/devicetree/bindings/spi/spi-bus.txt @@ -12,6 +12,7 @@ The SPI master node requires the following properties: - #size-cells - should be zero. - compatible - name of SPI bus controller following generic names recommended practice. +- cs-gpios - (optional) gpios chip select. No other properties are required in the SPI bus node. It is assumed that a driver for an SPI bus device will understand that it is an SPI bus. However, the binding does not attempt to define the specific method for @@ -24,6 +25,22 @@ support describing the chip select layout. Optional property: - num-cs : total number of chipselects +If cs-gpios is used the number of chip select will automatically increased +with max(cs-gpios > hw cs) + +So if for example the controller has 2 CS lines, and the cs-gpios +property looks like this: + +cs-gpios = <&gpio1 0 0> <0> <&gpio1 1 0> <&gpio1 2 0>; + +Then it should be configured so that num_chipselect = 4 with the +following mapping: + +cs0 : &gpio1 0 0 +cs1 : native +cs2 : &gpio1 1 0 +cs3 : &gpio1 2 0 + SPI slave nodes must be children of the SPI master node and can contain the following properties. - reg - (required) chip select address of device. @@ -37,6 +54,9 @@ contain the following properties. - spi-cs-high - (optional) Empty property indicating device requires chip select active high +If a gpio chipselect is used for the SPI slave the gpio number will be passed +via the cs_gpio + SPI example for an MPC5200 SPI bus: spi@f00 { #address-cells = <1>; diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 84c2861d6f4d..1587a4a5ff41 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -327,6 +328,7 @@ struct spi_device *spi_alloc_device(struct spi_master *master) spi->dev.parent = &master->dev; spi->dev.bus = &spi_bus_type; spi->dev.release = spidev_release; + spi->cs_gpio = -EINVAL; device_initialize(&spi->dev); return spi; } @@ -344,15 +346,16 @@ EXPORT_SYMBOL_GPL(spi_alloc_device); int spi_add_device(struct spi_device *spi) { static DEFINE_MUTEX(spi_add_lock); - struct device *dev = spi->master->dev.parent; + struct spi_master *master = spi->master; + struct device *dev = master->dev.parent; struct device *d; int status; /* Chipselects are numbered 0..max; validate. */ - if (spi->chip_select >= spi->master->num_chipselect) { + if (spi->chip_select >= master->num_chipselect) { dev_err(dev, "cs%d >= max %d\n", spi->chip_select, - spi->master->num_chipselect); + master->num_chipselect); return -EINVAL; } @@ -376,6 +379,9 @@ int spi_add_device(struct spi_device *spi) goto done; } + if (master->cs_gpios) + spi->cs_gpio = master->cs_gpios[spi->chip_select]; + /* Drivers may modify this initial i/o setup, but will * normally rely on the device being setup. Devices * using SPI_CS_HIGH can't coexist well otherwise... @@ -946,6 +952,44 @@ struct spi_master *spi_alloc_master(struct device *dev, unsigned size) } EXPORT_SYMBOL_GPL(spi_alloc_master); +#ifdef CONFIG_OF +static int of_spi_register_master(struct spi_master *master) +{ + u16 nb; + int i, *cs; + struct device_node *np = master->dev.of_node; + + if (!np) + return 0; + + nb = of_gpio_named_count(np, "cs-gpios"); + master->num_chipselect = max(nb, master->num_chipselect); + + if (nb < 1) + return 0; + + cs = devm_kzalloc(&master->dev, + sizeof(int) * master->num_chipselect, + GFP_KERNEL); + master->cs_gpios = cs; + + if (!master->cs_gpios) + return -ENOMEM; + + memset(cs, -EINVAL, master->num_chipselect); + + for (i = 0; i < nb; i++) + cs[i] = of_get_named_gpio(np, "cs-gpios", i); + + return 0; +} +#else +static int of_spi_register_master(struct spi_master *master) +{ + return 0; +} +#endif + /** * spi_register_master - register SPI master controller * @master: initialized master, originally from spi_alloc_master() @@ -977,6 +1021,10 @@ int spi_register_master(struct spi_master *master) if (!dev) return -ENODEV; + status = of_spi_register_master(master); + if (status) + return status; + /* even if it's just one always-selected device, there must * be at least one chipselect */ diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index fa702aeb5038..f62918946d86 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -90,6 +90,7 @@ struct spi_device { void *controller_state; void *controller_data; char modalias[SPI_NAME_SIZE]; + int cs_gpio; /* chip select gpio */ /* * likely need more hooks for more protocol options affecting how @@ -362,6 +363,8 @@ struct spi_master { int (*transfer_one_message)(struct spi_master *master, struct spi_message *mesg); int (*unprepare_transfer_hardware)(struct spi_master *master); + /* gpio chip select */ + int *cs_gpios; }; static inline void *spi_master_get_devdata(struct spi_master *master) -- cgit v1.2.3 From f40f38d1dc8dee0c19b2102ea549b696116f61da Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 21 Nov 2012 13:43:05 -0200 Subject: ARM: mx5: Replace clk_register_clkdev with clock DT lookup Similarly as it was done for mx6q, use a DT lookup in order to make maintainance task for the clock devices easier. Signed-off-by: Fabio Estevam Acked-by: Shawn Guo Signed-off-by: Sascha Hauer --- .../devicetree/bindings/clock/imx5-clock.txt | 191 +++++++++++++++++++++ arch/arm/boot/dts/imx51.dtsi | 39 +++++ arch/arm/boot/dts/imx53.dtsi | 48 ++++++ arch/arm/mach-imx/clk-imx51-imx53.c | 26 +-- arch/arm/mach-imx/imx51-dt.c | 28 +-- arch/arm/mach-imx/mach-imx53.c | 31 +--- 6 files changed, 293 insertions(+), 70 deletions(-) create mode 100644 Documentation/devicetree/bindings/clock/imx5-clock.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/clock/imx5-clock.txt b/Documentation/devicetree/bindings/clock/imx5-clock.txt new file mode 100644 index 000000000000..04ad47876be0 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/imx5-clock.txt @@ -0,0 +1,191 @@ +* Clock bindings for Freescale i.MX5 + +Required properties: +- compatible: Should be "fsl,-ccm" , where can be imx51 or imx53 +- reg: Address and length of the register set +- interrupts: Should contain CCM interrupt +- #clock-cells: Should be <1> + +The clock consumer should specify the desired clock by having the clock +ID in its "clocks" phandle cell. The following is a full list of i.MX5 +clocks and IDs. + + Clock ID + --------------------------- + dummy 0 + ckil 1 + osc 2 + ckih1 3 + ckih2 4 + ahb 5 + ipg 6 + axi_a 7 + axi_b 8 + uart_pred 9 + uart_root 10 + esdhc_a_pred 11 + esdhc_b_pred 12 + esdhc_c_s 13 + esdhc_d_s 14 + emi_sel 15 + emi_slow_podf 16 + nfc_podf 17 + ecspi_pred 18 + ecspi_podf 19 + usboh3_pred 20 + usboh3_podf 21 + usb_phy_pred 22 + usb_phy_podf 23 + cpu_podf 24 + di_pred 25 + tve_di 26 + tve_s 27 + uart1_ipg_gate 28 + uart1_per_gate 29 + uart2_ipg_gate 30 + uart2_per_gate 31 + uart3_ipg_gate 32 + uart3_per_gate 33 + i2c1_gate 34 + i2c2_gate 35 + gpt_ipg_gate 36 + pwm1_ipg_gate 37 + pwm1_hf_gate 38 + pwm2_ipg_gate 39 + pwm2_hf_gate 40 + gpt_hf_gate 41 + fec_gate 42 + usboh3_per_gate 43 + esdhc1_ipg_gate 44 + esdhc2_ipg_gate 45 + esdhc3_ipg_gate 46 + esdhc4_ipg_gate 47 + ssi1_ipg_gate 48 + ssi2_ipg_gate 49 + ssi3_ipg_gate 50 + ecspi1_ipg_gate 51 + ecspi1_per_gate 52 + ecspi2_ipg_gate 53 + ecspi2_per_gate 54 + cspi_ipg_gate 55 + sdma_gate 56 + emi_slow_gate 57 + ipu_s 58 + ipu_gate 59 + nfc_gate 60 + ipu_di1_gate 61 + vpu_s 62 + vpu_gate 63 + vpu_reference_gate 64 + uart4_ipg_gate 65 + uart4_per_gate 66 + uart5_ipg_gate 67 + uart5_per_gate 68 + tve_gate 69 + tve_pred 70 + esdhc1_per_gate 71 + esdhc2_per_gate 72 + esdhc3_per_gate 73 + esdhc4_per_gate 74 + usb_phy_gate 75 + hsi2c_gate 76 + mipi_hsc1_gate 77 + mipi_hsc2_gate 78 + mipi_esc_gate 79 + mipi_hsp_gate 80 + ldb_di1_div_3_5 81 + ldb_di1_div 82 + ldb_di0_div_3_5 83 + ldb_di0_div 84 + ldb_di1_gate 85 + can2_serial_gate 86 + can2_ipg_gate 87 + i2c3_gate 88 + lp_apm 89 + periph_apm 90 + main_bus 91 + ahb_max 92 + aips_tz1 93 + aips_tz2 94 + tmax1 95 + tmax2 96 + tmax3 97 + spba 98 + uart_sel 99 + esdhc_a_sel 100 + esdhc_b_sel 101 + esdhc_a_podf 102 + esdhc_b_podf 103 + ecspi_sel 104 + usboh3_sel 105 + usb_phy_sel 106 + iim_gate 107 + usboh3_gate 108 + emi_fast_gate 109 + ipu_di0_gate 110 + gpc_dvfs 111 + pll1_sw 112 + pll2_sw 113 + pll3_sw 114 + ipu_di0_sel 115 + ipu_di1_sel 116 + tve_ext_sel 117 + mx51_mipi 118 + pll4_sw 119 + ldb_di1_sel 120 + di_pll4_podf 121 + ldb_di0_sel 122 + ldb_di0_gate 123 + usb_phy1_gate 124 + usb_phy2_gate 125 + per_lp_apm 126 + per_pred1 127 + per_pred2 128 + per_podf 129 + per_root 130 + ssi_apm 131 + ssi1_root_sel 132 + ssi2_root_sel 133 + ssi3_root_sel 134 + ssi_ext1_sel 135 + ssi_ext2_sel 136 + ssi_ext1_com_sel 137 + ssi_ext2_com_sel 138 + ssi1_root_pred 139 + ssi1_root_podf 140 + ssi2_root_pred 141 + ssi2_root_podf 142 + ssi_ext1_pred 143 + ssi_ext1_podf 144 + ssi_ext2_pred 145 + ssi_ext2_podf 146 + ssi1_root_gate 147 + ssi2_root_gate 148 + ssi3_root_gate 149 + ssi_ext1_gate 150 + ssi_ext2_gate 151 + epit1_ipg_gate 152 + epit1_hf_gate 153 + epit2_ipg_gate 154 + epit2_hf_gate 155 + can_sel 156 + can1_serial_gate 157 + can1_ipg_gate 158 + +Examples (for mx53): + +clks: ccm@53fd4000{ + compatible = "fsl,imx53-ccm"; + reg = <0x53fd4000 0x4000>; + interrupts = <0 71 0x04 0 72 0x04>; + #clock-cells = <1>; +}; + +can1: can@53fc8000 { + compatible = "fsl,imx53-flexcan", "fsl,p1010-flexcan"; + reg = <0x53fc8000 0x4000>; + interrupts = <82>; + clocks = <&clks 158>, <&clks 157>; + clock-names = "ipg", "per"; + status = "disabled"; +}; diff --git a/arch/arm/boot/dts/imx51.dtsi b/arch/arm/boot/dts/imx51.dtsi index 44c7af791fa5..8cf69c702553 100644 --- a/arch/arm/boot/dts/imx51.dtsi +++ b/arch/arm/boot/dts/imx51.dtsi @@ -87,6 +87,8 @@ compatible = "fsl,imx51-esdhc"; reg = <0x70004000 0x4000>; interrupts = <1>; + clocks = <&clks 44>, <&clks 0>, <&clks 71>; + clock-names = "ipg", "ahb", "per"; status = "disabled"; }; @@ -94,6 +96,8 @@ compatible = "fsl,imx51-esdhc"; reg = <0x70008000 0x4000>; interrupts = <2>; + clocks = <&clks 45>, <&clks 0>, <&clks 72>; + clock-names = "ipg", "ahb", "per"; status = "disabled"; }; @@ -101,6 +105,8 @@ compatible = "fsl,imx51-uart", "fsl,imx21-uart"; reg = <0x7000c000 0x4000>; interrupts = <33>; + clocks = <&clks 32>, <&clks 33>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -110,6 +116,8 @@ compatible = "fsl,imx51-ecspi"; reg = <0x70010000 0x4000>; interrupts = <36>; + clocks = <&clks 51>, <&clks 52>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -117,6 +125,7 @@ compatible = "fsl,imx51-ssi", "fsl,imx21-ssi"; reg = <0x70014000 0x4000>; interrupts = <30>; + clocks = <&clks 49>; fsl,fifo-depth = <15>; fsl,ssi-dma-events = <25 24 23 22>; /* TX0 RX0 TX1 RX1 */ status = "disabled"; @@ -126,6 +135,8 @@ compatible = "fsl,imx51-esdhc"; reg = <0x70020000 0x4000>; interrupts = <3>; + clocks = <&clks 46>, <&clks 0>, <&clks 73>; + clock-names = "ipg", "ahb", "per"; status = "disabled"; }; @@ -133,6 +144,8 @@ compatible = "fsl,imx51-esdhc"; reg = <0x70024000 0x4000>; interrupts = <4>; + clocks = <&clks 47>, <&clks 0>, <&clks 74>; + clock-names = "ipg", "ahb", "per"; status = "disabled"; }; }; @@ -209,12 +222,14 @@ compatible = "fsl,imx51-wdt", "fsl,imx21-wdt"; reg = <0x73f98000 0x4000>; interrupts = <58>; + clocks = <&clks 0>; }; wdog@73f9c000 { /* WDOG2 */ compatible = "fsl,imx51-wdt", "fsl,imx21-wdt"; reg = <0x73f9c000 0x4000>; interrupts = <59>; + clocks = <&clks 0>; status = "disabled"; }; @@ -398,6 +413,8 @@ compatible = "fsl,imx51-uart", "fsl,imx21-uart"; reg = <0x73fbc000 0x4000>; interrupts = <31>; + clocks = <&clks 28>, <&clks 29>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -405,8 +422,17 @@ compatible = "fsl,imx51-uart", "fsl,imx21-uart"; reg = <0x73fc0000 0x4000>; interrupts = <32>; + clocks = <&clks 30>, <&clks 31>; + clock-names = "ipg", "per"; status = "disabled"; }; + + clks: ccm@73fd4000{ + compatible = "fsl,imx51-ccm"; + reg = <0x73fd4000 0x4000>; + interrupts = <0 71 0x04 0 72 0x04>; + #clock-cells = <1>; + }; }; aips@80000000 { /* AIPS2 */ @@ -422,6 +448,8 @@ compatible = "fsl,imx51-ecspi"; reg = <0x83fac000 0x4000>; interrupts = <37>; + clocks = <&clks 53>, <&clks 54>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -429,6 +457,8 @@ compatible = "fsl,imx51-sdma", "fsl,imx35-sdma"; reg = <0x83fb0000 0x4000>; interrupts = <6>; + clocks = <&clks 56>, <&clks 56>; + clock-names = "ipg", "ahb"; fsl,sdma-ram-script-name = "imx/sdma/sdma-imx51.bin"; }; @@ -438,6 +468,8 @@ compatible = "fsl,imx51-cspi", "fsl,imx35-cspi"; reg = <0x83fc0000 0x4000>; interrupts = <38>; + clocks = <&clks 55>, <&clks 0>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -447,6 +479,7 @@ compatible = "fsl,imx51-i2c", "fsl,imx21-i2c"; reg = <0x83fc4000 0x4000>; interrupts = <63>; + clocks = <&clks 35>; status = "disabled"; }; @@ -456,6 +489,7 @@ compatible = "fsl,imx51-i2c", "fsl,imx21-i2c"; reg = <0x83fc8000 0x4000>; interrupts = <62>; + clocks = <&clks 34>; status = "disabled"; }; @@ -463,6 +497,7 @@ compatible = "fsl,imx51-ssi", "fsl,imx21-ssi"; reg = <0x83fcc000 0x4000>; interrupts = <29>; + clocks = <&clks 48>; fsl,fifo-depth = <15>; fsl,ssi-dma-events = <29 28 27 26>; /* TX0 RX0 TX1 RX1 */ status = "disabled"; @@ -478,6 +513,7 @@ compatible = "fsl,imx51-nand"; reg = <0x83fdb000 0x1000 0xcfff0000 0x10000>; interrupts = <8>; + clocks = <&clks 60>; status = "disabled"; }; @@ -485,6 +521,7 @@ compatible = "fsl,imx51-ssi", "fsl,imx21-ssi"; reg = <0x83fe8000 0x4000>; interrupts = <96>; + clocks = <&clks 50>; fsl,fifo-depth = <15>; fsl,ssi-dma-events = <47 46 37 35>; /* TX0 RX0 TX1 RX1 */ status = "disabled"; @@ -494,6 +531,8 @@ compatible = "fsl,imx51-fec", "fsl,imx27-fec"; reg = <0x83fec000 0x4000>; interrupts = <87>; + clocks = <&clks 42>, <&clks 42>, <&clks 42>; + clock-names = "ipg", "ahb", "ptp"; status = "disabled"; }; }; diff --git a/arch/arm/boot/dts/imx53.dtsi b/arch/arm/boot/dts/imx53.dtsi index 8317a1727118..3be27723e8ab 100644 --- a/arch/arm/boot/dts/imx53.dtsi +++ b/arch/arm/boot/dts/imx53.dtsi @@ -92,6 +92,8 @@ compatible = "fsl,imx53-esdhc"; reg = <0x50004000 0x4000>; interrupts = <1>; + clocks = <&clks 44>, <&clks 0>, <&clks 71>; + clock-names = "ipg", "ahb", "per"; status = "disabled"; }; @@ -99,6 +101,8 @@ compatible = "fsl,imx53-esdhc"; reg = <0x50008000 0x4000>; interrupts = <2>; + clocks = <&clks 45>, <&clks 0>, <&clks 72>; + clock-names = "ipg", "ahb", "per"; status = "disabled"; }; @@ -106,6 +110,8 @@ compatible = "fsl,imx53-uart", "fsl,imx21-uart"; reg = <0x5000c000 0x4000>; interrupts = <33>; + clocks = <&clks 32>, <&clks 33>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -115,6 +121,8 @@ compatible = "fsl,imx53-ecspi", "fsl,imx51-ecspi"; reg = <0x50010000 0x4000>; interrupts = <36>; + clocks = <&clks 51>, <&clks 52>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -122,6 +130,7 @@ compatible = "fsl,imx53-ssi", "fsl,imx21-ssi"; reg = <0x50014000 0x4000>; interrupts = <30>; + clocks = <&clks 49>; fsl,fifo-depth = <15>; fsl,ssi-dma-events = <25 24 23 22>; /* TX0 RX0 TX1 RX1 */ status = "disabled"; @@ -131,6 +140,8 @@ compatible = "fsl,imx53-esdhc"; reg = <0x50020000 0x4000>; interrupts = <3>; + clocks = <&clks 46>, <&clks 0>, <&clks 73>; + clock-names = "ipg", "ahb", "per"; status = "disabled"; }; @@ -138,6 +149,8 @@ compatible = "fsl,imx53-esdhc"; reg = <0x50024000 0x4000>; interrupts = <4>; + clocks = <&clks 47>, <&clks 0>, <&clks 74>; + clock-names = "ipg", "ahb", "per"; status = "disabled"; }; }; @@ -214,12 +227,14 @@ compatible = "fsl,imx53-wdt", "fsl,imx21-wdt"; reg = <0x53f98000 0x4000>; interrupts = <58>; + clocks = <&clks 0>; }; wdog@53f9c000 { /* WDOG2 */ compatible = "fsl,imx53-wdt", "fsl,imx21-wdt"; reg = <0x53f9c000 0x4000>; interrupts = <59>; + clocks = <&clks 0>; status = "disabled"; }; @@ -382,6 +397,8 @@ compatible = "fsl,imx53-uart", "fsl,imx21-uart"; reg = <0x53fbc000 0x4000>; interrupts = <31>; + clocks = <&clks 28>, <&clks 29>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -389,6 +406,8 @@ compatible = "fsl,imx53-uart", "fsl,imx21-uart"; reg = <0x53fc0000 0x4000>; interrupts = <32>; + clocks = <&clks 30>, <&clks 31>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -396,6 +415,8 @@ compatible = "fsl,imx53-flexcan", "fsl,p1010-flexcan"; reg = <0x53fc8000 0x4000>; interrupts = <82>; + clocks = <&clks 158>, <&clks 157>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -403,9 +424,18 @@ compatible = "fsl,imx53-flexcan", "fsl,p1010-flexcan"; reg = <0x53fcc000 0x4000>; interrupts = <83>; + clocks = <&clks 158>, <&clks 157>; + clock-names = "ipg", "per"; status = "disabled"; }; + clks: ccm@53fd4000{ + compatible = "fsl,imx53-ccm"; + reg = <0x53fd4000 0x4000>; + interrupts = <0 71 0x04 0 72 0x04>; + #clock-cells = <1>; + }; + gpio5: gpio@53fdc000 { compatible = "fsl,imx53-gpio", "fsl,imx35-gpio"; reg = <0x53fdc000 0x4000>; @@ -442,6 +472,7 @@ compatible = "fsl,imx53-i2c", "fsl,imx21-i2c"; reg = <0x53fec000 0x4000>; interrupts = <64>; + clocks = <&clks 88>; status = "disabled"; }; @@ -449,6 +480,8 @@ compatible = "fsl,imx53-uart", "fsl,imx21-uart"; reg = <0x53ff0000 0x4000>; interrupts = <13>; + clocks = <&clks 65>, <&clks 66>; + clock-names = "ipg", "per"; status = "disabled"; }; }; @@ -464,6 +497,8 @@ compatible = "fsl,imx53-uart", "fsl,imx21-uart"; reg = <0x63f90000 0x4000>; interrupts = <86>; + clocks = <&clks 67>, <&clks 68>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -473,6 +508,8 @@ compatible = "fsl,imx53-ecspi", "fsl,imx51-ecspi"; reg = <0x63fac000 0x4000>; interrupts = <37>; + clocks = <&clks 53>, <&clks 54>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -480,6 +517,8 @@ compatible = "fsl,imx53-sdma", "fsl,imx35-sdma"; reg = <0x63fb0000 0x4000>; interrupts = <6>; + clocks = <&clks 56>, <&clks 56>; + clock-names = "ipg", "ahb"; fsl,sdma-ram-script-name = "imx/sdma/sdma-imx53.bin"; }; @@ -489,6 +528,8 @@ compatible = "fsl,imx53-cspi", "fsl,imx35-cspi"; reg = <0x63fc0000 0x4000>; interrupts = <38>; + clocks = <&clks 55>, <&clks 0>; + clock-names = "ipg", "per"; status = "disabled"; }; @@ -498,6 +539,7 @@ compatible = "fsl,imx53-i2c", "fsl,imx21-i2c"; reg = <0x63fc4000 0x4000>; interrupts = <63>; + clocks = <&clks 35>; status = "disabled"; }; @@ -507,6 +549,7 @@ compatible = "fsl,imx53-i2c", "fsl,imx21-i2c"; reg = <0x63fc8000 0x4000>; interrupts = <62>; + clocks = <&clks 34>; status = "disabled"; }; @@ -514,6 +557,7 @@ compatible = "fsl,imx53-ssi", "fsl,imx21-ssi"; reg = <0x63fcc000 0x4000>; interrupts = <29>; + clocks = <&clks 48>; fsl,fifo-depth = <15>; fsl,ssi-dma-events = <29 28 27 26>; /* TX0 RX0 TX1 RX1 */ status = "disabled"; @@ -529,6 +573,7 @@ compatible = "fsl,imx53-nand"; reg = <0x63fdb000 0x1000 0xf7ff0000 0x10000>; interrupts = <8>; + clocks = <&clks 60>; status = "disabled"; }; @@ -536,6 +581,7 @@ compatible = "fsl,imx53-ssi", "fsl,imx21-ssi"; reg = <0x63fe8000 0x4000>; interrupts = <96>; + clocks = <&clks 50>; fsl,fifo-depth = <15>; fsl,ssi-dma-events = <47 46 45 44>; /* TX0 RX0 TX1 RX1 */ status = "disabled"; @@ -545,6 +591,8 @@ compatible = "fsl,imx53-fec", "fsl,imx25-fec"; reg = <0x63fec000 0x4000>; interrupts = <87>; + clocks = <&clks 42>, <&clks 42>, <&clks 42>; + clock-names = "ipg", "ahb", "ptp"; status = "disabled"; }; }; diff --git a/arch/arm/mach-imx/clk-imx51-imx53.c b/arch/arm/mach-imx/clk-imx51-imx53.c index 73b241db63c8..e8c0473c7568 100644 --- a/arch/arm/mach-imx/clk-imx51-imx53.c +++ b/arch/arm/mach-imx/clk-imx51-imx53.c @@ -87,6 +87,7 @@ enum imx5_clks { }; static struct clk *clk[clk_max]; +static struct clk_onecell_data clk_data; static void __init mx5_clocks_common_init(unsigned long rate_ckil, unsigned long rate_osc, unsigned long rate_ckih1, @@ -318,6 +319,7 @@ int __init mx51_clocks_init(unsigned long rate_ckil, unsigned long rate_osc, unsigned long rate_ckih1, unsigned long rate_ckih2) { int i; + struct device_node *np; clk[pll1_sw] = imx_clk_pllv2("pll1_sw", "osc", MX51_DPLL1_BASE); clk[pll2_sw] = imx_clk_pllv2("pll2_sw", "osc", MX51_DPLL2_BASE); @@ -346,6 +348,11 @@ int __init mx51_clocks_init(unsigned long rate_ckil, unsigned long rate_osc, pr_err("i.MX51 clk %d: register failed with %ld\n", i, PTR_ERR(clk[i])); + np = of_find_compatible_node(NULL, NULL, "fsl,imx51-ccm"); + clk_data.clks = clk; + clk_data.clk_num = ARRAY_SIZE(clk); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + mx5_clocks_common_init(rate_ckil, rate_osc, rate_ckih1, rate_ckih2); clk_register_clkdev(clk[hsi2c_gate], NULL, "imx21-i2c.2"); @@ -368,10 +375,6 @@ int __init mx51_clocks_init(unsigned long rate_ckil, unsigned long rate_osc, clk_register_clkdev(clk[esdhc4_ipg_gate], "ipg", "sdhci-esdhc-imx51.3"); clk_register_clkdev(clk[dummy], "ahb", "sdhci-esdhc-imx51.3"); clk_register_clkdev(clk[esdhc4_per_gate], "per", "sdhci-esdhc-imx51.3"); - clk_register_clkdev(clk[ssi1_ipg_gate], NULL, "83fcc000.ssi"); - clk_register_clkdev(clk[ssi2_ipg_gate], NULL, "70014000.ssi"); - clk_register_clkdev(clk[ssi3_ipg_gate], NULL, "83fe8000.ssi"); - clk_register_clkdev(clk[nfc_gate], NULL, "83fdb000.nand"); /* set the usboh3 parent to pll2_sw */ clk_set_parent(clk[usboh3_sel], clk[pll2_sw]); @@ -395,6 +398,7 @@ int __init mx53_clocks_init(unsigned long rate_ckil, unsigned long rate_osc, { int i; unsigned long r; + struct device_node *np; clk[pll1_sw] = imx_clk_pllv2("pll1_sw", "osc", MX53_DPLL1_BASE); clk[pll2_sw] = imx_clk_pllv2("pll2_sw", "osc", MX53_DPLL2_BASE); @@ -439,6 +443,11 @@ int __init mx53_clocks_init(unsigned long rate_ckil, unsigned long rate_osc, pr_err("i.MX53 clk %d: register failed with %ld\n", i, PTR_ERR(clk[i])); + np = of_find_compatible_node(NULL, NULL, "fsl,imx53-ccm"); + clk_data.clks = clk; + clk_data.clk_num = ARRAY_SIZE(clk); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + mx5_clocks_common_init(rate_ckil, rate_osc, rate_ckih1, rate_ckih2); clk_register_clkdev(clk[vpu_gate], NULL, "imx53-vpu.0"); @@ -461,15 +470,6 @@ int __init mx53_clocks_init(unsigned long rate_ckil, unsigned long rate_osc, clk_register_clkdev(clk[esdhc4_ipg_gate], "ipg", "sdhci-esdhc-imx53.3"); clk_register_clkdev(clk[dummy], "ahb", "sdhci-esdhc-imx53.3"); clk_register_clkdev(clk[esdhc4_per_gate], "per", "sdhci-esdhc-imx53.3"); - clk_register_clkdev(clk[ssi1_ipg_gate], NULL, "63fcc000.ssi"); - clk_register_clkdev(clk[ssi2_ipg_gate], NULL, "50014000.ssi"); - clk_register_clkdev(clk[ssi3_ipg_gate], NULL, "63fd0000.ssi"); - clk_register_clkdev(clk[nfc_gate], NULL, "63fdb000.nand"); - clk_register_clkdev(clk[can1_ipg_gate], "ipg", "53fc8000.can"); - clk_register_clkdev(clk[can1_serial_gate], "per", "53fc8000.can"); - clk_register_clkdev(clk[can2_ipg_gate], "ipg", "53fcc000.can"); - clk_register_clkdev(clk[can2_serial_gate], "per", "53fcc000.can"); - clk_register_clkdev(clk[dummy], NULL, "53fa4000.rtc"); /* set SDHC root clock to 200MHZ*/ clk_set_rate(clk[esdhc_a_podf], 200000000); diff --git a/arch/arm/mach-imx/imx51-dt.c b/arch/arm/mach-imx/imx51-dt.c index 50742990a136..5ffa40c673f8 100644 --- a/arch/arm/mach-imx/imx51-dt.c +++ b/arch/arm/mach-imx/imx51-dt.c @@ -19,35 +19,9 @@ #include "common.h" #include "mx51.h" -/* - * Lookup table for attaching a specific name and platform_data pointer to - * devices as they get created by of_platform_populate(). Ideally this table - * would not exist, but the current clock implementation depends on some devices - * having a specific name. - */ -static const struct of_dev_auxdata imx51_auxdata_lookup[] __initconst = { - OF_DEV_AUXDATA("fsl,imx51-uart", MX51_UART1_BASE_ADDR, "imx21-uart.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-uart", MX51_UART2_BASE_ADDR, "imx21-uart.1", NULL), - OF_DEV_AUXDATA("fsl,imx51-uart", MX51_UART3_BASE_ADDR, "imx21-uart.2", NULL), - OF_DEV_AUXDATA("fsl,imx51-fec", MX51_FEC_BASE_ADDR, "imx27-fec.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-esdhc", MX51_ESDHC1_BASE_ADDR, "sdhci-esdhc-imx51.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-esdhc", MX51_ESDHC2_BASE_ADDR, "sdhci-esdhc-imx51.1", NULL), - OF_DEV_AUXDATA("fsl,imx51-esdhc", MX51_ESDHC3_BASE_ADDR, "sdhci-esdhc-imx51.2", NULL), - OF_DEV_AUXDATA("fsl,imx51-esdhc", MX51_ESDHC4_BASE_ADDR, "sdhci-esdhc-imx51.3", NULL), - OF_DEV_AUXDATA("fsl,imx51-ecspi", MX51_ECSPI1_BASE_ADDR, "imx51-ecspi.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-ecspi", MX51_ECSPI2_BASE_ADDR, "imx51-ecspi.1", NULL), - OF_DEV_AUXDATA("fsl,imx51-cspi", MX51_CSPI_BASE_ADDR, "imx35-cspi.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-i2c", MX51_I2C1_BASE_ADDR, "imx21-i2c.0", NULL), - OF_DEV_AUXDATA("fsl,imx51-i2c", MX51_I2C2_BASE_ADDR, "imx21-i2c.1", NULL), - OF_DEV_AUXDATA("fsl,imx51-sdma", MX51_SDMA_BASE_ADDR, "imx35-sdma", NULL), - OF_DEV_AUXDATA("fsl,imx51-wdt", MX51_WDOG1_BASE_ADDR, "imx2-wdt.0", NULL), - { /* sentinel */ } -}; - static void __init imx51_dt_init(void) { - of_platform_populate(NULL, of_default_bus_match_table, - imx51_auxdata_lookup, NULL); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); } static void __init imx51_timer_init(void) diff --git a/arch/arm/mach-imx/mach-imx53.c b/arch/arm/mach-imx/mach-imx53.c index e71e62610eba..860284dea0e7 100644 --- a/arch/arm/mach-imx/mach-imx53.c +++ b/arch/arm/mach-imx/mach-imx53.c @@ -23,34 +23,6 @@ #include "common.h" #include "mx53.h" -/* - * Lookup table for attaching a specific name and platform_data pointer to - * devices as they get created by of_platform_populate(). Ideally this table - * would not exist, but the current clock implementation depends on some devices - * having a specific name. - */ -static const struct of_dev_auxdata imx53_auxdata_lookup[] __initconst = { - OF_DEV_AUXDATA("fsl,imx53-uart", MX53_UART1_BASE_ADDR, "imx21-uart.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-uart", MX53_UART2_BASE_ADDR, "imx21-uart.1", NULL), - OF_DEV_AUXDATA("fsl,imx53-uart", MX53_UART3_BASE_ADDR, "imx21-uart.2", NULL), - OF_DEV_AUXDATA("fsl,imx53-uart", MX53_UART4_BASE_ADDR, "imx21-uart.3", NULL), - OF_DEV_AUXDATA("fsl,imx53-uart", MX53_UART5_BASE_ADDR, "imx21-uart.4", NULL), - OF_DEV_AUXDATA("fsl,imx53-fec", MX53_FEC_BASE_ADDR, "imx25-fec.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-esdhc", MX53_ESDHC1_BASE_ADDR, "sdhci-esdhc-imx53.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-esdhc", MX53_ESDHC2_BASE_ADDR, "sdhci-esdhc-imx53.1", NULL), - OF_DEV_AUXDATA("fsl,imx53-esdhc", MX53_ESDHC3_BASE_ADDR, "sdhci-esdhc-imx53.2", NULL), - OF_DEV_AUXDATA("fsl,imx53-esdhc", MX53_ESDHC4_BASE_ADDR, "sdhci-esdhc-imx53.3", NULL), - OF_DEV_AUXDATA("fsl,imx53-ecspi", MX53_ECSPI1_BASE_ADDR, "imx51-ecspi.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-ecspi", MX53_ECSPI2_BASE_ADDR, "imx51-ecspi.1", NULL), - OF_DEV_AUXDATA("fsl,imx53-cspi", MX53_CSPI_BASE_ADDR, "imx35-cspi.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C1_BASE_ADDR, "imx21-i2c.0", NULL), - OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C2_BASE_ADDR, "imx21-i2c.1", NULL), - OF_DEV_AUXDATA("fsl,imx53-i2c", MX53_I2C3_BASE_ADDR, "imx21-i2c.2", NULL), - OF_DEV_AUXDATA("fsl,imx53-sdma", MX53_SDMA_BASE_ADDR, "imx35-sdma", NULL), - OF_DEV_AUXDATA("fsl,imx53-wdt", MX53_WDOG1_BASE_ADDR, "imx2-wdt.0", NULL), - { /* sentinel */ } -}; - static void __init imx53_qsb_init(void) { struct clk *clk; @@ -69,8 +41,7 @@ static void __init imx53_dt_init(void) if (of_machine_is_compatible("fsl,imx53-qsb")) imx53_qsb_init(); - of_platform_populate(NULL, of_default_bus_match_table, - imx53_auxdata_lookup, NULL); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); } static void __init imx53_timer_init(void) -- cgit v1.2.3 From 13861701a83765cc442ffe9bac0126ec75a3342e Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 22 Nov 2012 10:05:13 +0100 Subject: ARM i.MX6: rename PLLs according to datasheet In recent reference manuals the PLLs were renumbered. PLL8 now is PLL6 and vice versa. Change the code according to the reference manual to avoid confusion. Signed-off-by: Sascha Hauer Acked-by: Shawn Guo --- Documentation/devicetree/bindings/clock/imx6q-clock.txt | 4 ++-- arch/arm/mach-imx/clk-imx6q.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/clock/imx6q-clock.txt b/Documentation/devicetree/bindings/clock/imx6q-clock.txt index 492bd991d52a..bb71d4f8004b 100644 --- a/Documentation/devicetree/bindings/clock/imx6q-clock.txt +++ b/Documentation/devicetree/bindings/clock/imx6q-clock.txt @@ -187,9 +187,9 @@ clocks and IDs. pll3_usb_otg 172 pll4_audio 173 pll5_video 174 - pll6_mlb 175 + pll8_mlb 175 pll7_usb_host 176 - pll8_enet 177 + pll6_enet 177 ssi1_ipg 178 ssi2_ipg 179 ssi3_ipg 180 diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c index a37756c22bb4..0d60cb0e30fb 100644 --- a/arch/arm/mach-imx/clk-imx6q.c +++ b/arch/arm/mach-imx/clk-imx6q.c @@ -152,7 +152,7 @@ enum mx6q_clks { gpmi_bch_apb, gpmi_bch, gpmi_io, gpmi_apb, sata, sdma, spba, ssi1, ssi2, ssi3, uart_ipg, uart_serial, usboh3, usdhc1, usdhc2, usdhc3, usdhc4, vdo_axi, vpu_axi, cko1, pll1_sys, pll2_bus, pll3_usb_otg, - pll4_audio, pll5_video, pll6_mlb, pll7_usb_host, pll8_enet, ssi1_ipg, + pll4_audio, pll5_video, pll8_mlb, pll7_usb_host, pll6_enet, ssi1_ipg, ssi2_ipg, ssi3_ipg, rom, usbphy1, usbphy2, ldb_di0_div_3_5, ldb_di1_div_3_5, clk_max }; @@ -196,9 +196,9 @@ int __init mx6q_clocks_init(void) clk[pll3_usb_otg] = imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", base + 0x10, 0x2000, 0x3); clk[pll4_audio] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4_audio", "osc", base + 0x70, 0x2000, 0x7f); clk[pll5_video] = imx_clk_pllv3(IMX_PLLV3_AV, "pll5_video", "osc", base + 0xa0, 0x2000, 0x7f); - clk[pll6_mlb] = imx_clk_pllv3(IMX_PLLV3_MLB, "pll6_mlb", "osc", base + 0xd0, 0x2000, 0x0); + clk[pll6_enet] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll6_enet", "osc", base + 0xe0, 0x182000, 0x3); clk[pll7_usb_host] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7_usb_host","osc", base + 0x20, 0x2000, 0x3); - clk[pll8_enet] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll8_enet", "osc", base + 0xe0, 0x182000, 0x3); + clk[pll8_mlb] = imx_clk_pllv3(IMX_PLLV3_MLB, "pll8_mlb", "osc", base + 0xd0, 0x2000, 0x0); clk[usbphy1] = imx_clk_gate("usbphy1", "pll3_usb_otg", base + 0x10, 6); clk[usbphy2] = imx_clk_gate("usbphy2", "pll7_usb_host", base + 0x20, 6); @@ -358,7 +358,7 @@ int __init mx6q_clocks_init(void) clk[ldb_di1] = imx_clk_gate2("ldb_di1", "ldb_di1_podf", base + 0x74, 14); clk[ipu2_di1] = imx_clk_gate2("ipu2_di1", "ipu2_di1_sel", base + 0x74, 10); clk[hsi_tx] = imx_clk_gate2("hsi_tx", "hsi_tx_podf", base + 0x74, 16); - clk[mlb] = imx_clk_gate2("mlb", "pll6_mlb", base + 0x74, 18); + clk[mlb] = imx_clk_gate2("mlb", "pll8_mlb", base + 0x74, 18); clk[mmdc_ch0_axi] = imx_clk_gate2("mmdc_ch0_axi", "mmdc_ch0_axi_podf", base + 0x74, 20); clk[mmdc_ch1_axi] = imx_clk_gate2("mmdc_ch1_axi", "mmdc_ch1_axi_podf", base + 0x74, 22); clk[ocram] = imx_clk_gate2("ocram", "ahb", base + 0x74, 28); -- cgit v1.2.3 From 7a04092c733fd72ded1294719046cfdb31feced3 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 Nov 2012 14:42:31 +0100 Subject: ARM i.MX6: Fix ethernet PLL clocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In current code the ethernet PLL is not handled correctly. The PLL runs at 500MHz and has different outputs. Only the enet reference clock is implemented. This patch changes the PLL so that it outputs 500MHz and adds the additional outputs as dividers. This now matches the datasheet which says: > This PLL synthesizes a low jitter clock from 24 MHz reference clock. > The PLL outputs a 500 MHz clock. The reference clocks generated by this PLL are: > • Ref_PCIe = 125 MHz > • Ref_SATA = 100 MHz > • Ref_ethernet, which is configurable based on the PLL_ENET[1:0] register field. Signed-off-by: Sascha Hauer Acked-by: Shawn Guo --- .../devicetree/bindings/clock/imx6q-clock.txt | 5 ++ arch/arm/mach-imx/clk-imx6q.c | 20 ++++++- arch/arm/mach-imx/clk-pllv3.c | 63 +--------------------- 3 files changed, 25 insertions(+), 63 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/clock/imx6q-clock.txt b/Documentation/devicetree/bindings/clock/imx6q-clock.txt index bb71d4f8004b..d77b4e68dc42 100644 --- a/Documentation/devicetree/bindings/clock/imx6q-clock.txt +++ b/Documentation/devicetree/bindings/clock/imx6q-clock.txt @@ -198,6 +198,11 @@ clocks and IDs. usbphy2 183 ldb_di0_div_3_5 184 ldb_di1_div_3_5 185 + sata_ref 186 + sata_ref_100m 187 + pcie_ref 188 + pcie_ref_125m 189 + enet_ref 190 Examples: diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c index 0d60cb0e30fb..d8049d3f9801 100644 --- a/arch/arm/mach-imx/clk-imx6q.c +++ b/arch/arm/mach-imx/clk-imx6q.c @@ -154,6 +154,7 @@ enum mx6q_clks { usdhc4, vdo_axi, vpu_axi, cko1, pll1_sys, pll2_bus, pll3_usb_otg, pll4_audio, pll5_video, pll8_mlb, pll7_usb_host, pll6_enet, ssi1_ipg, ssi2_ipg, ssi3_ipg, rom, usbphy1, usbphy2, ldb_di0_div_3_5, ldb_di1_div_3_5, + sata_ref, sata_ref_100m, pcie_ref, pcie_ref_125m, enet_ref, clk_max }; @@ -164,6 +165,13 @@ static enum mx6q_clks const clks_init_on[] __initconst = { mmdc_ch0_axi, rom, }; +static struct clk_div_table clk_enet_ref_table[] = { + { .val = 0, .div = 20, }, + { .val = 1, .div = 10, }, + { .val = 2, .div = 5, }, + { .val = 3, .div = 4, }, +}; + int __init mx6q_clocks_init(void) { struct device_node *np; @@ -196,13 +204,23 @@ int __init mx6q_clocks_init(void) clk[pll3_usb_otg] = imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", base + 0x10, 0x2000, 0x3); clk[pll4_audio] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4_audio", "osc", base + 0x70, 0x2000, 0x7f); clk[pll5_video] = imx_clk_pllv3(IMX_PLLV3_AV, "pll5_video", "osc", base + 0xa0, 0x2000, 0x7f); - clk[pll6_enet] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll6_enet", "osc", base + 0xe0, 0x182000, 0x3); + clk[pll6_enet] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll6_enet", "osc", base + 0xe0, 0x2000, 0x3); clk[pll7_usb_host] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7_usb_host","osc", base + 0x20, 0x2000, 0x3); clk[pll8_mlb] = imx_clk_pllv3(IMX_PLLV3_MLB, "pll8_mlb", "osc", base + 0xd0, 0x2000, 0x0); clk[usbphy1] = imx_clk_gate("usbphy1", "pll3_usb_otg", base + 0x10, 6); clk[usbphy2] = imx_clk_gate("usbphy2", "pll7_usb_host", base + 0x20, 6); + clk[sata_ref] = imx_clk_fixed_factor("sata_ref", "pll6_enet", 1, 5); + clk[pcie_ref] = imx_clk_fixed_factor("pcie_ref", "pll6_enet", 1, 4); + + clk[sata_ref_100m] = imx_clk_gate("sata_ref_100m", "sata_ref", base + 0xe0, 20); + clk[pcie_ref_125m] = imx_clk_gate("pcie_ref_125m", "pcie_ref", base + 0xe0, 19); + + clk[enet_ref] = clk_register_divider_table(NULL, "enet_ref", "pll6_enet", 0, + base + 0xe0, 0, 2, 0, clk_enet_ref_table, + &imx_ccm_lock); + /* name parent_name reg idx */ clk[pll2_pfd0_352m] = imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0); clk[pll2_pfd1_594m] = imx_clk_pfd("pll2_pfd1_594m", "pll2_bus", base + 0x100, 1); diff --git a/arch/arm/mach-imx/clk-pllv3.c b/arch/arm/mach-imx/clk-pllv3.c index 36aac947bce1..59e74339ab08 100644 --- a/arch/arm/mach-imx/clk-pllv3.c +++ b/arch/arm/mach-imx/clk-pllv3.c @@ -287,66 +287,7 @@ static const struct clk_ops clk_pllv3_av_ops = { static unsigned long clk_pllv3_enet_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct clk_pllv3 *pll = to_clk_pllv3(hw); - u32 div = readl_relaxed(pll->base) & pll->div_mask; - - switch (div) { - case 0: - return 25000000; - case 1: - return 50000000; - case 2: - return 100000000; - case 3: - return 125000000; - } - - return 0; -} - -static long clk_pllv3_enet_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) -{ - if (rate >= 125000000) - rate = 125000000; - else if (rate >= 100000000) - rate = 100000000; - else if (rate >= 50000000) - rate = 50000000; - else - rate = 25000000; - return rate; -} - -static int clk_pllv3_enet_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - struct clk_pllv3 *pll = to_clk_pllv3(hw); - u32 val, div; - - switch (rate) { - case 25000000: - div = 0; - break; - case 50000000: - div = 1; - break; - case 100000000: - div = 2; - break; - case 125000000: - div = 3; - break; - default: - return -EINVAL; - } - - val = readl_relaxed(pll->base); - val &= ~pll->div_mask; - val |= div; - writel_relaxed(val, pll->base); - - return 0; + return 500000000; } static const struct clk_ops clk_pllv3_enet_ops = { @@ -355,8 +296,6 @@ static const struct clk_ops clk_pllv3_enet_ops = { .enable = clk_pllv3_enable, .disable = clk_pllv3_disable, .recalc_rate = clk_pllv3_enet_recalc_rate, - .round_rate = clk_pllv3_enet_round_rate, - .set_rate = clk_pllv3_enet_set_rate, }; static const struct clk_ops clk_pllv3_mlb_ops = { -- cgit v1.2.3 From ce20364bf75b0e91156698eea49f1e8586b212c1 Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Thu, 25 Oct 2012 09:39:13 +0530 Subject: pwm: Add SPEAr PWM chip driver support Add support for PWM chips present on SPEAr platforms. These PWM chips support 4 channel output with programmable duty cycle and frequency. More details on these PWM chips can be obtained from relevant chapter of reference manual, present at following[1] location. 1. http://www.st.com/internet/mcu/product/251211.jsp Cc: Thierry Reding Signed-off-by: Shiraz Hashim Signed-off-by: Viresh Kumar Reviewed-by: Vipin Kumar Acked-by: Viresh Kumar Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/spear-pwm.txt | 18 ++ drivers/pwm/Kconfig | 11 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-spear.c | 276 +++++++++++++++++++++ 4 files changed, 306 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/spear-pwm.txt create mode 100644 drivers/pwm/pwm-spear.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pwm/spear-pwm.txt b/Documentation/devicetree/bindings/pwm/spear-pwm.txt new file mode 100644 index 000000000000..3ac779d83386 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/spear-pwm.txt @@ -0,0 +1,18 @@ +== ST SPEAr SoC PWM controller == + +Required properties: +- compatible: should be one of: + - "st,spear320-pwm" + - "st,spear1340-pwm" +- reg: physical base address and length of the controller's registers +- #pwm-cells: number of cells used to specify PWM which is fixed to 2 on + SPEAr. The first cell specifies the per-chip index of the PWM to use and + the second cell is the period in nanoseconds. + +Example: + + pwm: pwm@a8000000 { + compatible ="st,spear320-pwm"; + reg = <0xa8000000 0x1000>; + #pwm-cells = <2>; + }; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index ed81720e7b2b..6e556c7da81e 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -112,6 +112,17 @@ config PWM_SAMSUNG To compile this driver as a module, choose M here: the module will be called pwm-samsung. +config PWM_SPEAR + tristate "STMicroelectronics SPEAr PWM support" + depends on PLAT_SPEAR + depends on OF + help + Generic PWM framework driver for the PWM controller on ST + SPEAr SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-spear. + config PWM_TEGRA tristate "NVIDIA Tegra PWM support" depends on ARCH_TEGRA diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index acfe4821c58b..3b3f4c9aa4ef 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_PWM_MXS) += pwm-mxs.o obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o obj-$(CONFIG_PWM_PXA) += pwm-pxa.o obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o +obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o diff --git a/drivers/pwm/pwm-spear.c b/drivers/pwm/pwm-spear.c new file mode 100644 index 000000000000..6a8fd9b5dc42 --- /dev/null +++ b/drivers/pwm/pwm-spear.c @@ -0,0 +1,276 @@ +/* + * ST Microelectronics SPEAr Pulse Width Modulator driver + * + * Copyright (C) 2012 ST Microelectronics + * Shiraz Hashim + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NUM_PWM 4 + +/* PWM registers and bits definitions */ +#define PWMCR 0x00 /* Control Register */ +#define PWMCR_PWM_ENABLE 0x1 +#define PWMCR_PRESCALE_SHIFT 2 +#define PWMCR_MIN_PRESCALE 0x00 +#define PWMCR_MAX_PRESCALE 0x3FFF + +#define PWMDCR 0x04 /* Duty Cycle Register */ +#define PWMDCR_MIN_DUTY 0x0001 +#define PWMDCR_MAX_DUTY 0xFFFF + +#define PWMPCR 0x08 /* Period Register */ +#define PWMPCR_MIN_PERIOD 0x0001 +#define PWMPCR_MAX_PERIOD 0xFFFF + +/* Following only available on 13xx SoCs */ +#define PWMMCR 0x3C /* Master Control Register */ +#define PWMMCR_PWM_ENABLE 0x1 + +/** + * struct spear_pwm_chip - struct representing pwm chip + * + * @mmio_base: base address of pwm chip + * @clk: pointer to clk structure of pwm chip + * @chip: linux pwm chip representation + * @dev: pointer to device structure of pwm chip + */ +struct spear_pwm_chip { + void __iomem *mmio_base; + struct clk *clk; + struct pwm_chip chip; + struct device *dev; +}; + +static inline struct spear_pwm_chip *to_spear_pwm_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct spear_pwm_chip, chip); +} + +static inline u32 spear_pwm_readl(struct spear_pwm_chip *chip, unsigned int num, + unsigned long offset) +{ + return readl_relaxed(chip->mmio_base + (num << 4) + offset); +} + +static inline void spear_pwm_writel(struct spear_pwm_chip *chip, + unsigned int num, unsigned long offset, + unsigned long val) +{ + writel_relaxed(val, chip->mmio_base + (num << 4) + offset); +} + +int spear_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, + int period_ns) +{ + struct spear_pwm_chip *pc = to_spear_pwm_chip(chip); + u64 val, div, clk_rate; + unsigned long prescale = PWMCR_MIN_PRESCALE, pv, dc; + int ret; + + /* + * Find pv, dc and prescale to suit duty_ns and period_ns. This is done + * according to formulas described below: + * + * period_ns = 10^9 * (PRESCALE + 1) * PV / PWM_CLK_RATE + * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE + * + * PV = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1)) + * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1)) + */ + clk_rate = clk_get_rate(pc->clk); + while (1) { + div = 1000000000; + div *= 1 + prescale; + val = clk_rate * period_ns; + pv = div64_u64(val, div); + val = clk_rate * duty_ns; + dc = div64_u64(val, div); + + /* if duty_ns and period_ns are not achievable then return */ + if (pv < PWMPCR_MIN_PERIOD || dc < PWMDCR_MIN_DUTY) + return -EINVAL; + + /* + * if pv and dc have crossed their upper limit, then increase + * prescale and recalculate pv and dc. + */ + if (pv > PWMPCR_MAX_PERIOD || dc > PWMDCR_MAX_DUTY) { + if (++prescale > PWMCR_MAX_PRESCALE) + return -EINVAL; + continue; + } + break; + } + + /* + * NOTE: the clock to PWM has to be enabled first before writing to the + * registers. + */ + ret = clk_enable(pc->clk); + if (ret) + return ret; + + spear_pwm_writel(pc, pwm->hwpwm, PWMCR, + prescale << PWMCR_PRESCALE_SHIFT); + spear_pwm_writel(pc, pwm->hwpwm, PWMDCR, dc); + spear_pwm_writel(pc, pwm->hwpwm, PWMPCR, pv); + clk_disable(pc->clk); + + return 0; +} + +static int spear_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct spear_pwm_chip *pc = to_spear_pwm_chip(chip); + int rc = 0; + u32 val; + + rc = clk_enable(pc->clk); + if (!rc) + return rc; + + val = spear_pwm_readl(pc, pwm->hwpwm, PWMCR); + val |= PWMCR_PWM_ENABLE; + spear_pwm_writel(pc, pwm->hwpwm, PWMCR, val); + + return 0; +} + +static void spear_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct spear_pwm_chip *pc = to_spear_pwm_chip(chip); + u32 val; + + val = spear_pwm_readl(pc, pwm->hwpwm, PWMCR); + val &= ~PWMCR_PWM_ENABLE; + spear_pwm_writel(pc, pwm->hwpwm, PWMCR, val); + + clk_disable(pc->clk); +} + +static const struct pwm_ops spear_pwm_ops = { + .config = spear_pwm_config, + .enable = spear_pwm_enable, + .disable = spear_pwm_disable, + .owner = THIS_MODULE, +}; + +static int spear_pwm_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct spear_pwm_chip *pc; + struct resource *r; + int ret; + u32 val; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no memory resources defined\n"); + return -ENODEV; + } + + pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); + if (!pc) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r); + if (!pc->mmio_base) + return -EADDRNOTAVAIL; + + pc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pc->clk)) + return PTR_ERR(pc->clk); + + pc->dev = &pdev->dev; + platform_set_drvdata(pdev, pc); + + pc->chip.dev = &pdev->dev; + pc->chip.ops = &spear_pwm_ops; + pc->chip.base = -1; + pc->chip.npwm = NUM_PWM; + + ret = clk_prepare(pc->clk); + if (!ret) + return ret; + + if (of_device_is_compatible(np, "st,spear1340-pwm")) { + ret = clk_enable(pc->clk); + if (!ret) { + clk_unprepare(pc->clk); + return ret; + } + /* + * Following enables PWM chip, channels would still be + * enabled individually through their control register + */ + val = readl_relaxed(pc->mmio_base + PWMMCR); + val |= PWMMCR_PWM_ENABLE; + writel_relaxed(val, pc->mmio_base + PWMMCR); + + clk_disable(pc->clk); + } + + ret = pwmchip_add(&pc->chip); + if (!ret) { + clk_unprepare(pc->clk); + dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); + } + + return ret; +} + +static int spear_pwm_remove(struct platform_device *pdev) +{ + struct spear_pwm_chip *pc = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < NUM_PWM; i++) + pwm_disable(&pc->chip.pwms[i]); + + /* clk was prepared in probe, hence unprepare it here */ + clk_unprepare(pc->clk); + return pwmchip_remove(&pc->chip); +} + +static struct of_device_id spear_pwm_of_match[] = { + { .compatible = "st,spear320-pwm" }, + { .compatible = "st,spear1340-pwm" }, + { } +}; + +MODULE_DEVICE_TABLE(of, spear_pwm_of_match); + +static struct platform_driver spear_pwm_driver = { + .driver = { + .name = "spear-pwm", + .of_match_table = spear_pwm_of_match, + }, + .probe = spear_pwm_probe, + .remove = spear_pwm_remove, +}; + +module_platform_driver(spear_pwm_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Shiraz Hashim "); +MODULE_AUTHOR("Viresh Kumar "); +MODULE_ALIAS("platform:spear-pwm"); -- cgit v1.2.3 From 83af24027b3df1af5c5a9aa9adcdcfeb3429d3be Mon Sep 17 00:00:00 2001 From: "Philip, Avinash" Date: Wed, 21 Nov 2012 13:10:44 +0530 Subject: pwm: Device tree support for PWM polarity Add support for encoding PWM properties in bit encoded form with of_pwm_xlate_with_flags() function support. Platforms require platform specific PWM properties has to populate in 3rd cell of the pwm-specifier and PWM driver should also set .of_xlate support with this function. Currently PWM property polarity encoded in bit position 0 of the third cell in pwm-specifier. Signed-off-by: Philip, Avinash Acked-by: Grant Likely Signed-off-by: Thierry Reding --- Documentation/devicetree/bindings/pwm/pwm.txt | 17 +++++++++++++--- drivers/pwm/core.c | 28 +++++++++++++++++++++++++++ include/linux/pwm.h | 3 +++ 3 files changed, 45 insertions(+), 3 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt index 73ec962bfe8c..06e67247859a 100644 --- a/Documentation/devicetree/bindings/pwm/pwm.txt +++ b/Documentation/devicetree/bindings/pwm/pwm.txt @@ -37,10 +37,21 @@ device: pwm-names = "backlight"; }; +Note that in the example above, specifying the "pwm-names" is redundant +because the name "backlight" would be used as fallback anyway. + pwm-specifier typically encodes the chip-relative PWM number and the PWM -period in nanoseconds. Note that in the example above, specifying the -"pwm-names" is redundant because the name "backlight" would be used as -fallback anyway. +period in nanoseconds. + +Optionally, the pwm-specifier can encode a number of flags in a third cell: +- bit 0: PWM signal polarity (0: normal polarity, 1: inverse polarity) + +Example with optional PWM specifier for inverse polarity + + bl: backlight { + pwms = <&pwm 0 5000000 1>; + pwm-names = "backlight"; + }; 2) PWM controller nodes ----------------------- diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index f5acdaa52707..780cb6b8a8f0 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -32,6 +32,9 @@ #define MAX_PWMS 1024 +/* flags in the third cell of the DT PWM specifier */ +#define PWM_SPEC_POLARITY (1 << 0) + static DEFINE_MUTEX(pwm_lookup_lock); static LIST_HEAD(pwm_lookup_list); static DEFINE_MUTEX(pwm_lock); @@ -129,6 +132,31 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label) return 0; } +struct pwm_device * +of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args) +{ + struct pwm_device *pwm; + + if (pc->of_pwm_n_cells < 3) + return ERR_PTR(-EINVAL); + + if (args->args[0] >= pc->npwm) + return ERR_PTR(-EINVAL); + + pwm = pwm_request_from_chip(pc, args->args[0], NULL); + if (IS_ERR(pwm)) + return pwm; + + pwm_set_period(pwm, args->args[1]); + + if (args->args[2] & PWM_SPEC_POLARITY) + pwm_set_polarity(pwm, PWM_POLARITY_INVERSED); + else + pwm_set_polarity(pwm, PWM_POLARITY_NORMAL); + + return pwm; +} + static struct pwm_device * of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) { diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 112b31436848..6d661f32e0e4 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -171,6 +171,9 @@ struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, unsigned int index, const char *label); +struct pwm_device *of_pwm_xlate_with_flags(struct pwm_chip *pc, + const struct of_phandle_args *args); + struct pwm_device *pwm_get(struct device *dev, const char *consumer); void pwm_put(struct pwm_device *pwm); -- cgit v1.2.3 From cc899bc3dce9101920b120abe413299f84233e29 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 22 Nov 2012 12:24:24 +0000 Subject: Documentation: Fix historical inconsistency in STMPE DT doc Previously a generic binding 'i2c-client-wake' was created which enabled I2C devices to register themselves as wake-up devices. This binding was later over-thrown by 'wakeup-source'. The STMPE driver was fixed-up, but the document was neglected. This patch aims to rectify that. Cc: Samuel Ortiz Cc: devicetree-discuss@lists.ozlabs.org Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- Documentation/devicetree/bindings/mfd/stmpe.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mfd/stmpe.txt b/Documentation/devicetree/bindings/mfd/stmpe.txt index 8f0aeda2e326..c98450887d5e 100644 --- a/Documentation/devicetree/bindings/mfd/stmpe.txt +++ b/Documentation/devicetree/bindings/mfd/stmpe.txt @@ -8,7 +8,7 @@ Optional properties: - interrupts : The interrupt outputs from the controller - interrupt-controller : Marks the device node as an interrupt controller - interrupt-parent : Specifies which IRQ controller we're connected to - - i2c-client-wake : Marks the input device as wakable + - wakeup-source : Marks the input device as wakable - st,autosleep-timeout : Valid entries (ms); 4, 16, 32, 64, 128, 256, 512 and 1024 Example: @@ -20,6 +20,6 @@ Example: interrupt-parent = <&gpio6>; interrupt-controller; - i2c-client-wake; + wakeup-source; st,autosleep-timeout = <1024>; }; -- cgit v1.2.3 From 385a4c2e286571ba824ee5312f506f198866c3b5 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 14 Nov 2012 18:28:39 +0800 Subject: ASoC: ak4104: add reset line property to DT bindings This device doesn't have a pdata definition for legacy boards, and unless anyone need to control the reset GPIO, it's not worth adding one. So this feature is only available to DT users for now. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/ak4104.txt | 5 +++++ sound/soc/codecs/ak4104.c | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/ak4104.txt b/Documentation/devicetree/bindings/sound/ak4104.txt index 1f8e5066d443..b902ee39cf89 100644 --- a/Documentation/devicetree/bindings/sound/ak4104.txt +++ b/Documentation/devicetree/bindings/sound/ak4104.txt @@ -8,6 +8,11 @@ Required properties: - reg : The chip select number on the SPI bus +Optional properties: + + - reset-gpio : a GPIO spec for the reset pin. If specified, it will be + deasserted before communication to the device starts. + Example: spdif: ak4104@0 { diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index d4d485808894..eec086b226f7 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include /* AK4104 registers addresses */ @@ -204,6 +206,7 @@ static const struct regmap_config ak4104_regmap = { static int ak4104_spi_probe(struct spi_device *spi) { + struct device_node *np = spi->dev.of_node; struct ak4104_private *ak4104; unsigned int val; int ret; @@ -225,6 +228,20 @@ static int ak4104_spi_probe(struct spi_device *spi) return ret; } + if (np) { + enum of_gpio_flags flags; + int gpio = of_get_named_gpio_flags(np, "reset-gpio", 0, &flags); + + if (gpio_is_valid(gpio)) { + ret = devm_gpio_request_one(&spi->dev, gpio, + flags & OF_GPIO_ACTIVE_LOW ? + GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH, + "ak4104 reset"); + if (ret < 0) + return ret; + } + } + /* read the 'reserved' register - according to the datasheet, it * should contain 0x5b. Not a good way to verify the presence of * the device, but there is no hardware ID register. */ -- cgit v1.2.3 From 77dae54ab385033e488d8b07045bc7f8d931740f Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sat, 20 Oct 2012 13:10:00 +0200 Subject: ARM: Kirkwood: ehci-orion: Add device tree binding Based on previous work by Michael Walle and Jason Cooper. Made their work actually work, which required added interrupt from DT and auxdata, along with setting the dma_mask, which DT does not currently do. Signed-off-by: Andrew Lunn Tested-by: Sebastian Hesselbarth Acked-by: Alan Stern Signed-off-by: Jason Cooper --- .../devicetree/bindings/usb/ehci-orion.txt | 15 +++++++++ drivers/usb/host/ehci-orion.c | 38 ++++++++++++++++++++-- 2 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/usb/ehci-orion.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/usb/ehci-orion.txt b/Documentation/devicetree/bindings/usb/ehci-orion.txt new file mode 100644 index 000000000000..6bc09ec14c4d --- /dev/null +++ b/Documentation/devicetree/bindings/usb/ehci-orion.txt @@ -0,0 +1,15 @@ +* EHCI controller, Orion Marvell variants + +Required properties: +- compatible: must be "marvell,orion-ehci" +- reg: physical base address of the controller and length of memory mapped + region. +- interrupts: The EHCI interrupt + +Example: + + ehci@50000 { + compatible = "marvell,orion-ehci"; + reg = <0x50000 0x1000>; + interrupts = <19>; + }; diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 9c2717d66730..e7e8275028d3 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -14,6 +14,9 @@ #include #include #include +#include +#include +#include #define rdl(off) __raw_readl(hcd->regs + (off)) #define wrl(off, val) __raw_writel((val), hcd->regs + (off)) @@ -181,6 +184,8 @@ ehci_orion_conf_mbus_windows(struct usb_hcd *hcd, } } +static u64 ehci_orion_dma_mask = DMA_BIT_MASK(32); + static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) { struct orion_ehci_data *pd = pdev->dev.platform_data; @@ -191,13 +196,17 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) struct clk *clk; void __iomem *regs; int irq, err; + enum orion_ehci_phy_ver phy_version; if (usb_disabled()) return -ENODEV; pr_debug("Initializing Orion-SoC USB Host Controller\n"); - irq = platform_get_irq(pdev, 0); + if (pdev->dev.of_node) + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + else + irq = platform_get_irq(pdev, 0); if (irq <= 0) { dev_err(&pdev->dev, "Found HC with no IRQ. Check %s setup!\n", @@ -215,6 +224,14 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) goto err1; } + /* + * Right now device-tree probed devices don't get dma_mask + * set. Since shared usb code relies on it, set it here for + * now. Once we have dma capability bindings this can go away. + */ + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &ehci_orion_dma_mask; + if (!request_mem_region(res->start, resource_size(res), ehci_orion_hc_driver.description)) { dev_dbg(&pdev->dev, "controller already in use\n"); @@ -262,7 +279,12 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) /* * setup Orion USB controller. */ - switch (pd->phy_version) { + if (pdev->dev.of_node) + phy_version = EHCI_PHY_NA; + else + phy_version = pd->phy_version; + + switch (phy_version) { case EHCI_PHY_NA: /* dont change USB phy settings */ break; case EHCI_PHY_ORION: @@ -317,9 +339,19 @@ static int __exit ehci_orion_drv_remove(struct platform_device *pdev) MODULE_ALIAS("platform:orion-ehci"); +static const struct of_device_id ehci_orion_dt_ids[] __devinitdata = { + { .compatible = "marvell,orion-ehci", }, + {}, +}; +MODULE_DEVICE_TABLE(of, ehci_orion_dt_ids); + static struct platform_driver ehci_orion_driver = { .probe = ehci_orion_drv_probe, .remove = __exit_p(ehci_orion_drv_remove), .shutdown = usb_hcd_platform_shutdown, - .driver.name = "orion-ehci", + .driver = { + .name = "orion-ehci", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ehci_orion_dt_ids), + }, }; -- cgit v1.2.3 From 96ff0f5c7efd4a2205c48a76a6a1fcd2731e6128 Mon Sep 17 00:00:00 2001 From: Jamie Lentin Date: Sat, 17 Nov 2012 09:51:04 +0100 Subject: power: Add simple poweroff-gpio driver Given appropriate devicetree bindings, this driver registers a pm_power_off function to set a GPIO line high/low to power down your board. Signed-off-by: Jamie Lentin Signed-off-by: Andrew Lunn Tested-by:Simon Baatz Signed-off-by: Jason Cooper --- .../devicetree/bindings/gpio/gpio-poweroff.txt | 22 ++++ drivers/power/Kconfig | 3 + drivers/power/Makefile | 1 + drivers/power/reset/Kconfig | 15 +++ drivers/power/reset/Makefile | 1 + drivers/power/reset/gpio-poweroff.c | 129 +++++++++++++++++++++ 6 files changed, 171 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/gpio-poweroff.txt create mode 100644 drivers/power/reset/Kconfig create mode 100644 drivers/power/reset/Makefile create mode 100644 drivers/power/reset/gpio-poweroff.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/gpio/gpio-poweroff.txt b/Documentation/devicetree/bindings/gpio/gpio-poweroff.txt new file mode 100644 index 000000000000..558cdf3c9abc --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-poweroff.txt @@ -0,0 +1,22 @@ +GPIO line that should be set high/low to power off a device + +Required properties: +- compatible : should be "gpio-poweroff". +- gpios : The GPIO to set high/low, see "gpios property" in + Documentation/devicetree/bindings/gpio/gpio.txt. If the pin should be + low to power down the board set it to "Active Low", otherwise set + gpio to "Active High". + +Optional properties: +- input : Initially configure the GPIO line as an input. Only reconfigure + it to an output when the pm_power_off function is called. If this optional + property is not specified, the GPIO is initialized as an output in its + inactive state. + + +Examples: + +gpio-poweroff { + compatible = "gpio-poweroff"; + gpios = <&gpio 4 0>; /* GPIO 4 Active Low */ +}; diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 49a893972318..b1d956d81f0c 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -335,6 +335,9 @@ config AB8500_BATTERY_THERM_ON_BATCTRL help Say Y to enable battery temperature measurements using thermistor connected on BATCTRL ADC. + +source "drivers/power/reset/Kconfig" + endif # POWER_SUPPLY source "drivers/power/avs/Kconfig" diff --git a/drivers/power/Makefile b/drivers/power/Makefile index b949cf85590c..f1d99f4a0bc3 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -49,3 +49,4 @@ obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o obj-$(CONFIG_POWER_AVS) += avs/ obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o +obj-$(CONFIG_POWER_RESET) += reset/ diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig new file mode 100644 index 000000000000..6461b489fb09 --- /dev/null +++ b/drivers/power/reset/Kconfig @@ -0,0 +1,15 @@ +menuconfig POWER_RESET + bool "Board level reset or power off" + help + Provides a number of drivers which either reset a complete board + or shut it down, by manipulating the main power supply on the board. + + Say Y here to enable board reset and power off + +config POWER_RESET_GPIO + bool "GPIO power-off driver" + depends on OF_GPIO && POWER_RESET + help + This driver supports turning off your board via a GPIO line. + If your board needs a GPIO high/low to power down, say Y and + create a binding in your devicetree. diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile new file mode 100644 index 000000000000..751488a4a0c5 --- /dev/null +++ b/drivers/power/reset/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o diff --git a/drivers/power/reset/gpio-poweroff.c b/drivers/power/reset/gpio-poweroff.c new file mode 100644 index 000000000000..0491e5335d02 --- /dev/null +++ b/drivers/power/reset/gpio-poweroff.c @@ -0,0 +1,129 @@ +/* + * Toggles a GPIO pin to power down a device + * + * Jamie Lentin + * Andrew Lunn + * + * Copyright (C) 2012 Jamie Lentin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Hold configuration here, cannot be more than one instance of the driver + * since pm_power_off itself is global. + */ +static int gpio_num = -1; +static int gpio_active_low; + +static void gpio_poweroff_do_poweroff(void) +{ + BUG_ON(gpio_num == -1); + + /* drive it active */ + gpio_direction_output(gpio_num, !gpio_active_low); + mdelay(100); + /* rising edge or drive inactive */ + gpio_set_value(gpio_num, gpio_active_low); + mdelay(100); + /* falling edge */ + gpio_set_value(gpio_num, !gpio_active_low); + + /* give it some time */ + mdelay(3000); + + WARN_ON(1); +} + +static int __devinit gpio_poweroff_probe(struct platform_device *pdev) +{ + enum of_gpio_flags flags; + bool input = false; + int ret; + + /* If a pm_power_off function has already been added, leave it alone */ + if (pm_power_off != NULL) { + pr_err("%s: pm_power_off function already registered", + __func__); + return -EBUSY; + } + + gpio_num = of_get_gpio_flags(pdev->dev.of_node, 0, &flags); + if (gpio_num < 0) { + pr_err("%s: Could not get GPIO configuration: %d", + __func__, gpio_num); + return -ENODEV; + } + gpio_active_low = flags & OF_GPIO_ACTIVE_LOW; + + if (of_get_property(pdev->dev.of_node, "input", NULL)) + input = true; + + ret = gpio_request(gpio_num, "poweroff-gpio"); + if (ret) { + pr_err("%s: Could not get GPIO %d", __func__, gpio_num); + return ret; + } + if (input) { + if (gpio_direction_input(gpio_num)) { + pr_err("Could not set direction of GPIO %d to input", + gpio_num); + goto err; + } + } else { + if (gpio_direction_output(gpio_num, gpio_active_low)) { + pr_err("Could not set direction of GPIO %d", gpio_num); + goto err; + } + } + + pm_power_off = &gpio_poweroff_do_poweroff; + return 0; + +err: + gpio_free(gpio_num); + return -ENODEV; +} + +static int __devexit gpio_poweroff_remove(struct platform_device *pdev) +{ + if (gpio_num != -1) + gpio_free(gpio_num); + if (pm_power_off == &gpio_poweroff_do_poweroff) + pm_power_off = NULL; + + return 0; +} + +static const struct of_device_id of_gpio_poweroff_match[] = { + { .compatible = "gpio-poweroff", }, + {}, +}; + +static struct platform_driver gpio_poweroff_driver = { + .probe = gpio_poweroff_probe, + .remove = __devexit_p(gpio_poweroff_remove), + .driver = { + .name = "poweroff-gpio", + .owner = THIS_MODULE, + .of_match_table = of_gpio_poweroff_match, + }, +}; + +module_platform_driver(gpio_poweroff_driver); + +MODULE_AUTHOR("Jamie Lentin "); +MODULE_DESCRIPTION("GPIO poweroff driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:poweroff-gpio"); -- cgit v1.2.3 From 4e5bc99ce97fec27466a0654b35d4d3ae0afb054 Mon Sep 17 00:00:00 2001 From: Valentin Longchamp Date: Sat, 17 Nov 2012 17:00:55 +0100 Subject: ARM: Kirkwood: support 98DX412x kirkwoods with pinctrl The Marvell 98DX412x SoC embed a kirkwood variant that does not have pinctrl support yet. Even though this kirkwood is very similar to the 88f6281, on the MPP front a lot of pins are not available. That's why a new kirkwood pinctrl variant is needed. Signed-off-by: Valentin Longchamp Signed-off-by: Andrew Lunn Signed-off-by: Jason Cooper --- .../bindings/pinctrl/marvell,kirkwood-pinctrl.txt | 39 ++ arch/arm/boot/dts/kirkwood-98dx4122.dtsi | 31 ++ drivers/pinctrl/mvebu/pinctrl-kirkwood.c | 568 +++++++++++---------- 3 files changed, 360 insertions(+), 278 deletions(-) create mode 100644 arch/arm/boot/dts/kirkwood-98dx4122.dtsi (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pinctrl/marvell,kirkwood-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/marvell,kirkwood-pinctrl.txt index 361bccb7ec89..95daf6335c37 100644 --- a/Documentation/devicetree/bindings/pinctrl/marvell,kirkwood-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/marvell,kirkwood-pinctrl.txt @@ -7,8 +7,10 @@ Required properties: - compatible: "marvell,88f6180-pinctrl", "marvell,88f6190-pinctrl", "marvell,88f6192-pinctrl", "marvell,88f6281-pinctrl", "marvell,88f6282-pinctrl" + "marvell,98dx4122-pinctrl" This driver supports all kirkwood variants, i.e. 88f6180, 88f619x, and 88f628x. +It also support the 88f6281-based variant in the 98dx412x Bobcat SoCs. Available mpp pins/groups and functions: Note: brackets (x) are not part of the mpp name for marvell,function and given @@ -277,3 +279,40 @@ mpp46 46 gpio, ts(mp10), tdm(fs), lcd(hsync) mpp47 47 gpio, ts(mp11), tdm(drx), lcd(vsync) mpp48 48 gpio, ts(mp12), tdm(dtx), lcd(d16) mpp49 49 gpo, tdm(rx0ql), pex(clkreq), lcd(d17) + +* Marvell Bobcat 98dx4122 + +name pins functions +================================================================================ +mpp0 0 gpio, nand(io2), spi(cs) +mpp1 1 gpo, nand(io3), spi(mosi) +mpp2 2 gpo, nand(io4), spi(sck) +mpp3 3 gpo, nand(io5), spi(miso) +mpp4 4 gpio, nand(io6), uart0(rxd) +mpp5 5 gpo, nand(io7), uart0(txd) +mpp6 6 sysrst(out), spi(mosi) +mpp7 7 gpo, pex(rsto), spi(cs) +mpp8 8 gpio, twsi0(sda), uart0(rts), uart1(rts) +mpp9 9 gpio, twsi(sck), uart0(cts), uart1(cts) +mpp10 10 gpo, spi(sck), uart0(txd) +mpp11 11 gpio, spi(miso), uart0(rxd) +mpp13 13 gpio, uart1(txd) +mpp14 14 gpio, uart1(rxd) +mpp15 15 gpio, uart0(rts) +mpp16 16 gpio, uart0(cts) +mpp18 18 gpo, nand(io0) +mpp19 19 gpo, nand(io1) +mpp34 34 gpio +mpp35 35 gpio +mpp36 36 gpio +mpp37 37 gpio +mpp38 38 gpio +mpp39 39 gpio +mpp40 40 gpio +mpp41 41 gpio +mpp42 42 gpio +mpp43 43 gpio +mpp44 44 gpio +mpp45 45 gpio +mpp49 49 gpio + diff --git a/arch/arm/boot/dts/kirkwood-98dx4122.dtsi b/arch/arm/boot/dts/kirkwood-98dx4122.dtsi new file mode 100644 index 000000000000..3271e4c8ea07 --- /dev/null +++ b/arch/arm/boot/dts/kirkwood-98dx4122.dtsi @@ -0,0 +1,31 @@ +/ { + ocp@f1000000 { + pinctrl: pinctrl@10000 { + compatible = "marvell,98dx4122-pinctrl"; + reg = <0x10000 0x20>; + + pmx_nand: pmx-nand { + marvell,pins = "mpp0", "mpp1", "mpp2", "mpp3", + "mpp4", "mpp5", "mpp18", + "mpp19"; + marvell,function = "nand"; + }; + pmx_spi: pmx-spi { + marvell,pins = "mpp0", "mpp1", "mpp2", "mpp3"; + marvell,function = "spi"; + }; + pmx_twsi0: pmx-twsi0 { + marvell,pins = "mpp8", "mpp9"; + marvell,function = "twsi0"; + }; + pmx_uart0: pmx-uart0 { + marvell,pins = "mpp10", "mpp11"; + marvell,function = "uart0"; + }; + pmx_uart1: pmx-uart1 { + marvell,pins = "mpp13", "mpp14"; + marvell,function = "uart1"; + }; + }; + }; +}; diff --git a/drivers/pinctrl/mvebu/pinctrl-kirkwood.c b/drivers/pinctrl/mvebu/pinctrl-kirkwood.c index 9a74ef674a0e..fa6ce31c94d9 100644 --- a/drivers/pinctrl/mvebu/pinctrl-kirkwood.c +++ b/drivers/pinctrl/mvebu/pinctrl-kirkwood.c @@ -21,340 +21,341 @@ #include "pinctrl-mvebu.h" -#define V(f6180, f6190, f6192, f6281, f6282) \ +#define V(f6180, f6190, f6192, f6281, f6282, dx4122) \ ((f6180 << 0) | (f6190 << 1) | (f6192 << 2) | \ - (f6281 << 3) | (f6282 << 4)) + (f6281 << 3) | (f6282 << 4) | (dx4122 << 5)) enum kirkwood_variant { - VARIANT_MV88F6180 = V(1, 0, 0, 0, 0), - VARIANT_MV88F6190 = V(0, 1, 0, 0, 0), - VARIANT_MV88F6192 = V(0, 0, 1, 0, 0), - VARIANT_MV88F6281 = V(0, 0, 0, 1, 0), - VARIANT_MV88F6282 = V(0, 0, 0, 0, 1), + VARIANT_MV88F6180 = V(1, 0, 0, 0, 0, 0), + VARIANT_MV88F6190 = V(0, 1, 0, 0, 0, 0), + VARIANT_MV88F6192 = V(0, 0, 1, 0, 0, 0), + VARIANT_MV88F6281 = V(0, 0, 0, 1, 0, 0), + VARIANT_MV88F6282 = V(0, 0, 0, 0, 1, 0), + VARIANT_MV98DX4122 = V(0, 0, 0, 0, 0, 1), }; static struct mvebu_mpp_mode mv88f6xxx_mpp_modes[] = { MPP_MODE(0, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "nand", "io2", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "spi", "cs", V(1, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "nand", "io2", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "spi", "cs", V(1, 1, 1, 1, 1, 1))), MPP_MODE(1, - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "nand", "io3", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "spi", "mosi", V(1, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "nand", "io3", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "spi", "mosi", V(1, 1, 1, 1, 1, 1))), MPP_MODE(2, - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "nand", "io4", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "spi", "sck", V(1, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "nand", "io4", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "spi", "sck", V(1, 1, 1, 1, 1, 1))), MPP_MODE(3, - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "nand", "io5", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "spi", "miso", V(1, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "nand", "io5", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "spi", "miso", V(1, 1, 1, 1, 1, 1))), MPP_MODE(4, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "nand", "io6", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "uart0", "rxd", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x5, "sata1", "act", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "hsync", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xd, "ptp", "clk", V(1, 1, 1, 1, 0))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "nand", "io6", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "uart0", "rxd", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x5, "sata1", "act", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "hsync", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xd, "ptp", "clk", V(1, 1, 1, 1, 0, 0))), MPP_MODE(5, - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "nand", "io7", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "uart0", "txd", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "ptp", "trig", V(1, 1, 1, 1, 0)), - MPP_VAR_FUNCTION(0x5, "sata0", "act", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "vsync", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "nand", "io7", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "uart0", "txd", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x4, "ptp", "trig", V(1, 1, 1, 1, 0, 0)), + MPP_VAR_FUNCTION(0x5, "sata0", "act", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "vsync", V(0, 0, 0, 0, 1, 0))), MPP_MODE(6, - MPP_VAR_FUNCTION(0x0, "sysrst", "out", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "spi", "mosi", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "ptp", "trig", V(1, 1, 1, 1, 0))), + MPP_VAR_FUNCTION(0x0, "sysrst", "out", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "spi", "mosi", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "ptp", "trig", V(1, 1, 1, 1, 0, 0))), MPP_MODE(7, - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "pex", "rsto", V(1, 1, 1, 1, 0)), - MPP_VAR_FUNCTION(0x2, "spi", "cs", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ptp", "trig", V(1, 1, 1, 1, 0)), - MPP_VAR_FUNCTION(0xb, "lcd", "pwm", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "pex", "rsto", V(1, 1, 1, 1, 0, 1)), + MPP_VAR_FUNCTION(0x2, "spi", "cs", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x3, "ptp", "trig", V(1, 1, 1, 1, 0, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "pwm", V(0, 0, 0, 0, 1, 0))), MPP_MODE(8, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "twsi0", "sda", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "uart0", "rts", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "uart1", "rts", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "mii-1", "rxerr", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x5, "sata1", "prsnt", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xc, "ptp", "clk", V(1, 1, 1, 1, 0)), - MPP_VAR_FUNCTION(0xd, "mii", "col", V(1, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "twsi0", "sda", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "uart0", "rts", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x3, "uart1", "rts", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x4, "mii-1", "rxerr", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x5, "sata1", "prsnt", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xc, "ptp", "clk", V(1, 1, 1, 1, 0, 0)), + MPP_VAR_FUNCTION(0xd, "mii", "col", V(1, 1, 1, 1, 1, 0))), MPP_MODE(9, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "twsi0", "sck", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "uart0", "cts", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "uart1", "cts", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x5, "sata0", "prsnt", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xc, "ptp", "evreq", V(1, 1, 1, 1, 0)), - MPP_VAR_FUNCTION(0xd, "mii", "crs", V(1, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "twsi0", "sck", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "uart0", "cts", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x3, "uart1", "cts", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x5, "sata0", "prsnt", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xc, "ptp", "evreq", V(1, 1, 1, 1, 0, 0)), + MPP_VAR_FUNCTION(0xd, "mii", "crs", V(1, 1, 1, 1, 1, 0))), MPP_MODE(10, - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "spi", "sck", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0X3, "uart0", "txd", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x5, "sata1", "act", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xc, "ptp", "trig", V(1, 1, 1, 1, 0))), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "spi", "sck", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0X3, "uart0", "txd", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x5, "sata1", "act", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xc, "ptp", "trig", V(1, 1, 1, 1, 0, 0))), MPP_MODE(11, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "spi", "miso", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "uart0", "rxd", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "ptp-1", "evreq", V(1, 1, 1, 1, 0)), - MPP_VAR_FUNCTION(0xc, "ptp-2", "trig", V(1, 1, 1, 1, 0)), - MPP_VAR_FUNCTION(0xd, "ptp", "clk", V(1, 1, 1, 1, 0)), - MPP_VAR_FUNCTION(0x5, "sata0", "act", V(0, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "spi", "miso", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x3, "uart0", "rxd", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x4, "ptp-1", "evreq", V(1, 1, 1, 1, 0, 0)), + MPP_VAR_FUNCTION(0xc, "ptp-2", "trig", V(1, 1, 1, 1, 0, 0)), + MPP_VAR_FUNCTION(0xd, "ptp", "clk", V(1, 1, 1, 1, 0, 0)), + MPP_VAR_FUNCTION(0x5, "sata0", "act", V(0, 1, 1, 1, 1, 0))), MPP_MODE(12, - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 0, 1)), - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 0)), - MPP_VAR_FUNCTION(0x1, "sdio", "clk", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xa, "audio", "spdifo", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xb, "spi", "mosi", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xd, "twsi1", "sda", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 0, 1, 0)), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 0, 0)), + MPP_VAR_FUNCTION(0x1, "sdio", "clk", V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xa, "audio", "spdifo", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xb, "spi", "mosi", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xd, "twsi1", "sda", V(0, 0, 0, 0, 1, 0))), MPP_MODE(13, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "sdio", "cmd", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "uart1", "txd", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xa, "audio", "rmclk", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "pwm", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "sdio", "cmd", V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "uart1", "txd", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0xa, "audio", "rmclk", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "pwm", V(0, 0, 0, 0, 1, 0))), MPP_MODE(14, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "sdio", "d0", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "uart1", "rxd", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "sata1", "prsnt", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xa, "audio", "spdifi", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xb, "audio-1", "sdi", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xd, "mii", "col", V(1, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "sdio", "d0", V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "uart1", "rxd", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x4, "sata1", "prsnt", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xa, "audio", "spdifi", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xb, "audio-1", "sdi", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xd, "mii", "col", V(1, 1, 1, 1, 1, 0))), MPP_MODE(15, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "sdio", "d1", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "uart0", "rts", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "uart1", "txd", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "sata0", "act", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "spi", "cs", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "sdio", "d1", V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "uart0", "rts", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x3, "uart1", "txd", V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "sata0", "act", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "spi", "cs", V(0, 0, 0, 0, 1, 0))), MPP_MODE(16, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "sdio", "d2", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "uart0", "cts", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "uart1", "rxd", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "sata1", "act", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "extclk", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xd, "mii", "crs", V(1, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "sdio", "d2", V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "uart0", "cts", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x3, "uart1", "rxd", V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "sata1", "act", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "extclk", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xd, "mii", "crs", V(1, 1, 1, 1, 1, 0))), MPP_MODE(17, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "sdio", "d3", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "sata0", "prsnt", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xa, "sata1", "act", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xd, "twsi1", "sck", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "sdio", "d3", V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "sata0", "prsnt", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xa, "sata1", "act", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xd, "twsi1", "sck", V(0, 0, 0, 0, 1, 0))), MPP_MODE(18, - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "nand", "io0", V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "pex", "clkreq", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "nand", "io0", V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "pex", "clkreq", V(0, 0, 0, 0, 1, 0))), MPP_MODE(19, - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "nand", "io1", V(1, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(1, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "nand", "io1", V(1, 1, 1, 1, 1, 1))), MPP_MODE(20, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp0", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "tx0ql", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "txd0", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "spdifi", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x5, "sata1", "act", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d0", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xc, "mii", "rxerr", V(1, 0, 0, 0, 0))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp0", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "tx0ql", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "txd0", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "spdifi", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x5, "sata1", "act", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d0", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xc, "mii", "rxerr", V(1, 0, 0, 0, 0, 0))), MPP_MODE(21, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp1", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "rx0ql", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "txd1", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "spdifi", V(1, 0, 0, 0, 0)), - MPP_VAR_FUNCTION(0x4, "audio", "spdifo", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x5, "sata0", "act", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d1", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp1", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "rx0ql", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "txd1", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "spdifi", V(1, 0, 0, 0, 0, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "spdifo", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x5, "sata0", "act", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d1", V(0, 0, 0, 0, 1, 0))), MPP_MODE(22, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp2", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "tx2ql", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "txd2", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "spdifo", V(1, 0, 0, 0, 0)), - MPP_VAR_FUNCTION(0x4, "audio", "rmclk", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x5, "sata1", "prsnt", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d2", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp2", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "tx2ql", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "txd2", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "spdifo", V(1, 0, 0, 0, 0, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "rmclk", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x5, "sata1", "prsnt", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d2", V(0, 0, 0, 0, 1, 0))), MPP_MODE(23, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp3", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "rx2ql", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "txd3", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "rmclk", V(1, 0, 0, 0, 0)), - MPP_VAR_FUNCTION(0x4, "audio", "bclk", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x5, "sata0", "prsnt", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d3", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp3", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "rx2ql", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "txd3", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "rmclk", V(1, 0, 0, 0, 0, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "bclk", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x5, "sata0", "prsnt", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d3", V(0, 0, 0, 0, 1, 0))), MPP_MODE(24, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp4", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "spi-cs0", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "rxd0", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "bclk", V(1, 0, 0, 0, 0)), - MPP_VAR_FUNCTION(0x4, "audio", "sdo", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d4", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp4", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "spi-cs0", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "rxd0", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "bclk", V(1, 0, 0, 0, 0, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "sdo", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d4", V(0, 0, 0, 0, 1, 0))), MPP_MODE(25, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp5", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "spi-sck", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "rxd1", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "sdo", V(1, 0, 0, 0, 0)), - MPP_VAR_FUNCTION(0x4, "audio", "lrclk", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d5", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp5", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "spi-sck", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "rxd1", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "sdo", V(1, 0, 0, 0, 0, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "lrclk", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d5", V(0, 0, 0, 0, 1, 0))), MPP_MODE(26, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp6", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "spi-miso", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "rxd2", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "lrclk", V(1, 0, 0, 0, 0)), - MPP_VAR_FUNCTION(0x4, "audio", "mclk", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d6", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp6", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "spi-miso", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "rxd2", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "lrclk", V(1, 0, 0, 0, 0, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "mclk", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d6", V(0, 0, 0, 0, 1, 0))), MPP_MODE(27, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp7", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "spi-mosi", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "rxd3", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "mclk", V(1, 0, 0, 0, 0)), - MPP_VAR_FUNCTION(0x4, "audio", "sdi", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d7", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp7", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "spi-mosi", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "rxd3", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "mclk", V(1, 0, 0, 0, 0, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "sdi", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d7", V(0, 0, 0, 0, 1, 0))), MPP_MODE(28, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp8", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "int", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "col", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "sdi", V(1, 0, 0, 0, 0)), - MPP_VAR_FUNCTION(0x4, "audio", "extclk", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d8", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp8", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "int", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "col", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "sdi", V(1, 0, 0, 0, 0, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "extclk", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d8", V(0, 0, 0, 0, 1, 0))), MPP_MODE(29, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp9", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "rst", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "txclk", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "extclk", V(1, 0, 0, 0, 0)), - MPP_VAR_FUNCTION(0xb, "lcd", "d9", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(1, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp9", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "rst", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "txclk", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "extclk", V(1, 0, 0, 0, 0, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d9", V(0, 0, 0, 0, 1, 0))), MPP_MODE(30, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp10", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "pclk", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "rxctl", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d10", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp10", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "pclk", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "rxctl", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d10", V(0, 0, 0, 0, 1, 0))), MPP_MODE(31, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp11", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "fs", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "rxclk", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d11", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp11", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "fs", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "rxclk", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d11", V(0, 0, 0, 0, 1, 0))), MPP_MODE(32, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp12", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "drx", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "txclko", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d12", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp12", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "drx", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "txclko", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d12", V(0, 0, 0, 0, 1, 0))), MPP_MODE(33, - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "dtx", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "txctl", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d13", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "dtx", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "txctl", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d13", V(0, 0, 0, 0, 1, 0))), MPP_MODE(34, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "spi-cs1", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "txen", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x5, "sata1", "act", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d14", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "tdm", "spi-cs1", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "txen", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x5, "sata1", "act", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d14", V(0, 0, 0, 0, 1, 0))), MPP_MODE(35, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "tx0ql", V(0, 0, 1, 1, 1)), - MPP_VAR_FUNCTION(0x3, "ge1", "rxerr", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0x5, "sata0", "act", V(0, 1, 1, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d15", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xc, "mii", "rxerr", V(0, 1, 1, 1, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 1, 1, 1, 1, 1)), + MPP_VAR_FUNCTION(0x2, "tdm", "tx0ql", V(0, 0, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x3, "ge1", "rxerr", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0x5, "sata0", "act", V(0, 1, 1, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d15", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xc, "mii", "rxerr", V(0, 1, 1, 1, 1, 0))), MPP_MODE(36, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp0", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "spi-cs1", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "spdifi", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "twsi1", "sda", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "ts", "mp0", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "spi-cs1", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "spdifi", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "twsi1", "sda", V(0, 0, 0, 0, 1, 0))), MPP_MODE(37, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp1", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "tx2ql", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "spdifo", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "twsi1", "sck", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "ts", "mp1", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "tx2ql", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "spdifo", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "twsi1", "sck", V(0, 0, 0, 0, 1, 0))), MPP_MODE(38, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp2", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "rx2ql", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "rmclk", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d18", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "ts", "mp2", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "rx2ql", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "rmclk", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d18", V(0, 0, 0, 0, 1, 0))), MPP_MODE(39, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp3", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "spi-cs0", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "bclk", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d19", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "ts", "mp3", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "spi-cs0", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "bclk", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d19", V(0, 0, 0, 0, 1, 0))), MPP_MODE(40, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp4", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "spi-sck", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "sdo", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d20", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "ts", "mp4", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "spi-sck", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "sdo", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d20", V(0, 0, 0, 0, 1, 0))), MPP_MODE(41, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp5", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "spi-miso", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "lrclk", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d21", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "ts", "mp5", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "spi-miso", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "lrclk", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d21", V(0, 0, 0, 0, 1, 0))), MPP_MODE(42, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp6", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "spi-mosi", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "mclk", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d22", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "ts", "mp6", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "spi-mosi", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "mclk", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d22", V(0, 0, 0, 0, 1, 0))), MPP_MODE(43, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp7", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "int", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "sdi", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d23", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "ts", "mp7", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "int", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "sdi", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d23", V(0, 0, 0, 0, 1, 0))), MPP_MODE(44, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp8", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "rst", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x4, "audio", "extclk", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "clk", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "ts", "mp8", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "rst", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x4, "audio", "extclk", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "clk", V(0, 0, 0, 0, 1, 0))), MPP_MODE(45, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp9", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "pclk", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "e", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 1)), + MPP_VAR_FUNCTION(0x1, "ts", "mp9", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "pclk", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "e", V(0, 0, 0, 0, 1, 0))), MPP_MODE(46, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp10", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "fs", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "hsync", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp10", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "fs", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "hsync", V(0, 0, 0, 0, 1, 0))), MPP_MODE(47, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp11", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "drx", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "vsync", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp11", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "drx", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "vsync", V(0, 0, 0, 0, 1, 0))), MPP_MODE(48, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp12", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x2, "tdm", "dtx", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d16", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp12", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "dtx", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d16", V(0, 0, 0, 0, 1, 0))), MPP_MODE(49, - MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 0)), - MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0x1, "ts", "mp9", V(0, 0, 0, 1, 0)), - MPP_VAR_FUNCTION(0x2, "tdm", "rx0ql", V(0, 0, 0, 1, 1)), - MPP_VAR_FUNCTION(0x5, "ptp", "clk", V(0, 0, 0, 1, 0)), - MPP_VAR_FUNCTION(0xa, "pex", "clkreq", V(0, 0, 0, 0, 1)), - MPP_VAR_FUNCTION(0xb, "lcd", "d17", V(0, 0, 0, 0, 1))), + MPP_VAR_FUNCTION(0x0, "gpio", NULL, V(0, 0, 0, 1, 0, 1)), + MPP_VAR_FUNCTION(0x0, "gpo", NULL, V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0x1, "ts", "mp9", V(0, 0, 0, 1, 0, 0)), + MPP_VAR_FUNCTION(0x2, "tdm", "rx0ql", V(0, 0, 0, 1, 1, 0)), + MPP_VAR_FUNCTION(0x5, "ptp", "clk", V(0, 0, 0, 1, 0, 0)), + MPP_VAR_FUNCTION(0xa, "pex", "clkreq", V(0, 0, 0, 0, 1, 0)), + MPP_VAR_FUNCTION(0xb, "lcd", "d17", V(0, 0, 0, 0, 1, 0))), }; static struct mvebu_mpp_ctrl mv88f6180_mpp_controls[] = { @@ -433,12 +434,23 @@ static struct mvebu_pinctrl_soc_info mv88f6282_info = { .ngpioranges = ARRAY_SIZE(mv88f628x_gpio_ranges), }; +static struct mvebu_pinctrl_soc_info mv98dx4122_info = { + .variant = VARIANT_MV98DX4122, + .controls = mv88f628x_mpp_controls, + .ncontrols = ARRAY_SIZE(mv88f628x_mpp_controls), + .modes = mv88f6xxx_mpp_modes, + .nmodes = ARRAY_SIZE(mv88f6xxx_mpp_modes), + .gpioranges = mv88f628x_gpio_ranges, + .ngpioranges = ARRAY_SIZE(mv88f628x_gpio_ranges), +}; + static struct of_device_id kirkwood_pinctrl_of_match[] __devinitdata = { { .compatible = "marvell,88f6180-pinctrl", .data = &mv88f6180_info }, { .compatible = "marvell,88f6190-pinctrl", .data = &mv88f6190_info }, { .compatible = "marvell,88f6192-pinctrl", .data = &mv88f6192_info }, { .compatible = "marvell,88f6281-pinctrl", .data = &mv88f6281_info }, { .compatible = "marvell,88f6282-pinctrl", .data = &mv88f6282_info }, + { .compatible = "marvell,98dx4122-pinctrl", .data = &mv98dx4122_info }, { } }; -- cgit v1.2.3 From 1723d90915d4689fa2e8cd4151d45ea38c96cb99 Mon Sep 17 00:00:00 2001 From: Xuelin Shi Date: Wed, 21 Nov 2012 17:01:20 +0800 Subject: powerpc/dma/raidengine: add raidengine device The RaidEngine is a new Freescale hardware that used for parity computation offloading in RAID5/6. This patch adds the device node in device tree and related binding documentation. Signed-off-by: Harninder Rai Signed-off-by: Naveen Burmi Signed-off-by: Xuelin Shi Signed-off-by: Kumar Gala --- .../devicetree/bindings/powerpc/fsl/raideng.txt | 81 +++++++++++++++++++++ arch/powerpc/boot/dts/fsl/p5020si-post.dtsi | 1 + arch/powerpc/boot/dts/fsl/p5020si-pre.dtsi | 6 ++ arch/powerpc/boot/dts/fsl/qoriq-raid1.0-0.dtsi | 85 ++++++++++++++++++++++ 4 files changed, 173 insertions(+) create mode 100644 Documentation/devicetree/bindings/powerpc/fsl/raideng.txt create mode 100644 arch/powerpc/boot/dts/fsl/qoriq-raid1.0-0.dtsi (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/powerpc/fsl/raideng.txt b/Documentation/devicetree/bindings/powerpc/fsl/raideng.txt new file mode 100644 index 000000000000..4ad29b9ac2ac --- /dev/null +++ b/Documentation/devicetree/bindings/powerpc/fsl/raideng.txt @@ -0,0 +1,81 @@ +* Freescale 85xx RAID Engine nodes + +RAID Engine nodes are defined to describe on-chip RAID accelerators. Each RAID +Engine should have a separate node. + +Supported chips: +P5020, P5040 + +Required properties: + +- compatible: Should contain "fsl,raideng-v1.0" as the value + This identifies RAID Engine block. 1 in 1.0 represents + major number whereas 0 represents minor number. The + version matches the hardware IP version. +- reg: offset and length of the register set for the device +- ranges: standard ranges property specifying the translation + between child address space and parent address space + +Example: + /* P5020 */ + raideng: raideng@320000 { + compatible = "fsl,raideng-v1.0"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x320000 0x10000>; + ranges = <0 0x320000 0x10000>; + }; + + +There must be a sub-node for each job queue present in RAID Engine +This node must be a sub-node of the main RAID Engine node + +- compatible: Should contain "fsl,raideng-v1.0-job-queue" as the value + This identifies the job queue interface +- reg: offset and length of the register set for job queue +- ranges: standard ranges property specifying the translation + between child address space and parent address space + +Example: + /* P5020 */ + raideng_jq0@1000 { + compatible = "fsl,raideng-v1.0-job-queue"; + reg = <0x1000 0x1000>; + ranges = <0x0 0x1000 0x1000>; + }; + + +There must be a sub-node for each job ring present in RAID Engine +This node must be a sub-node of job queue node + +- compatible: Must contain "fsl,raideng-v1.0-job-ring" as the value + This identifies job ring. Should contain either + "fsl,raideng-v1.0-hp-ring" or "fsl,raideng-v1.0-lp-ring" + depending upon whether ring has high or low priority +- reg: offset and length of the register set for job ring +- interrupts: interrupt mapping for job ring IRQ + +Optional property: + +- fsl,liodn: Specifies the LIODN to be used for Job Ring. This + property is normally set by firmware. Value + is of 12-bits which is the LIODN number for this JR. + This property is used by the IOMMU (PAMU) to distinquish + transactions from this JR and than be able to do address + translation & protection accordingly. + +Example: + /* P5020 */ + raideng_jq0@1000 { + compatible = "fsl,raideng-v1.0-job-queue"; + reg = <0x1000 0x1000>; + ranges = <0x0 0x1000 0x1000>; + + raideng_jr0: jr@0 { + compatible = "fsl,raideng-v1.0-job-ring", "fsl,raideng-v1.0-hp-ring"; + reg = <0x0 0x400>; + interrupts = <139 2 0 0>; + interrupt-parent = <&mpic>; + fsl,liodn = <0x41>; + }; + }; diff --git a/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi b/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi index 64b6abea8464..5d7205b7bb05 100644 --- a/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi @@ -354,4 +354,5 @@ /include/ "qoriq-sata2-0.dtsi" /include/ "qoriq-sata2-1.dtsi" /include/ "qoriq-sec4.2-0.dtsi" +/include/ "qoriq-raid1.0-0.dtsi" }; diff --git a/arch/powerpc/boot/dts/fsl/p5020si-pre.dtsi b/arch/powerpc/boot/dts/fsl/p5020si-pre.dtsi index 0a198b0a77e5..8df47fc45ab5 100644 --- a/arch/powerpc/boot/dts/fsl/p5020si-pre.dtsi +++ b/arch/powerpc/boot/dts/fsl/p5020si-pre.dtsi @@ -73,6 +73,12 @@ rtic_c = &rtic_c; rtic_d = &rtic_d; sec_mon = &sec_mon; + + raideng = &raideng; + raideng_jr0 = &raideng_jr0; + raideng_jr1 = &raideng_jr1; + raideng_jr2 = &raideng_jr2; + raideng_jr3 = &raideng_jr3; }; cpus { diff --git a/arch/powerpc/boot/dts/fsl/qoriq-raid1.0-0.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-raid1.0-0.dtsi new file mode 100644 index 000000000000..8d2e8aa6cf8a --- /dev/null +++ b/arch/powerpc/boot/dts/fsl/qoriq-raid1.0-0.dtsi @@ -0,0 +1,85 @@ +/* + * QorIQ RAID 1.0 device tree stub [ controller @ offset 0x320000 ] + * + * Copyright 2012 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +raideng: raideng@320000 { + compatible = "fsl,raideng-v1.0"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x320000 0x10000>; + ranges = <0 0x320000 0x10000>; + + raideng_jq0@1000 { + compatible = "fsl,raideng-v1.0-job-queue"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x1000 0x1000>; + ranges = <0x0 0x1000 0x1000>; + + raideng_jr0: jr@0 { + compatible = "fsl,raideng-v1.0-job-ring", "fsl,raideng-v1.0-hp-ring"; + reg = <0x0 0x400>; + interrupts = <139 2 0 0>; + interrupt-parent = <&mpic>; + }; + + raideng_jr1: jr@400 { + compatible = "fsl,raideng-v1.0-job-ring", "fsl,raideng-v1.0-lp-ring"; + reg = <0x400 0x400>; + interrupts = <140 2 0 0>; + interrupt-parent = <&mpic>; + }; + }; + + raideng_jq1@2000 { + compatible = "fsl,raideng-v1.0-job-queue"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x2000 0x1000>; + ranges = <0x0 0x2000 0x1000>; + + raideng_jr2: jr@0 { + compatible = "fsl,raideng-v1.0-job-ring", "fsl,raideng-v1.0-hp-ring"; + reg = <0x0 0x400>; + interrupts = <141 2 0 0>; + interrupt-parent = <&mpic>; + }; + + raideng_jr3: jr@400 { + compatible = "fsl,raideng-v1.0-job-ring", "fsl,raideng-v1.0-lp-ring"; + reg = <0x400 0x400>; + interrupts = <142 2 0 0>; + interrupt-parent = <&mpic>; + }; + }; +}; -- cgit v1.2.3 From 80515a5a2e3c35e2994105f19af27650e8a16c51 Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Fri, 3 Aug 2012 15:33:10 +0530 Subject: ARM: SPEAr3xx: shirq: simplify and move the shared irq multiplexor to DT SPEAr3xx architecture includes shared/multiplexed irqs for certain set of devices. The multiplexor provides a single interrupt to parent interrupt controller (VIC) on behalf of a group of devices. There can be multiple groups available on SPEAr3xx variants but not exceeding 4. The number of devices in a group can differ, further they may share same set of status/mask registers spanning across different bit masks. Also in some cases the group may not have enable or other registers. This makes software little complex. Present implementation was non-DT and had few complex data structures to decipher banks, number of irqs supported, mask and registers involved. This patch simplifies the overall design and convert it in to DT. It also removes all registration from individual SoC files and bring them in to common shirq.c. Also updated the corresponding documentation for DT binding of shirq. Signed-off-by: Shiraz Hashim Signed-off-by: Viresh Kumar --- .../devicetree/bindings/arm/spear/shirq.txt | 48 ++++ arch/arm/mach-spear3xx/include/mach/irqs.h | 10 +- arch/arm/mach-spear3xx/spear300.c | 103 ------- arch/arm/mach-spear3xx/spear310.c | 202 -------------- arch/arm/mach-spear3xx/spear320.c | 204 -------------- arch/arm/mach-spear3xx/spear3xx.c | 4 + arch/arm/plat-spear/include/plat/shirq.h | 39 ++- arch/arm/plat-spear/shirq.c | 297 +++++++++++++++++---- 8 files changed, 316 insertions(+), 591 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/spear/shirq.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/spear/shirq.txt b/Documentation/devicetree/bindings/arm/spear/shirq.txt new file mode 100644 index 000000000000..13fbb8866bd6 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/spear/shirq.txt @@ -0,0 +1,48 @@ +* SPEAr Shared IRQ layer (shirq) + +SPEAr3xx architecture includes shared/multiplexed irqs for certain set +of devices. The multiplexor provides a single interrupt to parent +interrupt controller (VIC) on behalf of a group of devices. + +There can be multiple groups available on SPEAr3xx variants but not +exceeding 4. The number of devices in a group can differ, further they +may share same set of status/mask registers spanning across different +bit masks. Also in some cases the group may not have enable or other +registers. This makes software little complex. + +A single node in the device tree is used to describe the shared +interrupt multiplexor (one node for all groups). A group in the +interrupt controller shares config/control registers with other groups. +For example, a 32-bit interrupt enable/disable config register can +accommodate upto 4 interrupt groups. + +Required properties: + - compatible: should be, either of + - "st,spear300-shirq" + - "st,spear310-shirq" + - "st,spear320-shirq" + - interrupt-controller: Identifies the node as an interrupt controller. + - #interrupt-cells: should be <1> which basically contains the offset + (starting from 0) of interrupts for all the groups. + - reg: Base address and size of shirq registers. + - interrupts: The list of interrupts generated by the groups which are + then connected to a parent interrupt controller. Each group is + associated with one of the interrupts, hence number of interrupts (to + parent) is equal to number of groups. The format of the interrupt + specifier depends in the interrupt parent controller. + + Optional properties: + - interrupt-parent: pHandle of the parent interrupt controller, if not + inherited from the parent node. + +Example: + +The following is an example from the SPEAr320 SoC dtsi file. + +shirq: interrupt-controller@0xb3000000 { + compatible = "st,spear320-shirq"; + reg = <0xb3000000 0x1000>; + interrupts = <28 29 30 1>; + #interrupt-cells = <1>; + interrupt-controller; +}; diff --git a/arch/arm/mach-spear3xx/include/mach/irqs.h b/arch/arm/mach-spear3xx/include/mach/irqs.h index 803de76f5f36..f95e5b2b6686 100644 --- a/arch/arm/mach-spear3xx/include/mach/irqs.h +++ b/arch/arm/mach-spear3xx/include/mach/irqs.h @@ -14,14 +14,6 @@ #ifndef __MACH_IRQS_H #define __MACH_IRQS_H -/* FIXME: probe all these from DT */ -#define SPEAR3XX_IRQ_INTRCOMM_RAS_ARM 1 -#define SPEAR3XX_IRQ_GEN_RAS_1 28 -#define SPEAR3XX_IRQ_GEN_RAS_2 29 -#define SPEAR3XX_IRQ_GEN_RAS_3 30 -#define SPEAR3XX_IRQ_VIC_END 32 -#define SPEAR3XX_VIRQ_START SPEAR3XX_IRQ_VIC_END - -#define NR_IRQS 160 +#define NR_IRQS 256 #endif /* __MACH_IRQS_H */ diff --git a/arch/arm/mach-spear3xx/spear300.c b/arch/arm/mach-spear3xx/spear300.c index 6ec300549960..a69cbfdb07ee 100644 --- a/arch/arm/mach-spear3xx/spear300.c +++ b/arch/arm/mach-spear3xx/spear300.c @@ -17,102 +17,9 @@ #include #include #include -#include #include #include -/* Base address of various IPs */ -#define SPEAR300_TELECOM_BASE UL(0x50000000) - -/* Interrupt registers offsets and masks */ -#define SPEAR300_INT_ENB_MASK_REG 0x54 -#define SPEAR300_INT_STS_MASK_REG 0x58 -#define SPEAR300_IT_PERS_S_IRQ_MASK (1 << 0) -#define SPEAR300_IT_CHANGE_S_IRQ_MASK (1 << 1) -#define SPEAR300_I2S_IRQ_MASK (1 << 2) -#define SPEAR300_TDM_IRQ_MASK (1 << 3) -#define SPEAR300_CAMERA_L_IRQ_MASK (1 << 4) -#define SPEAR300_CAMERA_F_IRQ_MASK (1 << 5) -#define SPEAR300_CAMERA_V_IRQ_MASK (1 << 6) -#define SPEAR300_KEYBOARD_IRQ_MASK (1 << 7) -#define SPEAR300_GPIO1_IRQ_MASK (1 << 8) - -#define SPEAR300_SHIRQ_RAS1_MASK 0x1FF - -#define SPEAR300_SOC_CONFIG_BASE UL(0x99000000) - - -/* SPEAr300 Virtual irq definitions */ -/* IRQs sharing IRQ_GEN_RAS_1 */ -#define SPEAR300_VIRQ_IT_PERS_S (SPEAR3XX_VIRQ_START + 0) -#define SPEAR300_VIRQ_IT_CHANGE_S (SPEAR3XX_VIRQ_START + 1) -#define SPEAR300_VIRQ_I2S (SPEAR3XX_VIRQ_START + 2) -#define SPEAR300_VIRQ_TDM (SPEAR3XX_VIRQ_START + 3) -#define SPEAR300_VIRQ_CAMERA_L (SPEAR3XX_VIRQ_START + 4) -#define SPEAR300_VIRQ_CAMERA_F (SPEAR3XX_VIRQ_START + 5) -#define SPEAR300_VIRQ_CAMERA_V (SPEAR3XX_VIRQ_START + 6) -#define SPEAR300_VIRQ_KEYBOARD (SPEAR3XX_VIRQ_START + 7) -#define SPEAR300_VIRQ_GPIO1 (SPEAR3XX_VIRQ_START + 8) - -/* IRQs sharing IRQ_GEN_RAS_3 */ -#define SPEAR300_IRQ_CLCD SPEAR3XX_IRQ_GEN_RAS_3 - -/* IRQs sharing IRQ_INTRCOMM_RAS_ARM */ -#define SPEAR300_IRQ_SDHCI SPEAR3XX_IRQ_INTRCOMM_RAS_ARM - -/* spear3xx shared irq */ -static struct shirq_dev_config shirq_ras1_config[] = { - { - .virq = SPEAR300_VIRQ_IT_PERS_S, - .enb_mask = SPEAR300_IT_PERS_S_IRQ_MASK, - .status_mask = SPEAR300_IT_PERS_S_IRQ_MASK, - }, { - .virq = SPEAR300_VIRQ_IT_CHANGE_S, - .enb_mask = SPEAR300_IT_CHANGE_S_IRQ_MASK, - .status_mask = SPEAR300_IT_CHANGE_S_IRQ_MASK, - }, { - .virq = SPEAR300_VIRQ_I2S, - .enb_mask = SPEAR300_I2S_IRQ_MASK, - .status_mask = SPEAR300_I2S_IRQ_MASK, - }, { - .virq = SPEAR300_VIRQ_TDM, - .enb_mask = SPEAR300_TDM_IRQ_MASK, - .status_mask = SPEAR300_TDM_IRQ_MASK, - }, { - .virq = SPEAR300_VIRQ_CAMERA_L, - .enb_mask = SPEAR300_CAMERA_L_IRQ_MASK, - .status_mask = SPEAR300_CAMERA_L_IRQ_MASK, - }, { - .virq = SPEAR300_VIRQ_CAMERA_F, - .enb_mask = SPEAR300_CAMERA_F_IRQ_MASK, - .status_mask = SPEAR300_CAMERA_F_IRQ_MASK, - }, { - .virq = SPEAR300_VIRQ_CAMERA_V, - .enb_mask = SPEAR300_CAMERA_V_IRQ_MASK, - .status_mask = SPEAR300_CAMERA_V_IRQ_MASK, - }, { - .virq = SPEAR300_VIRQ_KEYBOARD, - .enb_mask = SPEAR300_KEYBOARD_IRQ_MASK, - .status_mask = SPEAR300_KEYBOARD_IRQ_MASK, - }, { - .virq = SPEAR300_VIRQ_GPIO1, - .enb_mask = SPEAR300_GPIO1_IRQ_MASK, - .status_mask = SPEAR300_GPIO1_IRQ_MASK, - }, -}; - -static struct spear_shirq shirq_ras1 = { - .irq = SPEAR3XX_IRQ_GEN_RAS_1, - .dev_config = shirq_ras1_config, - .dev_count = ARRAY_SIZE(shirq_ras1_config), - .regs = { - .enb_reg = SPEAR300_INT_ENB_MASK_REG, - .status_reg = SPEAR300_INT_STS_MASK_REG, - .status_reg_mask = SPEAR300_SHIRQ_RAS1_MASK, - .clear_reg = -1, - }, -}; - /* DMAC platform data's slave info */ struct pl08x_channel_data spear300_dma_info[] = { { @@ -285,21 +192,11 @@ static struct of_dev_auxdata spear300_auxdata_lookup[] __initdata = { static void __init spear300_dt_init(void) { - int ret; - pl080_plat_data.slave_channels = spear300_dma_info; pl080_plat_data.num_slave_channels = ARRAY_SIZE(spear300_dma_info); of_platform_populate(NULL, of_default_bus_match_table, spear300_auxdata_lookup, NULL); - - /* shared irq registration */ - shirq_ras1.regs.base = ioremap(SPEAR300_TELECOM_BASE, SZ_4K); - if (shirq_ras1.regs.base) { - ret = spear_shirq_register(&shirq_ras1); - if (ret) - pr_err("Error registering Shared IRQ\n"); - } } static const char * const spear300_dt_board_compat[] = { diff --git a/arch/arm/mach-spear3xx/spear310.c b/arch/arm/mach-spear3xx/spear310.c index 1d0e435b9045..b963ebb10b56 100644 --- a/arch/arm/mach-spear3xx/spear310.c +++ b/arch/arm/mach-spear3xx/spear310.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include @@ -27,176 +26,6 @@ #define SPEAR310_UART3_BASE UL(0xB2100000) #define SPEAR310_UART4_BASE UL(0xB2180000) #define SPEAR310_UART5_BASE UL(0xB2200000) -#define SPEAR310_SOC_CONFIG_BASE UL(0xB4000000) - -/* Interrupt registers offsets and masks */ -#define SPEAR310_INT_STS_MASK_REG 0x04 -#define SPEAR310_SMII0_IRQ_MASK (1 << 0) -#define SPEAR310_SMII1_IRQ_MASK (1 << 1) -#define SPEAR310_SMII2_IRQ_MASK (1 << 2) -#define SPEAR310_SMII3_IRQ_MASK (1 << 3) -#define SPEAR310_WAKEUP_SMII0_IRQ_MASK (1 << 4) -#define SPEAR310_WAKEUP_SMII1_IRQ_MASK (1 << 5) -#define SPEAR310_WAKEUP_SMII2_IRQ_MASK (1 << 6) -#define SPEAR310_WAKEUP_SMII3_IRQ_MASK (1 << 7) -#define SPEAR310_UART1_IRQ_MASK (1 << 8) -#define SPEAR310_UART2_IRQ_MASK (1 << 9) -#define SPEAR310_UART3_IRQ_MASK (1 << 10) -#define SPEAR310_UART4_IRQ_MASK (1 << 11) -#define SPEAR310_UART5_IRQ_MASK (1 << 12) -#define SPEAR310_EMI_IRQ_MASK (1 << 13) -#define SPEAR310_TDM_HDLC_IRQ_MASK (1 << 14) -#define SPEAR310_RS485_0_IRQ_MASK (1 << 15) -#define SPEAR310_RS485_1_IRQ_MASK (1 << 16) - -#define SPEAR310_SHIRQ_RAS1_MASK 0x000FF -#define SPEAR310_SHIRQ_RAS2_MASK 0x01F00 -#define SPEAR310_SHIRQ_RAS3_MASK 0x02000 -#define SPEAR310_SHIRQ_INTRCOMM_RAS_MASK 0x1C000 - -/* SPEAr310 Virtual irq definitions */ -/* IRQs sharing IRQ_GEN_RAS_1 */ -#define SPEAR310_VIRQ_SMII0 (SPEAR3XX_VIRQ_START + 0) -#define SPEAR310_VIRQ_SMII1 (SPEAR3XX_VIRQ_START + 1) -#define SPEAR310_VIRQ_SMII2 (SPEAR3XX_VIRQ_START + 2) -#define SPEAR310_VIRQ_SMII3 (SPEAR3XX_VIRQ_START + 3) -#define SPEAR310_VIRQ_WAKEUP_SMII0 (SPEAR3XX_VIRQ_START + 4) -#define SPEAR310_VIRQ_WAKEUP_SMII1 (SPEAR3XX_VIRQ_START + 5) -#define SPEAR310_VIRQ_WAKEUP_SMII2 (SPEAR3XX_VIRQ_START + 6) -#define SPEAR310_VIRQ_WAKEUP_SMII3 (SPEAR3XX_VIRQ_START + 7) - -/* IRQs sharing IRQ_GEN_RAS_2 */ -#define SPEAR310_VIRQ_UART1 (SPEAR3XX_VIRQ_START + 8) -#define SPEAR310_VIRQ_UART2 (SPEAR3XX_VIRQ_START + 9) -#define SPEAR310_VIRQ_UART3 (SPEAR3XX_VIRQ_START + 10) -#define SPEAR310_VIRQ_UART4 (SPEAR3XX_VIRQ_START + 11) -#define SPEAR310_VIRQ_UART5 (SPEAR3XX_VIRQ_START + 12) - -/* IRQs sharing IRQ_GEN_RAS_3 */ -#define SPEAR310_VIRQ_EMI (SPEAR3XX_VIRQ_START + 13) -#define SPEAR310_VIRQ_PLGPIO (SPEAR3XX_VIRQ_START + 14) - -/* IRQs sharing IRQ_INTRCOMM_RAS_ARM */ -#define SPEAR310_VIRQ_TDM_HDLC (SPEAR3XX_VIRQ_START + 15) -#define SPEAR310_VIRQ_RS485_0 (SPEAR3XX_VIRQ_START + 16) -#define SPEAR310_VIRQ_RS485_1 (SPEAR3XX_VIRQ_START + 17) - - -/* spear3xx shared irq */ -static struct shirq_dev_config shirq_ras1_config[] = { - { - .virq = SPEAR310_VIRQ_SMII0, - .status_mask = SPEAR310_SMII0_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_SMII1, - .status_mask = SPEAR310_SMII1_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_SMII2, - .status_mask = SPEAR310_SMII2_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_SMII3, - .status_mask = SPEAR310_SMII3_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_WAKEUP_SMII0, - .status_mask = SPEAR310_WAKEUP_SMII0_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_WAKEUP_SMII1, - .status_mask = SPEAR310_WAKEUP_SMII1_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_WAKEUP_SMII2, - .status_mask = SPEAR310_WAKEUP_SMII2_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_WAKEUP_SMII3, - .status_mask = SPEAR310_WAKEUP_SMII3_IRQ_MASK, - }, -}; - -static struct spear_shirq shirq_ras1 = { - .irq = SPEAR3XX_IRQ_GEN_RAS_1, - .dev_config = shirq_ras1_config, - .dev_count = ARRAY_SIZE(shirq_ras1_config), - .regs = { - .enb_reg = -1, - .status_reg = SPEAR310_INT_STS_MASK_REG, - .status_reg_mask = SPEAR310_SHIRQ_RAS1_MASK, - .clear_reg = -1, - }, -}; - -static struct shirq_dev_config shirq_ras2_config[] = { - { - .virq = SPEAR310_VIRQ_UART1, - .status_mask = SPEAR310_UART1_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_UART2, - .status_mask = SPEAR310_UART2_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_UART3, - .status_mask = SPEAR310_UART3_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_UART4, - .status_mask = SPEAR310_UART4_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_UART5, - .status_mask = SPEAR310_UART5_IRQ_MASK, - }, -}; - -static struct spear_shirq shirq_ras2 = { - .irq = SPEAR3XX_IRQ_GEN_RAS_2, - .dev_config = shirq_ras2_config, - .dev_count = ARRAY_SIZE(shirq_ras2_config), - .regs = { - .enb_reg = -1, - .status_reg = SPEAR310_INT_STS_MASK_REG, - .status_reg_mask = SPEAR310_SHIRQ_RAS2_MASK, - .clear_reg = -1, - }, -}; - -static struct shirq_dev_config shirq_ras3_config[] = { - { - .virq = SPEAR310_VIRQ_EMI, - .status_mask = SPEAR310_EMI_IRQ_MASK, - }, -}; - -static struct spear_shirq shirq_ras3 = { - .irq = SPEAR3XX_IRQ_GEN_RAS_3, - .dev_config = shirq_ras3_config, - .dev_count = ARRAY_SIZE(shirq_ras3_config), - .regs = { - .enb_reg = -1, - .status_reg = SPEAR310_INT_STS_MASK_REG, - .status_reg_mask = SPEAR310_SHIRQ_RAS3_MASK, - .clear_reg = -1, - }, -}; - -static struct shirq_dev_config shirq_intrcomm_ras_config[] = { - { - .virq = SPEAR310_VIRQ_TDM_HDLC, - .status_mask = SPEAR310_TDM_HDLC_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_RS485_0, - .status_mask = SPEAR310_RS485_0_IRQ_MASK, - }, { - .virq = SPEAR310_VIRQ_RS485_1, - .status_mask = SPEAR310_RS485_1_IRQ_MASK, - }, -}; - -static struct spear_shirq shirq_intrcomm_ras = { - .irq = SPEAR3XX_IRQ_INTRCOMM_RAS_ARM, - .dev_config = shirq_intrcomm_ras_config, - .dev_count = ARRAY_SIZE(shirq_intrcomm_ras_config), - .regs = { - .enb_reg = -1, - .status_reg = SPEAR310_INT_STS_MASK_REG, - .status_reg_mask = SPEAR310_SHIRQ_INTRCOMM_RAS_MASK, - .clear_reg = -1, - }, -}; /* DMAC platform data's slave info */ struct pl08x_channel_data spear310_dma_info[] = { @@ -405,42 +234,11 @@ static struct of_dev_auxdata spear310_auxdata_lookup[] __initdata = { static void __init spear310_dt_init(void) { - void __iomem *base; - int ret; - pl080_plat_data.slave_channels = spear310_dma_info; pl080_plat_data.num_slave_channels = ARRAY_SIZE(spear310_dma_info); of_platform_populate(NULL, of_default_bus_match_table, spear310_auxdata_lookup, NULL); - - /* shared irq registration */ - base = ioremap(SPEAR310_SOC_CONFIG_BASE, SZ_4K); - if (base) { - /* shirq 1 */ - shirq_ras1.regs.base = base; - ret = spear_shirq_register(&shirq_ras1); - if (ret) - pr_err("Error registering Shared IRQ 1\n"); - - /* shirq 2 */ - shirq_ras2.regs.base = base; - ret = spear_shirq_register(&shirq_ras2); - if (ret) - pr_err("Error registering Shared IRQ 2\n"); - - /* shirq 3 */ - shirq_ras3.regs.base = base; - ret = spear_shirq_register(&shirq_ras3); - if (ret) - pr_err("Error registering Shared IRQ 3\n"); - - /* shirq 4 */ - shirq_intrcomm_ras.regs.base = base; - ret = spear_shirq_register(&shirq_intrcomm_ras); - if (ret) - pr_err("Error registering Shared IRQ 4\n"); - } } static const char * const spear310_dt_board_compat[] = { diff --git a/arch/arm/mach-spear3xx/spear320.c b/arch/arm/mach-spear3xx/spear320.c index fd823c624575..707504b84e0e 100644 --- a/arch/arm/mach-spear3xx/spear320.c +++ b/arch/arm/mach-spear3xx/spear320.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -28,184 +27,6 @@ #define SPEAR320_SSP0_BASE UL(0xA5000000) #define SPEAR320_SSP1_BASE UL(0xA6000000) -/* Interrupt registers offsets and masks */ -#define SPEAR320_INT_STS_MASK_REG 0x04 -#define SPEAR320_INT_CLR_MASK_REG 0x04 -#define SPEAR320_INT_ENB_MASK_REG 0x08 -#define SPEAR320_GPIO_IRQ_MASK (1 << 0) -#define SPEAR320_I2S_PLAY_IRQ_MASK (1 << 1) -#define SPEAR320_I2S_REC_IRQ_MASK (1 << 2) -#define SPEAR320_EMI_IRQ_MASK (1 << 7) -#define SPEAR320_CLCD_IRQ_MASK (1 << 8) -#define SPEAR320_SPP_IRQ_MASK (1 << 9) -#define SPEAR320_SDHCI_IRQ_MASK (1 << 10) -#define SPEAR320_CAN_U_IRQ_MASK (1 << 11) -#define SPEAR320_CAN_L_IRQ_MASK (1 << 12) -#define SPEAR320_UART1_IRQ_MASK (1 << 13) -#define SPEAR320_UART2_IRQ_MASK (1 << 14) -#define SPEAR320_SSP1_IRQ_MASK (1 << 15) -#define SPEAR320_SSP2_IRQ_MASK (1 << 16) -#define SPEAR320_SMII0_IRQ_MASK (1 << 17) -#define SPEAR320_MII1_SMII1_IRQ_MASK (1 << 18) -#define SPEAR320_WAKEUP_SMII0_IRQ_MASK (1 << 19) -#define SPEAR320_WAKEUP_MII1_SMII1_IRQ_MASK (1 << 20) -#define SPEAR320_I2C1_IRQ_MASK (1 << 21) - -#define SPEAR320_SHIRQ_RAS1_MASK 0x000380 -#define SPEAR320_SHIRQ_RAS3_MASK 0x000007 -#define SPEAR320_SHIRQ_INTRCOMM_RAS_MASK 0x3FF800 - -/* SPEAr320 Virtual irq definitions */ -/* IRQs sharing IRQ_GEN_RAS_1 */ -#define SPEAR320_VIRQ_EMI (SPEAR3XX_VIRQ_START + 0) -#define SPEAR320_VIRQ_CLCD (SPEAR3XX_VIRQ_START + 1) -#define SPEAR320_VIRQ_SPP (SPEAR3XX_VIRQ_START + 2) - -/* IRQs sharing IRQ_GEN_RAS_2 */ -#define SPEAR320_IRQ_SDHCI SPEAR3XX_IRQ_GEN_RAS_2 - -/* IRQs sharing IRQ_GEN_RAS_3 */ -#define SPEAR320_VIRQ_PLGPIO (SPEAR3XX_VIRQ_START + 3) -#define SPEAR320_VIRQ_I2S_PLAY (SPEAR3XX_VIRQ_START + 4) -#define SPEAR320_VIRQ_I2S_REC (SPEAR3XX_VIRQ_START + 5) - -/* IRQs sharing IRQ_INTRCOMM_RAS_ARM */ -#define SPEAR320_VIRQ_CANU (SPEAR3XX_VIRQ_START + 6) -#define SPEAR320_VIRQ_CANL (SPEAR3XX_VIRQ_START + 7) -#define SPEAR320_VIRQ_UART1 (SPEAR3XX_VIRQ_START + 8) -#define SPEAR320_VIRQ_UART2 (SPEAR3XX_VIRQ_START + 9) -#define SPEAR320_VIRQ_SSP1 (SPEAR3XX_VIRQ_START + 10) -#define SPEAR320_VIRQ_SSP2 (SPEAR3XX_VIRQ_START + 11) -#define SPEAR320_VIRQ_SMII0 (SPEAR3XX_VIRQ_START + 12) -#define SPEAR320_VIRQ_MII1_SMII1 (SPEAR3XX_VIRQ_START + 13) -#define SPEAR320_VIRQ_WAKEUP_SMII0 (SPEAR3XX_VIRQ_START + 14) -#define SPEAR320_VIRQ_WAKEUP_MII1_SMII1 (SPEAR3XX_VIRQ_START + 15) -#define SPEAR320_VIRQ_I2C1 (SPEAR3XX_VIRQ_START + 16) - -/* spear3xx shared irq */ -static struct shirq_dev_config shirq_ras1_config[] = { - { - .virq = SPEAR320_VIRQ_EMI, - .status_mask = SPEAR320_EMI_IRQ_MASK, - .clear_mask = SPEAR320_EMI_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_CLCD, - .status_mask = SPEAR320_CLCD_IRQ_MASK, - .clear_mask = SPEAR320_CLCD_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_SPP, - .status_mask = SPEAR320_SPP_IRQ_MASK, - .clear_mask = SPEAR320_SPP_IRQ_MASK, - }, -}; - -static struct spear_shirq shirq_ras1 = { - .irq = SPEAR3XX_IRQ_GEN_RAS_1, - .dev_config = shirq_ras1_config, - .dev_count = ARRAY_SIZE(shirq_ras1_config), - .regs = { - .enb_reg = -1, - .status_reg = SPEAR320_INT_STS_MASK_REG, - .status_reg_mask = SPEAR320_SHIRQ_RAS1_MASK, - .clear_reg = SPEAR320_INT_CLR_MASK_REG, - .reset_to_clear = 1, - }, -}; - -static struct shirq_dev_config shirq_ras3_config[] = { - { - .virq = SPEAR320_VIRQ_PLGPIO, - .enb_mask = SPEAR320_GPIO_IRQ_MASK, - .status_mask = SPEAR320_GPIO_IRQ_MASK, - .clear_mask = SPEAR320_GPIO_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_I2S_PLAY, - .enb_mask = SPEAR320_I2S_PLAY_IRQ_MASK, - .status_mask = SPEAR320_I2S_PLAY_IRQ_MASK, - .clear_mask = SPEAR320_I2S_PLAY_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_I2S_REC, - .enb_mask = SPEAR320_I2S_REC_IRQ_MASK, - .status_mask = SPEAR320_I2S_REC_IRQ_MASK, - .clear_mask = SPEAR320_I2S_REC_IRQ_MASK, - }, -}; - -static struct spear_shirq shirq_ras3 = { - .irq = SPEAR3XX_IRQ_GEN_RAS_3, - .dev_config = shirq_ras3_config, - .dev_count = ARRAY_SIZE(shirq_ras3_config), - .regs = { - .enb_reg = SPEAR320_INT_ENB_MASK_REG, - .reset_to_enb = 1, - .status_reg = SPEAR320_INT_STS_MASK_REG, - .status_reg_mask = SPEAR320_SHIRQ_RAS3_MASK, - .clear_reg = SPEAR320_INT_CLR_MASK_REG, - .reset_to_clear = 1, - }, -}; - -static struct shirq_dev_config shirq_intrcomm_ras_config[] = { - { - .virq = SPEAR320_VIRQ_CANU, - .status_mask = SPEAR320_CAN_U_IRQ_MASK, - .clear_mask = SPEAR320_CAN_U_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_CANL, - .status_mask = SPEAR320_CAN_L_IRQ_MASK, - .clear_mask = SPEAR320_CAN_L_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_UART1, - .status_mask = SPEAR320_UART1_IRQ_MASK, - .clear_mask = SPEAR320_UART1_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_UART2, - .status_mask = SPEAR320_UART2_IRQ_MASK, - .clear_mask = SPEAR320_UART2_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_SSP1, - .status_mask = SPEAR320_SSP1_IRQ_MASK, - .clear_mask = SPEAR320_SSP1_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_SSP2, - .status_mask = SPEAR320_SSP2_IRQ_MASK, - .clear_mask = SPEAR320_SSP2_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_SMII0, - .status_mask = SPEAR320_SMII0_IRQ_MASK, - .clear_mask = SPEAR320_SMII0_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_MII1_SMII1, - .status_mask = SPEAR320_MII1_SMII1_IRQ_MASK, - .clear_mask = SPEAR320_MII1_SMII1_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_WAKEUP_SMII0, - .status_mask = SPEAR320_WAKEUP_SMII0_IRQ_MASK, - .clear_mask = SPEAR320_WAKEUP_SMII0_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_WAKEUP_MII1_SMII1, - .status_mask = SPEAR320_WAKEUP_MII1_SMII1_IRQ_MASK, - .clear_mask = SPEAR320_WAKEUP_MII1_SMII1_IRQ_MASK, - }, { - .virq = SPEAR320_VIRQ_I2C1, - .status_mask = SPEAR320_I2C1_IRQ_MASK, - .clear_mask = SPEAR320_I2C1_IRQ_MASK, - }, -}; - -static struct spear_shirq shirq_intrcomm_ras = { - .irq = SPEAR3XX_IRQ_INTRCOMM_RAS_ARM, - .dev_config = shirq_intrcomm_ras_config, - .dev_count = ARRAY_SIZE(shirq_intrcomm_ras_config), - .regs = { - .enb_reg = -1, - .status_reg = SPEAR320_INT_STS_MASK_REG, - .status_reg_mask = SPEAR320_SHIRQ_INTRCOMM_RAS_MASK, - .clear_reg = SPEAR320_INT_CLR_MASK_REG, - .reset_to_clear = 1, - }, -}; - /* DMAC platform data's slave info */ struct pl08x_channel_data spear320_dma_info[] = { { @@ -416,36 +237,11 @@ static struct of_dev_auxdata spear320_auxdata_lookup[] __initdata = { static void __init spear320_dt_init(void) { - void __iomem *base; - int ret; - pl080_plat_data.slave_channels = spear320_dma_info; pl080_plat_data.num_slave_channels = ARRAY_SIZE(spear320_dma_info); of_platform_populate(NULL, of_default_bus_match_table, spear320_auxdata_lookup, NULL); - - /* shared irq registration */ - base = ioremap(SPEAR320_SOC_CONFIG_BASE, SZ_4K); - if (base) { - /* shirq 1 */ - shirq_ras1.regs.base = base; - ret = spear_shirq_register(&shirq_ras1); - if (ret) - pr_err("Error registering Shared IRQ 1\n"); - - /* shirq 3 */ - shirq_ras3.regs.base = base; - ret = spear_shirq_register(&shirq_ras3); - if (ret) - pr_err("Error registering Shared IRQ 3\n"); - - /* shirq 4 */ - shirq_intrcomm_ras.regs.base = base; - ret = spear_shirq_register(&shirq_intrcomm_ras); - if (ret) - pr_err("Error registering Shared IRQ 4\n"); - } } static const char * const spear320_dt_board_compat[] = { diff --git a/arch/arm/mach-spear3xx/spear3xx.c b/arch/arm/mach-spear3xx/spear3xx.c index 98144baf8883..f1aaf5b168b2 100644 --- a/arch/arm/mach-spear3xx/spear3xx.c +++ b/arch/arm/mach-spear3xx/spear3xx.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -121,6 +122,9 @@ struct sys_timer spear3xx_timer = { static const struct of_device_id vic_of_match[] __initconst = { { .compatible = "arm,pl190-vic", .data = vic_of_init, }, + { .compatible = "st,spear300-shirq", .data = spear300_shirq_of_init, }, + { .compatible = "st,spear310-shirq", .data = spear310_shirq_of_init, }, + { .compatible = "st,spear320-shirq", .data = spear320_shirq_of_init, }, { /* Sentinel */ } }; diff --git a/arch/arm/plat-spear/include/plat/shirq.h b/arch/arm/plat-spear/include/plat/shirq.h index 88a7fbd24793..c51b355f00de 100644 --- a/arch/arm/plat-spear/include/plat/shirq.h +++ b/arch/arm/plat-spear/include/plat/shirq.h @@ -17,25 +17,9 @@ #include #include -/* - * struct shirq_dev_config: shared irq device configuration - * - * virq: virtual irq number of device - * enb_mask: enable mask of device - * status_mask: status mask of device - * clear_mask: clear mask of device - */ -struct shirq_dev_config { - u32 virq; - u32 enb_mask; - u32 status_mask; - u32 clear_mask; -}; - /* * struct shirq_regs: shared irq register configuration * - * base: base address of shared irq register * enb_reg: enable register offset * reset_to_enb: val 1 indicates, we need to clear bit for enabling interrupt * status_reg: status register offset @@ -44,11 +28,9 @@ struct shirq_dev_config { * reset_to_clear: val 1 indicates, we need to clear bit for clearing interrupt */ struct shirq_regs { - void __iomem *base; u32 enb_reg; u32 reset_to_enb; u32 status_reg; - u32 status_reg_mask; u32 clear_reg; u32 reset_to_clear; }; @@ -57,17 +39,28 @@ struct shirq_regs { * struct spear_shirq: shared irq structure * * irq: hardware irq number - * dev_config: array of device config structures which are using "irq" line - * dev_count: size of dev_config array + * irq_base: base irq in linux domain + * irq_nr: no. of shared interrupts in a particular block + * irq_bit_off: starting bit offset in the status register + * invalid_irq: irq group is currently disabled + * base: base address of shared irq register * regs: register configuration for shared irq block */ struct spear_shirq { u32 irq; - struct shirq_dev_config *dev_config; - u32 dev_count; + u32 irq_base; + u32 irq_nr; + u32 irq_bit_off; + int invalid_irq; + void __iomem *base; struct shirq_regs regs; }; -int spear_shirq_register(struct spear_shirq *shirq); +int __init spear300_shirq_of_init(struct device_node *np, + struct device_node *parent); +int __init spear310_shirq_of_init(struct device_node *np, + struct device_node *parent); +int __init spear320_shirq_of_init(struct device_node *np, + struct device_node *parent); #endif /* __PLAT_SHIRQ_H */ diff --git a/arch/arm/plat-spear/shirq.c b/arch/arm/plat-spear/shirq.c index 853e891e1184..955c7249a5c1 100644 --- a/arch/arm/plat-spear/shirq.c +++ b/arch/arm/plat-spear/shirq.c @@ -10,56 +10,182 @@ * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include +#include #include #include +#include +#include +#include +#include #include #include -struct spear_shirq *shirq; static DEFINE_SPINLOCK(lock); -static void shirq_irq_mask(struct irq_data *d) +/* spear300 shared irq registers offsets and masks */ +#define SPEAR300_INT_ENB_MASK_REG 0x54 +#define SPEAR300_INT_STS_MASK_REG 0x58 + +static struct spear_shirq spear300_shirq_ras1 = { + .irq_nr = 9, + .irq_bit_off = 0, + .regs = { + .enb_reg = SPEAR300_INT_ENB_MASK_REG, + .status_reg = SPEAR300_INT_STS_MASK_REG, + .clear_reg = -1, + }, +}; + +static struct spear_shirq *spear300_shirq_blocks[] = { + &spear300_shirq_ras1, +}; + +/* spear310 shared irq registers offsets and masks */ +#define SPEAR310_INT_STS_MASK_REG 0x04 + +static struct spear_shirq spear310_shirq_ras1 = { + .irq_nr = 8, + .irq_bit_off = 0, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR310_INT_STS_MASK_REG, + .clear_reg = -1, + }, +}; + +static struct spear_shirq spear310_shirq_ras2 = { + .irq_nr = 5, + .irq_bit_off = 8, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR310_INT_STS_MASK_REG, + .clear_reg = -1, + }, +}; + +static struct spear_shirq spear310_shirq_ras3 = { + .irq_nr = 1, + .irq_bit_off = 13, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR310_INT_STS_MASK_REG, + .clear_reg = -1, + }, +}; + +static struct spear_shirq spear310_shirq_intrcomm_ras = { + .irq_nr = 3, + .irq_bit_off = 14, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR310_INT_STS_MASK_REG, + .clear_reg = -1, + }, +}; + +static struct spear_shirq *spear310_shirq_blocks[] = { + &spear310_shirq_ras1, + &spear310_shirq_ras2, + &spear310_shirq_ras3, + &spear310_shirq_intrcomm_ras, +}; + +/* spear320 shared irq registers offsets and masks */ +#define SPEAR320_INT_STS_MASK_REG 0x04 +#define SPEAR320_INT_CLR_MASK_REG 0x04 +#define SPEAR320_INT_ENB_MASK_REG 0x08 + +static struct spear_shirq spear320_shirq_ras1 = { + .irq_nr = 3, + .irq_bit_off = 7, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR320_INT_STS_MASK_REG, + .clear_reg = SPEAR320_INT_CLR_MASK_REG, + .reset_to_clear = 1, + }, +}; + +static struct spear_shirq spear320_shirq_ras2 = { + .irq_nr = 1, + .irq_bit_off = 10, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR320_INT_STS_MASK_REG, + .clear_reg = SPEAR320_INT_CLR_MASK_REG, + .reset_to_clear = 1, + }, +}; + +static struct spear_shirq spear320_shirq_ras3 = { + .irq_nr = 3, + .irq_bit_off = 0, + .invalid_irq = 1, + .regs = { + .enb_reg = SPEAR320_INT_ENB_MASK_REG, + .reset_to_enb = 1, + .status_reg = SPEAR320_INT_STS_MASK_REG, + .clear_reg = SPEAR320_INT_CLR_MASK_REG, + .reset_to_clear = 1, + }, +}; + +static struct spear_shirq spear320_shirq_intrcomm_ras = { + .irq_nr = 11, + .irq_bit_off = 11, + .regs = { + .enb_reg = -1, + .status_reg = SPEAR320_INT_STS_MASK_REG, + .clear_reg = SPEAR320_INT_CLR_MASK_REG, + .reset_to_clear = 1, + }, +}; + +static struct spear_shirq *spear320_shirq_blocks[] = { + &spear320_shirq_ras3, + &spear320_shirq_ras1, + &spear320_shirq_ras2, + &spear320_shirq_intrcomm_ras, +}; + +static void shirq_irq_mask_unmask(struct irq_data *d, bool mask) { struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); - u32 val, id = d->irq - shirq->dev_config[0].virq; + u32 val, offset = d->irq - shirq->irq_base; unsigned long flags; - if ((shirq->regs.enb_reg == -1) || shirq->dev_config[id].enb_mask == -1) + if (shirq->regs.enb_reg == -1) return; spin_lock_irqsave(&lock, flags); - val = readl(shirq->regs.base + shirq->regs.enb_reg); - if (shirq->regs.reset_to_enb) - val |= shirq->dev_config[id].enb_mask; + val = readl(shirq->base + shirq->regs.enb_reg); + + if (mask ^ shirq->regs.reset_to_enb) + val &= ~(0x1 << shirq->irq_bit_off << offset); else - val &= ~(shirq->dev_config[id].enb_mask); - writel(val, shirq->regs.base + shirq->regs.enb_reg); + val |= 0x1 << shirq->irq_bit_off << offset; + + writel(val, shirq->base + shirq->regs.enb_reg); spin_unlock_irqrestore(&lock, flags); + } -static void shirq_irq_unmask(struct irq_data *d) +static void shirq_irq_mask(struct irq_data *d) { - struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); - u32 val, id = d->irq - shirq->dev_config[0].virq; - unsigned long flags; - - if ((shirq->regs.enb_reg == -1) || shirq->dev_config[id].enb_mask == -1) - return; + shirq_irq_mask_unmask(d, 1); +} - spin_lock_irqsave(&lock, flags); - val = readl(shirq->regs.base + shirq->regs.enb_reg); - if (shirq->regs.reset_to_enb) - val &= ~(shirq->dev_config[id].enb_mask); - else - val |= shirq->dev_config[id].enb_mask; - writel(val, shirq->regs.base + shirq->regs.enb_reg); - spin_unlock_irqrestore(&lock, flags); +static void shirq_irq_unmask(struct irq_data *d) +{ + shirq_irq_mask_unmask(d, 0); } static struct irq_chip shirq_chip = { - .name = "spear_shirq", + .name = "spear-shirq", .irq_ack = shirq_irq_mask, .irq_mask = shirq_irq_mask, .irq_unmask = shirq_irq_unmask, @@ -67,52 +193,123 @@ static struct irq_chip shirq_chip = { static void shirq_handler(unsigned irq, struct irq_desc *desc) { - u32 i, val, mask; + u32 i, j, val, mask, tmp; + struct irq_chip *chip; struct spear_shirq *shirq = irq_get_handler_data(irq); - desc->irq_data.chip->irq_ack(&desc->irq_data); - while ((val = readl(shirq->regs.base + shirq->regs.status_reg) & - shirq->regs.status_reg_mask)) { - for (i = 0; (i < shirq->dev_count) && val; i++) { - if (!(shirq->dev_config[i].status_mask & val)) + chip = irq_get_chip(irq); + chip->irq_ack(&desc->irq_data); + + mask = ((0x1 << shirq->irq_nr) - 1) << shirq->irq_bit_off; + while ((val = readl(shirq->base + shirq->regs.status_reg) & + mask)) { + + val >>= shirq->irq_bit_off; + for (i = 0, j = 1; i < shirq->irq_nr; i++, j <<= 1) { + + if (!(j & val)) continue; - generic_handle_irq(shirq->dev_config[i].virq); + generic_handle_irq(shirq->irq_base + i); /* clear interrupt */ - val &= ~shirq->dev_config[i].status_mask; - if ((shirq->regs.clear_reg == -1) || - shirq->dev_config[i].clear_mask == -1) + if (shirq->regs.clear_reg == -1) continue; - mask = readl(shirq->regs.base + shirq->regs.clear_reg); + + tmp = readl(shirq->base + shirq->regs.clear_reg); if (shirq->regs.reset_to_clear) - mask &= ~shirq->dev_config[i].clear_mask; + tmp &= ~(j << shirq->irq_bit_off); else - mask |= shirq->dev_config[i].clear_mask; - writel(mask, shirq->regs.base + shirq->regs.clear_reg); + tmp |= (j << shirq->irq_bit_off); + writel(tmp, shirq->base + shirq->regs.clear_reg); } } - desc->irq_data.chip->irq_unmask(&desc->irq_data); + chip->irq_unmask(&desc->irq_data); } -int spear_shirq_register(struct spear_shirq *shirq) +static void __init spear_shirq_register(struct spear_shirq *shirq) { int i; - if (!shirq || !shirq->dev_config || !shirq->regs.base) - return -EFAULT; - - if (!shirq->dev_count) - return -EINVAL; + if (shirq->invalid_irq) + return; irq_set_chained_handler(shirq->irq, shirq_handler); - for (i = 0; i < shirq->dev_count; i++) { - irq_set_chip_and_handler(shirq->dev_config[i].virq, + for (i = 0; i < shirq->irq_nr; i++) { + irq_set_chip_and_handler(shirq->irq_base + i, &shirq_chip, handle_simple_irq); - set_irq_flags(shirq->dev_config[i].virq, IRQF_VALID); - irq_set_chip_data(shirq->dev_config[i].virq, shirq); + set_irq_flags(shirq->irq_base + i, IRQF_VALID); + irq_set_chip_data(shirq->irq_base + i, shirq); } irq_set_handler_data(shirq->irq, shirq); +} + +static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr, + struct device_node *np) +{ + int i, irq_base, hwirq = 0, irq_nr = 0; + static struct irq_domain *shirq_domain; + void __iomem *base; + + base = of_iomap(np, 0); + if (!base) { + pr_err("%s: failed to map shirq registers\n", __func__); + return -ENXIO; + } + + for (i = 0; i < block_nr; i++) + irq_nr += shirq_blocks[i]->irq_nr; + + irq_base = irq_alloc_descs(-1, 0, irq_nr, 0); + if (IS_ERR_VALUE(irq_base)) { + pr_err("%s: irq desc alloc failed\n", __func__); + goto err_unmap; + } + + shirq_domain = irq_domain_add_legacy(np, irq_nr, irq_base, 0, + &irq_domain_simple_ops, NULL); + if (WARN_ON(!shirq_domain)) { + pr_warn("%s: irq domain init failed\n", __func__); + goto err_free_desc; + } + + for (i = 0; i < block_nr; i++) { + shirq_blocks[i]->base = base; + shirq_blocks[i]->irq_base = irq_find_mapping(shirq_domain, + hwirq); + shirq_blocks[i]->irq = irq_of_parse_and_map(np, i); + + spear_shirq_register(shirq_blocks[i]); + hwirq += shirq_blocks[i]->irq_nr; + } + return 0; + +err_free_desc: + irq_free_descs(irq_base, irq_nr); +err_unmap: + iounmap(base); + return -ENXIO; +} + +int __init spear300_shirq_of_init(struct device_node *np, + struct device_node *parent) +{ + return shirq_init(spear300_shirq_blocks, + ARRAY_SIZE(spear300_shirq_blocks), np); +} + +int __init spear310_shirq_of_init(struct device_node *np, + struct device_node *parent) +{ + return shirq_init(spear310_shirq_blocks, + ARRAY_SIZE(spear310_shirq_blocks), np); +} + +int __init spear320_shirq_of_init(struct device_node *np, + struct device_node *parent) +{ + return shirq_init(spear320_shirq_blocks, + ARRAY_SIZE(spear320_shirq_blocks), np); } -- cgit v1.2.3 From 420993221175a45db5af012c53fd2fef4d9533dc Mon Sep 17 00:00:00 2001 From: Deepak Sikri Date: Tue, 27 Nov 2012 14:05:26 +0100 Subject: cpufreq: SPEAr: Add CPUFreq driver SPEAr is an ARM based family of SoCs. This patch adds in support of cpufreq driver for SPEAr SoCs. It is supported via DT only and so bindings are present in binding document. Signed-off-by: Deepak Sikri Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- .../devicetree/bindings/cpufreq/cpufreq-spear.txt | 42 +++ arch/arm/Kconfig | 1 + drivers/cpufreq/Kconfig.arm | 7 + drivers/cpufreq/Makefile | 1 + drivers/cpufreq/spear-cpufreq.c | 291 +++++++++++++++++++++ 5 files changed, 342 insertions(+) create mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-spear.txt create mode 100644 drivers/cpufreq/spear-cpufreq.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-spear.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-spear.txt new file mode 100644 index 000000000000..f3d44984d91c --- /dev/null +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-spear.txt @@ -0,0 +1,42 @@ +SPEAr cpufreq driver +------------------- + +SPEAr SoC cpufreq driver for CPU frequency scaling. +It supports both uniprocessor (UP) and symmetric multiprocessor (SMP) systems +which share clock across all CPUs. + +Required properties: +- cpufreq_tbl: Table of frequencies CPU could be transitioned into, in the + increasing order. + +Optional properties: +- clock-latency: Specify the possible maximum transition latency for clock, in + unit of nanoseconds. + +Both required and optional properties listed above must be defined under node +/cpus/cpu@0. + +Examples: +-------- +cpus { + + <...> + + cpu@0 { + compatible = "arm,cortex-a9"; + reg = <0>; + + <...> + + cpufreq_tbl = < 166000 + 200000 + 250000 + 300000 + 400000 + 500000 + 600000 >; + }; + + <...> + +}; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ade7e924bef5..159e99737e31 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -904,6 +904,7 @@ config ARCH_NOMADIK config PLAT_SPEAR bool "ST SPEAr" + select ARCH_HAS_CPUFREQ select ARCH_REQUIRE_GPIOLIB select ARM_AMBA select CLKDEV_LOOKUP diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 5961e6415f08..a0b3661d90b0 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -76,3 +76,10 @@ config ARM_EXYNOS5250_CPUFREQ help This adds the CPUFreq driver for Samsung EXYNOS5250 SoC. + +config ARM_SPEAR_CPUFREQ + bool "SPEAr CPUFreq support" + depends on PLAT_SPEAR + default y + help + This adds the CPUFreq driver support for SPEAr SOCs. diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 5b1413e47216..1f254ec087c1 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o +obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o ################################################################################## # PowerPC platform drivers diff --git a/drivers/cpufreq/spear-cpufreq.c b/drivers/cpufreq/spear-cpufreq.c new file mode 100644 index 000000000000..4575cfe41755 --- /dev/null +++ b/drivers/cpufreq/spear-cpufreq.c @@ -0,0 +1,291 @@ +/* + * drivers/cpufreq/spear-cpufreq.c + * + * CPU Frequency Scaling for SPEAr platform + * + * Copyright (C) 2012 ST Microelectronics + * Deepak Sikri + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +/* SPEAr CPUFreq driver data structure */ +static struct { + struct clk *clk; + unsigned int transition_latency; + struct cpufreq_frequency_table *freq_tbl; + u32 cnt; +} spear_cpufreq; + +int spear_cpufreq_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, spear_cpufreq.freq_tbl); +} + +static unsigned int spear_cpufreq_get(unsigned int cpu) +{ + return clk_get_rate(spear_cpufreq.clk) / 1000; +} + +static struct clk *spear1340_cpu_get_possible_parent(unsigned long newfreq) +{ + struct clk *sys_pclk; + int pclk; + /* + * In SPEAr1340, cpu clk's parent sys clk can take input from + * following sources + */ + const char *sys_clk_src[] = { + "sys_syn_clk", + "pll1_clk", + "pll2_clk", + "pll3_clk", + }; + + /* + * As sys clk can have multiple source with their own range + * limitation so we choose possible sources accordingly + */ + if (newfreq <= 300000000) + pclk = 0; /* src is sys_syn_clk */ + else if (newfreq > 300000000 && newfreq <= 500000000) + pclk = 3; /* src is pll3_clk */ + else if (newfreq == 600000000) + pclk = 1; /* src is pll1_clk */ + else + return ERR_PTR(-EINVAL); + + /* Get parent to sys clock */ + sys_pclk = clk_get(NULL, sys_clk_src[pclk]); + if (IS_ERR(sys_pclk)) + pr_err("Failed to get %s clock\n", sys_clk_src[pclk]); + + return sys_pclk; +} + +/* + * In SPEAr1340, we cannot use newfreq directly because we need to actually + * access a source clock (clk) which might not be ancestor of cpu at present. + * Hence in SPEAr1340 we would operate on source clock directly before switching + * cpu clock to it. + */ +static int spear1340_set_cpu_rate(struct clk *sys_pclk, unsigned long newfreq) +{ + struct clk *sys_clk; + int ret = 0; + + sys_clk = clk_get_parent(spear_cpufreq.clk); + if (IS_ERR(sys_clk)) { + pr_err("failed to get cpu's parent (sys) clock\n"); + return PTR_ERR(sys_clk); + } + + /* Set the rate of the source clock before changing the parent */ + ret = clk_set_rate(sys_pclk, newfreq); + if (ret) { + pr_err("Failed to set sys clk rate to %lu\n", newfreq); + return ret; + } + + ret = clk_set_parent(sys_clk, sys_pclk); + if (ret) { + pr_err("Failed to set sys clk parent\n"); + return ret; + } + + return 0; +} + +static int spear_cpufreq_target(struct cpufreq_policy *policy, + unsigned int target_freq, unsigned int relation) +{ + struct cpufreq_freqs freqs; + unsigned long newfreq; + struct clk *srcclk; + int index, ret, mult = 1; + + if (cpufreq_frequency_table_target(policy, spear_cpufreq.freq_tbl, + target_freq, relation, &index)) + return -EINVAL; + + freqs.cpu = policy->cpu; + freqs.old = spear_cpufreq_get(0); + + newfreq = spear_cpufreq.freq_tbl[index].frequency * 1000; + if (of_machine_is_compatible("st,spear1340")) { + /* + * SPEAr1340 is special in the sense that due to the possibility + * of multiple clock sources for cpu clk's parent we can have + * different clock source for different frequency of cpu clk. + * Hence we need to choose one from amongst these possible clock + * sources. + */ + srcclk = spear1340_cpu_get_possible_parent(newfreq); + if (IS_ERR(srcclk)) { + pr_err("Failed to get src clk\n"); + return PTR_ERR(srcclk); + } + + /* SPEAr1340: src clk is always 2 * intended cpu clk */ + mult = 2; + } else { + /* + * src clock to be altered is ancestor of cpu clock. Hence we + * can directly work on cpu clk + */ + srcclk = spear_cpufreq.clk; + } + + newfreq = clk_round_rate(srcclk, newfreq * mult); + if (newfreq < 0) { + pr_err("clk_round_rate failed for cpu src clock\n"); + return newfreq; + } + + freqs.new = newfreq / 1000; + freqs.new /= mult; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + if (mult == 2) + ret = spear1340_set_cpu_rate(srcclk, newfreq); + else + ret = clk_set_rate(spear_cpufreq.clk, newfreq); + + /* Get current rate after clk_set_rate, in case of failure */ + if (ret) { + pr_err("CPU Freq: cpu clk_set_rate failed: %d\n", ret); + freqs.new = clk_get_rate(spear_cpufreq.clk) / 1000; + } + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + return ret; +} + +static int spear_cpufreq_init(struct cpufreq_policy *policy) +{ + int ret; + + ret = cpufreq_frequency_table_cpuinfo(policy, spear_cpufreq.freq_tbl); + if (ret) { + pr_err("cpufreq_frequency_table_cpuinfo() failed"); + return ret; + } + + cpufreq_frequency_table_get_attr(spear_cpufreq.freq_tbl, policy->cpu); + policy->cpuinfo.transition_latency = spear_cpufreq.transition_latency; + policy->cur = spear_cpufreq_get(0); + + cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu)); + cpumask_copy(policy->related_cpus, policy->cpus); + + return 0; +} + +static int spear_cpufreq_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; +} + +static struct freq_attr *spear_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver spear_cpufreq_driver = { + .name = "cpufreq-spear", + .flags = CPUFREQ_STICKY, + .verify = spear_cpufreq_verify, + .target = spear_cpufreq_target, + .get = spear_cpufreq_get, + .init = spear_cpufreq_init, + .exit = spear_cpufreq_exit, + .attr = spear_cpufreq_attr, +}; + +static int spear_cpufreq_driver_init(void) +{ + struct device_node *np; + const struct property *prop; + struct cpufreq_frequency_table *freq_tbl; + const __be32 *val; + int cnt, i, ret; + + np = of_find_node_by_path("/cpus/cpu@0"); + if (!np) { + pr_err("No cpu node found"); + return -ENODEV; + } + + if (of_property_read_u32(np, "clock-latency", + &spear_cpufreq.transition_latency)) + spear_cpufreq.transition_latency = CPUFREQ_ETERNAL; + + prop = of_find_property(np, "cpufreq_tbl", NULL); + if (!prop || !prop->value) { + pr_err("Invalid cpufreq_tbl"); + ret = -ENODEV; + goto out_put_node; + } + + cnt = prop->length / sizeof(u32); + val = prop->value; + + freq_tbl = kmalloc(sizeof(*freq_tbl) * (cnt + 1), GFP_KERNEL); + if (!freq_tbl) { + ret = -ENOMEM; + goto out_put_node; + } + + for (i = 0; i < cnt; i++) { + freq_tbl[i].index = i; + freq_tbl[i].frequency = be32_to_cpup(val++); + } + + freq_tbl[i].index = i; + freq_tbl[i].frequency = CPUFREQ_TABLE_END; + + spear_cpufreq.freq_tbl = freq_tbl; + + of_node_put(np); + + spear_cpufreq.clk = clk_get(NULL, "cpu_clk"); + if (IS_ERR(spear_cpufreq.clk)) { + pr_err("Unable to get CPU clock\n"); + ret = PTR_ERR(spear_cpufreq.clk); + goto out_put_mem; + } + + ret = cpufreq_register_driver(&spear_cpufreq_driver); + if (!ret) + return 0; + + pr_err("failed register driver: %d\n", ret); + clk_put(spear_cpufreq.clk); + +out_put_mem: + kfree(freq_tbl); + return ret; + +out_put_node: + of_node_put(np); + return ret; +} +late_initcall(spear_cpufreq_driver_init); + +MODULE_AUTHOR("Deepak Sikri "); +MODULE_DESCRIPTION("SPEAr CPUFreq driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 3ee11aef75db51c69cb8cb91dd01afb28036f1b5 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Wed, 26 Sep 2012 18:02:50 +0200 Subject: arm: l2x0: add aurora related properties to OF binding Aurora is a L2 Cache Controller designed to be compatible with the L2x0 Cache Controller. L2X0 OF bindings are extended to support some specificity of Aurora (no cache id part number available through hardware, always write through mode, choice between outer cache and system cache). Signed-off-by: Gregory CLEMENT Signed-off-by: Yehuda Yitschak Tested-and-reviewed-by: Lior Amsalem Acked-by: Arnd Bergmann Cc: Grant Likely Cc: Rob Herring Cc: Russell King Cc: Barry Song <21cnbao@gmail.com> Cc: Will Deacon Cc: Arnd Bergmann Cc: Olof Johansson Signed-off-by: Jason Cooper --- Documentation/devicetree/bindings/arm/l2cc.txt | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/l2cc.txt b/Documentation/devicetree/bindings/arm/l2cc.txt index 7ca52161e7ab..76b0ee6ee9a4 100644 --- a/Documentation/devicetree/bindings/arm/l2cc.txt +++ b/Documentation/devicetree/bindings/arm/l2cc.txt @@ -10,6 +10,12 @@ Required properties: "arm,pl310-cache" "arm,l220-cache" "arm,l210-cache" + "marvell,aurora-system-cache": Marvell Controller designed to be + compatible with the ARM one, with system cache mode (meaning + maintenance operations on L1 are broadcasted to the L2 and L2 + performs the same operation). + "marvell,"aurora-outer-cache: Marvell Controller designed to be + compatible with the ARM one with outer cache mode. - cache-unified : Specifies the cache is a unified cache. - cache-level : Should be set to 2 for a level 2 cache. - reg : Physical base address and size of cache controller's memory mapped @@ -29,6 +35,9 @@ Optional properties: filter. Addresses in the filter window are directed to the M1 port. Other addresses will go to the M0 port. - interrupts : 1 combined interrupt. +- cache-id-part: cache id part number to be used if it is not present + on hardware +- wt-override: If present then L2 is forced to Write through mode Example: -- cgit v1.2.3 From 77b71b370ed06c75bdebef09be438d5275f70fc1 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Tue, 27 Nov 2012 14:04:32 +0530 Subject: regulator: add device tree support for max8997 Add device tree based discovery support for max8997. Signed-off-by: Thomas Abraham Acked-by: MyungJoo Ham Reviewed-by: Tomasz Figa Signed-off-by: Mark Brown --- .../bindings/regulator/max8997-regulator.txt | 146 ++++++++++++++++++++ drivers/mfd/max8997.c | 73 +++++++++- drivers/regulator/max8997.c | 148 ++++++++++++++++++++- include/linux/mfd/max8997-private.h | 1 + include/linux/mfd/max8997.h | 1 + 5 files changed, 366 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/regulator/max8997-regulator.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/regulator/max8997-regulator.txt b/Documentation/devicetree/bindings/regulator/max8997-regulator.txt new file mode 100644 index 000000000000..9fd69a18b0ba --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/max8997-regulator.txt @@ -0,0 +1,146 @@ +* Maxim MAX8997 Voltage and Current Regulator + +The Maxim MAX8997 is a multi-function device which includes volatage and +current regulators, rtc, charger controller and other sub-blocks. It is +interfaced to the host controller using a i2c interface. Each sub-block is +addressed by the host system using different i2c slave address. This document +describes the bindings for 'pmic' sub-block of max8997. + +Required properties: +- compatible: Should be "maxim,max8997-pmic". +- reg: Specifies the i2c slave address of the pmic block. It should be 0x66. + +- max8997,pmic-buck1-dvs-voltage: A set of 8 voltage values in micro-volt (uV) + units for buck1 when changing voltage using gpio dvs. Refer to [1] below + for additional information. + +- max8997,pmic-buck2-dvs-voltage: A set of 8 voltage values in micro-volt (uV) + units for buck2 when changing voltage using gpio dvs. Refer to [1] below + for additional information. + +- max8997,pmic-buck5-dvs-voltage: A set of 8 voltage values in micro-volt (uV) + units for buck5 when changing voltage using gpio dvs. Refer to [1] below + for additional information. + +[1] If none of the 'max8997,pmic-buck[1/2/5]-uses-gpio-dvs' optional + property is specified, the 'max8997,pmic-buck[1/2/5]-dvs-voltage' + property should specify atleast one voltage level (which would be a + safe operating voltage). + + If either of the 'max8997,pmic-buck[1/2/5]-uses-gpio-dvs' optional + property is specified, then all the eigth voltage values for the + 'max8997,pmic-buck[1/2/5]-dvs-voltage' should be specified. + +Optional properties: +- interrupt-parent: Specifies the phandle of the interrupt controller to which + the interrupts from max8997 are delivered to. +- interrupts: Interrupt specifiers for two interrupt sources. + - First interrupt specifier is for 'irq1' interrupt. + - Second interrupt specifier is for 'alert' interrupt. +- max8997,pmic-buck1-uses-gpio-dvs: 'buck1' can be controlled by gpio dvs. +- max8997,pmic-buck2-uses-gpio-dvs: 'buck2' can be controlled by gpio dvs. +- max8997,pmic-buck5-uses-gpio-dvs: 'buck5' can be controlled by gpio dvs. + +Additional properties required if either of the optional properties are used: +- max8997,pmic-ignore-gpiodvs-side-effect: When GPIO-DVS mode is used for + multiple bucks, changing the voltage value of one of the bucks may affect + that of another buck, which is the side effect of the change (set_voltage). + Use this property to ignore such side effects and change the voltage. + +- max8997,pmic-buck125-default-dvs-idx: Default voltage setting selected from + the possible 8 options selectable by the dvs gpios. The value of this + property should be between 0 and 7. If not specified or if out of range, the + default value of this property is set to 0. + +- max8997,pmic-buck125-dvs-gpios: GPIO specifiers for three host gpio's used + for dvs. The format of the gpio specifier depends in the gpio controller. + +Regulators: The regulators of max8997 that have to be instantiated should be +included in a sub-node named 'regulators'. Regulator nodes included in this +sub-node should be of the format as listed below. + + regulator_name { + standard regulator bindings here + }; + +The following are the names of the regulators that the max8997 pmic block +supports. Note: The 'n' in LDOn and BUCKn represents the LDO or BUCK number +as per the datasheet of max8997. + + - LDOn + - valid values for n are 1 to 18 and 21 + - Example: LDO0, LD01, LDO2, LDO21 + - BUCKn + - valid values for n are 1 to 7. + - Example: BUCK1, BUCK2, BUCK3, BUCK7 + + - ENVICHG: Battery Charging Current Monitor Output. This is a fixed + voltage type regulator + + - ESAFEOUT1: (ldo19) + - ESAFEOUT2: (ld020) + + - CHARGER_CV: main battery charger voltage control + - CHARGER: main battery charger current control + - CHARGER_TOPOFF: end of charge current threshold level + +The bindings inside the regulator nodes use the standard regulator bindings +which are documented elsewhere. + +Example: + + max8997_pmic@66 { + compatible = "maxim,max8997-pmic"; + interrupt-parent = <&wakeup_eint>; + reg = <0x66>; + interrupts = <4 0>, <3 0>; + + max8997,pmic-buck1-uses-gpio-dvs; + max8997,pmic-buck2-uses-gpio-dvs; + max8997,pmic-buck5-uses-gpio-dvs; + + max8997,pmic-ignore-gpiodvs-side-effect; + max8997,pmic-buck125-default-dvs-idx = <0>; + + max8997,pmic-buck125-dvs-gpios = <&gpx0 0 1 0 0>, /* SET1 */ + <&gpx0 1 1 0 0>, /* SET2 */ + <&gpx0 2 1 0 0>; /* SET3 */ + + max8997,pmic-buck1-dvs-voltage = <1350000>, <1300000>, + <1250000>, <1200000>, + <1150000>, <1100000>, + <1000000>, <950000>; + + max8997,pmic-buck2-dvs-voltage = <1100000>, <1100000>, + <1100000>, <1100000>, + <1000000>, <1000000>, + <1000000>, <1000000>; + + max8997,pmic-buck5-dvs-voltage = <1200000>, <1200000>, + <1200000>, <1200000>, + <1200000>, <1200000>, + <1200000>, <1200000>; + + regulators { + ldo1_reg: LDO1 { + regulator-name = "VDD_ABB_3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + ldo2_reg: LDO2 { + regulator-name = "VDD_ALIVE_1.1V"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + regulator-always-on; + }; + + buck1_reg: BUCK1 { + regulator-name = "VDD_ARM_1.2V"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-boot-on; + }; + }; + }; diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c index f123517065ec..abd5c80c7cf5 100644 --- a/drivers/mfd/max8997.c +++ b/drivers/mfd/max8997.c @@ -21,8 +21,10 @@ * This driver is based on max8998.c */ +#include #include #include +#include #include #include #include @@ -47,6 +49,13 @@ static struct mfd_cell max8997_devs[] = { { .name = "max8997-led", .id = 2 }, }; +#ifdef CONFIG_OF +static struct of_device_id __devinitdata max8997_pmic_dt_match[] = { + { .compatible = "maxim,max8997-pmic", .data = TYPE_MAX8997 }, + {}, +}; +#endif + int max8997_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest) { struct max8997_dev *max8997 = i2c_get_clientdata(i2c); @@ -123,6 +132,58 @@ int max8997_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask) } EXPORT_SYMBOL_GPL(max8997_update_reg); +#ifdef CONFIG_OF +/* + * Only the common platform data elements for max8997 are parsed here from the + * device tree. Other sub-modules of max8997 such as pmic, rtc and others have + * to parse their own platform data elements from device tree. + * + * The max8997 platform data structure is instantiated here and the drivers for + * the sub-modules need not instantiate another instance while parsing their + * platform data. + */ +static struct max8997_platform_data *max8997_i2c_parse_dt_pdata( + struct device *dev) +{ + struct max8997_platform_data *pd; + + pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); + if (!pd) { + dev_err(dev, "could not allocate memory for pdata\n"); + return ERR_PTR(-ENOMEM); + } + + pd->ono = irq_of_parse_and_map(dev->of_node, 1); + + /* + * ToDo: the 'wakeup' member in the platform data is more of a linux + * specfic information. Hence, there is no binding for that yet and + * not parsed here. + */ + + return pd; +} +#else +static struct max8997_platform_data *max8997_i2c_parse_dt_pdata( + struct device *dev) +{ + return 0; +} +#endif + +static inline int max8997_i2c_get_driver_data(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ +#ifdef CONFIG_OF + if (i2c->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(max8997_pmic_dt_match, i2c->dev.of_node); + return (int)match->data; + } +#endif + return (int)id->driver_data; +} + static int max8997_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -137,12 +198,21 @@ static int max8997_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, max8997); max8997->dev = &i2c->dev; max8997->i2c = i2c; - max8997->type = id->driver_data; + max8997->type = max8997_i2c_get_driver_data(i2c, id); max8997->irq = i2c->irq; + if (max8997->dev->of_node) { + pdata = max8997_i2c_parse_dt_pdata(max8997->dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err; + } + } + if (!pdata) goto err; + max8997->pdata = pdata; max8997->ono = pdata->ono; mutex_init(&max8997->iolock); @@ -434,6 +504,7 @@ static struct i2c_driver max8997_i2c_driver = { .name = "max8997", .owner = THIS_MODULE, .pm = &max8997_pm, + .of_match_table = of_match_ptr(max8997_pmic_dt_match), }, .probe = max8997_i2c_probe, .remove = max8997_i2c_remove, diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c index 64cf2ee38f6c..b56c4326853d 100644 --- a/drivers/regulator/max8997.c +++ b/drivers/regulator/max8997.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,7 @@ #include #include #include +#include struct max8997_data { struct device *dev; @@ -933,10 +935,145 @@ static struct regulator_desc regulators[] = { max8997_charger_fixedstate_ops), }; +#ifdef CONFIG_OF +static int max8997_pmic_dt_parse_dvs_gpio(struct max8997_dev *iodev, + struct max8997_platform_data *pdata, + struct device_node *pmic_np) +{ + int i, gpio; + + for (i = 0; i < 3; i++) { + gpio = of_get_named_gpio(pmic_np, + "max8997,pmic-buck125-dvs-gpios", i); + if (!gpio_is_valid(gpio)) { + dev_err(iodev->dev, "invalid gpio[%d]: %d\n", i, gpio); + return -EINVAL; + } + pdata->buck125_gpios[i] = gpio; + } + return 0; +} + +static int max8997_pmic_dt_parse_pdata(struct max8997_dev *iodev, + struct max8997_platform_data *pdata) +{ + struct device_node *pmic_np, *regulators_np, *reg_np; + struct max8997_regulator_data *rdata; + unsigned int i, dvs_voltage_nr = 1, ret; + + pmic_np = iodev->dev->of_node; + if (!pmic_np) { + dev_err(iodev->dev, "could not find pmic sub-node\n"); + return -ENODEV; + } + + regulators_np = of_find_node_by_name(pmic_np, "regulators"); + if (!regulators_np) { + dev_err(iodev->dev, "could not find regulators sub-node\n"); + return -EINVAL; + } + + /* count the number of regulators to be supported in pmic */ + pdata->num_regulators = 0; + for_each_child_of_node(regulators_np, reg_np) + pdata->num_regulators++; + + rdata = devm_kzalloc(iodev->dev, sizeof(*rdata) * + pdata->num_regulators, GFP_KERNEL); + if (!rdata) { + dev_err(iodev->dev, "could not allocate memory for " + "regulator data\n"); + return -ENOMEM; + } + + pdata->regulators = rdata; + for_each_child_of_node(regulators_np, reg_np) { + for (i = 0; i < ARRAY_SIZE(regulators); i++) + if (!of_node_cmp(reg_np->name, regulators[i].name)) + break; + + if (i == ARRAY_SIZE(regulators)) { + dev_warn(iodev->dev, "don't know how to configure " + "regulator %s\n", reg_np->name); + continue; + } + + rdata->id = i; + rdata->initdata = of_get_regulator_init_data( + iodev->dev, reg_np); + rdata->reg_node = reg_np; + rdata++; + } + + if (of_get_property(pmic_np, "max8997,pmic-buck1-uses-gpio-dvs", NULL)) + pdata->buck1_gpiodvs = true; + + if (of_get_property(pmic_np, "max8997,pmic-buck2-uses-gpio-dvs", NULL)) + pdata->buck2_gpiodvs = true; + + if (of_get_property(pmic_np, "max8997,pmic-buck5-uses-gpio-dvs", NULL)) + pdata->buck5_gpiodvs = true; + + if (pdata->buck1_gpiodvs || pdata->buck2_gpiodvs || + pdata->buck5_gpiodvs) { + ret = max8997_pmic_dt_parse_dvs_gpio(iodev, pdata, pmic_np); + if (ret) + return -EINVAL; + + if (of_property_read_u32(pmic_np, + "max8997,pmic-buck125-default-dvs-idx", + &pdata->buck125_default_idx)) { + pdata->buck125_default_idx = 0; + } else { + if (pdata->buck125_default_idx >= 8) { + pdata->buck125_default_idx = 0; + dev_info(iodev->dev, "invalid value for " + "default dvs index, using 0 instead\n"); + } + } + + if (of_get_property(pmic_np, + "max8997,pmic-ignore-gpiodvs-side-effect", NULL)) + pdata->ignore_gpiodvs_side_effect = true; + + dvs_voltage_nr = 8; + } + + if (of_property_read_u32_array(pmic_np, + "max8997,pmic-buck1-dvs-voltage", + pdata->buck1_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck1 voltages not specified\n"); + return -EINVAL; + } + + if (of_property_read_u32_array(pmic_np, + "max8997,pmic-buck2-dvs-voltage", + pdata->buck2_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck2 voltages not specified\n"); + return -EINVAL; + } + + if (of_property_read_u32_array(pmic_np, + "max8997,pmic-buck5-dvs-voltage", + pdata->buck5_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck5 voltages not specified\n"); + return -EINVAL; + } + + return 0; +} +#else +static int max8997_pmic_dt_parse_pdata(struct max8997_dev *iodev, + struct max8997_platform_data *pdata) +{ + return 0; +} +#endif /* CONFIG_OF */ + static __devinit int max8997_pmic_probe(struct platform_device *pdev) { struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev); + struct max8997_platform_data *pdata = iodev->pdata; struct regulator_config config = { }; struct regulator_dev **rdev; struct max8997_data *max8997; @@ -944,11 +1081,17 @@ static __devinit int max8997_pmic_probe(struct platform_device *pdev) int i, ret, size, nr_dvs; u8 max_buck1 = 0, max_buck2 = 0, max_buck5 = 0; - if (!pdata) { + if (IS_ERR_OR_NULL(pdata)) { dev_err(pdev->dev.parent, "No platform init data supplied.\n"); return -ENODEV; } + if (iodev->dev->of_node) { + ret = max8997_pmic_dt_parse_pdata(iodev, pdata); + if (ret) + return ret; + } + max8997 = devm_kzalloc(&pdev->dev, sizeof(struct max8997_data), GFP_KERNEL); if (!max8997) @@ -1104,6 +1247,7 @@ static __devinit int max8997_pmic_probe(struct platform_device *pdev) config.dev = max8997->dev; config.init_data = pdata->regulators[i].initdata; config.driver_data = max8997; + config.of_node = pdata->regulators[i].reg_node; rdev[i] = regulator_register(®ulators[id], &config); if (IS_ERR(rdev[i])) { diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h index 830152cfae33..6ae21bf47d64 100644 --- a/include/linux/mfd/max8997-private.h +++ b/include/linux/mfd/max8997-private.h @@ -316,6 +316,7 @@ enum max8997_irq { #define MAX8997_NUM_GPIO 12 struct max8997_dev { struct device *dev; + struct max8997_platform_data *pdata; struct i2c_client *i2c; /* 0xcc / PMIC, Battery Control, and FLASH */ struct i2c_client *rtc; /* slave addr 0x0c */ struct i2c_client *haptic; /* slave addr 0x90 */ diff --git a/include/linux/mfd/max8997.h b/include/linux/mfd/max8997.h index 328d8e24b533..1d4a4fe6ac33 100644 --- a/include/linux/mfd/max8997.h +++ b/include/linux/mfd/max8997.h @@ -75,6 +75,7 @@ enum max8998_regulators { struct max8997_regulator_data { int id; struct regulator_init_data *initdata; + struct device_node *reg_node; }; enum max8997_muic_usb_type { -- cgit v1.2.3 From af0ba001d208e117b5f4e4f504672b42a664a7f7 Mon Sep 17 00:00:00 2001 From: "Philip, Avinash" Date: Tue, 27 Nov 2012 14:18:06 +0530 Subject: pwm: Add TI PWM subsystem driver In some platforms (like am33xx), PWM sub modules (ECAP, EHRPWM, EQEP) are integrated to PWM subsystem. These PWM submodules has resources shared and only one register bit-field is provided to control module/clock enable/disable, makes it difficult to handle common resources from independent PWMSS submodule drivers. So the solution here implemented in this patch is, to create driver for PWMSS and take the role of parent driver for PWM submodules. PWMSS parent driver enumerates all the child nodes under PWMSS module. Also symbol "pwmss_submodule_state_change" exported to enable clock gating for individual PWMSS submodules, and submodule drivers has to enable clock gating from their drivers. As this is only supported during DT boot, the parent/child relationship is created and populated in DT execution flow. The only required change is inside DTS file, making EHRPWM & ECAP as a child to PWMSS node. Signed-off-by: Philip, Avinash Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/pwm-tipwmss.txt | 31 +++++ drivers/pwm/Kconfig | 11 ++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-tipwmss.c | 139 +++++++++++++++++++++ drivers/pwm/pwm-tipwmss.h | 39 ++++++ 5 files changed, 221 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt create mode 100644 drivers/pwm/pwm-tipwmss.c create mode 100644 drivers/pwm/pwm-tipwmss.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt b/Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt new file mode 100644 index 000000000000..f7eae77f8354 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt @@ -0,0 +1,31 @@ +TI SOC based PWM Subsystem + +Required properties: +- compatible: Must be "ti,am33xx-pwmss"; +- reg: physical base address and size of the registers map. +- address-cells: Specify the number of u32 entries needed in child nodes. + Should set to 1. +- size-cells: specify number of u32 entries needed to specify child nodes size + in reg property. Should set to 1. +- ranges: describes the address mapping of a memory-mapped bus. Should set to + physical address map of child's base address, physical address within + parent's address space and length of the address map. For am33xx, + 3 set of child register maps present, ECAP register space, EQEP + register space, EHRPWM register space. + +Also child nodes should also populated under PWMSS DT node. + +Example: +pwmss0: pwmss@48300000 { + compatible = "ti,am33xx-pwmss"; + reg = <0x48300000 0x10>; + ti,hwmods = "epwmss0"; + #address-cells = <1>; + #size-cells = <1>; + status = "disabled"; + ranges = <0x48300100 0x48300100 0x80 /* ECAP */ + 0x48300180 0x48300180 0x80 /* EQEP */ + 0x48300200 0x48300200 0x80>; /* EHRPWM */ + + /* child nodes go here */ +}; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 6e556c7da81e..3dcb76d86a97 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -136,6 +136,7 @@ config PWM_TEGRA config PWM_TIECAP tristate "ECAP PWM support" depends on SOC_AM33XX + select PWM_TIPWMSS help PWM driver support for the ECAP APWM controller found on AM33XX TI SOC @@ -146,6 +147,7 @@ config PWM_TIECAP config PWM_TIEHRPWM tristate "EHRPWM PWM support" depends on SOC_AM33XX + select PWM_TIPWMSS help PWM driver support for the EHRPWM controller found on AM33XX TI SOC @@ -153,6 +155,15 @@ config PWM_TIEHRPWM To compile this driver as a module, choose M here: the module will be called pwm-tiehrpwm. +config PWM_TIPWMSS + bool + depends on SOC_AM33XX && (PWM_TIEHRPWM || PWM_TIECAP) + help + PWM Subsystem driver support for AM33xx SOC. + + PWM submodules require PWM config space access from submodule + drivers and require common parent driver support. + config PWM_TWL6030 tristate "TWL6030 PWM support" depends on TWL4030_CORE diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 3b3f4c9aa4ef..4fb39f8b6ba9 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -12,5 +12,6 @@ obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o +obj-$(CONFIG_PWM_TIPWMSS) += pwm-tipwmss.o obj-$(CONFIG_PWM_TWL6030) += pwm-twl6030.o obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o diff --git a/drivers/pwm/pwm-tipwmss.c b/drivers/pwm/pwm-tipwmss.c new file mode 100644 index 000000000000..3448a1c88590 --- /dev/null +++ b/drivers/pwm/pwm-tipwmss.c @@ -0,0 +1,139 @@ +/* + * TI PWM Subsystem driver + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "pwm-tipwmss.h" + +#define PWMSS_CLKCONFIG 0x8 /* Clock gating reg */ +#define PWMSS_CLKSTATUS 0xc /* Clock gating status reg */ + +struct pwmss_info { + void __iomem *mmio_base; + struct mutex pwmss_lock; + u16 pwmss_clkconfig; +}; + +u16 pwmss_submodule_state_change(struct device *dev, int set) +{ + struct pwmss_info *info = dev_get_drvdata(dev); + u16 val; + + mutex_lock(&info->pwmss_lock); + val = readw(info->mmio_base + PWMSS_CLKCONFIG); + val |= set; + writew(val , info->mmio_base + PWMSS_CLKCONFIG); + mutex_unlock(&info->pwmss_lock); + + return readw(info->mmio_base + PWMSS_CLKSTATUS); +} +EXPORT_SYMBOL(pwmss_submodule_state_change); + +static const struct of_device_id pwmss_of_match[] = { + { .compatible = "ti,am33xx-pwmss" }, + {}, +}; +MODULE_DEVICE_TABLE(of, pwmss_of_match); + +static int pwmss_probe(struct platform_device *pdev) +{ + int ret; + struct resource *r; + struct pwmss_info *info; + struct device_node *node = pdev->dev.of_node; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + mutex_init(&info->pwmss_lock); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no memory resource defined\n"); + return -ENODEV; + } + + info->mmio_base = devm_request_and_ioremap(&pdev->dev, r); + if (!info->mmio_base) + return -EADDRNOTAVAIL; + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + platform_set_drvdata(pdev, info); + + /* Populate all the child nodes here... */ + ret = of_platform_populate(node, NULL, NULL, &pdev->dev); + if (ret) + dev_err(&pdev->dev, "no child node found\n"); + + return ret; +} + +static int pwmss_remove(struct platform_device *pdev) +{ + struct pwmss_info *info = platform_get_drvdata(pdev); + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + mutex_destroy(&info->pwmss_lock); + return 0; +} + +static int pwmss_suspend(struct device *dev) +{ + struct pwmss_info *info = dev_get_drvdata(dev); + + info->pwmss_clkconfig = readw(info->mmio_base + PWMSS_CLKCONFIG); + pm_runtime_put_sync(dev); + return 0; +} + +static int pwmss_resume(struct device *dev) +{ + struct pwmss_info *info = dev_get_drvdata(dev); + + pm_runtime_get_sync(dev); + writew(info->pwmss_clkconfig, info->mmio_base + PWMSS_CLKCONFIG); + return 0; +} + +static SIMPLE_DEV_PM_OPS(pwmss_pm_ops, pwmss_suspend, pwmss_resume); + +static struct platform_driver pwmss_driver = { + .driver = { + .name = "pwmss", + .owner = THIS_MODULE, + .pm = &pwmss_pm_ops, + .of_match_table = pwmss_of_match, + }, + .probe = pwmss_probe, + .remove = pwmss_remove, +}; + +module_platform_driver(pwmss_driver); + +MODULE_DESCRIPTION("PWM Subsystem driver"); +MODULE_AUTHOR("Texas Instruments"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/pwm-tipwmss.h b/drivers/pwm/pwm-tipwmss.h new file mode 100644 index 000000000000..11f76a1e266b --- /dev/null +++ b/drivers/pwm/pwm-tipwmss.h @@ -0,0 +1,39 @@ +/* + * TI PWM Subsystem driver + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 __TIPWMSS_H +#define __TIPWMSS_H + +#ifdef CONFIG_PWM_TIPWMSS +/* PWM substem clock gating */ +#define PWMSS_ECAPCLK_EN BIT(0) +#define PWMSS_ECAPCLK_STOP_REQ BIT(1) +#define PWMSS_EPWMCLK_EN BIT(8) +#define PWMSS_EPWMCLK_STOP_REQ BIT(9) + +#define PWMSS_ECAPCLK_EN_ACK BIT(0) +#define PWMSS_EPWMCLK_EN_ACK BIT(8) + +extern u16 pwmss_submodule_state_change(struct device *dev, int set); +#else +static inline u16 pwmss_submodule_state_change(struct device *dev, int set) +{ + /* return success status value */ + return 0xFFFF; +} +#endif +#endif /* __TIPWMSS_H */ -- cgit v1.2.3 From 333b08ee8c6e120d67118e4eb71c45f5c369c8a4 Mon Sep 17 00:00:00 2001 From: "Philip, Avinash" Date: Tue, 27 Nov 2012 14:18:09 +0530 Subject: pwm: tiecap: Add device-tree binding This patch 1. Add support for device-tree binding for ECAP APWM driver. 2. Set size of pwm-cells set to 3 to support PWM channel number, PWM period & polarity configuration from device tree. 3. Add enable/disable clock gating in PWM subsystem common config space. 4. When here set .owner member in platform_driver structure to THIS_MODULE. Signed-off-by: Philip, Avinash Cc: Grant Likely Cc: Rob Herring Cc: Rob Landley Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/pwm-tiecap.txt | 23 ++++++++++++ drivers/pwm/pwm-tiecap.c | 42 +++++++++++++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/pwm/pwm-tiecap.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt b/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt new file mode 100644 index 000000000000..131e8c11d26f --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt @@ -0,0 +1,23 @@ +TI SOC ECAP based APWM controller + +Required properties: +- compatible: Must be "ti,am33xx-ecap" +- #pwm-cells: Should be 3. Number of cells being used to specify PWM property. + First cell specifies the per-chip index of the PWM to use, the second + cell is the period in nanoseconds and bit 0 in the third cell is used to + encode the polarity of PWM output. Set bit 0 of the third in PWM specifier + to 1 for inverse polarity & set to 0 for normal polarity. +- reg: physical base address and size of the registers map. + +Optional properties: +- ti,hwmods: Name of the hwmod associated to the ECAP: + "ecap", being the 0-based instance number from the HW spec + +Example: + +ecap0: ecap@0 { + compatible = "ti,am33xx-ecap"; + #pwm-cells = <3>; + reg = <0x48300100 0x80>; + ti,hwmods = "ecap0"; +}; diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index d6d4cf05565e..0f541c5cbe98 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -25,6 +25,9 @@ #include #include #include +#include + +#include "pwm-tipwmss.h" /* ECAP registers and bits definitions */ #define CAP1 0x08 @@ -184,12 +187,19 @@ static const struct pwm_ops ecap_pwm_ops = { .owner = THIS_MODULE, }; +static const struct of_device_id ecap_of_match[] = { + { .compatible = "ti,am33xx-ecap" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ecap_of_match); + static int __devinit ecap_pwm_probe(struct platform_device *pdev) { int ret; struct resource *r; struct clk *clk; struct ecap_pwm_chip *pc; + u16 status; pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); if (!pc) { @@ -211,6 +221,8 @@ static int __devinit ecap_pwm_probe(struct platform_device *pdev) pc->chip.dev = &pdev->dev; pc->chip.ops = &ecap_pwm_ops; + pc->chip.of_xlate = of_pwm_xlate_with_flags; + pc->chip.of_pwm_n_cells = 3; pc->chip.base = -1; pc->chip.npwm = 1; @@ -231,14 +243,40 @@ static int __devinit ecap_pwm_probe(struct platform_device *pdev) } pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + status = pwmss_submodule_state_change(pdev->dev.parent, + PWMSS_ECAPCLK_EN); + if (!(status & PWMSS_ECAPCLK_EN_ACK)) { + dev_err(&pdev->dev, "PWMSS config space clock enable failed\n"); + ret = -EINVAL; + goto pwmss_clk_failure; + } + + pm_runtime_put_sync(&pdev->dev); + platform_set_drvdata(pdev, pc); return 0; + +pwmss_clk_failure: + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pwmchip_remove(&pc->chip); + return ret; } static int __devexit ecap_pwm_remove(struct platform_device *pdev) { struct ecap_pwm_chip *pc = platform_get_drvdata(pdev); + pm_runtime_get_sync(&pdev->dev); + /* + * Due to hardware misbehaviour, acknowledge of the stop_req + * is missing. Hence checking of the status bit skipped. + */ + pwmss_submodule_state_change(pdev->dev.parent, PWMSS_ECAPCLK_STOP_REQ); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); return pwmchip_remove(&pc->chip); @@ -246,7 +284,9 @@ static int __devexit ecap_pwm_remove(struct platform_device *pdev) static struct platform_driver ecap_pwm_driver = { .driver = { - .name = "ecap", + .name = "ecap", + .owner = THIS_MODULE, + .of_match_table = ecap_of_match, }, .probe = ecap_pwm_probe, .remove = __devexit_p(ecap_pwm_remove), -- cgit v1.2.3 From 53ad9e8d37031397ee141f9bf701c0fba4257b0f Mon Sep 17 00:00:00 2001 From: "Philip, Avinash" Date: Tue, 27 Nov 2012 14:18:13 +0530 Subject: pwm: tiehrpwm: Add device-tree binding This patch 1. Add support for device-tree binding for EHRWPM driver. 2. Set size of pwm-cells set to 3 to support PWM channel number, PWM period & polarity configuration from device tree. 3. Add enable/disable clock gating in PWM subsystem common config space. 4. When here set .owner member in platform_driver structure to THIS_MODULE. Signed-off-by: Philip, Avinash Cc: Grant Likely Cc: Rob Herring Cc: Rob Landley Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/pwm-tiehrpwm.txt | 23 ++++++++++++ drivers/pwm/pwm-tiehrpwm.c | 42 +++++++++++++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt new file mode 100644 index 000000000000..4fc7079d822e --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt @@ -0,0 +1,23 @@ +TI SOC EHRPWM based PWM controller + +Required properties: +- compatible : Must be "ti,am33xx-ehrpwm" +- #pwm-cells: Should be 3. Number of cells being used to specify PWM property. + First cell specifies the per-chip index of the PWM to use, the second + cell is the period in nanoseconds and bit 0 in the third cell is used to + encode the polarity of PWM output. Set bit 0 of the third in PWM specifier + to 1 for inverse polarity & set to 0 for normal polarity. +- reg: physical base address and size of the registers map. + +Optional properties: +- ti,hwmods: Name of the hwmod associated to the EHRPWM: + "ehrpwm", being the 0-based instance number from the HW spec + +Example: + +ehrpwm0: ehrpwm@0 { + compatible = "ti,am33xx-ehrpwm"; + #pwm-cells = <3>; + reg = <0x48300200 0x100>; + ti,hwmods = "ehrpwm0"; +}; diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 565f96ad2787..ee7a8b3dbed9 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -25,6 +25,9 @@ #include #include #include +#include + +#include "pwm-tipwmss.h" /* EHRPWM registers and bits definitions */ @@ -399,12 +402,19 @@ static const struct pwm_ops ehrpwm_pwm_ops = { .owner = THIS_MODULE, }; +static const struct of_device_id ehrpwm_of_match[] = { + { .compatible = "ti,am33xx-ehrpwm" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ehrpwm_of_match); + static int __devinit ehrpwm_pwm_probe(struct platform_device *pdev) { int ret; struct resource *r; struct clk *clk; struct ehrpwm_pwm_chip *pc; + u16 status; pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); if (!pc) { @@ -426,6 +436,8 @@ static int __devinit ehrpwm_pwm_probe(struct platform_device *pdev) pc->chip.dev = &pdev->dev; pc->chip.ops = &ehrpwm_pwm_ops; + pc->chip.of_xlate = of_pwm_xlate_with_flags; + pc->chip.of_pwm_n_cells = 3; pc->chip.base = -1; pc->chip.npwm = NUM_PWM_CHANNEL; @@ -453,14 +465,40 @@ static int __devinit ehrpwm_pwm_probe(struct platform_device *pdev) } pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + status = pwmss_submodule_state_change(pdev->dev.parent, + PWMSS_EPWMCLK_EN); + if (!(status & PWMSS_EPWMCLK_EN_ACK)) { + dev_err(&pdev->dev, "PWMSS config space clock enable failed\n"); + ret = -EINVAL; + goto pwmss_clk_failure; + } + + pm_runtime_put_sync(&pdev->dev); + platform_set_drvdata(pdev, pc); return 0; + +pwmss_clk_failure: + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pwmchip_remove(&pc->chip); + return ret; } static int __devexit ehrpwm_pwm_remove(struct platform_device *pdev) { struct ehrpwm_pwm_chip *pc = platform_get_drvdata(pdev); + pm_runtime_get_sync(&pdev->dev); + /* + * Due to hardware misbehaviour, acknowledge of the stop_req + * is missing. Hence checking of the status bit skipped. + */ + pwmss_submodule_state_change(pdev->dev.parent, PWMSS_EPWMCLK_STOP_REQ); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); return pwmchip_remove(&pc->chip); @@ -468,7 +506,9 @@ static int __devexit ehrpwm_pwm_remove(struct platform_device *pdev) static struct platform_driver ehrpwm_pwm_driver = { .driver = { - .name = "ehrpwm", + .name = "ehrpwm", + .owner = THIS_MODULE, + .of_match_table = ehrpwm_of_match, }, .probe = ehrpwm_pwm_probe, .remove = __devexit_p(ehrpwm_pwm_remove), -- cgit v1.2.3 From 3744c26386971db1c7ecf5aa5900882deae60492 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 27 Nov 2012 11:09:57 +0100 Subject: pwm: New driver to support PWMs on TWL4030/6030 series of PMICs The driver supports the following PWM outputs: TWL4030 PWM0 and PWM1 TWL6030 PWM1 and PWM2 On TWL4030 the PWM signals are muxed. Upon requesting the PWM the driver will select the correct mux so the PWM can be used. When the PWM has been freed the original configuration is going to be restored. Signed-off-by: Peter Ujfalusi Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/ti,twl-pwm.txt | 17 + drivers/pwm/Kconfig | 9 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-twl.c | 359 +++++++++++++++++++++ 4 files changed, 386 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt create mode 100644 drivers/pwm/pwm-twl.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt b/Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt new file mode 100644 index 000000000000..2943ee5fce00 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt @@ -0,0 +1,17 @@ +Texas Instruments TWL series PWM drivers + +Supported PWMs: +On TWL4030 series: PWM1 and PWM2 +On TWL6030 series: PWM0 and PWM1 + +Required properties: +- compatible: "ti,twl4030-pwm" or "ti,twl6030-pwm" +- #pwm-cells: should be 2. The first cell specifies the per-chip index + of the PWM to use and the second cell is the period in nanoseconds. + +Example: + +twl_pwm: pwm { + compatible = "ti,twl6030-pwm"; + #pwm-cells = <2>; +}; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 3dcb76d86a97..e1792d5e3861 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -173,6 +173,15 @@ config PWM_TWL6030 To compile this driver as a module, choose M here: the module will be called pwm-twl6030. +config PWM_TWL + tristate "TWL4030/6030 PWM support" + depends on TWL4030_CORE + help + Generic PWM framework driver for TWL4030/6030. + + To compile this driver as a module, choose M here: the module + will be called pwm-twl. + config PWM_VT8500 tristate "vt8500 pwm support" depends on ARCH_VT8500 diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 4fb39f8b6ba9..3b7efa7a40aa 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -14,4 +14,5 @@ obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o obj-$(CONFIG_PWM_TIPWMSS) += pwm-tipwmss.o obj-$(CONFIG_PWM_TWL6030) += pwm-twl6030.o +obj-$(CONFIG_PWM_TWL) += pwm-twl.o obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o diff --git a/drivers/pwm/pwm-twl.c b/drivers/pwm/pwm-twl.c new file mode 100644 index 000000000000..e65db95d5e59 --- /dev/null +++ b/drivers/pwm/pwm-twl.c @@ -0,0 +1,359 @@ +/* + * Driver for TWL4030/6030 Generic Pulse Width Modulator + * + * Copyright (C) 2012 Texas Instruments + * Author: Peter Ujfalusi + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include +#include + +/* + * This driver handles the PWMs of TWL4030 and TWL6030. + * The TRM names for the PWMs on TWL4030 are: PWM0, PWM1 + * TWL6030 also have two PWMs named in the TRM as PWM1, PWM2 + */ + +#define TWL_PWM_MAX 0x7f + +/* Registers, bits and macro for TWL4030 */ +#define TWL4030_GPBR1_REG 0x0c +#define TWL4030_PMBR1_REG 0x0d + +/* GPBR1 register bits */ +#define TWL4030_PWMXCLK_ENABLE (1 << 0) +#define TWL4030_PWMX_ENABLE (1 << 2) +#define TWL4030_PWMX_BITS (TWL4030_PWMX_ENABLE | TWL4030_PWMXCLK_ENABLE) +#define TWL4030_PWM_TOGGLE(pwm, x) ((x) << (pwm)) + +/* PMBR1 register bits */ +#define TWL4030_GPIO6_PWM0_MUTE_MASK (0x03 << 2) +#define TWL4030_GPIO6_PWM0_MUTE_PWM0 (0x01 << 2) +#define TWL4030_GPIO7_VIBRASYNC_PWM1_MASK (0x03 << 4) +#define TWL4030_GPIO7_VIBRASYNC_PWM1_PWM1 (0x03 << 4) + +/* Register, bits and macro for TWL6030 */ +#define TWL6030_TOGGLE3_REG 0x92 + +#define TWL6030_PWMXR (1 << 0) +#define TWL6030_PWMXS (1 << 1) +#define TWL6030_PWMXEN (1 << 2) +#define TWL6030_PWM_TOGGLE(pwm, x) ((x) << (pwm * 3)) + +struct twl_pwm_chip { + struct pwm_chip chip; + struct mutex mutex; + u8 twl6030_toggle3; + u8 twl4030_pwm_mux; +}; + +static inline struct twl_pwm_chip *to_twl(struct pwm_chip *chip) +{ + return container_of(chip, struct twl_pwm_chip, chip); +} + +static int twl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + int duty_cycle = DIV_ROUND_UP(duty_ns * TWL_PWM_MAX, period_ns) + 1; + u8 pwm_config[2] = { 1, 0 }; + int base, ret; + + /* + * To configure the duty period: + * On-cycle is set to 1 (the minimum allowed value) + * The off time of 0 is not configurable, so the mapping is: + * 0 -> off cycle = 2, + * 1 -> off cycle = 2, + * 2 -> off cycle = 3, + * 126 - > off cycle 127, + * 127 - > off cycle 1 + * When on cycle == off cycle the PWM will be always on + */ + if (duty_cycle == 1) + duty_cycle = 2; + else if (duty_cycle > TWL_PWM_MAX) + duty_cycle = 1; + + base = pwm->hwpwm * 3; + + pwm_config[1] = duty_cycle; + + ret = twl_i2c_write(TWL_MODULE_PWM, pwm_config, base, 2); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label); + + return ret; +} + +static int twl4030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwm_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_GPBR1_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read GPBR1\n", pwm->label); + goto out; + } + + val |= TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMXCLK_ENABLE); + + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); + + val |= TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMX_ENABLE); + + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); + return ret; +} + +static void twl4030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwm_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_GPBR1_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read GPBR1\n", pwm->label); + goto out; + } + + val &= ~TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMX_ENABLE); + + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + + val &= ~TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMXCLK_ENABLE); + + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); +} + +static int twl4030_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwm_chip *twl = to_twl(chip); + int ret; + u8 val, mask, bits; + + if (pwm->hwpwm == 1) { + mask = TWL4030_GPIO7_VIBRASYNC_PWM1_MASK; + bits = TWL4030_GPIO7_VIBRASYNC_PWM1_PWM1; + } else { + mask = TWL4030_GPIO6_PWM0_MUTE_MASK; + bits = TWL4030_GPIO6_PWM0_MUTE_PWM0; + } + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_PMBR1_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read PMBR1\n", pwm->label); + goto out; + } + + /* Save the current MUX configuration for the PWM */ + twl->twl4030_pwm_mux &= ~mask; + twl->twl4030_pwm_mux |= (val & mask); + + /* Select PWM functionality */ + val &= ~mask; + val |= bits; + + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_PMBR1_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to request PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); + return ret; +} + +static void twl4030_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwm_chip *twl = container_of(chip, struct twl_pwm_chip, + chip); + int ret; + u8 val, mask; + + if (pwm->hwpwm == 1) + mask = TWL4030_GPIO7_VIBRASYNC_PWM1_MASK; + else + mask = TWL4030_GPIO6_PWM0_MUTE_MASK; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_PMBR1_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read PMBR1\n", pwm->label); + goto out; + } + + /* Restore the MUX configuration for the PWM */ + val &= ~mask; + val |= (twl->twl4030_pwm_mux & mask); + + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_PMBR1_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to free PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); +} + +static int twl6030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwm_chip *twl = container_of(chip, struct twl_pwm_chip, + chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + val = twl->twl6030_toggle3; + val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN); + val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXR); + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); + goto out; + } + + twl->twl6030_toggle3 = val; +out: + mutex_unlock(&twl->mutex); + return 0; +} + +static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwm_chip *twl = container_of(chip, struct twl_pwm_chip, + chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + val = twl->twl6030_toggle3; + val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXR); + val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN); + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read TOGGLE3\n", pwm->label); + goto out; + } + + val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN); + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + goto out; + } + + twl->twl6030_toggle3 = val; +out: + mutex_unlock(&twl->mutex); +} + +static const struct pwm_ops twl4030_pwm_ops = { + .config = twl_pwm_config, + .enable = twl4030_pwm_enable, + .disable = twl4030_pwm_disable, + .request = twl4030_pwm_request, + .free = twl4030_pwm_free, +}; + +static const struct pwm_ops twl6030_pwm_ops = { + .config = twl_pwm_config, + .enable = twl6030_pwm_enable, + .disable = twl6030_pwm_disable, +}; + +static int twl_pwm_probe(struct platform_device *pdev) +{ + struct twl_pwm_chip *twl; + int ret; + + twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL); + if (!twl) + return -ENOMEM; + + if (twl_class_is_4030()) + twl->chip.ops = &twl4030_pwm_ops; + else + twl->chip.ops = &twl6030_pwm_ops; + + twl->chip.dev = &pdev->dev; + twl->chip.base = -1; + twl->chip.npwm = 2; + + mutex_init(&twl->mutex); + + ret = pwmchip_add(&twl->chip); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, twl); + + return 0; +} + +static int twl_pwm_remove(struct platform_device *pdev) +{ + struct twl_pwm_chip *twl = platform_get_drvdata(pdev); + + return pwmchip_remove(&twl->chip); +} + +#ifdef CONFIG_OF +static struct of_device_id twl_pwm_of_match[] = { + { .compatible = "ti,twl4030-pwm" }, + { .compatible = "ti,twl6030-pwm" }, + { }, +}; +MODULE_DEVICE_TABLE(of, twl_pwm_of_match); +#endif + +static struct platform_driver twl_pwm_driver = { + .driver = { + .name = "twl-pwm", + .of_match_table = of_match_ptr(twl_pwm_of_match), + }, + .probe = twl_pwm_probe, + .remove = twl_pwm_remove, +}; +module_platform_driver(twl_pwm_driver); + +MODULE_AUTHOR("Peter Ujfalusi "); +MODULE_DESCRIPTION("PWM driver for TWL4030 and TWL6030"); +MODULE_ALIAS("platform:twl-pwm"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From aa7656471df6cad1a4ed82ba888a40e9bc46aa19 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 27 Nov 2012 11:09:58 +0100 Subject: pwm: New driver to support PWM driven LEDs on TWL4030/6030 series of PMICs The driver supports the following LED outputs as generic PWM driver: TWL4030 LEDA and LEDB (PWMA and PWMB) TWL6030 Charging indicator LED (PWM LED) On TWL6030 when the PWM requested LED is configured to be controlled by SW. In this case the user can enable/disable and set the duty period freely. When the PWM has been freed, the LED driver is put back to HW control. Signed-off-by: Peter Ujfalusi Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/ti,twl-pwmled.txt | 17 + drivers/pwm/Kconfig | 9 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-twl-led.c | 344 +++++++++++++++++++++ 4 files changed, 371 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/ti,twl-pwmled.txt create mode 100644 drivers/pwm/pwm-twl-led.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pwm/ti,twl-pwmled.txt b/Documentation/devicetree/bindings/pwm/ti,twl-pwmled.txt new file mode 100644 index 000000000000..cb64f3acc10f --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/ti,twl-pwmled.txt @@ -0,0 +1,17 @@ +Texas Instruments TWL series PWM drivers connected to LED terminals + +Supported PWMs: +On TWL4030 series: PWMA and PWMB (connected to LEDA and LEDB terminals) +On TWL6030 series: LED PWM (mainly used as charging indicator LED) + +Required properties: +- compatible: "ti,twl4030-pwmled" or "ti,twl6030-pwmled" +- #pwm-cells: should be 2. The first cell specifies the per-chip index + of the PWM to use and the second cell is the period in nanoseconds. + +Example: + +twl_pwmled: pwmled { + compatible = "ti,twl6030-pwmled"; + #pwm-cells = <2>; +}; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index e1792d5e3861..fe788a7c5495 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -182,6 +182,15 @@ config PWM_TWL To compile this driver as a module, choose M here: the module will be called pwm-twl. +config PWM_TWL_LED + tristate "TWL4030/6030 PWM support for LED drivers" + depends on TWL4030_CORE + help + Generic PWM framework driver for TWL4030/6030 LED terminals. + + To compile this driver as a module, choose M here: the module + will be called pwm-twl-led. + config PWM_VT8500 tristate "vt8500 pwm support" depends on ARCH_VT8500 diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 3b7efa7a40aa..4b10133452c4 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -15,4 +15,5 @@ obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o obj-$(CONFIG_PWM_TIPWMSS) += pwm-tipwmss.o obj-$(CONFIG_PWM_TWL6030) += pwm-twl6030.o obj-$(CONFIG_PWM_TWL) += pwm-twl.o +obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o diff --git a/drivers/pwm/pwm-twl-led.c b/drivers/pwm/pwm-twl-led.c new file mode 100644 index 000000000000..9dfa0f3eca30 --- /dev/null +++ b/drivers/pwm/pwm-twl-led.c @@ -0,0 +1,344 @@ +/* + * Driver for TWL4030/6030 Pulse Width Modulator used as LED driver + * + * Copyright (C) 2012 Texas Instruments + * Author: Peter Ujfalusi + * + * This driver is a complete rewrite of the former pwm-twl6030.c authorded by: + * Hemanth V + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include +#include + +/* + * This driver handles the PWM driven LED terminals of TWL4030 and TWL6030. + * To generate the signal on TWL4030: + * - LEDA uses PWMA + * - LEDB uses PWMB + * TWL6030 has one LED pin with dedicated LEDPWM + */ + +#define TWL4030_LED_MAX 0x7f +#define TWL6030_LED_MAX 0xff + +/* Registers, bits and macro for TWL4030 */ +#define TWL4030_LEDEN_REG 0x00 +#define TWL4030_PWMA_REG 0x01 + +#define TWL4030_LEDXON (1 << 0) +#define TWL4030_LEDXPWM (1 << 4) +#define TWL4030_LED_PINS (TWL4030_LEDXON | TWL4030_LEDXPWM) +#define TWL4030_LED_TOGGLE(led, x) ((x) << (led)) + +/* Register, bits and macro for TWL6030 */ +#define TWL6030_LED_PWM_CTRL1 0xf4 +#define TWL6030_LED_PWM_CTRL2 0xf5 + +#define TWL6040_LED_MODE_HW 0x00 +#define TWL6040_LED_MODE_ON 0x01 +#define TWL6040_LED_MODE_OFF 0x02 +#define TWL6040_LED_MODE_MASK 0x03 + +struct twl_pwmled_chip { + struct pwm_chip chip; + struct mutex mutex; +}; + +static inline struct twl_pwmled_chip *to_twl(struct pwm_chip *chip) +{ + return container_of(chip, struct twl_pwmled_chip, chip); +} + +static int twl4030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + int duty_cycle = DIV_ROUND_UP(duty_ns * TWL4030_LED_MAX, period_ns) + 1; + u8 pwm_config[2] = { 1, 0 }; + int base, ret; + + /* + * To configure the duty period: + * On-cycle is set to 1 (the minimum allowed value) + * The off time of 0 is not configurable, so the mapping is: + * 0 -> off cycle = 2, + * 1 -> off cycle = 2, + * 2 -> off cycle = 3, + * 126 - > off cycle 127, + * 127 - > off cycle 1 + * When on cycle == off cycle the PWM will be always on + */ + if (duty_cycle == 1) + duty_cycle = 2; + else if (duty_cycle > TWL4030_LED_MAX) + duty_cycle = 1; + + base = pwm->hwpwm * 2 + TWL4030_PWMA_REG; + + pwm_config[1] = duty_cycle; + + ret = twl_i2c_write(TWL4030_MODULE_LED, pwm_config, base, 2); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label); + + return ret; +} + +static int twl4030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwmled_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL4030_MODULE_LED, &val, TWL4030_LEDEN_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read LEDEN\n", pwm->label); + goto out; + } + + val |= TWL4030_LED_TOGGLE(pwm->hwpwm, TWL4030_LED_PINS); + + ret = twl_i2c_write_u8(TWL4030_MODULE_LED, val, TWL4030_LEDEN_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); + return ret; +} + +static void twl4030_pwmled_disable(struct pwm_chip *chip, + struct pwm_device *pwm) +{ + struct twl_pwmled_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL4030_MODULE_LED, &val, TWL4030_LEDEN_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read LEDEN\n", pwm->label); + goto out; + } + + val &= ~TWL4030_LED_TOGGLE(pwm->hwpwm, TWL4030_LED_PINS); + + ret = twl_i2c_write_u8(TWL4030_MODULE_LED, val, TWL4030_LEDEN_REG); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); +} + +static int twl6030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + int duty_cycle = (duty_ns * TWL6030_LED_MAX) / period_ns; + u8 on_time; + int ret; + + on_time = duty_cycle & 0xff; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, on_time, + TWL6030_LED_PWM_CTRL1); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label); + + return ret; +} + +static int twl6030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwmled_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n", + pwm->label); + goto out; + } + + val &= ~TWL6040_LED_MODE_MASK; + val |= TWL6040_LED_MODE_ON; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); + return ret; +} + +static void twl6030_pwmled_disable(struct pwm_chip *chip, + struct pwm_device *pwm) +{ + struct twl_pwmled_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n", + pwm->label); + goto out; + } + + val &= ~TWL6040_LED_MODE_MASK; + val |= TWL6040_LED_MODE_OFF; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); +} + +static int twl6030_pwmled_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwmled_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n", + pwm->label); + goto out; + } + + val &= ~TWL6040_LED_MODE_MASK; + val |= TWL6040_LED_MODE_OFF; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to request PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); + return ret; +} + +static void twl6030_pwmled_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct twl_pwmled_chip *twl = to_twl(chip); + int ret; + u8 val; + + mutex_lock(&twl->mutex); + ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n", + pwm->label); + goto out; + } + + val &= ~TWL6040_LED_MODE_MASK; + val |= TWL6040_LED_MODE_HW; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to free PWM\n", pwm->label); + +out: + mutex_unlock(&twl->mutex); +} + +static const struct pwm_ops twl4030_pwmled_ops = { + .enable = twl4030_pwmled_enable, + .disable = twl4030_pwmled_disable, + .config = twl4030_pwmled_config, +}; + +static const struct pwm_ops twl6030_pwmled_ops = { + .enable = twl6030_pwmled_enable, + .disable = twl6030_pwmled_disable, + .config = twl6030_pwmled_config, + .request = twl6030_pwmled_request, + .free = twl6030_pwmled_free, +}; + +static int twl_pwmled_probe(struct platform_device *pdev) +{ + struct twl_pwmled_chip *twl; + int ret; + + twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL); + if (!twl) + return -ENOMEM; + + if (twl_class_is_4030()) { + twl->chip.ops = &twl4030_pwmled_ops; + twl->chip.npwm = 2; + } else { + twl->chip.ops = &twl6030_pwmled_ops; + twl->chip.npwm = 1; + } + + twl->chip.dev = &pdev->dev; + twl->chip.base = -1; + + mutex_init(&twl->mutex); + + ret = pwmchip_add(&twl->chip); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, twl); + + return 0; +} + +static int twl_pwmled_remove(struct platform_device *pdev) +{ + struct twl_pwmled_chip *twl = platform_get_drvdata(pdev); + + return pwmchip_remove(&twl->chip); +} + +#ifdef CONFIG_OF +static struct of_device_id twl_pwmled_of_match[] = { + { .compatible = "ti,twl4030-pwmled" }, + { .compatible = "ti,twl6030-pwmled" }, + { }, +}; +MODULE_DEVICE_TABLE(of, twl_pwmled_of_match); +#endif + +static struct platform_driver twl_pwmled_driver = { + .driver = { + .name = "twl-pwmled", + .of_match_table = of_match_ptr(twl_pwmled_of_match), + }, + .probe = twl_pwmled_probe, + .remove = twl_pwmled_remove, +}; +module_platform_driver(twl_pwmled_driver); + +MODULE_AUTHOR("Peter Ujfalusi "); +MODULE_DESCRIPTION("PWM driver for TWL4030 and TWL6030 LED outputs"); +MODULE_ALIAS("platform:twl-pwmled"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 1c08e7e3b2acc9f78bb5f478ffc7999007e309fc Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 28 Nov 2012 11:35:43 +0100 Subject: ASoC: omap-abe-twl6040: Use "ti,jack-detection" DT property as boolean Handle the jack detection inforamtion as bool from devicetree. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/omap-abe-twl6040.txt | 4 ++-- sound/soc/omap/omap-abe-twl6040.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/omap-abe-twl6040.txt b/Documentation/devicetree/bindings/sound/omap-abe-twl6040.txt index 65dec876cb2d..fd40c852d7c7 100644 --- a/Documentation/devicetree/bindings/sound/omap-abe-twl6040.txt +++ b/Documentation/devicetree/bindings/sound/omap-abe-twl6040.txt @@ -12,7 +12,7 @@ Required properties: Optional properties: - ti,dmic: phandle for the OMAP dmic node if the machine have it connected -- ti,jack_detection: Need to be set to <1> if the board capable to detect jack +- ti,jack_detection: Need to be present if the board capable to detect jack insertion, removal. Available audio endpoints for the audio-routing table: @@ -59,7 +59,7 @@ sound { compatible = "ti,abe-twl6040"; ti,model = "SDP4430"; - ti,jack-detection = <1>; + ti,jack-detection; ti,mclk-freq = <38400000>; ti,mcpdm = <&mcpdm>; diff --git a/sound/soc/omap/omap-abe-twl6040.c b/sound/soc/omap/omap-abe-twl6040.c index a57a4e68dcc6..1d6ea8609d28 100644 --- a/sound/soc/omap/omap-abe-twl6040.c +++ b/sound/soc/omap/omap-abe-twl6040.c @@ -331,8 +331,8 @@ static __devinit int omap_abe_probe(struct platform_device *pdev) num_links = 1; } - of_property_read_u32(node, "ti,jack-detection", - &priv->jack_detection); + priv->jack_detection = of_property_read_bool(node, + "ti,jack-detection"); of_property_read_u32(node, "ti,mclk-freq", &priv->mclk_freq); if (!priv->mclk_freq) { -- cgit v1.2.3 From 825e90d0a138738657509d7460d77edb28fcfdc0 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Date: Sat, 13 Oct 2012 05:48:00 +0900 Subject: video: exynos_dp: device tree documentation Add documentation for the DT bindings in exynos display port driver. Signed-off-by: Ajay Kumar Acked-by: Rob Herring Signed-off-by: Jingoo Han --- .../devicetree/bindings/video/exynos_dp.txt | 80 ++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 Documentation/devicetree/bindings/video/exynos_dp.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/video/exynos_dp.txt b/Documentation/devicetree/bindings/video/exynos_dp.txt new file mode 100644 index 000000000000..c60da67a5d76 --- /dev/null +++ b/Documentation/devicetree/bindings/video/exynos_dp.txt @@ -0,0 +1,80 @@ +The Exynos display port interface should be configured based on +the type of panel connected to it. + +We use two nodes: + -dp-controller node + -dptx-phy node(defined inside dp-controller node) + +For the DP-PHY initialization, we use the dptx-phy node. +Required properties for dptx-phy: + -reg: + Base address of DP PHY register. + -samsung,enable-mask: + The bit-mask used to enable/disable DP PHY. + +For the Panel initialization, we read data from dp-controller node. +Required properties for dp-controller: + -compatible: + should be "samsung,exynos5-dp". + -reg: + physical base address of the controller and length + of memory mapped region. + -interrupts: + interrupt combiner values. + -interrupt-parent: + phandle to Interrupt combiner node. + -samsung,color-space: + input video data format. + COLOR_RGB = 0, COLOR_YCBCR422 = 1, COLOR_YCBCR444 = 2 + -samsung,dynamic-range: + dynamic range for input video data. + VESA = 0, CEA = 1 + -samsung,ycbcr-coeff: + YCbCr co-efficients for input video. + COLOR_YCBCR601 = 0, COLOR_YCBCR709 = 1 + -samsung,color-depth: + number of bits per colour component. + COLOR_6 = 0, COLOR_8 = 1, COLOR_10 = 2, COLOR_12 = 3 + -samsung,link-rate: + link rate supported by the panel. + LINK_RATE_1_62GBPS = 0x6, LINK_RATE_2_70GBPS = 0x0A + -samsung,lane-count: + number of lanes supported by the panel. + LANE_COUNT1 = 1, LANE_COUNT2 = 2, LANE_COUNT4 = 4 + +Optional properties for dp-controller: + -interlaced: + interlace scan mode. + Progressive if defined, Interlaced if not defined + -vsync-active-high: + VSYNC polarity configuration. + High if defined, Low if not defined + -hsync-active-high: + HSYNC polarity configuration. + High if defined, Low if not defined + +Example: + +SOC specific portion: + dp-controller { + compatible = "samsung,exynos5-dp"; + reg = <0x145b0000 0x10000>; + interrupts = <10 3>; + interrupt-parent = <&combiner>; + + dptx-phy { + reg = <0x10040720>; + samsung,enable-mask = <1>; + }; + + }; + +Board Specific portion: + dp-controller { + samsung,color-space = <0>; + samsung,dynamic-range = <0>; + samsung,ycbcr-coeff = <0>; + samsung,color-depth = <1>; + samsung,link-rate = <0x0a>; + samsung,lane-count = <4>; + }; -- cgit v1.2.3 From 86605cfe8c7c166999bc7476b17940c68bf2f8b7 Mon Sep 17 00:00:00 2001 From: Vipul Kumar Samar Date: Mon, 26 Nov 2012 17:06:51 +0530 Subject: gpio: stmpe: Add DT support for stmpe gpio This patch allows the STMPE GPIO driver to be successfully probed and initialised when Device Tree support is enabled. Bindings are mentioned in Documentation too. Acked-by: Lee Jones Signed-off-by: Vipul Kumar Samar Signed-off-by: Viresh Kumar Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/gpio/gpio-stmpe.txt | 18 ++++++++++++++++++ drivers/gpio/gpio-stmpe.c | 10 ++++++++-- drivers/mfd/stmpe.c | 2 ++ 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/gpio/gpio-stmpe.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/gpio/gpio-stmpe.txt b/Documentation/devicetree/bindings/gpio/gpio-stmpe.txt new file mode 100644 index 000000000000..a0e4cf885213 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-stmpe.txt @@ -0,0 +1,18 @@ +STMPE gpio +---------- + +Required properties: + - compatible: "st,stmpe-gpio" + +Optional properties: + - st,norequest-mask: bitmask specifying which GPIOs should _not_ be requestable + due to different usage (e.g. touch, keypad) + +Node name must be stmpe_gpio and should be child node of stmpe node to which it +belongs. + +Example: + stmpe_gpio { + compatible = "st,stmpe-gpio"; + st,norequest-mask = <0x20>; //gpio 5 can't be used + }; diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index dce34727bbf8..522c90ee96e8 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -12,6 +12,7 @@ #include #include #include +#include #include /* @@ -304,6 +305,7 @@ static void stmpe_gpio_irq_remove(struct stmpe_gpio *stmpe_gpio) static int __devinit stmpe_gpio_probe(struct platform_device *pdev) { struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent); + struct device_node *np = pdev->dev.of_node; struct stmpe_gpio_platform_data *pdata; struct stmpe_gpio *stmpe_gpio; int ret; @@ -321,13 +323,17 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev) stmpe_gpio->dev = &pdev->dev; stmpe_gpio->stmpe = stmpe; - stmpe_gpio->norequest_mask = pdata ? pdata->norequest_mask : 0; - stmpe_gpio->chip = template_chip; stmpe_gpio->chip.ngpio = stmpe->num_gpios; stmpe_gpio->chip.dev = &pdev->dev; stmpe_gpio->chip.base = pdata ? pdata->gpio_base : -1; + if (pdata) + stmpe_gpio->norequest_mask = pdata->norequest_mask; + else if (np) + of_property_read_u32(np, "st,norequest-mask", + &stmpe_gpio->norequest_mask); + if (irq >= 0) stmpe_gpio->irq_base = stmpe->irq_base + STMPE_INT_GPIO(0); else diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index c94f521f392c..79e88d1fd99a 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -294,12 +294,14 @@ static struct resource stmpe_gpio_resources[] = { static struct mfd_cell stmpe_gpio_cell = { .name = "stmpe-gpio", + .of_compatible = "st,stmpe-gpio", .resources = stmpe_gpio_resources, .num_resources = ARRAY_SIZE(stmpe_gpio_resources), }; static struct mfd_cell stmpe_gpio_cell_noirq = { .name = "stmpe-gpio", + .of_compatible = "st,stmpe-gpio", /* gpio cell resources consist of an irq only so no resources here */ }; -- cgit v1.2.3 From 600587a5be61bb218034bda9b134abf2af7da097 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Wed, 5 Dec 2012 16:08:24 +0000 Subject: of/vendor-prefixes: add Imagination Technologies The "powervr" prefix which is currently described as "Imagination Technologies" isn't really appropriate for non-PowerVR hardware, so deprecate it, changing the description of "powervr" to "PowerVR (deprecated, use img)", and add a separate "img" prefix for "Imagination Technologies Ltd.". Signed-off-by: James Hogan Signed-off-by: Grant Likely --- Documentation/devicetree/bindings/vendor-prefixes.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 98271503f006..770a0193ca1b 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -26,6 +26,7 @@ gef GE Fanuc Intelligent Platforms Embedded Systems, Inc. hp Hewlett Packard ibm International Business Machines (IBM) idt Integrated Device Technologies, Inc. +img Imagination Technologies Ltd. intercontrol Inter Control Group linux Linux-specific binding marvell Marvell Technology Group Ltd. @@ -37,7 +38,7 @@ nvidia NVIDIA nxp NXP Semiconductors onnn ON Semiconductor Corp. picochip Picochip Ltd -powervr Imagination Technologies +powervr PowerVR (deprecated, use img) qcom Qualcomm, Inc. ramtron Ramtron International realtek Realtek Semiconductor Corp. -- cgit v1.2.3 From a2c0911c09190125f52c9941b9d187f601c2f7be Mon Sep 17 00:00:00 2001 From: Vakul Garg Date: Thu, 6 Dec 2012 17:16:30 +0800 Subject: crypto: caam - Updated SEC-4.0 device tree binding for ERA information. The compatible property in device tree "crypto" node has been enhanced to provide SEC ERA information to the applications. Signed-off-by: Vakul Garg Signed-off-by: Herbert Xu --- Documentation/devicetree/bindings/crypto/fsl-sec4.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/crypto/fsl-sec4.txt b/Documentation/devicetree/bindings/crypto/fsl-sec4.txt index bd7ce120bc13..fc9ce6f1688c 100644 --- a/Documentation/devicetree/bindings/crypto/fsl-sec4.txt +++ b/Documentation/devicetree/bindings/crypto/fsl-sec4.txt @@ -54,7 +54,8 @@ PROPERTIES - compatible Usage: required Value type: - Definition: Must include "fsl,sec-v4.0" + Definition: Must include "fsl,sec-v4.0". Also includes SEC + ERA versions (optional) with which the device is compatible. - #address-cells Usage: required @@ -106,7 +107,7 @@ PROPERTIES EXAMPLE crypto@300000 { - compatible = "fsl,sec-v4.0"; + compatible = "fsl,sec-v4.0", "fsl,sec-era-v2.0"; #address-cells = <1>; #size-cells = <1>; reg = <0x300000 0x10000>; -- cgit v1.2.3 From f67605394f0ba44cff297120fda6bb407414ba75 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 6 Dec 2012 09:53:13 +0000 Subject: devicetree/bindings: Move gpio-leds binding into leds directory Merely reorganizing documentation. No functional changes. It makes more sense for the gpio-leds binding to be grouped with other led bindings than with gpio drivers. Signed-off-by: Grant Likely --- Documentation/devicetree/bindings/gpio/led.txt | 52 ---------------------- .../devicetree/bindings/leds/leds-gpio.txt | 52 ++++++++++++++++++++++ 2 files changed, 52 insertions(+), 52 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/led.txt create mode 100644 Documentation/devicetree/bindings/leds/leds-gpio.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/gpio/led.txt b/Documentation/devicetree/bindings/gpio/led.txt deleted file mode 100644 index df1b3080f6b8..000000000000 --- a/Documentation/devicetree/bindings/gpio/led.txt +++ /dev/null @@ -1,52 +0,0 @@ -LEDs connected to GPIO lines - -Required properties: -- compatible : should be "gpio-leds". - -Each LED is represented as a sub-node of the gpio-leds device. Each -node's name represents the name of the corresponding LED. - -LED sub-node properties: -- gpios : Should specify the LED's GPIO, see "gpios property" in - Documentation/devicetree/bindings/gpio/gpio.txt. Active low LEDs should be - indicated using flags in the GPIO specifier. -- label : (optional) - see Documentation/devicetree/bindings/leds/common.txt -- linux,default-trigger : (optional) - see Documentation/devicetree/bindings/leds/common.txt -- default-state: (optional) The initial state of the LED. Valid - values are "on", "off", and "keep". If the LED is already on or off - and the default-state property is set the to same value, then no - glitch should be produced where the LED momentarily turns off (or - on). The "keep" setting will keep the LED at whatever its current - state is, without producing a glitch. The default is off if this - property is not present. - -Examples: - -leds { - compatible = "gpio-leds"; - hdd { - label = "IDE Activity"; - gpios = <&mcu_pio 0 1>; /* Active low */ - linux,default-trigger = "ide-disk"; - }; - - fault { - gpios = <&mcu_pio 1 0>; - /* Keep LED on if BIOS detected hardware fault */ - default-state = "keep"; - }; -}; - -run-control { - compatible = "gpio-leds"; - red { - gpios = <&mpc8572 6 0>; - default-state = "off"; - }; - green { - gpios = <&mpc8572 7 0>; - default-state = "on"; - }; -}; diff --git a/Documentation/devicetree/bindings/leds/leds-gpio.txt b/Documentation/devicetree/bindings/leds/leds-gpio.txt new file mode 100644 index 000000000000..df1b3080f6b8 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-gpio.txt @@ -0,0 +1,52 @@ +LEDs connected to GPIO lines + +Required properties: +- compatible : should be "gpio-leds". + +Each LED is represented as a sub-node of the gpio-leds device. Each +node's name represents the name of the corresponding LED. + +LED sub-node properties: +- gpios : Should specify the LED's GPIO, see "gpios property" in + Documentation/devicetree/bindings/gpio/gpio.txt. Active low LEDs should be + indicated using flags in the GPIO specifier. +- label : (optional) + see Documentation/devicetree/bindings/leds/common.txt +- linux,default-trigger : (optional) + see Documentation/devicetree/bindings/leds/common.txt +- default-state: (optional) The initial state of the LED. Valid + values are "on", "off", and "keep". If the LED is already on or off + and the default-state property is set the to same value, then no + glitch should be produced where the LED momentarily turns off (or + on). The "keep" setting will keep the LED at whatever its current + state is, without producing a glitch. The default is off if this + property is not present. + +Examples: + +leds { + compatible = "gpio-leds"; + hdd { + label = "IDE Activity"; + gpios = <&mcu_pio 0 1>; /* Active low */ + linux,default-trigger = "ide-disk"; + }; + + fault { + gpios = <&mcu_pio 1 0>; + /* Keep LED on if BIOS detected hardware fault */ + default-state = "keep"; + }; +}; + +run-control { + compatible = "gpio-leds"; + red { + gpios = <&mpc8572 6 0>; + default-state = "off"; + }; + green { + gpios = <&mpc8572 7 0>; + default-state = "on"; + }; +}; -- cgit v1.2.3 From 3a96dff0f828ae9dfb43efd49a9b67a74c6dc360 Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Sun, 18 Nov 2012 15:33:06 +1300 Subject: mmc: SD/MMC Host Controller for Wondermedia WM8505/WM8650 This patch adds support for the SD/MMC host controller found on Wondermedia 8xxx series SoCs, currently supported under arm/arch-vt8500. A binding document is also included, based on mmc.txt with additional properties. Signed-off-by: Tony Prisk Signed-off-by: Chris Ball --- .../devicetree/bindings/mmc/vt8500-sdmmc.txt | 23 + MAINTAINERS | 1 + drivers/mmc/host/Kconfig | 11 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/wmt-sdmmc.c | 1029 ++++++++++++++++++++ 5 files changed, 1065 insertions(+) create mode 100644 Documentation/devicetree/bindings/mmc/vt8500-sdmmc.txt create mode 100644 drivers/mmc/host/wmt-sdmmc.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mmc/vt8500-sdmmc.txt b/Documentation/devicetree/bindings/mmc/vt8500-sdmmc.txt new file mode 100644 index 000000000000..d7fb6abb3eb8 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/vt8500-sdmmc.txt @@ -0,0 +1,23 @@ +* Wondermedia WM8505/WM8650 SD/MMC Host Controller + +This file documents differences between the core properties described +by mmc.txt and the properties used by the wmt-sdmmc driver. + +Required properties: +- compatible: Should be "wm,wm8505-sdhc". +- interrupts: Two interrupts are required - regular irq and dma irq. + +Optional properties: +- sdon-inverted: SD_ON bit is inverted on the controller + +Examples: + +sdhc@d800a000 { + compatible = "wm,wm8505-sdhc"; + reg = <0xd800a000 0x1000>; + interrupts = <20 21>; + clocks = <&sdhc>; + bus-width = <4>; + sdon-inverted; +}; + diff --git a/MAINTAINERS b/MAINTAINERS index d2189fb78dcb..cfafe1b6b004 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1221,6 +1221,7 @@ F: drivers/video/wm8505fb* F: drivers/video/wmt_ge_rops.* F: drivers/tty/serial/vt8500_serial.c F: drivers/rtc/rtc-vt8500-c +F: drivers/mmc/host/wmt-sdmmc.c ARM/ZIPIT Z2 SUPPORT M: Marek Vasut diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 5c7c846e03ef..83eb1e06ff76 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -601,3 +601,14 @@ config MMC_USHC Note: These controllers only support SDIO cards and do not support MMC or SD memory cards. + +config MMC_WMT + tristate "Wondermedia SD/MMC Host Controller support" + depends on ARCH_VT8500 + default y + help + This selects support for the SD/MMC Host Controller on + Wondermedia WM8505/WM8650 based SoCs. + + To compile this driver as a module, choose M here: the + module will be called wmt-sdmmc. diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index cd64b5df9703..39d5e1234709 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o obj-$(CONFIG_MMC_VUB300) += vub300.o obj-$(CONFIG_MMC_USHC) += ushc.o +obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c new file mode 100644 index 000000000000..5ba4605e4f80 --- /dev/null +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -0,0 +1,1029 @@ +/* + * WM8505/WM8650 SD/MMC Host Controller + * + * Copyright (C) 2010 Tony Prisk + * Copyright (C) 2008 WonderMedia Technologies, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + + +#define DRIVER_NAME "wmt-sdhc" + + +/* MMC/SD controller registers */ +#define SDMMC_CTLR 0x00 +#define SDMMC_CMD 0x01 +#define SDMMC_RSPTYPE 0x02 +#define SDMMC_ARG 0x04 +#define SDMMC_BUSMODE 0x08 +#define SDMMC_BLKLEN 0x0C +#define SDMMC_BLKCNT 0x0E +#define SDMMC_RSP 0x10 +#define SDMMC_CBCR 0x20 +#define SDMMC_INTMASK0 0x24 +#define SDMMC_INTMASK1 0x25 +#define SDMMC_STS0 0x28 +#define SDMMC_STS1 0x29 +#define SDMMC_STS2 0x2A +#define SDMMC_STS3 0x2B +#define SDMMC_RSPTIMEOUT 0x2C +#define SDMMC_CLK 0x30 /* VT8500 only */ +#define SDMMC_EXTCTRL 0x34 +#define SDMMC_SBLKLEN 0x38 +#define SDMMC_DMATIMEOUT 0x3C + + +/* SDMMC_CTLR bit fields */ +#define CTLR_CMD_START 0x01 +#define CTLR_CMD_WRITE 0x04 +#define CTLR_FIFO_RESET 0x08 + +/* SDMMC_BUSMODE bit fields */ +#define BM_SPI_MODE 0x01 +#define BM_FOURBIT_MODE 0x02 +#define BM_EIGHTBIT_MODE 0x04 +#define BM_SD_OFF 0x10 +#define BM_SPI_CS 0x20 +#define BM_SD_POWER 0x40 +#define BM_SOFT_RESET 0x80 +#define BM_ONEBIT_MASK 0xFD + +/* SDMMC_BLKLEN bit fields */ +#define BLKL_CRCERR_ABORT 0x0800 +#define BLKL_CD_POL_HIGH 0x1000 +#define BLKL_GPI_CD 0x2000 +#define BLKL_DATA3_CD 0x4000 +#define BLKL_INT_ENABLE 0x8000 + +/* SDMMC_INTMASK0 bit fields */ +#define INT0_MBLK_TRAN_DONE_INT_EN 0x10 +#define INT0_BLK_TRAN_DONE_INT_EN 0x20 +#define INT0_CD_INT_EN 0x40 +#define INT0_DI_INT_EN 0x80 + +/* SDMMC_INTMASK1 bit fields */ +#define INT1_CMD_RES_TRAN_DONE_INT_EN 0x02 +#define INT1_CMD_RES_TOUT_INT_EN 0x04 +#define INT1_MBLK_AUTO_STOP_INT_EN 0x08 +#define INT1_DATA_TOUT_INT_EN 0x10 +#define INT1_RESCRC_ERR_INT_EN 0x20 +#define INT1_RCRC_ERR_INT_EN 0x40 +#define INT1_WCRC_ERR_INT_EN 0x80 + +/* SDMMC_STS0 bit fields */ +#define STS0_WRITE_PROTECT 0x02 +#define STS0_CD_DATA3 0x04 +#define STS0_CD_GPI 0x08 +#define STS0_MBLK_DONE 0x10 +#define STS0_BLK_DONE 0x20 +#define STS0_CARD_DETECT 0x40 +#define STS0_DEVICE_INS 0x80 + +/* SDMMC_STS1 bit fields */ +#define STS1_SDIO_INT 0x01 +#define STS1_CMDRSP_DONE 0x02 +#define STS1_RSP_TIMEOUT 0x04 +#define STS1_AUTOSTOP_DONE 0x08 +#define STS1_DATA_TIMEOUT 0x10 +#define STS1_RSP_CRC_ERR 0x20 +#define STS1_RCRC_ERR 0x40 +#define STS1_WCRC_ERR 0x80 + +/* SDMMC_STS2 bit fields */ +#define STS2_CMD_RES_BUSY 0x10 +#define STS2_DATARSP_BUSY 0x20 +#define STS2_DIS_FORCECLK 0x80 + + +/* MMC/SD DMA Controller Registers */ +#define SDDMA_GCR 0x100 +#define SDDMA_IER 0x104 +#define SDDMA_ISR 0x108 +#define SDDMA_DESPR 0x10C +#define SDDMA_RBR 0x110 +#define SDDMA_DAR 0x114 +#define SDDMA_BAR 0x118 +#define SDDMA_CPR 0x11C +#define SDDMA_CCR 0x120 + + +/* SDDMA_GCR bit fields */ +#define DMA_GCR_DMA_EN 0x00000001 +#define DMA_GCR_SOFT_RESET 0x00000100 + +/* SDDMA_IER bit fields */ +#define DMA_IER_INT_EN 0x00000001 + +/* SDDMA_ISR bit fields */ +#define DMA_ISR_INT_STS 0x00000001 + +/* SDDMA_RBR bit fields */ +#define DMA_RBR_FORMAT 0x40000000 +#define DMA_RBR_END 0x80000000 + +/* SDDMA_CCR bit fields */ +#define DMA_CCR_RUN 0x00000080 +#define DMA_CCR_IF_TO_PERIPHERAL 0x00000000 +#define DMA_CCR_PERIPHERAL_TO_IF 0x00400000 + +/* SDDMA_CCR event status */ +#define DMA_CCR_EVT_NO_STATUS 0x00000000 +#define DMA_CCR_EVT_UNDERRUN 0x00000001 +#define DMA_CCR_EVT_OVERRUN 0x00000002 +#define DMA_CCR_EVT_DESP_READ 0x00000003 +#define DMA_CCR_EVT_DATA_RW 0x00000004 +#define DMA_CCR_EVT_EARLY_END 0x00000005 +#define DMA_CCR_EVT_SUCCESS 0x0000000F + +#define PDMA_READ 0x00 +#define PDMA_WRITE 0x01 + +#define WMT_SD_POWER_OFF 0 +#define WMT_SD_POWER_ON 1 + +struct wmt_dma_descriptor { + u32 flags; + u32 data_buffer_addr; + u32 branch_addr; + u32 reserved1; +}; + +struct wmt_mci_caps { + unsigned int f_min; + unsigned int f_max; + u32 ocr_avail; + u32 caps; + u32 max_seg_size; + u32 max_segs; + u32 max_blk_size; +}; + +struct wmt_mci_priv { + struct mmc_host *mmc; + void __iomem *sdmmc_base; + + int irq_regular; + int irq_dma; + + void *dma_desc_buffer; + dma_addr_t dma_desc_device_addr; + + struct completion cmdcomp; + struct completion datacomp; + + struct completion *comp_cmd; + struct completion *comp_dma; + + struct mmc_request *req; + struct mmc_command *cmd; + + struct clk *clk_sdmmc; + struct device *dev; + + u8 power_inverted; + u8 cd_inverted; +}; + +static void wmt_set_sd_power(struct wmt_mci_priv *priv, int enable) +{ + u32 reg_tmp; + if (enable) { + if (priv->power_inverted) { + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_SD_OFF, + priv->sdmmc_base + SDMMC_BUSMODE); + } else { + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp & (~BM_SD_OFF), + priv->sdmmc_base + SDMMC_BUSMODE); + } + } else { + if (priv->power_inverted) { + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp & (~BM_SD_OFF), + priv->sdmmc_base + SDMMC_BUSMODE); + } else { + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_SD_OFF, + priv->sdmmc_base + SDMMC_BUSMODE); + } + } +} + +static void wmt_mci_read_response(struct mmc_host *mmc) +{ + struct wmt_mci_priv *priv; + int idx1, idx2; + u8 tmp_resp; + u32 response; + + priv = mmc_priv(mmc); + + for (idx1 = 0; idx1 < 4; idx1++) { + response = 0; + for (idx2 = 0; idx2 < 4; idx2++) { + if ((idx1 == 3) && (idx2 == 3)) + tmp_resp = readb(priv->sdmmc_base + SDMMC_RSP); + else + tmp_resp = readb(priv->sdmmc_base + SDMMC_RSP + + (idx1*4) + idx2 + 1); + response |= (tmp_resp << (idx2 * 8)); + } + priv->cmd->resp[idx1] = cpu_to_be32(response); + } +} + +static void wmt_mci_start_command(struct wmt_mci_priv *priv) +{ + u32 reg_tmp; + + reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR); + writeb(reg_tmp | CTLR_CMD_START, priv->sdmmc_base + SDMMC_CTLR); +} + +static int wmt_mci_send_command(struct mmc_host *mmc, u8 command, u8 cmdtype, + u32 arg, u8 rsptype) +{ + struct wmt_mci_priv *priv; + u32 reg_tmp; + + priv = mmc_priv(mmc); + + /* write command, arg, resptype registers */ + writeb(command, priv->sdmmc_base + SDMMC_CMD); + writel(arg, priv->sdmmc_base + SDMMC_ARG); + writeb(rsptype, priv->sdmmc_base + SDMMC_RSPTYPE); + + /* reset response FIFO */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR); + writeb(reg_tmp | CTLR_FIFO_RESET, priv->sdmmc_base + SDMMC_CTLR); + + /* ensure clock enabled - VT3465 */ + wmt_set_sd_power(priv, WMT_SD_POWER_ON); + + /* clear status bits */ + writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS2); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS3); + + /* set command type */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR); + writeb((reg_tmp & 0x0F) | (cmdtype << 4), + priv->sdmmc_base + SDMMC_CTLR); + + return 0; +} + +static void wmt_mci_disable_dma(struct wmt_mci_priv *priv) +{ + writel(DMA_ISR_INT_STS, priv->sdmmc_base + SDDMA_ISR); + writel(0, priv->sdmmc_base + SDDMA_IER); +} + +static void wmt_complete_data_request(struct wmt_mci_priv *priv) +{ + struct mmc_request *req; + req = priv->req; + + req->data->bytes_xfered = req->data->blksz * req->data->blocks; + + /* unmap the DMA pages used for write data */ + if (req->data->flags & MMC_DATA_WRITE) + dma_unmap_sg(mmc_dev(priv->mmc), req->data->sg, + req->data->sg_len, DMA_TO_DEVICE); + else + dma_unmap_sg(mmc_dev(priv->mmc), req->data->sg, + req->data->sg_len, DMA_FROM_DEVICE); + + /* Check if the DMA ISR returned a data error */ + if ((req->cmd->error) || (req->data->error)) + mmc_request_done(priv->mmc, req); + else { + wmt_mci_read_response(priv->mmc); + if (!req->data->stop) { + /* single-block read/write requests end here */ + mmc_request_done(priv->mmc, req); + } else { + /* + * we change the priv->cmd variable so the response is + * stored in the stop struct rather than the original + * calling command struct + */ + priv->comp_cmd = &priv->cmdcomp; + init_completion(priv->comp_cmd); + priv->cmd = req->data->stop; + wmt_mci_send_command(priv->mmc, req->data->stop->opcode, + 7, req->data->stop->arg, 9); + wmt_mci_start_command(priv); + } + } +} + +static irqreturn_t wmt_mci_dma_isr(int irq_num, void *data) +{ + struct mmc_host *mmc; + struct wmt_mci_priv *priv; + + int status; + + priv = (struct wmt_mci_priv *)data; + mmc = priv->mmc; + + status = readl(priv->sdmmc_base + SDDMA_CCR) & 0x0F; + + if (status != DMA_CCR_EVT_SUCCESS) { + dev_err(priv->dev, "DMA Error: Status = %d\n", status); + priv->req->data->error = -ETIMEDOUT; + complete(priv->comp_dma); + return IRQ_HANDLED; + } + + priv->req->data->error = 0; + + wmt_mci_disable_dma(priv); + + complete(priv->comp_dma); + + if (priv->comp_cmd) { + if (completion_done(priv->comp_cmd)) { + /* + * if the command (regular) interrupt has already + * completed, finish off the request otherwise we wait + * for the command interrupt and finish from there. + */ + wmt_complete_data_request(priv); + } + } + + return IRQ_HANDLED; +} + +static irqreturn_t wmt_mci_regular_isr(int irq_num, void *data) +{ + struct wmt_mci_priv *priv; + u32 status0; + u32 status1; + u32 status2; + u32 reg_tmp; + int cmd_done; + + priv = (struct wmt_mci_priv *)data; + cmd_done = 0; + status0 = readb(priv->sdmmc_base + SDMMC_STS0); + status1 = readb(priv->sdmmc_base + SDMMC_STS1); + status2 = readb(priv->sdmmc_base + SDMMC_STS2); + + /* Check for card insertion */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_INTMASK0); + if ((reg_tmp & INT0_DI_INT_EN) && (status0 & STS0_DEVICE_INS)) { + mmc_detect_change(priv->mmc, 0); + if (priv->cmd) + priv->cmd->error = -ETIMEDOUT; + if (priv->comp_cmd) + complete(priv->comp_cmd); + if (priv->comp_dma) { + wmt_mci_disable_dma(priv); + complete(priv->comp_dma); + } + writeb(STS0_DEVICE_INS, priv->sdmmc_base + SDMMC_STS0); + return IRQ_HANDLED; + } + + if ((!priv->req->data) || + ((priv->req->data->stop) && (priv->cmd == priv->req->data->stop))) { + /* handle non-data & stop_transmission requests */ + if (status1 & STS1_CMDRSP_DONE) { + priv->cmd->error = 0; + cmd_done = 1; + } else if ((status1 & STS1_RSP_TIMEOUT) || + (status1 & STS1_DATA_TIMEOUT)) { + priv->cmd->error = -ETIMEDOUT; + cmd_done = 1; + } + + if (cmd_done) { + priv->comp_cmd = NULL; + + if (!priv->cmd->error) + wmt_mci_read_response(priv->mmc); + + priv->cmd = NULL; + + mmc_request_done(priv->mmc, priv->req); + } + } else { + /* handle data requests */ + if (status1 & STS1_CMDRSP_DONE) { + if (priv->cmd) + priv->cmd->error = 0; + if (priv->comp_cmd) + complete(priv->comp_cmd); + } + + if ((status1 & STS1_RSP_TIMEOUT) || + (status1 & STS1_DATA_TIMEOUT)) { + if (priv->cmd) + priv->cmd->error = -ETIMEDOUT; + if (priv->comp_cmd) + complete(priv->comp_cmd); + if (priv->comp_dma) { + wmt_mci_disable_dma(priv); + complete(priv->comp_dma); + } + } + + if (priv->comp_dma) { + /* + * If the dma interrupt has already completed, finish + * off the request; otherwise we wait for the DMA + * interrupt and finish from there. + */ + if (completion_done(priv->comp_dma)) + wmt_complete_data_request(priv); + } + } + + writeb(status0, priv->sdmmc_base + SDMMC_STS0); + writeb(status1, priv->sdmmc_base + SDMMC_STS1); + writeb(status2, priv->sdmmc_base + SDMMC_STS2); + + return IRQ_HANDLED; +} + +static void wmt_reset_hardware(struct mmc_host *mmc) +{ + struct wmt_mci_priv *priv; + u32 reg_tmp; + + priv = mmc_priv(mmc); + + /* reset controller */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + SDMMC_BUSMODE); + + /* reset response FIFO */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR); + writeb(reg_tmp | CTLR_FIFO_RESET, priv->sdmmc_base + SDMMC_CTLR); + + /* enable GPI pin to detect card */ + writew(BLKL_INT_ENABLE | BLKL_GPI_CD, priv->sdmmc_base + SDMMC_BLKLEN); + + /* clear interrupt status */ + writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); + + /* setup interrupts */ + writeb(INT0_CD_INT_EN | INT0_DI_INT_EN, priv->sdmmc_base + + SDMMC_INTMASK0); + writeb(INT1_DATA_TOUT_INT_EN | INT1_CMD_RES_TRAN_DONE_INT_EN | + INT1_CMD_RES_TOUT_INT_EN, priv->sdmmc_base + SDMMC_INTMASK1); + + /* set the DMA timeout */ + writew(8191, priv->sdmmc_base + SDMMC_DMATIMEOUT); + + /* auto clock freezing enable */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_STS2); + writeb(reg_tmp | STS2_DIS_FORCECLK, priv->sdmmc_base + SDMMC_STS2); + + /* set a default clock speed of 400Khz */ + clk_set_rate(priv->clk_sdmmc, 400000); +} + +static int wmt_dma_init(struct mmc_host *mmc) +{ + struct wmt_mci_priv *priv; + + priv = mmc_priv(mmc); + + writel(DMA_GCR_SOFT_RESET, priv->sdmmc_base + SDDMA_GCR); + writel(DMA_GCR_DMA_EN, priv->sdmmc_base + SDDMA_GCR); + if ((readl(priv->sdmmc_base + SDDMA_GCR) & DMA_GCR_DMA_EN) != 0) + return 0; + else + return 1; +} + +static void wmt_dma_init_descriptor(struct wmt_dma_descriptor *desc, + u16 req_count, u32 buffer_addr, u32 branch_addr, int end) +{ + desc->flags = 0x40000000 | req_count; + if (end) + desc->flags |= 0x80000000; + desc->data_buffer_addr = buffer_addr; + desc->branch_addr = branch_addr; +} + +static void wmt_dma_config(struct mmc_host *mmc, u32 descaddr, u8 dir) +{ + struct wmt_mci_priv *priv; + u32 reg_tmp; + + priv = mmc_priv(mmc); + + /* Enable DMA Interrupts */ + writel(DMA_IER_INT_EN, priv->sdmmc_base + SDDMA_IER); + + /* Write DMA Descriptor Pointer Register */ + writel(descaddr, priv->sdmmc_base + SDDMA_DESPR); + + writel(0x00, priv->sdmmc_base + SDDMA_CCR); + + if (dir == PDMA_WRITE) { + reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR); + writel(reg_tmp & DMA_CCR_IF_TO_PERIPHERAL, priv->sdmmc_base + + SDDMA_CCR); + } else { + reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR); + writel(reg_tmp | DMA_CCR_PERIPHERAL_TO_IF, priv->sdmmc_base + + SDDMA_CCR); + } +} + +static void wmt_dma_start(struct wmt_mci_priv *priv) +{ + u32 reg_tmp; + + reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR); + writel(reg_tmp | DMA_CCR_RUN, priv->sdmmc_base + SDDMA_CCR); +} + +static void wmt_mci_request(struct mmc_host *mmc, struct mmc_request *req) +{ + struct wmt_mci_priv *priv; + struct wmt_dma_descriptor *desc; + u8 command; + u8 cmdtype; + u32 arg; + u8 rsptype; + u32 reg_tmp; + + struct scatterlist *sg; + int i; + int sg_cnt; + int offset; + u32 dma_address; + int desc_cnt; + + priv = mmc_priv(mmc); + priv->req = req; + + /* + * Use the cmd variable to pass a pointer to the resp[] structure + * This is required on multi-block requests to pass the pointer to the + * stop command + */ + priv->cmd = req->cmd; + + command = req->cmd->opcode; + arg = req->cmd->arg; + rsptype = mmc_resp_type(req->cmd); + cmdtype = 0; + + /* rsptype=7 only valid for SPI commands - should be =2 for SD */ + if (rsptype == 7) + rsptype = 2; + /* rsptype=21 is R1B, convert for controller */ + if (rsptype == 21) + rsptype = 9; + + if (!req->data) { + wmt_mci_send_command(mmc, command, cmdtype, arg, rsptype); + wmt_mci_start_command(priv); + /* completion is now handled in the regular_isr() */ + } + if (req->data) { + priv->comp_cmd = &priv->cmdcomp; + init_completion(priv->comp_cmd); + + wmt_dma_init(mmc); + + /* set controller data length */ + reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); + writew((reg_tmp & 0xF800) | (req->data->blksz - 1), + priv->sdmmc_base + SDMMC_BLKLEN); + + /* set controller block count */ + writew(req->data->blocks, priv->sdmmc_base + SDMMC_BLKCNT); + + desc = (struct wmt_dma_descriptor *)priv->dma_desc_buffer; + + if (req->data->flags & MMC_DATA_WRITE) { + sg_cnt = dma_map_sg(mmc_dev(mmc), req->data->sg, + req->data->sg_len, DMA_TO_DEVICE); + cmdtype = 1; + if (req->data->blocks > 1) + cmdtype = 3; + } else { + sg_cnt = dma_map_sg(mmc_dev(mmc), req->data->sg, + req->data->sg_len, DMA_FROM_DEVICE); + cmdtype = 2; + if (req->data->blocks > 1) + cmdtype = 4; + } + + dma_address = priv->dma_desc_device_addr + 16; + desc_cnt = 0; + + for_each_sg(req->data->sg, sg, sg_cnt, i) { + offset = 0; + while (offset < sg_dma_len(sg)) { + wmt_dma_init_descriptor(desc, req->data->blksz, + sg_dma_address(sg)+offset, + dma_address, 0); + desc++; + desc_cnt++; + offset += req->data->blksz; + dma_address += 16; + if (desc_cnt == req->data->blocks) + break; + } + } + desc--; + desc->flags |= 0x80000000; + + if (req->data->flags & MMC_DATA_WRITE) + wmt_dma_config(mmc, priv->dma_desc_device_addr, + PDMA_WRITE); + else + wmt_dma_config(mmc, priv->dma_desc_device_addr, + PDMA_READ); + + wmt_mci_send_command(mmc, command, cmdtype, arg, rsptype); + + priv->comp_dma = &priv->datacomp; + init_completion(priv->comp_dma); + + wmt_dma_start(priv); + wmt_mci_start_command(priv); + } +} + +static void wmt_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct wmt_mci_priv *priv; + u32 reg_tmp; + + priv = mmc_priv(mmc); + + if (ios->power_mode == MMC_POWER_UP) { + wmt_reset_hardware(mmc); + + wmt_set_sd_power(priv, WMT_SD_POWER_ON); + } + if (ios->power_mode == MMC_POWER_OFF) + wmt_set_sd_power(priv, WMT_SD_POWER_OFF); + + if (ios->clock != 0) + clk_set_rate(priv->clk_sdmmc, ios->clock); + + switch (ios->bus_width) { + case MMC_BUS_WIDTH_8: + reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL); + writeb(reg_tmp | 0x04, priv->sdmmc_base + SDMMC_EXTCTRL); + break; + case MMC_BUS_WIDTH_4: + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_FOURBIT_MODE, priv->sdmmc_base + + SDMMC_BUSMODE); + + reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL); + writeb(reg_tmp & 0xFB, priv->sdmmc_base + SDMMC_EXTCTRL); + break; + case MMC_BUS_WIDTH_1: + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp & BM_ONEBIT_MASK, priv->sdmmc_base + + SDMMC_BUSMODE); + + reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL); + writeb(reg_tmp & 0xFB, priv->sdmmc_base + SDMMC_EXTCTRL); + break; + } +} + +static int wmt_mci_get_ro(struct mmc_host *mmc) +{ + struct wmt_mci_priv *priv = mmc_priv(mmc); + + return !(readb(priv->sdmmc_base + SDMMC_STS0) & STS0_WRITE_PROTECT); +} + +static int wmt_mci_get_cd(struct mmc_host *mmc) +{ + struct wmt_mci_priv *priv = mmc_priv(mmc); + u32 cd = (readb(priv->sdmmc_base + SDMMC_STS0) & STS0_CD_GPI) >> 3; + + return !(cd ^ priv->cd_inverted); +} + +static struct mmc_host_ops wmt_mci_ops = { + .request = wmt_mci_request, + .set_ios = wmt_mci_set_ios, + .get_ro = wmt_mci_get_ro, + .get_cd = wmt_mci_get_cd, +}; + +/* Controller capabilities */ +static struct wmt_mci_caps wm8505_caps = { + .f_min = 390425, + .f_max = 50000000, + .ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34, + .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED | + MMC_CAP_SD_HIGHSPEED, + .max_seg_size = 65024, + .max_segs = 128, + .max_blk_size = 2048, +}; + +static struct of_device_id wmt_mci_dt_ids[] = { + { .compatible = "wm,wm8505-sdhc", .data = &wm8505_caps }, + { /* Sentinel */ }, +}; + +static int __devinit wmt_mci_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct wmt_mci_priv *priv; + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_id = + of_match_device(wmt_mci_dt_ids, &pdev->dev); + const struct wmt_mci_caps *wmt_caps = of_id->data; + int ret; + int regular_irq, dma_irq; + + if (!of_id || !of_id->data) { + dev_err(&pdev->dev, "Controller capabilities data missing\n"); + return -EFAULT; + } + + if (!np) { + dev_err(&pdev->dev, "Missing SDMMC description in devicetree\n"); + return -EFAULT; + } + + regular_irq = irq_of_parse_and_map(np, 0); + dma_irq = irq_of_parse_and_map(np, 1); + + if (!regular_irq || !dma_irq) { + dev_err(&pdev->dev, "Getting IRQs failed!\n"); + ret = -ENXIO; + goto fail1; + } + + mmc = mmc_alloc_host(sizeof(struct wmt_mci_priv), &pdev->dev); + if (!mmc) { + dev_err(&pdev->dev, "Failed to allocate mmc_host\n"); + ret = -ENOMEM; + goto fail1; + } + + mmc->ops = &wmt_mci_ops; + mmc->f_min = wmt_caps->f_min; + mmc->f_max = wmt_caps->f_max; + mmc->ocr_avail = wmt_caps->ocr_avail; + mmc->caps = wmt_caps->caps; + + mmc->max_seg_size = wmt_caps->max_seg_size; + mmc->max_segs = wmt_caps->max_segs; + mmc->max_blk_size = wmt_caps->max_blk_size; + + mmc->max_req_size = (16*512*mmc->max_segs); + mmc->max_blk_count = mmc->max_req_size / 512; + + priv = mmc_priv(mmc); + priv->mmc = mmc; + priv->dev = &pdev->dev; + + priv->power_inverted = 0; + priv->cd_inverted = 0; + + if (of_get_property(np, "sdon-inverted", NULL)) + priv->power_inverted = 1; + if (of_get_property(np, "cd-inverted", NULL)) + priv->cd_inverted = 1; + + priv->sdmmc_base = of_iomap(np, 0); + if (!priv->sdmmc_base) { + dev_err(&pdev->dev, "Failed to map IO space\n"); + ret = -ENOMEM; + goto fail2; + } + + priv->irq_regular = regular_irq; + priv->irq_dma = dma_irq; + + ret = request_irq(regular_irq, wmt_mci_regular_isr, 0, "sdmmc", priv); + if (ret) { + dev_err(&pdev->dev, "Register regular IRQ fail\n"); + goto fail3; + } + + ret = request_irq(dma_irq, wmt_mci_dma_isr, 32, "sdmmc", priv); + if (ret) { + dev_err(&pdev->dev, "Register DMA IRQ fail\n"); + goto fail4; + } + + /* alloc some DMA buffers for descriptors/transfers */ + priv->dma_desc_buffer = dma_alloc_coherent(&pdev->dev, + mmc->max_blk_count * 16, + &priv->dma_desc_device_addr, + 208); + if (!priv->dma_desc_buffer) { + dev_err(&pdev->dev, "DMA alloc fail\n"); + ret = -EPERM; + goto fail5; + } + + platform_set_drvdata(pdev, mmc); + + priv->clk_sdmmc = of_clk_get(np, 0); + if (IS_ERR(priv->clk_sdmmc)) { + dev_err(&pdev->dev, "Error getting clock\n"); + ret = PTR_ERR(priv->clk_sdmmc); + goto fail5; + } + + clk_prepare_enable(priv->clk_sdmmc); + + /* configure the controller to a known 'ready' state */ + wmt_reset_hardware(mmc); + + mmc_add_host(mmc); + + dev_info(&pdev->dev, "WMT SDHC Controller initialized\n"); + + return 0; +fail5: + free_irq(dma_irq, priv); +fail4: + free_irq(regular_irq, priv); +fail3: + iounmap(priv->sdmmc_base); +fail2: + mmc_free_host(mmc); +fail1: + return ret; +} + +static int __devexit wmt_mci_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct wmt_mci_priv *priv; + struct resource *res; + u32 reg_tmp; + + mmc = platform_get_drvdata(pdev); + priv = mmc_priv(mmc); + + /* reset SD controller */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writel(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + SDMMC_BUSMODE); + reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); + writew(reg_tmp & ~(0xA000), priv->sdmmc_base + SDMMC_BLKLEN); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); + + /* release the dma buffers */ + dma_free_coherent(&pdev->dev, priv->mmc->max_blk_count * 16, + priv->dma_desc_buffer, priv->dma_desc_device_addr); + + mmc_remove_host(mmc); + + free_irq(priv->irq_regular, priv); + free_irq(priv->irq_dma, priv); + + iounmap(priv->sdmmc_base); + + clk_disable_unprepare(priv->clk_sdmmc); + clk_put(priv->clk_sdmmc); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, res->end - res->start + 1); + + mmc_free_host(mmc); + + platform_set_drvdata(pdev, NULL); + + dev_info(&pdev->dev, "WMT MCI device removed\n"); + + return 0; +} + +#ifdef CONFIG_PM +static int wmt_mci_suspend(struct device *dev) +{ + u32 reg_tmp; + struct platform_device *pdev = to_platform_device(dev); + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct wmt_mci_priv *priv; + int ret; + + if (!mmc) + return 0; + + priv = mmc_priv(mmc); + ret = mmc_suspend_host(mmc); + + if (!ret) { + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + + SDMMC_BUSMODE); + + reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); + writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN); + + writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); + + clk_disable(priv->clk_sdmmc); + } + return ret; +} + +static int wmt_mci_resume(struct device *dev) +{ + u32 reg_tmp; + struct platform_device *pdev = to_platform_device(dev); + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct wmt_mci_priv *priv; + int ret = 0; + + if (mmc) { + priv = mmc_priv(mmc); + clk_enable(priv->clk_sdmmc); + + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + + SDMMC_BUSMODE); + + reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); + writew(reg_tmp | (BLKL_GPI_CD | BLKL_INT_ENABLE), + priv->sdmmc_base + SDMMC_BLKLEN); + + reg_tmp = readb(priv->sdmmc_base + SDMMC_INTMASK0); + writeb(reg_tmp | INT0_DI_INT_EN, priv->sdmmc_base + + SDMMC_INTMASK0); + + ret = mmc_resume_host(mmc); + } + + return ret; +} + +static const struct dev_pm_ops wmt_mci_pm = { + .suspend = wmt_mci_suspend, + .resume = wmt_mci_resume, +}; + +#define wmt_mci_pm_ops (&wmt_mci_pm) + +#else /* !CONFIG_PM */ + +#define wmt_mci_pm_ops NULL + +#endif + +static struct platform_driver wmt_mci_driver = { + .probe = wmt_mci_probe, + .remove = __exit_p(wmt_mci_remove), + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .pm = wmt_mci_pm_ops, + .of_match_table = wmt_mci_dt_ids, + }, +}; + +module_platform_driver(wmt_mci_driver); + +MODULE_DESCRIPTION("Wondermedia MMC/SD Driver"); +MODULE_AUTHOR("Tony Prisk"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, wmt_mci_dt_ids); -- cgit v1.2.3 From 9f2f7cce3293dea973b320ab4b95b90b8ea2ec20 Mon Sep 17 00:00:00 2001 From: Abhilash Kesavan Date: Mon, 19 Nov 2012 10:26:18 +0530 Subject: mmc: dt: Fix typo in filename Fix typo in the synopsis dwmmc controller dt binding filename. Signed-off-by: Abhilash Kesavan Signed-off-by: Chris Ball --- .../devicetree/bindings/mmc/synopsis-dw-mshc.txt | 79 ++++++++++++++++++++++ .../devicetree/bindings/mmc/synposis-dw-mshc.txt | 79 ---------------------- 2 files changed, 79 insertions(+), 79 deletions(-) create mode 100644 Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt delete mode 100644 Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt new file mode 100644 index 000000000000..06cd32d08052 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt @@ -0,0 +1,79 @@ +* Synopsis Designware Mobile Storage Host Controller + +The Synopsis designware mobile storage host controller is used to interface +a SoC with storage medium such as eMMC or SD/MMC cards. This file documents +differences between the core mmc properties described by mmc.txt and the +properties used by the Synopsis Designware Mobile Storage Host Controller. + +Required Properties: + +* compatible: should be + - snps,dw-mshc: for controllers compliant with synopsis dw-mshc. +* #address-cells: should be 1. +* #size-cells: should be 0. + +# Slots: The slot specific information are contained within child-nodes with + each child-node representing a supported slot. There should be atleast one + child node representing a card slot. The name of the child node representing + the slot is recommended to be slot@n where n is the unique number of the slot + connnected to the controller. The following are optional properties which + can be included in the slot child node. + + * reg: specifies the physical slot number. The valid values of this + property is 0 to (num-slots -1), where num-slots is the value + specified by the num-slots property. + + * bus-width: as documented in mmc core bindings. + + * wp-gpios: specifies the write protect gpio line. The format of the + gpio specifier depends on the gpio controller. If the write-protect + line is not available, this property is optional. + +Optional properties: + +* num-slots: specifies the number of slots supported by the controller. + The number of physical slots actually used could be equal or less than the + value specified by num-slots. If this property is not specified, the value + of num-slot property is assumed to be 1. + +* fifo-depth: The maximum size of the tx/rx fifo's. If this property is not + specified, the default value of the fifo size is determined from the + controller registers. + +* card-detect-delay: Delay in milli-seconds before detecting card after card + insert event. The default value is 0. + +* supports-highspeed: Enables support for high speed cards (upto 50MHz) + +* broken-cd: as documented in mmc core bindings. + +Aliases: + +- All the MSHC controller nodes should be represented in the aliases node using + the following format 'mshc{n}' where n is a unique number for the alias. + +Example: + +The MSHC controller node can be split into two portions, SoC specific and +board specific portions as listed below. + + dwmmc0@12200000 { + compatible = "snps,dw-mshc"; + reg = <0x12200000 0x1000>; + interrupts = <0 75 0>; + #address-cells = <1>; + #size-cells = <0>; + }; + + dwmmc0@12200000 { + num-slots = <1>; + supports-highspeed; + broken-cd; + fifo-depth = <0x80>; + card-detect-delay = <200>; + + slot@0 { + reg = <0>; + bus-width = <8>; + }; + }; diff --git a/Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt deleted file mode 100644 index 06cd32d08052..000000000000 --- a/Documentation/devicetree/bindings/mmc/synposis-dw-mshc.txt +++ /dev/null @@ -1,79 +0,0 @@ -* Synopsis Designware Mobile Storage Host Controller - -The Synopsis designware mobile storage host controller is used to interface -a SoC with storage medium such as eMMC or SD/MMC cards. This file documents -differences between the core mmc properties described by mmc.txt and the -properties used by the Synopsis Designware Mobile Storage Host Controller. - -Required Properties: - -* compatible: should be - - snps,dw-mshc: for controllers compliant with synopsis dw-mshc. -* #address-cells: should be 1. -* #size-cells: should be 0. - -# Slots: The slot specific information are contained within child-nodes with - each child-node representing a supported slot. There should be atleast one - child node representing a card slot. The name of the child node representing - the slot is recommended to be slot@n where n is the unique number of the slot - connnected to the controller. The following are optional properties which - can be included in the slot child node. - - * reg: specifies the physical slot number. The valid values of this - property is 0 to (num-slots -1), where num-slots is the value - specified by the num-slots property. - - * bus-width: as documented in mmc core bindings. - - * wp-gpios: specifies the write protect gpio line. The format of the - gpio specifier depends on the gpio controller. If the write-protect - line is not available, this property is optional. - -Optional properties: - -* num-slots: specifies the number of slots supported by the controller. - The number of physical slots actually used could be equal or less than the - value specified by num-slots. If this property is not specified, the value - of num-slot property is assumed to be 1. - -* fifo-depth: The maximum size of the tx/rx fifo's. If this property is not - specified, the default value of the fifo size is determined from the - controller registers. - -* card-detect-delay: Delay in milli-seconds before detecting card after card - insert event. The default value is 0. - -* supports-highspeed: Enables support for high speed cards (upto 50MHz) - -* broken-cd: as documented in mmc core bindings. - -Aliases: - -- All the MSHC controller nodes should be represented in the aliases node using - the following format 'mshc{n}' where n is a unique number for the alias. - -Example: - -The MSHC controller node can be split into two portions, SoC specific and -board specific portions as listed below. - - dwmmc0@12200000 { - compatible = "snps,dw-mshc"; - reg = <0x12200000 0x1000>; - interrupts = <0 75 0>; - #address-cells = <1>; - #size-cells = <0>; - }; - - dwmmc0@12200000 { - num-slots = <1>; - supports-highspeed; - broken-cd; - fifo-depth = <0x80>; - card-detect-delay = <200>; - - slot@0 { - reg = <0>; - bus-width = <8>; - }; - }; -- cgit v1.2.3 From e5d0e9c567389e261fbad6311ea78e6b8d4a24e9 Mon Sep 17 00:00:00 2001 From: Abhilash Kesavan Date: Mon, 19 Nov 2012 10:26:19 +0530 Subject: mmc: dt: Add optional pm properties to core binding Add documentation for pm capabilties such as MMC_PM_KEEP_POWER and MMC_PM_WAKE_SDIO_IRQ. Signed-off-by: Abhilash Kesavan Signed-off-by: Chris Ball --- Documentation/devicetree/bindings/mmc/mmc.txt | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index 8e2e0ba2f486..b96e510979bf 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -22,6 +22,10 @@ Optional properties: - wp-inverted: when present, polarity on the wp gpio line is inverted - max-frequency: maximum operating clock frequency +Optional SDIO properties: +- keep-power-in-suspend: Preserves card power during a suspend/resume cycle +- enable-sdio-wakeup: Enables wake up of host system on SDIO IRQ assertion + Example: sdhci@ab000000 { @@ -33,4 +37,6 @@ sdhci@ab000000 { cd-inverted; wp-gpios = <&gpio 70 0>; max-frequency = <50000000>; + keep-power-in-suspend; + enable-sdio-wakeup; } -- cgit v1.2.3 From cd587096c0e2b85a67e77721a753679bac89b394 Mon Sep 17 00:00:00 2001 From: "Hebbar, Gururaja" Date: Mon, 19 Nov 2012 21:59:58 +0530 Subject: mmc: omap_hsmmc: Enable HSPE bit for high speed cards HSMMC IP on AM33xx need a special setting to handle High-speed cards. Other platforms like TI81xx, OMAP4 may need this as-well. This depends on the HSMMC IP timing closure done for the high speed cards. From AM335x TRM (SPRUH73F - 18.3.12 Output Signals Generation): The MMC/SD/SDIO output signals can be driven on either falling edge or rising edge depending on the SD_HCTL[2] HSPE bit. This feature allows to reach better timing performance, and thus to increase data transfer frequency. There are few pre-requisites for enabling the HSPE bit - Controller should support High-Speed-Enable Bit and - Controller should not be using DDR Mode and - Controller should advertise that it supports High Speed in capabilities register and - MMC/SD clock coming out of controller > 25MHz Signed-off-by: Hebbar, Gururaja Signed-off-by: Venkatraman S Signed-off-by: Chris Ball --- .../devicetree/bindings/mmc/ti-omap-hsmmc.txt | 1 + arch/arm/plat-omap/include/plat/mmc.h | 1 + drivers/mmc/host/omap_hsmmc.c | 30 +++++++++++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt index be76a23b34c4..ed271fc255b2 100644 --- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt +++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt @@ -19,6 +19,7 @@ ti,dual-volt: boolean, supports dual voltage cards "supply-name" examples are "vmmc", "vmmc_aux" etc ti,non-removable: non-removable slot (like eMMC) ti,needs-special-reset: Requires a special softreset sequence +ti,needs-special-hs-handling: HSMMC IP needs special setting for handling High Speed Example: mmc1: mmc@0x4809c000 { diff --git a/arch/arm/plat-omap/include/plat/mmc.h b/arch/arm/plat-omap/include/plat/mmc.h index 8b4e4f2da2f5..346af5b490be 100644 --- a/arch/arm/plat-omap/include/plat/mmc.h +++ b/arch/arm/plat-omap/include/plat/mmc.h @@ -126,6 +126,7 @@ struct omap_mmc_platform_data { /* we can put the features above into this variable */ #define HSMMC_HAS_PBIAS (1 << 0) #define HSMMC_HAS_UPDATED_RESET (1 << 1) +#define HSMMC_HAS_HSPE_SUPPORT (1 << 2) unsigned features; int switch_pin; /* gpio (card detect) */ diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 571cd80521c7..18d3f19ef4e5 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -63,6 +63,7 @@ #define VS18 (1 << 26) #define VS30 (1 << 25) +#define HSS (1 << 21) #define SDVS18 (0x5 << 9) #define SDVS30 (0x6 << 9) #define SDVS33 (0x7 << 9) @@ -90,6 +91,7 @@ #define MSBS (1 << 5) #define BCE (1 << 1) #define FOUR_BIT (1 << 1) +#define HSPE (1 << 2) #define DDR (1 << 19) #define DW8 (1 << 5) #define CC 0x1 @@ -495,6 +497,7 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host) struct mmc_ios *ios = &host->mmc->ios; unsigned long regval; unsigned long timeout; + unsigned long clkdiv; dev_vdbg(mmc_dev(host->mmc), "Set clock to %uHz\n", ios->clock); @@ -502,7 +505,8 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host) regval = OMAP_HSMMC_READ(host->base, SYSCTL); regval = regval & ~(CLKD_MASK | DTO_MASK); - regval = regval | (calc_divisor(host, ios) << 6) | (DTO << 16); + clkdiv = calc_divisor(host, ios); + regval = regval | (clkdiv << 6) | (DTO << 16); OMAP_HSMMC_WRITE(host->base, SYSCTL, regval); OMAP_HSMMC_WRITE(host->base, SYSCTL, OMAP_HSMMC_READ(host->base, SYSCTL) | ICE); @@ -513,6 +517,27 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host) && time_before(jiffies, timeout)) cpu_relax(); + /* + * Enable High-Speed Support + * Pre-Requisites + * - Controller should support High-Speed-Enable Bit + * - Controller should not be using DDR Mode + * - Controller should advertise that it supports High Speed + * in capabilities register + * - MMC/SD clock coming out of controller > 25MHz + */ + if ((mmc_slot(host).features & HSMMC_HAS_HSPE_SUPPORT) && + (ios->timing != MMC_TIMING_UHS_DDR50) && + ((OMAP_HSMMC_READ(host->base, CAPA) & HSS) == HSS)) { + regval = OMAP_HSMMC_READ(host->base, HCTL); + if (clkdiv && (clk_get_rate(host->fclk)/clkdiv) > 25000000) + regval |= HSPE; + else + regval &= ~HSPE; + + OMAP_HSMMC_WRITE(host->base, HCTL, regval); + } + omap_hsmmc_start_clock(host); } @@ -1715,6 +1740,9 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev) if (!of_property_read_u32(np, "max-frequency", &max_freq)) pdata->max_freq = max_freq; + if (of_find_property(np, "ti,needs-special-hs-handling", NULL)) + pdata->slots[0].features |= HSMMC_HAS_HSPE_SUPPORT; + return pdata; } #else -- cgit v1.2.3 From b96efccb8fc9939605599e5b2c026644973cc92d Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Fri, 16 Nov 2012 15:28:17 +0100 Subject: mmc: host: sdhci-s3c: Add support for pinctrl This patch adds support for pin configuration using pinctrl subsystem to the sdhci-s3c driver. Signed-off-by: Tomasz Figa Acked-by: Thomas Abraham Signed-off-by: Chris Ball --- .../devicetree/bindings/mmc/samsung-sdhci.txt | 20 +++++++++++++------- drivers/mmc/host/sdhci-s3c.c | 12 ++++++++++-- 2 files changed, 23 insertions(+), 9 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt b/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt index 630a7d7f4718..97e9e315400d 100644 --- a/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt +++ b/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt @@ -12,10 +12,6 @@ is used. The Samsung's SDHCI controller bindings extends this as listed below. [A] The property "samsung,cd-pinmux-gpio" can be used as stated in the "Optional Board Specific Properties" section below. -[B] If core card-detect bindings and "samsung,cd-pinmux-gpio" property - is not specified, it is assumed that there is no card detection - mechanism used. - Required SoC Specific Properties: - compatible: should be one of the following - "samsung,s3c6410-sdhci": For controllers compatible with s3c6410 sdhci @@ -24,14 +20,18 @@ Required SoC Specific Properties: controller. Required Board Specific Properties: -- gpios: Should specify the gpios used for clock, command and data lines. The - gpio specifier format depends on the gpio controller. +- Samsung GPIO variant (will be completely replaced by pinctrl): + - gpios: Should specify the gpios used for clock, command and data lines. The + gpio specifier format depends on the gpio controller. +- Pinctrl variant (preferred if available): + - pinctrl-0: Should specify pin control groups used for this controller. + - pinctrl-names: Should contain only one value - "default". Optional Board Specific Properties: - samsung,cd-pinmux-gpio: Specifies the card detect line that is routed through a pinmux to the card-detect pin of the card slot. This property should be used only if none of the mmc core card-detect properties are - used. + used. Only for Samsung GPIO variant. Example: sdhci@12530000 { @@ -40,12 +40,18 @@ Example: interrupts = <0 75 0>; bus-width = <4>; cd-gpios = <&gpk2 2 2 3 3>; + + /* Samsung GPIO variant */ gpios = <&gpk2 0 2 0 3>, /* clock line */ <&gpk2 1 2 0 3>, /* command line */ <&gpk2 3 2 3 3>, /* data line 0 */ <&gpk2 4 2 3 3>, /* data line 1 */ <&gpk2 5 2 3 3>, /* data line 2 */ <&gpk2 6 2 3 3>; /* data line 3 */ + + /* Pinctrl variant */ + pinctrl-0 = <&sd0_clk &sd0_cmd &sd0_bus4>; + pinctrl-names = "default"; }; Note: This example shows both SoC specific and board specific properties diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 93c5d62fcaff..99697696e7eb 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -57,6 +58,7 @@ struct sdhci_s3c { int ext_cd_irq; int ext_cd_gpio; int *gpios; + struct pinctrl *pctrl; struct clk *clk_io; struct clk *clk_bus[MAX_BUS_CLK]; @@ -484,8 +486,9 @@ static int __devinit sdhci_s3c_parse_dt(struct device *dev, return -EINVAL; } - dev_info(dev, "assuming no card detect line available\n"); - pdata->cd_type = S3C_SDHCI_CD_NONE; + /* assuming internal card detect that will be configured by pinctrl */ + pdata->cd_type = S3C_SDHCI_CD_INTERNAL; + goto setup_bus; found_cd: if (pdata->cd_type == S3C_SDHCI_CD_GPIO) { @@ -503,6 +506,9 @@ static int __devinit sdhci_s3c_parse_dt(struct device *dev, } setup_bus: + if (!IS_ERR(ourhost->pctrl)) + return 0; + /* get the gpios for command, clock and data lines */ for (cnt = 0; cnt < NUM_GPIOS(pdata->max_width); cnt++) { gpio = of_get_gpio(node, cnt); @@ -581,6 +587,8 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) goto err_pdata_io_clk; } + sc->pctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (pdev->dev.of_node) { ret = sdhci_s3c_parse_dt(&pdev->dev, host, pdata); if (ret) -- cgit v1.2.3 From 8ed765aac31b473e1881986d9fe2b6386f49f933 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sun, 25 Nov 2012 13:02:54 -0500 Subject: mmc: dt: add no-1-8-v device tree flag The OLPC XO-1.75 laptop includes a SDHCI controller which is 1.8v capable, and it truthfully reports so in its capabilities. This alternate voltage is used for driving new "UHS-I" SD cards at their full speed. However, what the controller doesn't know is that the motherboard physically doesn't have a 1.8v supply available, so attempting to switch to the 1.8v level will result in a situation that cannot be recovered from without physically replugging the SD card. Add a device tree flag that can be used on systems like these, and hook it up to the equivalent SDHCI quirk. Signed-off-by: Daniel Drake Reviewed-by: Philip Rakity Signed-off-by: Chris Ball --- Documentation/devicetree/bindings/mmc/mmc.txt | 2 ++ drivers/mmc/host/sdhci-pltfm.c | 3 +++ 2 files changed, 5 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index b96e510979bf..a591c6741d75 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -21,6 +21,8 @@ Optional properties: - cd-inverted: when present, polarity on the cd gpio line is inverted - wp-inverted: when present, polarity on the wp gpio line is inverted - max-frequency: maximum operating clock frequency +- no-1-8-v: when present, denotes that 1.8v card voltage is not supported on + this system, even if the controller claims it is. Optional SDIO properties: - keep-power-in-suspend: Preserves card power during a suspend/resume cycle diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index c6c3b1243597..d4283ef5917a 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -78,6 +78,9 @@ void sdhci_get_of_property(struct platform_device *pdev) if (of_get_property(np, "broken-cd", NULL)) host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; + if (of_get_property(np, "no-1-8-v", NULL)) + host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; + if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc")) host->quirks |= SDHCI_QUIRK_BROKEN_DMA; -- cgit v1.2.3 From c20151dff8a6d503c0d0cc4387c33a618cdabcb7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 6 Dec 2012 16:55:33 +0100 Subject: spi: Add support for specifying 3-wire mode via device tree This patch allows to specify that a SPI device is connected in 3-wire mode via device tree. Signed-off-by: Lars-Peter Clausen Acked-by: Rob Herring Signed-off-by: Grant Likely --- Documentation/devicetree/bindings/spi/spi-bus.txt | 2 ++ drivers/spi/spi.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/spi/spi-bus.txt b/Documentation/devicetree/bindings/spi/spi-bus.txt index 77a8b0d39b54..296015e3c632 100644 --- a/Documentation/devicetree/bindings/spi/spi-bus.txt +++ b/Documentation/devicetree/bindings/spi/spi-bus.txt @@ -53,6 +53,8 @@ contain the following properties. shifted clock phase (CPHA) mode - spi-cs-high - (optional) Empty property indicating device requires chip select active high +- spi-3wire - (optional) Empty property indicating device requires + 3-wire mode. If a gpio chipselect is used for the SPI slave the gpio number will be passed via the cs_gpio diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index b370d292d19c..2ca5f079ff01 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -861,6 +861,8 @@ static void of_register_spi_devices(struct spi_master *master) spi->mode |= SPI_CPOL; if (of_find_property(nc, "spi-cs-high", NULL)) spi->mode |= SPI_CS_HIGH; + if (of_find_property(nc, "spi-3wire", NULL)) + spi->mode |= SPI_3WIRE; /* Device speed */ prop = of_get_property(nc, "spi-max-frequency", &len); -- cgit v1.2.3 From ac713cc9fcb8a5f9503af08421772b85ca4685f1 Mon Sep 17 00:00:00 2001 From: Vipul Kumar Samar Date: Fri, 7 Dec 2012 20:29:37 +0530 Subject: mfd: stmpe: Update DT support for stmpe driver This patch extends existing DT support for stmpe devices. This updates: - missing header files in stmpe.c - stmpe_of_probe() with pwm, rotator and new bindings. - Bindings are updated in binding document. Acked-by: Lee Jones Acked-by: Linus Walleij Signed-off-by: Vipul Kumar Samar Signed-off-by: Viresh Kumar Signed-off-by: Samuel Ortiz --- Documentation/devicetree/bindings/mfd/stmpe.txt | 9 ++++++--- drivers/mfd/stmpe.c | 21 +++++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/mfd/stmpe.txt b/Documentation/devicetree/bindings/mfd/stmpe.txt index c98450887d5e..56edb5520685 100644 --- a/Documentation/devicetree/bindings/mfd/stmpe.txt +++ b/Documentation/devicetree/bindings/mfd/stmpe.txt @@ -1,8 +1,11 @@ -* STMPE Multi-Functional Device +* ST Microelectronics STMPE Multi-Functional Device + +STMPE is an MFD device which may expose the following inbuilt devices: gpio, +keypad, touchscreen, adc, pwm, rotator. Required properties: - - compatible : "st,stmpe[811|1601|2401|2403]" - - reg : I2C address of the device + - compatible : "st,stmpe[610|801|811|1601|2401|2403]" + - reg : I2C/SPI address of the device Optional properties: - interrupts : The interrupt outputs from the controller diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 90c6151bc52e..b18cc1a1b162 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -7,6 +7,7 @@ * Author: Rabin Vincent for ST-Ericsson */ +#include #include #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -1012,6 +1014,9 @@ void __devinit stmpe_of_probe(struct stmpe_platform_data *pdata, { struct device_node *child; + pdata->id = -1; + pdata->irq_trigger = IRQF_TRIGGER_NONE; + of_property_read_u32(np, "st,autosleep-timeout", &pdata->autosleep_timeout); @@ -1020,15 +1025,16 @@ void __devinit stmpe_of_probe(struct stmpe_platform_data *pdata, for_each_child_of_node(np, child) { if (!strcmp(child->name, "stmpe_gpio")) { pdata->blocks |= STMPE_BLOCK_GPIO; - } - if (!strcmp(child->name, "stmpe_keypad")) { + } else if (!strcmp(child->name, "stmpe_keypad")) { pdata->blocks |= STMPE_BLOCK_KEYPAD; - } - if (!strcmp(child->name, "stmpe_touchscreen")) { + } else if (!strcmp(child->name, "stmpe_touchscreen")) { pdata->blocks |= STMPE_BLOCK_TOUCHSCREEN; - } - if (!strcmp(child->name, "stmpe_adc")) { + } else if (!strcmp(child->name, "stmpe_adc")) { pdata->blocks |= STMPE_BLOCK_ADC; + } else if (!strcmp(child->name, "stmpe_pwm")) { + pdata->blocks |= STMPE_BLOCK_PWM; + } else if (!strcmp(child->name, "stmpe_rotator")) { + pdata->blocks |= STMPE_BLOCK_ROTATOR; } } } @@ -1099,6 +1105,9 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum) return -ENODEV; } stmpe->variant = stmpe_noirq_variant_info[stmpe->partnum]; + } else if (pdata->irq_trigger == IRQF_TRIGGER_NONE) { + pdata->irq_trigger = + irqd_get_trigger_type(irq_get_irq_data(stmpe->irq)); } ret = stmpe_chip_init(stmpe); -- cgit v1.2.3 From a2ed00da5047806233e288b991d9be997a5eb214 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 7 Dec 2012 12:30:38 -0800 Subject: drivers/video: add support for the Solomon SSD1307 OLED Controller Add support for the Solomon SSD1307 OLED controller found on the Crystalfontz CFA10036 board. This controller can drive a display with a resolution up to 128x39 and can operate over I2C or SPI. The current driver has only been tested on the CFA-10036, that is using this controller over I2C to driver a 96x16 OLED screen. [akpm@linux-foundation.org: checkpatch fixes] Signed-off-by: Maxime Ripard Cc: Brian Lilly Cc: Greg KH Cc: Florian Tobias Schandinat Cc: Thomas Petazzoni Cc: Tomi Valkeinen Signed-off-by: Andrew Morton Signed-off-by: Tomi Valkeinen --- .../devicetree/bindings/video/ssd1307fb.txt | 24 ++ drivers/video/Kconfig | 15 + drivers/video/Makefile | 1 + drivers/video/ssd1307fb.c | 396 +++++++++++++++++++++ 4 files changed, 436 insertions(+) create mode 100644 Documentation/devicetree/bindings/video/ssd1307fb.txt create mode 100644 drivers/video/ssd1307fb.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/video/ssd1307fb.txt b/Documentation/devicetree/bindings/video/ssd1307fb.txt new file mode 100644 index 000000000000..3d0060cff062 --- /dev/null +++ b/Documentation/devicetree/bindings/video/ssd1307fb.txt @@ -0,0 +1,24 @@ +* Solomon SSD1307 Framebuffer Driver + +Required properties: + - compatible: Should be "solomon,ssd1307fb-". The only supported bus for + now is i2c. + - reg: Should contain address of the controller on the I2C bus. Most likely + 0x3c or 0x3d + - pwm: Should contain the pwm to use according to the OF device tree PWM + specification [0] + - reset-gpios: Should contain the GPIO used to reset the OLED display + +Optional properties: + - reset-active-low: Is the reset gpio is active on physical low? + +[0]: Documentation/devicetree/bindings/pwm/pwm.txt + +Examples: +ssd1307: oled@3c { + compatible = "solomon,ssd1307fb-i2c"; + reg = <0x3c>; + pwms = <&pwm 4 3000>; + reset-gpios = <&gpio2 7>; + reset-active-low; +}; diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 9018a90b4588..9c31277b3a81 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2442,4 +2442,19 @@ config FB_SH_MOBILE_MERAM Up to 4 memory channels can be configured, allowing 4 RGB or 2 YCbCr framebuffers to be configured. +config FB_SSD1307 + tristate "Solomon SSD1307 framebuffer support" + depends on FB && I2C + depends on OF + depends on GENERIC_GPIO + select FB_SYS_FOPS + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select FB_DEFERRED_IO + select PWM + help + This driver implements support for the Solomon SSD1307 + OLED controller over I2C. + endmenu diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 23e948ebfab8..768a137a1bac 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -161,6 +161,7 @@ obj-$(CONFIG_FB_BFIN_7393) += bfin_adv7393fb.o obj-$(CONFIG_FB_MX3) += mx3fb.o obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o obj-$(CONFIG_FB_MXS) += mxsfb.o +obj-$(CONFIG_FB_SSD1307) += ssd1307fb.o # the test framebuffer is last obj-$(CONFIG_FB_VIRTUAL) += vfb.o diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c new file mode 100644 index 000000000000..6101f5c2f62f --- /dev/null +++ b/drivers/video/ssd1307fb.c @@ -0,0 +1,396 @@ +/* + * Driver for the Solomon SSD1307 OLED controler + * + * Copyright 2012 Free Electrons + * + * Licensed under the GPLv2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SSD1307FB_WIDTH 96 +#define SSD1307FB_HEIGHT 16 + +#define SSD1307FB_DATA 0x40 +#define SSD1307FB_COMMAND 0x80 + +#define SSD1307FB_CONTRAST 0x81 +#define SSD1307FB_SEG_REMAP_ON 0xa1 +#define SSD1307FB_DISPLAY_OFF 0xae +#define SSD1307FB_DISPLAY_ON 0xaf +#define SSD1307FB_START_PAGE_ADDRESS 0xb0 + +struct ssd1307fb_par { + struct i2c_client *client; + struct fb_info *info; + struct pwm_device *pwm; + u32 pwm_period; + int reset; +}; + +static struct fb_fix_screeninfo ssd1307fb_fix __devinitdata = { + .id = "Solomon SSD1307", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_MONO10, + .xpanstep = 0, + .ypanstep = 0, + .ywrapstep = 0, + .line_length = SSD1307FB_WIDTH / 8, + .accel = FB_ACCEL_NONE, +}; + +static struct fb_var_screeninfo ssd1307fb_var __devinitdata = { + .xres = SSD1307FB_WIDTH, + .yres = SSD1307FB_HEIGHT, + .xres_virtual = SSD1307FB_WIDTH, + .yres_virtual = SSD1307FB_HEIGHT, + .bits_per_pixel = 1, +}; + +static int ssd1307fb_write_array(struct i2c_client *client, u8 type, u8 *cmd, u32 len) +{ + u8 *buf; + int ret = 0; + + buf = kzalloc(len + 1, GFP_KERNEL); + if (!buf) { + dev_err(&client->dev, "Couldn't allocate sending buffer.\n"); + return -ENOMEM; + } + + buf[0] = type; + memcpy(buf + 1, cmd, len); + + ret = i2c_master_send(client, buf, len + 1); + if (ret != len + 1) { + dev_err(&client->dev, "Couldn't send I2C command.\n"); + goto error; + } + +error: + kfree(buf); + return ret; +} + +static inline int ssd1307fb_write_cmd_array(struct i2c_client *client, u8 *cmd, u32 len) +{ + return ssd1307fb_write_array(client, SSD1307FB_COMMAND, cmd, len); +} + +static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd) +{ + return ssd1307fb_write_cmd_array(client, &cmd, 1); +} + +static inline int ssd1307fb_write_data_array(struct i2c_client *client, u8 *cmd, u32 len) +{ + return ssd1307fb_write_array(client, SSD1307FB_DATA, cmd, len); +} + +static inline int ssd1307fb_write_data(struct i2c_client *client, u8 data) +{ + return ssd1307fb_write_data_array(client, &data, 1); +} + +static void ssd1307fb_update_display(struct ssd1307fb_par *par) +{ + u8 *vmem = par->info->screen_base; + int i, j, k; + + /* + * The screen is divided in pages, each having a height of 8 + * pixels, and the width of the screen. When sending a byte of + * data to the controller, it gives the 8 bits for the current + * column. I.e, the first byte are the 8 bits of the first + * column, then the 8 bits for the second column, etc. + * + * + * Representation of the screen, assuming it is 5 bits + * wide. Each letter-number combination is a bit that controls + * one pixel. + * + * A0 A1 A2 A3 A4 + * B0 B1 B2 B3 B4 + * C0 C1 C2 C3 C4 + * D0 D1 D2 D3 D4 + * E0 E1 E2 E3 E4 + * F0 F1 F2 F3 F4 + * G0 G1 G2 G3 G4 + * H0 H1 H2 H3 H4 + * + * If you want to update this screen, you need to send 5 bytes: + * (1) A0 B0 C0 D0 E0 F0 G0 H0 + * (2) A1 B1 C1 D1 E1 F1 G1 H1 + * (3) A2 B2 C2 D2 E2 F2 G2 H2 + * (4) A3 B3 C3 D3 E3 F3 G3 H3 + * (5) A4 B4 C4 D4 E4 F4 G4 H4 + */ + + for (i = 0; i < (SSD1307FB_HEIGHT / 8); i++) { + ssd1307fb_write_cmd(par->client, SSD1307FB_START_PAGE_ADDRESS + (i + 1)); + ssd1307fb_write_cmd(par->client, 0x00); + ssd1307fb_write_cmd(par->client, 0x10); + + for (j = 0; j < SSD1307FB_WIDTH; j++) { + u8 buf = 0; + for (k = 0; k < 8; k++) { + u32 page_length = SSD1307FB_WIDTH * i; + u32 index = page_length + (SSD1307FB_WIDTH * k + j) / 8; + u8 byte = *(vmem + index); + u8 bit = byte & (1 << (7 - (j % 8))); + bit = bit >> (7 - (j % 8)); + buf |= bit << k; + } + ssd1307fb_write_data(par->client, buf); + } + } +} + + +static ssize_t ssd1307fb_write(struct fb_info *info, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct ssd1307fb_par *par = info->par; + unsigned long total_size; + unsigned long p = *ppos; + u8 __iomem *dst; + + total_size = info->fix.smem_len; + + if (p > total_size) + return -EINVAL; + + if (count + p > total_size) + count = total_size - p; + + if (!count) + return -EINVAL; + + dst = (void __force *) (info->screen_base + p); + + if (copy_from_user(dst, buf, count)) + return -EFAULT; + + ssd1307fb_update_display(par); + + *ppos += count; + + return count; +} + +static void ssd1307fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) +{ + struct ssd1307fb_par *par = info->par; + sys_fillrect(info, rect); + ssd1307fb_update_display(par); +} + +static void ssd1307fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) +{ + struct ssd1307fb_par *par = info->par; + sys_copyarea(info, area); + ssd1307fb_update_display(par); +} + +static void ssd1307fb_imageblit(struct fb_info *info, const struct fb_image *image) +{ + struct ssd1307fb_par *par = info->par; + sys_imageblit(info, image); + ssd1307fb_update_display(par); +} + +static struct fb_ops ssd1307fb_ops = { + .owner = THIS_MODULE, + .fb_read = fb_sys_read, + .fb_write = ssd1307fb_write, + .fb_fillrect = ssd1307fb_fillrect, + .fb_copyarea = ssd1307fb_copyarea, + .fb_imageblit = ssd1307fb_imageblit, +}; + +static void ssd1307fb_deferred_io(struct fb_info *info, + struct list_head *pagelist) +{ + ssd1307fb_update_display(info->par); +} + +static struct fb_deferred_io ssd1307fb_defio = { + .delay = HZ, + .deferred_io = ssd1307fb_deferred_io, +}; + +static int __devinit ssd1307fb_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct fb_info *info; + u32 vmem_size = SSD1307FB_WIDTH * SSD1307FB_HEIGHT / 8; + struct ssd1307fb_par *par; + u8 *vmem; + int ret; + + if (!client->dev.of_node) { + dev_err(&client->dev, "No device tree data found!\n"); + return -EINVAL; + } + + info = framebuffer_alloc(sizeof(struct ssd1307fb_par), &client->dev); + if (!info) { + dev_err(&client->dev, "Couldn't allocate framebuffer.\n"); + return -ENOMEM; + } + + vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL); + if (!vmem) { + dev_err(&client->dev, "Couldn't allocate graphical memory.\n"); + ret = -ENOMEM; + goto fb_alloc_error; + } + + info->fbops = &ssd1307fb_ops; + info->fix = ssd1307fb_fix; + info->fbdefio = &ssd1307fb_defio; + + info->var = ssd1307fb_var; + info->var.red.length = 1; + info->var.red.offset = 0; + info->var.green.length = 1; + info->var.green.offset = 0; + info->var.blue.length = 1; + info->var.blue.offset = 0; + + info->screen_base = (u8 __force __iomem *)vmem; + info->fix.smem_start = (unsigned long)vmem; + info->fix.smem_len = vmem_size; + + fb_deferred_io_init(info); + + par = info->par; + par->info = info; + par->client = client; + + par->reset = of_get_named_gpio(client->dev.of_node, + "reset-gpios", 0); + if (!gpio_is_valid(par->reset)) { + ret = -EINVAL; + goto reset_oled_error; + } + + ret = devm_gpio_request_one(&client->dev, par->reset, + GPIOF_OUT_INIT_HIGH, + "oled-reset"); + if (ret) { + dev_err(&client->dev, + "failed to request gpio %d: %d\n", + par->reset, ret); + goto reset_oled_error; + } + + par->pwm = pwm_get(&client->dev, NULL); + if (IS_ERR(par->pwm)) { + dev_err(&client->dev, "Could not get PWM from device tree!\n"); + ret = PTR_ERR(par->pwm); + goto pwm_error; + } + + par->pwm_period = pwm_get_period(par->pwm); + + dev_dbg(&client->dev, "Using PWM%d with a %dns period.\n", par->pwm->pwm, par->pwm_period); + + ret = register_framebuffer(info); + if (ret) { + dev_err(&client->dev, "Couldn't register the framebuffer\n"); + goto fbreg_error; + } + + i2c_set_clientdata(client, info); + + /* Reset the screen */ + gpio_set_value(par->reset, 0); + udelay(4); + gpio_set_value(par->reset, 1); + udelay(4); + + /* Enable the PWM */ + pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period); + pwm_enable(par->pwm); + + /* Map column 127 of the OLED to segment 0 */ + ret = ssd1307fb_write_cmd(client, SSD1307FB_SEG_REMAP_ON); + if (ret < 0) { + dev_err(&client->dev, "Couldn't remap the screen.\n"); + goto remap_error; + } + + /* Turn on the display */ + ret = ssd1307fb_write_cmd(client, SSD1307FB_DISPLAY_ON); + if (ret < 0) { + dev_err(&client->dev, "Couldn't turn the display on.\n"); + goto remap_error; + } + + dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size); + + return 0; + +remap_error: + unregister_framebuffer(info); + pwm_disable(par->pwm); +fbreg_error: + pwm_put(par->pwm); +pwm_error: +reset_oled_error: + fb_deferred_io_cleanup(info); +fb_alloc_error: + framebuffer_release(info); + return ret; +} + +static int __devexit ssd1307fb_remove(struct i2c_client *client) +{ + struct fb_info *info = i2c_get_clientdata(client); + struct ssd1307fb_par *par = info->par; + + unregister_framebuffer(info); + pwm_disable(par->pwm); + pwm_put(par->pwm); + fb_deferred_io_cleanup(info); + framebuffer_release(info); + + return 0; +} + +static const struct i2c_device_id ssd1307fb_i2c_id[] = { + { "ssd1307fb", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id); + +static const struct of_device_id ssd1307fb_of_match[] = { + { .compatible = "solomon,ssd1307fb-i2c" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ssd1307fb_of_match); + +static struct i2c_driver ssd1307fb_driver = { + .probe = ssd1307fb_probe, + .remove = __devexit_p(ssd1307fb_remove), + .id_table = ssd1307fb_i2c_id, + .driver = { + .name = "ssd1307fb", + .of_match_table = of_match_ptr(ssd1307fb_of_match), + .owner = THIS_MODULE, + }, +}; + +module_i2c_driver(ssd1307fb_driver); + +MODULE_DESCRIPTION("FB driver for the Solomon SSD1307 OLED controler"); +MODULE_AUTHOR("Maxime Ripard "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 0c955b392ac83bcf8a8a3a087907c1c559496e07 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 10 Dec 2012 11:42:02 +0100 Subject: rtc: rtc-mv: Add the device tree binding documentation The support was already written, but the binding documentation was lacking. Signed-off-by: Gregory CLEMENT Acked-by: Andrew Lunn Signed-off-by: Grant Likely --- Documentation/devicetree/bindings/rtc/orion-rtc.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/orion-rtc.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/rtc/orion-rtc.txt b/Documentation/devicetree/bindings/rtc/orion-rtc.txt new file mode 100644 index 000000000000..3bf63ffa5160 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/orion-rtc.txt @@ -0,0 +1,18 @@ +* Mvebu Real Time Clock + +RTC controller for the Kirkwood, the Dove, the Armada 370 and the +Armada XP SoCs + +Required properties: +- compatible : Should be "marvell,orion-rtc" +- reg: physical base address of the controller and length of memory mapped + region. +- interrupts: IRQ line for the RTC. + +Example: + +rtc@10300 { + compatible = "marvell,orion-rtc"; + reg = <0xd0010300 0x20>; + interrupts = <50>; +}; -- cgit v1.2.3 From fed16bba8726dfb86f45429c6101db4c6927f4dd Mon Sep 17 00:00:00 2001 From: "Kumar, Anil" Date: Fri, 7 Dec 2012 14:39:34 +0530 Subject: mtd: nand: davinci: fix the binding documentation Since the aemif driver conversion to DT along with its movement to drivers/ folder is not yet done, fix NAND binding documentation to have NAND specific DT details only. Signed-off-by: Kumar, Anil Signed-off-by: Grant Likely --- .../devicetree/bindings/arm/davinci/nand.txt | 37 +++++++--------------- 1 file changed, 12 insertions(+), 25 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/davinci/nand.txt b/Documentation/devicetree/bindings/arm/davinci/nand.txt index e37241f1fdd8..49fc7ada929a 100644 --- a/Documentation/devicetree/bindings/arm/davinci/nand.txt +++ b/Documentation/devicetree/bindings/arm/davinci/nand.txt @@ -23,29 +23,16 @@ Recommended properties : - ti,davinci-nand-buswidth: buswidth 8 or 16 - ti,davinci-nand-use-bbt: use flash based bad block table support. -Example (enbw_cmc board): -aemif@60000000 { - compatible = "ti,davinci-aemif"; - #address-cells = <2>; - #size-cells = <1>; - reg = <0x68000000 0x80000>; - ranges = <2 0 0x60000000 0x02000000 - 3 0 0x62000000 0x02000000 - 4 0 0x64000000 0x02000000 - 5 0 0x66000000 0x02000000 - 6 0 0x68000000 0x02000000>; - nand@3,0 { - compatible = "ti,davinci-nand"; - reg = <3 0x0 0x807ff - 6 0x0 0x8000>; - #address-cells = <1>; - #size-cells = <1>; - ti,davinci-chipselect = <1>; - ti,davinci-mask-ale = <0>; - ti,davinci-mask-cle = <0>; - ti,davinci-mask-chipsel = <0>; - ti,davinci-ecc-mode = "hw"; - ti,davinci-ecc-bits = <4>; - ti,davinci-nand-use-bbt; - }; +Example(da850 EVM ): +nand_cs3@62000000 { + compatible = "ti,davinci-nand"; + reg = <0x62000000 0x807ff + 0x68000000 0x8000>; + ti,davinci-chipselect = <1>; + ti,davinci-mask-ale = <0>; + ti,davinci-mask-cle = <0>; + ti,davinci-mask-chipsel = <0>; + ti,davinci-ecc-mode = "hw"; + ti,davinci-ecc-bits = <4>; + ti,davinci-nand-use-bbt; }; -- cgit v1.2.3 From 850a5b670af6293fcb1852af57567d19150ff638 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 23 Nov 2012 13:44:39 +0100 Subject: spi/atmel: add DT support Use the newly introduce cs-gpios dt support on atmel. We do not use the hardware cs as it's wired and has bugs and limitations. As the controller believes that only active-low devices/systems exists. Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Nicolas Ferre Signed-off-by: Grant Likely --- .../devicetree/bindings/spi/spi_atmel.txt | 26 ++++++++++++++++++++++ drivers/spi/spi-atmel.c | 17 +++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/spi/spi_atmel.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/spi/spi_atmel.txt b/Documentation/devicetree/bindings/spi/spi_atmel.txt new file mode 100644 index 000000000000..07e04cdc0c9e --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi_atmel.txt @@ -0,0 +1,26 @@ +Atmel SPI device + +Required properties: +- compatible : should be "atmel,at91rm9200-spi". +- reg: Address and length of the register set for the device +- interrupts: Should contain spi interrupt +- cs-gpios: chipselects + +Example: + +spi1: spi@fffcc000 { + compatible = "atmel,at91rm9200-spi"; + reg = <0xfffcc000 0x4000>; + interrupts = <13 4 5>; + #address-cells = <1>; + #size-cells = <0>; + cs-gpios = <&pioB 3 0>; + status = "okay"; + + mmc-slot@0 { + compatible = "mmc-spi-slot"; + reg = <0>; + gpios = <&pioC 4 0>; /* CD */ + spi-max-frequency = <25000000>; + }; +}; diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 75c0c4f5fdf2..ab34497bcfee 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -768,6 +769,10 @@ static int atmel_spi_setup(struct spi_device *spi) /* chipselect must have been muxed as GPIO (e.g. in board setup) */ npcs_pin = (unsigned int)spi->controller_data; + + if (gpio_is_valid(spi->cs_gpio)) + npcs_pin = spi->cs_gpio; + asd = spi->controller_state; if (!asd) { asd = kzalloc(sizeof(struct atmel_spi_device), GFP_KERNEL); @@ -937,8 +942,9 @@ static int atmel_spi_probe(struct platform_device *pdev) /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->dev.of_node = pdev->dev.of_node; master->bus_num = pdev->id; - master->num_chipselect = 4; + master->num_chipselect = master->dev.of_node ? 0 : 4; master->setup = atmel_spi_setup; master->transfer = atmel_spi_transfer; master->cleanup = atmel_spi_cleanup; @@ -1064,11 +1070,20 @@ static int atmel_spi_resume(struct platform_device *pdev) #define atmel_spi_resume NULL #endif +#if defined(CONFIG_OF) +static const struct of_device_id atmel_spi_dt_ids[] = { + { .compatible = "atmel,at91rm9200-spi" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, atmel_spi_dt_ids); +#endif static struct platform_driver atmel_spi_driver = { .driver = { .name = "atmel_spi", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(atmel_spi_dt_ids), }, .suspend = atmel_spi_suspend, .resume = atmel_spi_resume, -- cgit v1.2.3 From 9e0344dcc225fe1a0e8b8af9ff7df44ec4613580 Mon Sep 17 00:00:00 2001 From: Afzal Mohammed Date: Mon, 17 Dec 2012 16:02:15 -0800 Subject: rtc: omap: dt support Enhance rtc-omap driver with DT capability Signed-off-by: Afzal Mohammed Acked-by: Sekhar Nori Cc: Grant Likely Cc: Sekhar Nori Cc: Kevin Hilman Cc: Russell King Cc: Alessandro Zummo Cc: Daniel Mack Cc: Vaibhav Hiremath Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/devicetree/bindings/rtc/rtc-omap.txt | 17 +++++++++++++++++ drivers/rtc/rtc-omap.c | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/rtc-omap.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/rtc/rtc-omap.txt b/Documentation/devicetree/bindings/rtc/rtc-omap.txt new file mode 100644 index 000000000000..b47aa415c820 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/rtc-omap.txt @@ -0,0 +1,17 @@ +TI Real Time Clock + +Required properties: +- compatible: "ti,da830-rtc" +- reg: Address range of rtc register set +- interrupts: rtc timer, alarm interrupts in order +- interrupt-parent: phandle for the interrupt controller + +Example: + +rtc@1c23000 { + compatible = "ti,da830-rtc"; + reg = <0x23000 0x1000>; + interrupts = <19 + 19>; + interrupt-parent = <&intc>; +}; diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index d948426283db..dff9ff476b0d 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include @@ -298,6 +300,8 @@ static struct rtc_class_ops omap_rtc_ops = { static int omap_rtc_alarm; static int omap_rtc_timer; +#define OMAP_RTC_DATA_DA830_IDX 1 + static struct platform_device_id omap_rtc_devtype[] = { { .name = DRIVER_NAME, @@ -309,12 +313,25 @@ static struct platform_device_id omap_rtc_devtype[] = { }; MODULE_DEVICE_TABLE(platform, omap_rtc_devtype); +static const struct of_device_id omap_rtc_of_match[] = { + { .compatible = "ti,da830-rtc", + .data = &omap_rtc_devtype[OMAP_RTC_DATA_DA830_IDX], + }, + {}, +}; +MODULE_DEVICE_TABLE(of, omap_rtc_of_match); + static int __init omap_rtc_probe(struct platform_device *pdev) { struct resource *res, *mem; struct rtc_device *rtc; u8 reg, new_ctrl; const struct platform_device_id *id_entry; + const struct of_device_id *of_id; + + of_id = of_match_device(omap_rtc_of_match, &pdev->dev); + if (of_id) + pdev->id_entry = of_id->data; omap_rtc_timer = platform_get_irq(pdev, 0); if (omap_rtc_timer <= 0) { @@ -510,6 +527,7 @@ static struct platform_driver omap_rtc_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(omap_rtc_of_match), }, .id_table = omap_rtc_devtype, }; -- cgit v1.2.3 From 968d21c2cf01bde4b0e3ff0b0f821b51b9da8ffd Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Mon, 17 Dec 2012 16:02:23 -0800 Subject: drivers/rtc/rtc-imxdi.c: add devicetree support Add device tree support to the rtc-imxdi driver. Signed-off-by: Roland Stigge Cc: Alessandro Zummo Cc: Grant Likely Cc: Sascha Hauer Cc: Russell King Cc: Shawn Guo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/devicetree/bindings/rtc/imxdi-rtc.txt | 17 +++++++++++++++++ drivers/rtc/rtc-imxdi.c | 11 +++++++++++ 2 files changed, 28 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/imxdi-rtc.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/rtc/imxdi-rtc.txt b/Documentation/devicetree/bindings/rtc/imxdi-rtc.txt new file mode 100644 index 000000000000..c9d80d7da141 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/imxdi-rtc.txt @@ -0,0 +1,17 @@ +* i.MX25 Real Time Clock controller + +This binding supports the following chips: i.MX25, i.MX53 + +Required properties: +- compatible: should be: "fsl,imx25-rtc" +- reg: physical base address of the controller and length of memory mapped + region. +- interrupts: rtc alarm interrupt + +Example: + +rtc@80056000 { + compatible = "fsl,imx53-rtc", "fsl,imx25-rtc"; + reg = <0x80056000 2000>; + interrupts = <29>; +}; diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c index 4eed51044c5d..18a4f0dd78a3 100644 --- a/drivers/rtc/rtc-imxdi.c +++ b/drivers/rtc/rtc-imxdi.c @@ -37,6 +37,7 @@ #include #include #include +#include /* DryIce Register Definitions */ @@ -495,10 +496,20 @@ static int __devexit dryice_rtc_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id dryice_dt_ids[] = { + { .compatible = "fsl,imx25-rtc" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, dryice_dt_ids); +#endif + static struct platform_driver dryice_rtc_driver = { .driver = { .name = "imxdi_rtc", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(dryice_dt_ids), }, .remove = __devexit_p(dryice_rtc_remove), }; -- cgit v1.2.3 From 6a79131318dd6f71ec46e0af1daa581e695fd5ab Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Fri, 14 Dec 2012 11:05:12 -0800 Subject: spi: fix tegra SPI binding examples Fix name of slink binding and address of sflash example to make it self consistent. Signed-off-by: Allen Martin Signed-off-by: Grant Likely --- Documentation/devicetree/bindings/spi/nvidia,tegra20-sflash.txt | 2 +- Documentation/devicetree/bindings/spi/nvidia,tegra20-slink.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/spi/nvidia,tegra20-sflash.txt b/Documentation/devicetree/bindings/spi/nvidia,tegra20-sflash.txt index 8cf24f6f0a99..7b53da5cb75b 100644 --- a/Documentation/devicetree/bindings/spi/nvidia,tegra20-sflash.txt +++ b/Documentation/devicetree/bindings/spi/nvidia,tegra20-sflash.txt @@ -13,7 +13,7 @@ Recommended properties: Example: -spi@7000d600 { +spi@7000c380 { compatible = "nvidia,tegra20-sflash"; reg = <0x7000c380 0x80>; interrupts = <0 39 0x04>; diff --git a/Documentation/devicetree/bindings/spi/nvidia,tegra20-slink.txt b/Documentation/devicetree/bindings/spi/nvidia,tegra20-slink.txt index f5b1ad1a1ec3..eefe15e3d95e 100644 --- a/Documentation/devicetree/bindings/spi/nvidia,tegra20-slink.txt +++ b/Documentation/devicetree/bindings/spi/nvidia,tegra20-slink.txt @@ -13,7 +13,7 @@ Recommended properties: Example: -slink@7000d600 { +spi@7000d600 { compatible = "nvidia,tegra20-slink"; reg = <0x7000d600 0x200>; interrupts = <0 82 0x04>; -- cgit v1.2.3 From 902e2e7d482c55395652ff78cb3457fc390b101d Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Mon, 26 Nov 2012 16:41:35 -0500 Subject: watchdog: davinci_wdt: add OF support This adds OF support for davinci_wdt driver. Signed-off-by: Murali Karicheri Acked-by: Grant Likely Signed-off-by: Wim Van Sebroeck --- Documentation/devicetree/bindings/watchdog/davinci-wdt.txt | 12 ++++++++++++ drivers/watchdog/davinci_wdt.c | 7 +++++++ 2 files changed, 19 insertions(+) create mode 100644 Documentation/devicetree/bindings/watchdog/davinci-wdt.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/watchdog/davinci-wdt.txt b/Documentation/devicetree/bindings/watchdog/davinci-wdt.txt new file mode 100644 index 000000000000..75558ccd9a05 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/davinci-wdt.txt @@ -0,0 +1,12 @@ +DaVinci Watchdog Timer (WDT) Controller + +Required properties: +- compatible : Should be "ti,davinci-wdt" +- reg : Should contain WDT registers location and length + +Examples: + +wdt: wdt@2320000 { + compatible = "ti,davinci-wdt"; + reg = <0x02320000 0x80>; +}; diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c index 6000aea7e2e1..e8e87246ea6d 100644 --- a/drivers/watchdog/davinci_wdt.c +++ b/drivers/watchdog/davinci_wdt.c @@ -262,10 +262,17 @@ static int davinci_wdt_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id davinci_wdt_of_match[] = { + { .compatible = "ti,davinci-wdt", }, + {}, +}; +MODULE_DEVICE_TABLE(of, davinci_wdt_of_match); + static struct platform_driver platform_wdt_driver = { .driver = { .name = "watchdog", .owner = THIS_MODULE, + .of_match_table = davinci_wdt_of_match, }, .probe = davinci_wdt_probe, .remove = davinci_wdt_remove, -- cgit v1.2.3 From 5ce568329e4fcf9e9050bff878f8157ca43bc882 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 12 Dec 2012 23:28:04 -0200 Subject: ASoC: wm8962: Add device tree support Add device tree support. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/wm8962.txt | 16 ++++++++++++++++ sound/soc/codecs/wm8962.c | 7 +++++++ 2 files changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/wm8962.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/wm8962.txt b/Documentation/devicetree/bindings/sound/wm8962.txt new file mode 100644 index 000000000000..dceb3b1c2bb7 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wm8962.txt @@ -0,0 +1,16 @@ +WM8962 audio CODEC + +This device supports I2C only. + +Required properties: + + - compatible : "wlf,wm8962" + + - reg : the I2C address of the device. + +Example: + +codec: wm8962@1a { + compatible = "wlf,wm8962"; + reg = <0x1a>; +}; diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index bd4b0db4cdaa..705d0a0e9137 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3758,10 +3758,17 @@ static const struct i2c_device_id wm8962_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, wm8962_i2c_id); +static const struct of_device_id wm8962_of_match[] = { + { .compatible = "wlf,wm8962", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8962_of_match); + static struct i2c_driver wm8962_i2c_driver = { .driver = { .name = "wm8962", .owner = THIS_MODULE, + .of_match_table = wm8962_of_match, .pm = &wm8962_pm, }, .probe = wm8962_i2c_probe, -- cgit v1.2.3 From fd23fb9f6bfd43a6e62b2646d18d5ca3edc3ebe3 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 10 Dec 2012 10:30:04 +0100 Subject: ALSA: ASoC: cs4271: add optional soft reset workaround The CS4271 requires its LRCLK and MCLK to be stable before its RESET line is de-asserted. That also means that clocks cannot be changed without putting the chip back into hardware reset, which also requires a complete re-initialization of all registers. One (undocumented) workaround is to assert and de-assert the PDN bit in the MODE2 register. This patch adds a new flag to both the DT bindings as well as to the platform data to enable that workaround. Signed-off-by: Daniel Mack Acked-by: Alexander Sverdlin Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/cs4271.txt | 12 ++++++++ include/sound/cs4271.h | 15 ++++++++++ sound/soc/codecs/cs4271.c | 34 ++++++++++++++++++++++ 3 files changed, 61 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/cs4271.txt b/Documentation/devicetree/bindings/sound/cs4271.txt index a850fb9c88ea..e2cd1d7539e5 100644 --- a/Documentation/devicetree/bindings/sound/cs4271.txt +++ b/Documentation/devicetree/bindings/sound/cs4271.txt @@ -20,6 +20,18 @@ Optional properties: !RESET pin - cirrus,amuteb-eq-bmutec: When given, the Codec's AMUTEB=BMUTEC flag is enabled. + - cirrus,enable-soft-reset: + The CS4271 requires its LRCLK and MCLK to be stable before its RESET + line is de-asserted. That also means that clocks cannot be changed + without putting the chip back into hardware reset, which also requires + a complete re-initialization of all registers. + + One (undocumented) workaround is to assert and de-assert the PDN bit + in the MODE2 register. This workaround can be enabled with this DT + property. + + Note that this is not needed in case the clocks are stable + throughout the entire runtime of the codec. Examples: diff --git a/include/sound/cs4271.h b/include/sound/cs4271.h index dd8c48d14ed9..70f45355acaa 100644 --- a/include/sound/cs4271.h +++ b/include/sound/cs4271.h @@ -20,6 +20,21 @@ struct cs4271_platform_data { int gpio_nreset; /* GPIO driving Reset pin, if any */ bool amutec_eq_bmutec; /* flag to enable AMUTEC=BMUTEC */ + + /* + * The CS4271 requires its LRCLK and MCLK to be stable before its RESET + * line is de-asserted. That also means that clocks cannot be changed + * without putting the chip back into hardware reset, which also requires + * a complete re-initialization of all registers. + * + * One (undocumented) workaround is to assert and de-assert the PDN bit + * in the MODE2 register. This workaround can be enabled with the + * following flag. + * + * Note that this is not needed in case the clocks are stable + * throughout the entire runtime of the codec. + */ + bool enable_soft_reset; }; #endif /* __CS4271_H */ diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index ac8742a1f25a..2415a4118dbd 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -167,6 +167,8 @@ struct cs4271_private { int gpio_nreset; /* GPIO that disable serial bus, if any */ int gpio_disable; + /* enable soft reset workaround */ + bool enable_soft_reset; }; /* @@ -325,6 +327,33 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream, int i, ret; unsigned int ratio, val; + if (cs4271->enable_soft_reset) { + /* + * Put the codec in soft reset and back again in case it's not + * currently streaming data. This way of bringing the codec in + * sync to the current clocks is not explicitly documented in + * the data sheet, but it seems to work fine, and in contrast + * to a read hardware reset, we don't have to sync back all + * registers every time. + */ + + if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + !dai->capture_active) || + (substream->stream == SNDRV_PCM_STREAM_CAPTURE && + !dai->playback_active)) { + ret = snd_soc_update_bits(codec, CS4271_MODE2, + CS4271_MODE2_PDN, + CS4271_MODE2_PDN); + if (ret < 0) + return ret; + + ret = snd_soc_update_bits(codec, CS4271_MODE2, + CS4271_MODE2_PDN, 0); + if (ret < 0) + return ret; + } + } + cs4271->rate = params_rate(params); /* Configure DAC */ @@ -484,6 +513,10 @@ static int cs4271_probe(struct snd_soc_codec *codec) if (of_get_property(codec->dev->of_node, "cirrus,amutec-eq-bmutec", NULL)) amutec_eq_bmutec = true; + + if (of_get_property(codec->dev->of_node, + "cirrus,enable-soft-reset", NULL)) + cs4271->enable_soft_reset = true; } #endif @@ -492,6 +525,7 @@ static int cs4271_probe(struct snd_soc_codec *codec) gpio_nreset = cs4271plat->gpio_nreset; amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec; + cs4271->enable_soft_reset = cs4271plat->enable_soft_reset; } if (gpio_nreset >= 0) -- cgit v1.2.3 From 408f181e0d4210ef7c77e825289d31fac530291c Mon Sep 17 00:00:00 2001 From: Barry Song Date: Fri, 14 Dec 2012 15:24:01 +0800 Subject: pinctrl: sirf: add missing DT-binding document While sending email to Linus for reviewing: "pinctrl: sirf: add DT-binding pinmux mapping support" https://patchwork.kernel.org/patch/1364361/ i have included the devicetree/bindings/pinctrl/pinctrl-sirf.txt But while sending pull request with commit 056876f6c73406c, i missed the document. this patch takes the document back. Signed-off-by: Barry Song Signed-off-by: Linus Walleij --- .../devicetree/bindings/pinctrl/pinctrl-sirf.txt | 44 ++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/pinctrl-sirf.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-sirf.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-sirf.txt new file mode 100644 index 000000000000..a2896df702ad --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-sirf.txt @@ -0,0 +1,44 @@ +CSR SiRFprimaII pinmux controller + +Required properties: +- compatible : "sirf,prima2-pinctrl" +- reg : Address range of the pinctrl registers +- interrupts : Interrupts used by every GPIO group +- gpio-controller : Indicates this device is a GPIO controller +- interrupt-controller : Marks the device node as an interrupt controller + +Please refer to pinctrl-bindings.txt in this directory for details of the common +pinctrl bindings used by client devices. + +SiRFprimaII's pinmux nodes act as a container for an abitrary number of subnodes. +Each of these subnodes represents some desired configuration for a group of pins. + +Required subnode-properties: +- sirf,pins : An array of strings. Each string contains the name of a group. +- sirf,function: A string containing the name of the function to mux to the + group. + + Valid values for group and function names can be found from looking at the + group and function arrays in driver files: + drivers/pinctrl/pinctrl-sirf.c + +For example, pinctrl might have subnodes like the following: + uart2_pins_a: uart2@0 { + uart { + sirf,pins = "uart2grp"; + sirf,function = "uart2"; + }; + }; + uart2_noflow_pins_a: uart2@1 { + uart { + sirf,pins = "uart2_nostreamctrlgrp"; + sirf,function = "uart2_nostreamctrl"; + }; + }; + +For a specific board, if it wants to use uart2 without hardware flow control, +it can add the following to its board-specific .dts file. +uart2: uart@0xb0070000 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_noflow_pins_a>; +} -- cgit v1.2.3 From fc2b04e7fbd119f4bf41b6821d2f8ce4bf9ae417 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Fri, 14 Dec 2012 17:15:59 +0800 Subject: pinctrl: sirf: enable GPIO pullup/down configuration from dts commit 7bec207427c2efb794 remove sirfsoc_gpio_set_pull function, this patches takes the feature back by adding sirf,pullups and sirf,pulldowns prop in dts, and the driver will set the GPIO pull according to the dts. Cc: Arnd Bergmann Signed-off-by: Barry Song Signed-off-by: Linus Walleij --- .../devicetree/bindings/pinctrl/pinctrl-sirf.txt | 3 ++ drivers/pinctrl/pinctrl-sirf.c | 48 ++++++++++++++++++++++ 2 files changed, 51 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-sirf.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-sirf.txt index a2896df702ad..c596a6ad3285 100644 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-sirf.txt +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-sirf.txt @@ -6,6 +6,9 @@ Required properties: - interrupts : Interrupts used by every GPIO group - gpio-controller : Indicates this device is a GPIO controller - interrupt-controller : Marks the device node as an interrupt controller +Optional properties: +- sirf,pullups : if n-th bit of m-th bank is set, set a pullup on GPIO-n of bank m +- sirf,pulldowns : if n-th bit of m-th bank is set, set a pulldown on GPIO-n of bank m Please refer to pinctrl-bindings.txt in this directory for details of the common pinctrl bindings used by client devices. diff --git a/drivers/pinctrl/pinctrl-sirf.c b/drivers/pinctrl/pinctrl-sirf.c index a4f0c5e487d5..30e1a38293a0 100644 --- a/drivers/pinctrl/pinctrl-sirf.c +++ b/drivers/pinctrl/pinctrl-sirf.c @@ -1663,6 +1663,44 @@ const struct irq_domain_ops sirfsoc_gpio_irq_simple_ops = { .xlate = irq_domain_xlate_twocell, }; +static void sirfsoc_gpio_set_pullup(const u32 *pullups) +{ + int i, n; + const unsigned long *p = (const unsigned long *)pullups; + + for (i = 0; i < SIRFSOC_GPIO_NO_OF_BANKS; i++) { + n = find_first_bit(p + i, BITS_PER_LONG); + while (n < BITS_PER_LONG) { + u32 offset = SIRFSOC_GPIO_CTRL(i, n); + u32 val = readl(sgpio_bank[i].chip.regs + offset); + val |= SIRFSOC_GPIO_CTL_PULL_MASK; + val |= SIRFSOC_GPIO_CTL_PULL_HIGH; + writel(val, sgpio_bank[i].chip.regs + offset); + + n = find_next_bit(p + i, BITS_PER_LONG, n + 1); + } + } +} + +static void sirfsoc_gpio_set_pulldown(const u32 *pulldowns) +{ + int i, n; + const unsigned long *p = (const unsigned long *)pulldowns; + + for (i = 0; i < SIRFSOC_GPIO_NO_OF_BANKS; i++) { + n = find_first_bit(p + i, BITS_PER_LONG); + while (n < BITS_PER_LONG) { + u32 offset = SIRFSOC_GPIO_CTRL(i, n); + u32 val = readl(sgpio_bank[i].chip.regs + offset); + val |= SIRFSOC_GPIO_CTL_PULL_MASK; + val &= ~SIRFSOC_GPIO_CTL_PULL_HIGH; + writel(val, sgpio_bank[i].chip.regs + offset); + + n = find_next_bit(p + i, BITS_PER_LONG, n + 1); + } + } +} + static int __devinit sirfsoc_gpio_probe(struct device_node *np) { int i, err = 0; @@ -1671,6 +1709,8 @@ static int __devinit sirfsoc_gpio_probe(struct device_node *np) struct platform_device *pdev; bool is_marco = false; + u32 pullups[SIRFSOC_GPIO_NO_OF_BANKS], pulldowns[SIRFSOC_GPIO_NO_OF_BANKS]; + pdev = of_find_device_by_node(np); if (!pdev) return -ENODEV; @@ -1726,6 +1766,14 @@ static int __devinit sirfsoc_gpio_probe(struct device_node *np) irq_set_handler_data(bank->parent_irq, bank); } + if (!of_property_read_u32_array(np, "sirf,pullups", pullups, + SIRFSOC_GPIO_NO_OF_BANKS)) + sirfsoc_gpio_set_pullup(pullups); + + if (!of_property_read_u32_array(np, "sirf,pulldowns", pulldowns, + SIRFSOC_GPIO_NO_OF_BANKS)) + sirfsoc_gpio_set_pulldown(pulldowns); + return 0; out: -- cgit v1.2.3 From c51ce403d3d9e3d54339c4563f17e958f3bc64df Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 25 Dec 2012 20:36:00 +0530 Subject: regulator: tps51632: add DT support Add DT support for the TI TPS51632. Add device binding document also. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- .../bindings/regulator/tps51632-regulator.txt | 27 ++++++++++ drivers/regulator/tps51632-regulator.c | 59 ++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/tps51632-regulator.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/regulator/tps51632-regulator.txt b/Documentation/devicetree/bindings/regulator/tps51632-regulator.txt new file mode 100644 index 000000000000..2f7e44a96414 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/tps51632-regulator.txt @@ -0,0 +1,27 @@ +TPS51632 Voltage regulators + +Required properties: +- compatible: Must be "ti,tps51632" +- reg: I2C slave address + +Optional properties: +- ti,enable-pwm-dvfs: Enable the DVFS voltage control through the PWM interface. +- ti,dvfs-step-20mV: The 20mV step voltage when PWM DVFS enabled. Missing this + will set 10mV step voltage in PWM DVFS mode. In normal mode, the voltage + step is 10mV as per datasheet. + +Any property defined as part of the core regulator binding, defined in +regulator.txt, can also be used. + +Example: + + tps51632 { + compatible = "ti,tps51632"; + reg = <0x43>; + regulator-name = "tps51632-vout"; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1500000>; + regulator-boot-on; + ti,enable-pwm-dvfs; + ti,dvfs-step-20mV; + }; diff --git a/drivers/regulator/tps51632-regulator.c b/drivers/regulator/tps51632-regulator.c index b96fb0e15a82..7560d0768b77 100644 --- a/drivers/regulator/tps51632-regulator.c +++ b/drivers/regulator/tps51632-regulator.c @@ -28,10 +28,13 @@ #include #include #include +#include +#include #include #include #include #include +#include #include #include @@ -252,6 +255,49 @@ static const struct regmap_config tps51632_regmap_config = { .cache_type = REGCACHE_RBTREE, }; +#if defined(CONFIG_OF) +static const struct of_device_id tps51632_of_match[] = { + { .compatible = "ti,tps51632",}, + {}, +}; +MODULE_DEVICE_TABLE(of, tps51632_of_match); + +static struct tps51632_regulator_platform_data * + of_get_tps51632_platform_data(struct device *dev) +{ + struct tps51632_regulator_platform_data *pdata; + struct device_node *np = dev->of_node; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "Memory alloc failed for platform data\n"); + return NULL; + } + + pdata->reg_init_data = of_get_regulator_init_data(dev, dev->of_node); + if (!pdata->reg_init_data) { + dev_err(dev, "Not able to get OF regulator init data\n"); + return NULL; + } + + pdata->enable_pwm_dvfs = + of_property_read_bool(np, "ti,enable-pwm-dvfs"); + pdata->dvfs_step_20mV = of_property_read_bool(np, "ti,dvfs-step-20mV"); + + pdata->base_voltage_uV = pdata->reg_init_data->constraints.min_uV ? : + TPS51632_MIN_VOLATGE; + pdata->max_voltage_uV = pdata->reg_init_data->constraints.max_uV ? : + TPS51632_MAX_VOLATGE; + return pdata; +} +#else +static struct tps51632_regulator_platform_data * + of_get_tps51632_platform_data(struct device *dev) +{ + return NULL; +} +#endif + static int tps51632_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -261,7 +307,19 @@ static int tps51632_probe(struct i2c_client *client, int ret; struct regulator_config config = { }; + if (client->dev.of_node) { + const struct of_device_id *match; + match = of_match_device(of_match_ptr(tps51632_of_match), + &client->dev); + if (!match) { + dev_err(&client->dev, "Error: No device match found\n"); + return -ENODEV; + } + } + pdata = client->dev.platform_data; + if (!pdata && client->dev.of_node) + pdata = of_get_tps51632_platform_data(&client->dev); if (!pdata) { dev_err(&client->dev, "No Platform data\n"); return -EINVAL; @@ -350,6 +408,7 @@ static struct i2c_driver tps51632_i2c_driver = { .driver = { .name = "tps51632", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tps51632_of_match), }, .probe = tps51632_probe, .remove = tps51632_remove, -- cgit v1.2.3 From 8899b8d93ec64b7a8e54807a68a958e1206535e2 Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Sun, 23 Dec 2012 22:03:37 +0200 Subject: watchdog: twl4030_wdt: add DT support Add DT support for twl4030_wdt. This is needed to get twl4030_wdt to probe when booting with DT. Signed-off-by: Aaro Koskinen Signed-off-by: Wim Van Sebroeck --- Documentation/devicetree/bindings/watchdog/twl4030-wdt.txt | 10 ++++++++++ arch/arm/boot/dts/twl4030.dtsi | 4 ++++ drivers/watchdog/twl4030_wdt.c | 11 +++++++++-- 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/watchdog/twl4030-wdt.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/watchdog/twl4030-wdt.txt b/Documentation/devicetree/bindings/watchdog/twl4030-wdt.txt new file mode 100644 index 000000000000..80a37193c0b8 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/twl4030-wdt.txt @@ -0,0 +1,10 @@ +Device tree bindings for twl4030-wdt driver (TWL4030 watchdog) + +Required properties: + compatible = "ti,twl4030-wdt"; + +Example: + +watchdog { + compatible = "ti,twl4030-wdt"; +}; diff --git a/arch/arm/boot/dts/twl4030.dtsi b/arch/arm/boot/dts/twl4030.dtsi index 63411b036932..ed0bc9546837 100644 --- a/arch/arm/boot/dts/twl4030.dtsi +++ b/arch/arm/boot/dts/twl4030.dtsi @@ -19,6 +19,10 @@ interrupts = <11>; }; + watchdog { + compatible = "ti,twl4030-wdt"; + }; + vdac: regulator-vdac { compatible = "ti,twl4030-vdac"; regulator-min-microvolt = <1800000>; diff --git a/drivers/watchdog/twl4030_wdt.c b/drivers/watchdog/twl4030_wdt.c index 81918cf8993b..0f03106f7516 100644 --- a/drivers/watchdog/twl4030_wdt.c +++ b/drivers/watchdog/twl4030_wdt.c @@ -131,14 +131,21 @@ static int twl4030_wdt_resume(struct platform_device *pdev) #define twl4030_wdt_resume NULL #endif +static const struct of_device_id twl_wdt_of_match[] = { + { .compatible = "ti,twl4030-wdt", }, + { }, +}; +MODULE_DEVICE_TABLE(of, twl_wdt_of_match); + static struct platform_driver twl4030_wdt_driver = { .probe = twl4030_wdt_probe, .remove = twl4030_wdt_remove, .suspend = twl4030_wdt_suspend, .resume = twl4030_wdt_resume, .driver = { - .owner = THIS_MODULE, - .name = "twl4030_wdt", + .owner = THIS_MODULE, + .name = "twl4030_wdt", + .of_match_table = twl_wdt_of_match, }, }; -- cgit v1.2.3 From bd0b286e838ef1ca19bbe1cb55f0ec7e0135de1f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 31 Dec 2012 11:51:48 +0100 Subject: ASoC: omap-twl4030: Add support for routing, voice port and jack detect Update the common machine driver to support more boards including Zoom2 and SDP3430. - Support for voice port of twl4030 - HS jack plug detection support - The audio routing can be fine tuned via pdata or via provided routing table from DT. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/omap-twl4030.txt | 46 +++++ sound/soc/omap/Kconfig | 2 + sound/soc/omap/omap-twl4030.c | 204 ++++++++++++++++++++- 3 files changed, 250 insertions(+), 2 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/omap-twl4030.txt b/Documentation/devicetree/bindings/sound/omap-twl4030.txt index 6fae51c7f766..1ab6bc8404d5 100644 --- a/Documentation/devicetree/bindings/sound/omap-twl4030.txt +++ b/Documentation/devicetree/bindings/sound/omap-twl4030.txt @@ -6,6 +6,52 @@ Required properties: - ti,mcbsp: phandle for the McBSP node - ti,codec: phandle for the twl4030 audio node +Optional properties: +- ti,mcbsp-voice: phandle for the McBSP node connected to the voice port of twl +- ti, jack-det-gpio: Jack detect GPIO +- ti,audio-routing: List of connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. + If the routing is not provided all possible connection will be available + +Available audio endpoints for the audio-routing table: + +Board connectors: + * Headset Stereophone + * Earpiece Spk + * Handsfree Spk + * Ext Spk + * Main Mic + * Sub Mic + * Headset Mic + * Carkit Mic + * Digital0 Mic + * Digital1 Mic + * Line In + +twl4030 pins: + * HSOL + * HSOR + * EARPIECE + * HFL + * HFR + * PREDRIVEL + * PREDRIVER + * CARKITL + * CARKITR + * MAINMIC + * SUBMIC + * HSMIC + * DIGIMIC0 + * DIGIMIC1 + * CARKITMIC + * AUXL + * AUXR + + * Headset Mic Bias + * Mic Bias 1 /* Used for Main Mic or Digimic0 */ + * Mic Bias 2 /* Used for Sub Mic or Digimic1 */ + Example: sound { diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 7048137f9a33..e8d2a2f983b5 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -91,6 +91,8 @@ config SND_OMAP_SOC_OMAP_TWL4030 - Gumstix Overo or CompuLab CM-T35/CM-T3730 - IGEP v2 - OMAP3EVM + - SDP3430 + - Zoom2 config SND_OMAP_SOC_OMAP_ABE_TWL6040 tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec" diff --git a/sound/soc/omap/omap-twl4030.c b/sound/soc/omap/omap-twl4030.c index 4541d28b5314..fd98509d0f49 100644 --- a/sound/soc/omap/omap-twl4030.c +++ b/sound/soc/omap/omap-twl4030.c @@ -11,6 +11,8 @@ * omap3evm (Author: Anuj Aggarwal ) * overo (Author: Steve Sakoman ) * igep0020 (Author: Enric Balletbo i Serra ) + * zoom2 (Author: Misael Lopez Cruz ) + * sdp3430 (Author: Misael Lopez Cruz ) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,14 +34,22 @@ #include #include #include +#include +#include #include #include #include +#include #include "omap-mcbsp.h" #include "omap-pcm.h" +struct omap_twl4030 { + int jack_detect; /* board can detect jack events */ + struct snd_soc_jack hs_jack; +}; + static int omap_twl4030_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -87,17 +97,164 @@ static struct snd_soc_ops omap_twl4030_ops = { .hw_params = omap_twl4030_hw_params, }; +static const struct snd_soc_dapm_widget dapm_widgets[] = { + SND_SOC_DAPM_SPK("Earpiece Spk", NULL), + SND_SOC_DAPM_SPK("Handsfree Spk", NULL), + SND_SOC_DAPM_HP("Headset Stereophone", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), + SND_SOC_DAPM_SPK("Carkit Spk", NULL), + + SND_SOC_DAPM_MIC("Main Mic", NULL), + SND_SOC_DAPM_MIC("Sub Mic", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Carkit Mic", NULL), + SND_SOC_DAPM_MIC("Digital0 Mic", NULL), + SND_SOC_DAPM_MIC("Digital1 Mic", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* Headset Stereophone: HSOL, HSOR */ + {"Headset Stereophone", NULL, "HSOL"}, + {"Headset Stereophone", NULL, "HSOR"}, + /* External Speakers: HFL, HFR */ + {"Handsfree Spk", NULL, "HFL"}, + {"Handsfree Spk", NULL, "HFR"}, + /* External Speakers: PredrivL, PredrivR */ + {"Ext Spk", NULL, "PREDRIVEL"}, + {"Ext Spk", NULL, "PREDRIVER"}, + /* Carkit speakers: CARKITL, CARKITR */ + {"Carkit Spk", NULL, "CARKITL"}, + {"Carkit Spk", NULL, "CARKITR"}, + /* Earpiece */ + {"Earpiece Spk", NULL, "EARPIECE"}, + + /* External Mics: MAINMIC, SUBMIC with bias */ + {"MAINMIC", NULL, "Main Mic"}, + {"Main Mic", NULL, "Mic Bias 1"}, + {"SUBMIC", NULL, "Sub Mic"}, + {"Sub Mic", NULL, "Mic Bias 2"}, + /* Headset Mic: HSMIC with bias */ + {"HSMIC", NULL, "Headset Mic"}, + {"Headset Mic", NULL, "Headset Mic Bias"}, + /* Digital Mics: DIGIMIC0, DIGIMIC1 with bias */ + {"DIGIMIC0", NULL, "Digital0 Mic"}, + {"Digital0 Mic", NULL, "Mic Bias 1"}, + {"DIGIMIC1", NULL, "Digital1 Mic"}, + {"Digital1 Mic", NULL, "Mic Bias 2"}, + /* Carkit In: CARKITMIC */ + {"CARKITMIC", NULL, "Carkit Mic"}, + /* Aux In: AUXL, AUXR */ + {"AUXL", NULL, "Line In"}, + {"AUXR", NULL, "Line In"}, +}; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin hs_jack_pins[] = { + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headset Stereophone", + .mask = SND_JACK_HEADPHONE, + }, +}; + +/* Headset jack detection gpios */ +static struct snd_soc_jack_gpio hs_jack_gpios[] = { + { + .name = "hsdet-gpio", + .report = SND_JACK_HEADSET, + .debounce_time = 200, + }, +}; + +static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm, + int connected, char *pin) +{ + if (!connected) + snd_soc_dapm_disable_pin(dapm, pin); +} + +static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_card *card = codec->card; + struct snd_soc_dapm_context *dapm = &codec->dapm; + struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev); + struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card); + int ret = 0; + + /* Headset jack detection only if it is supported */ + if (priv->jack_detect > 0) { + hs_jack_gpios[0].gpio = priv->jack_detect; + + ret = snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET, + &priv->hs_jack); + if (ret) + return ret; + + ret = snd_soc_jack_add_pins(&priv->hs_jack, + ARRAY_SIZE(hs_jack_pins), + hs_jack_pins); + if (ret) + return ret; + + ret = snd_soc_jack_add_gpios(&priv->hs_jack, + ARRAY_SIZE(hs_jack_gpios), + hs_jack_gpios); + if (ret) + return ret; + } + + /* + * NULL pdata means we booted with DT. In this case the routing is + * provided and the card is fully routed, no need to mark pins. + */ + if (!pdata || !pdata->custom_routing) + return ret; + + /* Disable not connected paths if not used */ + twl4030_disconnect_pin(dapm, pdata->has_ear, "Earpiece Spk"); + twl4030_disconnect_pin(dapm, pdata->has_hf, "Handsfree Spk"); + twl4030_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone"); + twl4030_disconnect_pin(dapm, pdata->has_predriv, "Ext Spk"); + twl4030_disconnect_pin(dapm, pdata->has_carkit, "Carkit Spk"); + + twl4030_disconnect_pin(dapm, pdata->has_mainmic, "Main Mic"); + twl4030_disconnect_pin(dapm, pdata->has_submic, "Sub Mic"); + twl4030_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic"); + twl4030_disconnect_pin(dapm, pdata->has_carkitmic, "Carkit Mic"); + twl4030_disconnect_pin(dapm, pdata->has_digimic0, "Digital0 Mic"); + twl4030_disconnect_pin(dapm, pdata->has_digimic1, "Digital1 Mic"); + twl4030_disconnect_pin(dapm, pdata->has_linein, "Line In"); + + return ret; +} + /* Digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link omap_twl4030_dai_links[] = { { - .name = "TWL4030", - .stream_name = "TWL4030", + .name = "TWL4030 HiFi", + .stream_name = "TWL4030 HiFi", .cpu_dai_name = "omap-mcbsp.2", .codec_dai_name = "twl4030-hifi", .platform_name = "omap-pcm-audio", .codec_name = "twl4030-codec", + .init = omap_twl4030_init, .ops = &omap_twl4030_ops, }, + { + .name = "TWL4030 Voice", + .stream_name = "TWL4030 Voice", + .cpu_dai_name = "omap-mcbsp.3", + .codec_dai_name = "twl4030-voice", + .platform_name = "omap-pcm-audio", + .codec_name = "twl4030-codec", + .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBM_CFM, + }, }; /* Audio machine driver */ @@ -105,6 +262,11 @@ static struct snd_soc_card omap_twl4030_card = { .owner = THIS_MODULE, .dai_link = omap_twl4030_dai_links, .num_links = ARRAY_SIZE(omap_twl4030_dai_links), + + .dapm_widgets = dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), }; static int omap_twl4030_probe(struct platform_device *pdev) @@ -112,12 +274,18 @@ static int omap_twl4030_probe(struct platform_device *pdev) struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev); struct device_node *node = pdev->dev.of_node; struct snd_soc_card *card = &omap_twl4030_card; + struct omap_twl4030 *priv; int ret = 0; card->dev = &pdev->dev; + priv = devm_kzalloc(&pdev->dev, sizeof(struct omap_twl4030), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + if (node) { struct device_node *dai_node; + struct property *prop; if (snd_soc_of_parse_card_name(card, "ti,model")) { dev_err(&pdev->dev, "Card name is not provided\n"); @@ -132,6 +300,27 @@ static int omap_twl4030_probe(struct platform_device *pdev) omap_twl4030_dai_links[0].cpu_dai_name = NULL; omap_twl4030_dai_links[0].cpu_of_node = dai_node; + dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0); + if (!dai_node) { + card->num_links = 1; + } else { + omap_twl4030_dai_links[1].cpu_dai_name = NULL; + omap_twl4030_dai_links[1].cpu_of_node = dai_node; + } + + priv->jack_detect = of_get_named_gpio(node, + "ti,jack-det-gpio", 0); + + /* Optional: audio routing can be provided */ + prop = of_find_property(node, "ti,audio-routing", NULL); + if (prop) { + ret = snd_soc_of_parse_audio_routing(card, + "ti,audio-routing"); + if (ret) + return ret; + + card->fully_routed = 1; + } } else if (pdata) { if (pdata->card_name) { card->name = pdata->card_name; @@ -139,11 +328,17 @@ static int omap_twl4030_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Card name is not provided\n"); return -ENODEV; } + + if (!pdata->voice_connected) + card->num_links = 1; + + priv->jack_detect = pdata->jack_detect; } else { dev_err(&pdev->dev, "Missing pdata\n"); return -ENODEV; } + snd_soc_card_set_drvdata(card, priv); ret = snd_soc_register_card(card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", @@ -157,7 +352,12 @@ static int omap_twl4030_probe(struct platform_device *pdev) static int omap_twl4030_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); + struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card); + if (priv->jack_detect > 0) + snd_soc_jack_free_gpios(&priv->hs_jack, + ARRAY_SIZE(hs_jack_gpios), + hs_jack_gpios); snd_soc_unregister_card(card); return 0; -- cgit v1.2.3 From 74250572941ed7cb877efdead5cbf92747242a54 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 28 Nov 2012 13:34:16 -0200 Subject: clk: imx: Remove 'clock-output-names' from the examples 'clock-output-names' is not used in any of the dts/dtsi files for i.mx. Remove it from the examples, so that the example and the real usage in the dtsi files can match. Signed-off-by: Fabio Estevam Acked-by: Rob Herring Signed-off-by: Shawn Guo --- Documentation/devicetree/bindings/clock/imx23-clock.txt | 5 ----- Documentation/devicetree/bindings/clock/imx25-clock.txt | 4 ---- Documentation/devicetree/bindings/clock/imx28-clock.txt | 5 ----- Documentation/devicetree/bindings/clock/imx6q-clock.txt | 4 ---- 4 files changed, 18 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/clock/imx23-clock.txt b/Documentation/devicetree/bindings/clock/imx23-clock.txt index baadbb11fe98..5083c0b834b2 100644 --- a/Documentation/devicetree/bindings/clock/imx23-clock.txt +++ b/Documentation/devicetree/bindings/clock/imx23-clock.txt @@ -60,11 +60,6 @@ clks: clkctrl@80040000 { compatible = "fsl,imx23-clkctrl"; reg = <0x80040000 0x2000>; #clock-cells = <1>; - clock-output-names = - ... - "uart", /* 32 */ - ... - "end_of_list"; }; auart0: serial@8006c000 { diff --git a/Documentation/devicetree/bindings/clock/imx25-clock.txt b/Documentation/devicetree/bindings/clock/imx25-clock.txt index c2a3525ecb4e..db4f2f05c4d0 100644 --- a/Documentation/devicetree/bindings/clock/imx25-clock.txt +++ b/Documentation/devicetree/bindings/clock/imx25-clock.txt @@ -146,10 +146,6 @@ clks: ccm@53f80000 { compatible = "fsl,imx25-ccm"; reg = <0x53f80000 0x4000>; interrupts = <31>; - clock-output-names = ... - "uart_ipg", - "uart_serial", - ...; }; uart1: serial@43f90000 { diff --git a/Documentation/devicetree/bindings/clock/imx28-clock.txt b/Documentation/devicetree/bindings/clock/imx28-clock.txt index 52a49a4a50b3..e6587af62ff0 100644 --- a/Documentation/devicetree/bindings/clock/imx28-clock.txt +++ b/Documentation/devicetree/bindings/clock/imx28-clock.txt @@ -83,11 +83,6 @@ clks: clkctrl@80040000 { compatible = "fsl,imx28-clkctrl"; reg = <0x80040000 0x2000>; #clock-cells = <1>; - clock-output-names = - ... - "uart", /* 45 */ - ... - "end_of_list"; }; auart0: serial@8006a000 { diff --git a/Documentation/devicetree/bindings/clock/imx6q-clock.txt b/Documentation/devicetree/bindings/clock/imx6q-clock.txt index d77b4e68dc42..f73fdf595568 100644 --- a/Documentation/devicetree/bindings/clock/imx6q-clock.txt +++ b/Documentation/devicetree/bindings/clock/imx6q-clock.txt @@ -211,10 +211,6 @@ clks: ccm@020c4000 { reg = <0x020c4000 0x4000>; interrupts = <0 87 0x04 0 88 0x04>; #clock-cells = <1>; - clock-output-names = ... - "uart_ipg", - "uart_serial", - ...; }; uart1: serial@02020000 { -- cgit v1.2.3 From 5343527bbfab3f71f64d8ba5d51ce12dec8d82d0 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 6 Jan 2013 11:10:35 +0100 Subject: Power: gpio-poweroff: Fix documentation and gpio_is_valid Improve the documentation to clarify level vs edge triggered power off. Improve the comments for level vs edge triggered power off. Make use of gpio_is_valid(). Reported-by: Stephen Warren Signed-off-by: Andrew Lunn Signed-off-by: Jason Cooper --- .../devicetree/bindings/gpio/gpio-poweroff.txt | 20 +++++++++++-- drivers/power/reset/gpio-poweroff.c | 33 ++++++++++------------ 2 files changed, 32 insertions(+), 21 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/gpio/gpio-poweroff.txt b/Documentation/devicetree/bindings/gpio/gpio-poweroff.txt index 558cdf3c9abc..d4eab9227ea4 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-poweroff.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-poweroff.txt @@ -1,4 +1,19 @@ -GPIO line that should be set high/low to power off a device +Driver a GPIO line that can be used to turn the power off. + +The driver supports both level triggered and edge triggered power off. +At driver load time, the driver will request the given gpio line and +install a pm_power_off handler. If the optional properties 'input' is +not found, the GPIO line will be driven in the inactive +state. Otherwise its configured as an input. + +When the pm_power_off is called, the gpio is configured as an output, +and drive active, so triggering a level triggered power off +condition. This will also cause an inactive->active edge condition, so +triggering positive edge triggered power off. After a delay of 100ms, +the GPIO is set to inactive, thus causing an active->inactive edge, +triggering negative edge triggered power off. After another 100ms +delay the GPIO is driver active again. If the power is still on and +the CPU still running after a 3000ms delay, a WARN_ON(1) is emitted. Required properties: - compatible : should be "gpio-poweroff". @@ -13,10 +28,9 @@ Optional properties: property is not specified, the GPIO is initialized as an output in its inactive state. - Examples: gpio-poweroff { compatible = "gpio-poweroff"; - gpios = <&gpio 4 0>; /* GPIO 4 Active Low */ + gpios = <&gpio 4 0>; }; diff --git a/drivers/power/reset/gpio-poweroff.c b/drivers/power/reset/gpio-poweroff.c index 0491e5335d02..adc943b3fc3f 100644 --- a/drivers/power/reset/gpio-poweroff.c +++ b/drivers/power/reset/gpio-poweroff.c @@ -29,15 +29,16 @@ static int gpio_active_low; static void gpio_poweroff_do_poweroff(void) { - BUG_ON(gpio_num == -1); + BUG_ON(!gpio_is_valid(gpio_num)); - /* drive it active */ + /* drive it active, also inactive->active edge */ gpio_direction_output(gpio_num, !gpio_active_low); mdelay(100); - /* rising edge or drive inactive */ + /* drive inactive, also active->inactive edge */ gpio_set_value(gpio_num, gpio_active_low); mdelay(100); - /* falling edge */ + + /* drive it active, also inactive->active edge */ gpio_set_value(gpio_num, !gpio_active_low); /* give it some time */ @@ -60,15 +61,12 @@ static int __devinit gpio_poweroff_probe(struct platform_device *pdev) } gpio_num = of_get_gpio_flags(pdev->dev.of_node, 0, &flags); - if (gpio_num < 0) { - pr_err("%s: Could not get GPIO configuration: %d", - __func__, gpio_num); - return -ENODEV; - } + if (!gpio_is_valid(gpio_num)) + return gpio_num; + gpio_active_low = flags & OF_GPIO_ACTIVE_LOW; - if (of_get_property(pdev->dev.of_node, "input", NULL)) - input = true; + input = of_property_read_bool(pdev->dev.of_node, "input"); ret = gpio_request(gpio_num, "poweroff-gpio"); if (ret) { @@ -98,8 +96,7 @@ err: static int __devexit gpio_poweroff_remove(struct platform_device *pdev) { - if (gpio_num != -1) - gpio_free(gpio_num); + gpio_free(gpio_num); if (pm_power_off == &gpio_poweroff_do_poweroff) pm_power_off = NULL; @@ -115,15 +112,15 @@ static struct platform_driver gpio_poweroff_driver = { .probe = gpio_poweroff_probe, .remove = __devexit_p(gpio_poweroff_remove), .driver = { - .name = "poweroff-gpio", - .owner = THIS_MODULE, - .of_match_table = of_gpio_poweroff_match, - }, + .name = "poweroff-gpio", + .owner = THIS_MODULE, + .of_match_table = of_gpio_poweroff_match, + }, }; module_platform_driver(gpio_poweroff_driver); MODULE_AUTHOR("Jamie Lentin "); MODULE_DESCRIPTION("GPIO poweroff driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:poweroff-gpio"); -- cgit v1.2.3 From e8fc721a9ab8dd6723063a92f5c5fdb5eaffbd6e Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Fri, 28 Dec 2012 13:25:09 +0100 Subject: power/reset: Add a new driver to turn QNAP board power off The QNAP NAS boxes have a microcontroller attached to the SoCs second serial port. By sending it a simple command, it will turn the power for the board off. This driver registers a function for pm_power_off to send such a command. Signed-off-by: Andrew Lunn Signed-off-by: Anton Vorontsov --- .../bindings/power_supply/qnap-poweroff.txt | 13 +++ drivers/power/reset/Kconfig | 9 ++ drivers/power/reset/Makefile | 1 + drivers/power/reset/qnap-poweroff.c | 116 +++++++++++++++++++++ 4 files changed, 139 insertions(+) create mode 100644 Documentation/devicetree/bindings/power_supply/qnap-poweroff.txt create mode 100644 drivers/power/reset/qnap-poweroff.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/power_supply/qnap-poweroff.txt b/Documentation/devicetree/bindings/power_supply/qnap-poweroff.txt new file mode 100644 index 000000000000..9a599d27bd75 --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/qnap-poweroff.txt @@ -0,0 +1,13 @@ +* QNAP Power Off + +QNAP NAS devices have a microcontroller controlling the main power +supply. This microcontroller is connected to UART1 of the Kirkwood and +Orion5x SoCs. Sending the charactor 'A', at 19200 baud, tells the +microcontroller to turn the power off. This driver adds a handler to +pm_power_off which is called to turn the power off. + +Required Properties: +- compatible: Should be "qnap,power-off" + +- reg: Address and length of the register set for UART1 +- clocks: tclk clock diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 6461b489fb09..6453aac701c0 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -13,3 +13,12 @@ config POWER_RESET_GPIO This driver supports turning off your board via a GPIO line. If your board needs a GPIO high/low to power down, say Y and create a binding in your devicetree. + +config POWER_RESET_QNAP + bool "QNAP power-off driver" + depends on OF_GPIO && POWER_RESET && PLAT_ORION + help + This driver supports turning off QNAP NAS devices by sending + commands to the microcontroller which controls the main power. + + Say Y if you have a QNAP NAS. diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 751488a4a0c5..c4d3b2d11b9e 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o +obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o diff --git a/drivers/power/reset/qnap-poweroff.c b/drivers/power/reset/qnap-poweroff.c new file mode 100644 index 000000000000..cecb317cf2fe --- /dev/null +++ b/drivers/power/reset/qnap-poweroff.c @@ -0,0 +1,116 @@ +/* + * QNAP Turbo NAS Board power off + * + * Copyright (C) 2012 Andrew Lunn + * + * Based on the code from: + * + * Copyright (C) 2009 Martin Michlmayr + * Copyright (C) 2008 Byron Bradley + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define UART1_REG(x) (base + ((UART_##x) << 2)) + +static void __iomem *base; +static unsigned long tclk; + +static void qnap_power_off(void) +{ + /* 19200 baud divisor */ + const unsigned divisor = ((tclk + (8 * 19200)) / (16 * 19200)); + + pr_err("%s: triggering power-off...\n", __func__); + + /* hijack UART1 and reset into sane state (19200,8n1) */ + writel(0x83, UART1_REG(LCR)); + writel(divisor & 0xff, UART1_REG(DLL)); + writel((divisor >> 8) & 0xff, UART1_REG(DLM)); + writel(0x03, UART1_REG(LCR)); + writel(0x00, UART1_REG(IER)); + writel(0x00, UART1_REG(FCR)); + writel(0x00, UART1_REG(MCR)); + + /* send the power-off command 'A' to PIC */ + writel('A', UART1_REG(TX)); +} + +static int qnap_power_off_probe(struct platform_device *pdev) +{ + struct resource *res; + struct clk *clk; + char symname[KSYM_NAME_LEN]; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Missing resource"); + return -EINVAL; + } + + base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!base) { + dev_err(&pdev->dev, "Unable to map resource"); + return -EINVAL; + } + + /* We need to know tclk in order to calculate the UART divisor */ + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "Clk missing"); + return PTR_ERR(clk); + } + + tclk = clk_get_rate(clk); + + /* Check that nothing else has already setup a handler */ + if (pm_power_off) { + lookup_symbol_name((ulong)pm_power_off, symname); + dev_err(&pdev->dev, + "pm_power_off already claimed %p %s", + pm_power_off, symname); + return -EBUSY; + } + pm_power_off = qnap_power_off; + + return 0; +} + +static int qnap_power_off_remove(struct platform_device *pdev) +{ + pm_power_off = NULL; + return 0; +} + +static const struct of_device_id qnap_power_off_of_match_table[] = { + { .compatible = "qnap,power-off", }, + {} +}; +MODULE_DEVICE_TABLE(of, qnap_power_off_of_match_table); + +static struct platform_driver qnap_power_off_driver = { + .probe = qnap_power_off_probe, + .remove = qnap_power_off_remove, + .driver = { + .owner = THIS_MODULE, + .name = "qnap_power_off", + .of_match_table = of_match_ptr(qnap_power_off_of_match_table), + }, +}; +module_platform_driver(qnap_power_off_driver); + +MODULE_AUTHOR("Andrew Lunn "); +MODULE_DESCRIPTION("QNAP Power off driver"); +MODULE_LICENSE("GPLv2+"); -- cgit v1.2.3 From ffd8f9a727f27270584c7472ca56cce1e4736888 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Fri, 28 Dec 2012 13:25:11 +0100 Subject: power/reset: Add a new driver implementing 'power off by restarting' Some devices, Buffalo Linkstation LS-XHL and LS-CHLv2 for example, power-off by restarting to letting u-boot hold the SoC until the user presses a key. Add a generic driver to implement this. It binds a function to pm_power_off, which calls arm_pm_restart. Signed-off-by: Andrew Lunn Tested-by: Michael Walle Signed-off-by: Anton Vorontsov --- .../bindings/power_supply/restart-poweroff.txt | 8 +++ drivers/power/reset/Kconfig | 8 +++ drivers/power/reset/Makefile | 1 + drivers/power/reset/restart-poweroff.c | 65 ++++++++++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 Documentation/devicetree/bindings/power_supply/restart-poweroff.txt create mode 100644 drivers/power/reset/restart-poweroff.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/power_supply/restart-poweroff.txt b/Documentation/devicetree/bindings/power_supply/restart-poweroff.txt new file mode 100644 index 000000000000..5776e684afda --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/restart-poweroff.txt @@ -0,0 +1,8 @@ +* Restart Power Off + +Buffalo Linkstation LS-XHL and LS-CHLv2, and other devices power off +by restarting and letting u-boot keep hold of the machine until the +user presses a button. + +Required Properties: +- compatible: Should be "restart-poweroff" diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 6453aac701c0..1ae65b822864 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -22,3 +22,11 @@ config POWER_RESET_QNAP commands to the microcontroller which controls the main power. Say Y if you have a QNAP NAS. + +config POWER_RESET_RESTART + bool "Restart power-off driver" + depends on ARM + help + Some boards don't actually have the ability to power off. + Instead they restart, and u-boot holds the SoC until the + user presses a key. u-boot then boots into Linux. diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index c4d3b2d11b9e..0f317f50c56f 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o +obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o \ No newline at end of file diff --git a/drivers/power/reset/restart-poweroff.c b/drivers/power/reset/restart-poweroff.c new file mode 100644 index 000000000000..b11b9e887174 --- /dev/null +++ b/drivers/power/reset/restart-poweroff.c @@ -0,0 +1,65 @@ +/* + * Power off by restarting and let u-boot keep hold of the machine + * until the user presses a button for example. + * + * Andrew Lunn + * + * Copyright (C) 2012 Andrew Lunn + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include + +static void restart_poweroff_do_poweroff(void) +{ + arm_pm_restart('h', NULL); +} + +static int __devinit restart_poweroff_probe(struct platform_device *pdev) +{ + /* If a pm_power_off function has already been added, leave it alone */ + if (pm_power_off != NULL) { + dev_err(&pdev->dev, + "pm_power_off function already registered"); + return -EBUSY; + } + + pm_power_off = &restart_poweroff_do_poweroff; + return 0; +} + +static int __devexit restart_poweroff_remove(struct platform_device *pdev) +{ + if (pm_power_off == &restart_poweroff_do_poweroff) + pm_power_off = NULL; + + return 0; +} + +static const struct of_device_id of_restart_poweroff_match[] = { + { .compatible = "restart-poweroff", }, + {}, +}; + +static struct platform_driver restart_poweroff_driver = { + .probe = restart_poweroff_probe, + .remove = __devexit_p(restart_poweroff_remove), + .driver = { + .name = "poweroff-restart", + .owner = THIS_MODULE, + .of_match_table = of_restart_poweroff_match, + }, +}; +module_platform_driver(restart_poweroff_driver); + +MODULE_AUTHOR("Andrew Lunn Date: Fri, 14 Sep 2012 17:41:56 -0500 Subject: of: Add generic device tree DMA helpers This is based upon the work by Benoit Cousson [1] and Nicolas Ferre [2] to add some basic helpers to retrieve a DMA controller device_node and the DMA request/channel information. Aim of DMA helpers - The purpose of device-tree is to describe the capabilites of the hardware. Thinking about DMA controllers purely from the context of the hardware to begin with, we can describe a device in terms of a DMA controller as follows ... 1. Number of DMA controllers 2. Number of channels (maybe physical or logical) 3. Mapping of DMA requests signals to DMA controller 4. Number of DMA interrupts 5. Mapping of DMA interrupts to channels - With the above in mind the aim of the DT DMA helper functions is to extract the above information from the DT and provide to the appropriate driver. However, due to the vast number of DMA controllers and not all are using a common driver (such as DMA Engine) it has been seen that this is not a trivial task. In previous discussions on this topic the following concerns have been raised ... 1. How does the binding support devices with multiple DMA controllers? 2. How to support both legacy DMA controllers not using DMA Engine as well as those that support DMA Engine. 3. When using with DMA Engine how do we support the various implementations where the opaque filter function parameter differs between implementations? 4. How do we handle DMA channels that are identified with a string versus a integer? - Hence the design of the DMA helpers has to accomodate the above or align on an agreement what can be or should be supported. Design of DMA helpers 1. Registering DMA controllers In the case of DMA controllers that are using DMA Engine, requesting a channel is performed by calling the following function. struct dma_chan *dma_request_channel(dma_cap_mask_t mask, dma_filter_fn filter_fn, void *filter_param); The mask variable is used to match a type of the device controller in a list of controllers. The filter_fn and filter_param are used to identify the required dma channel and return a handle to the dma channel of type dma_chan. From the examples I have seen, the mask and filter_fn are constant for a given DMA controller and therefore, we can specify these as controller specific data when registering the DMA controller with the device-tree DMA helpers. The filter_param variable is of an unknown type and is typically specific to the DMA engine implementation for a given DMA controller. To allow some flexibility in the type and formating of this filter_param we employ an xlate to translate the device-tree binding information into the appropriate format. The xlate function used for a DMA controller can also be specified when registering the DMA controller with the device-tree DMA helpers. Based upon the above, a function for registering the DMA controller with the DMA helpers now looks like the below. The data variable is used to pass a pointer to DMA controller specific data used by the xlate function. int of_dma_controller_register(struct device_node *np, struct dma_chan *(*of_dma_xlate) (struct of_phandle_args *, struct of_dma *), void *data) For example, in the case where DMA engine is used, we define the following structure (that stores the DMA engine capability mask and filter function) and pass this to the data variable in the above function. struct of_dma_filter_info { dma_cap_mask_t dma_cap; dma_filter_fn filter_fn; }; 2. Representing and requesting channel information Please see the dma binding documentation included in this patch for a description of how DMA controllers and client information should be represented with device-tree. For more information on how this binding came about please see [3]. In addition to this, feedback received from the Linux kernel summit showed a consensus (among those who attended) to use a name to identify DMA client information [4]. A DMA channel can be requested by calling the following function, where name is a required parameter used for identifying a DMA channel. This function has been designed to return a structure of type dma_chan to work with the DMA engine driver. Note that if DMA engine is used then drivers should be using the DMA engine API dma_request_slave_channel() (implemented in part 2 of this series, "dmaengine: add helper function to request a slave DMA channel") which will in turn call the below function if device-tree is present. The aim being to have a common DMA engine interface regardless of whether device tree is being used. struct dma_chan *of_dma_request_slave_channel(struct device_node *np, char *name) 3. Supporting legacy devices not using DMA Engine These devices present a problem, as there may not be a uniform way to easily support them with regard to device tree. Ideally, these should be migrated to DMA engine. However, if this is not possible, then they should still be able to use this binding, the only constaint imposed by this implementation is that when requesting a DMA channel via of_dma_request_slave_channel(), it will return a type of dma_chan. This implementation has been tested on OMAP4430 using the kernel v3.6-rc5. I have validated that MMC is working on the PANDA board with this implementation. My development branch for testing on OMAP can be found here [5]. v6: - minor corrections in DMA binding documentation v5: - minor update to binding documentation - added loop to exhaustively search for a slave channel in the case where there could be alternative channels available v4: - revert the removal of xlate function from v3 - update the proposed binding format and APIs based upon discussions [3] v3: - avoid passing an xlate function and instead pass DMA engine parameters - define number of dma channels and requests in dma-controller node v2: - remove of_dma_to_resource API - make property #dma-cells required (no fallback anymore) - another check in of_dma_xlate_onenumbercell() function [1] http://article.gmane.org/gmane.linux.drivers.devicetree/12022 [2] http://article.gmane.org/gmane.linux.ports.arm.omap/73622 [3] http://marc.info/?l=linux-omap&m=133582085008539&w=2 [4] http://pad.linaro.org/arm-mini-summit-2012 [5] https://github.com/jonhunter/linux/tree/dev-dt-dma Cc: Nicolas Ferre Cc: Benoit Cousson Cc: Stephen Warren Cc: Grant Likely Cc: Russell King Cc: Rob Herring Cc: Arnd Bergmann Cc: Vinod Koul Cc: Dan Williams Reviewed-by: Arnd Bergmann Reviewed-by: Nicolas Ferre Signed-off-by: Jon Hunter Reviewed-by: Stephen Warren Acked-by: Rob Herring Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/dma.txt | 81 ++++++++++ drivers/of/Makefile | 2 +- drivers/of/dma.c | 219 ++++++++++++++++++++++++++ include/linux/of_dma.h | 45 ++++++ 4 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/dma/dma.txt create mode 100644 drivers/of/dma.c create mode 100644 include/linux/of_dma.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/dma/dma.txt b/Documentation/devicetree/bindings/dma/dma.txt new file mode 100644 index 000000000000..a4f59a5967d4 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/dma.txt @@ -0,0 +1,81 @@ +* Generic DMA Controller and DMA request bindings + +Generic binding to provide a way for a driver using DMA Engine to retrieve the +DMA request or channel information that goes from a hardware device to a DMA +controller. + + +* DMA controller + +Required property: +- #dma-cells: Must be at least 1. Used to provide DMA controller + specific information. See DMA client binding below for + more details. + +Optional properties: +- #dma-channels: Number of DMA channels supported by the controller. +- #dma-requests: Number of DMA requests signals supported by the + controller. + +Example: + + dma: dma@48000000 { + compatible = "ti,omap-sdma" + reg = <0x48000000 0x1000>; + interrupts = <0 12 0x4 + 0 13 0x4 + 0 14 0x4 + 0 15 0x4>; + #dma-cells = <1>; + #dma-channels = <32>; + #dma-requests = <127>; + }; + + +* DMA client + +Client drivers should specify the DMA property using a phandle to the controller +followed by DMA controller specific data. + +Required property: +- dmas: List of one or more DMA specifiers, each consisting of + - A phandle pointing to DMA controller node + - A number of integer cells, as determined by the + #dma-cells property in the node referenced by phandle + containing DMA controller specific information. This + typically contains a DMA request line number or a + channel number, but can contain any data that is used + required for configuring a channel. +- dma-names: Contains one identifier string for each DMA specifier in + the dmas property. The specific strings that can be used + are defined in the binding of the DMA client device. + Multiple DMA specifiers can be used to represent + alternatives and in this case the dma-names for those + DMA specifiers must be identical (see examples). + +Examples: + +1. A device with one DMA read channel, one DMA write channel: + + i2c1: i2c@1 { + ... + dmas = <&dma 2 /* read channel */ + &dma 3>; /* write channel */ + dma-names = "rx", "tx" + ... + }; + +2. A single read-write channel with three alternative DMA controllers: + + dmas = <&dma1 5 + &dma2 7 + &dma3 2>; + dma-names = "rx-tx", "rx-tx", "rx-tx" + +3. A device with three channels, one of which has two alternatives: + + dmas = <&dma1 2 /* read channel */ + &dma1 3 /* write channel */ + &dma2 0 /* error read */ + &dma3 0>; /* alternative error read */ + dma-names = "rx", "tx", "error", "error"; diff --git a/drivers/of/Makefile b/drivers/of/Makefile index e027f444d10c..eafa107aed40 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -1,4 +1,4 @@ -obj-y = base.o +obj-y = base.o dma.o obj-$(CONFIG_OF_FLATTREE) += fdt.o obj-$(CONFIG_OF_PROMTREE) += pdt.o obj-$(CONFIG_OF_ADDRESS) += address.o diff --git a/drivers/of/dma.c b/drivers/of/dma.c new file mode 100644 index 000000000000..19ad37c066f5 --- /dev/null +++ b/drivers/of/dma.c @@ -0,0 +1,219 @@ +/* + * Device tree helpers for DMA request / controller + * + * Based on of_gpio.c + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(of_dma_list); + +/** + * of_dma_find_controller - Find a DMA controller in DT DMA helpers list + * @np: device node of DMA controller + */ +static struct of_dma *of_dma_find_controller(struct device_node *np) +{ + struct of_dma *ofdma; + + if (list_empty(&of_dma_list)) { + pr_err("empty DMA controller list\n"); + return NULL; + } + + list_for_each_entry_rcu(ofdma, &of_dma_list, of_dma_controllers) + if (ofdma->of_node == np) + return ofdma; + + return NULL; +} + +/** + * of_dma_controller_register - Register a DMA controller to DT DMA helpers + * @np: device node of DMA controller + * @of_dma_xlate: translation function which converts a phandle + * arguments list into a dma_chan structure + * @data pointer to controller specific data to be used by + * translation function + * + * Returns 0 on success or appropriate errno value on error. + * + * Allocated memory should be freed with appropriate of_dma_controller_free() + * call. + */ +int of_dma_controller_register(struct device_node *np, + struct dma_chan *(*of_dma_xlate) + (struct of_phandle_args *, struct of_dma *), + void *data) +{ + struct of_dma *ofdma; + int nbcells; + + if (!np || !of_dma_xlate) { + pr_err("%s: not enough information provided\n", __func__); + return -EINVAL; + } + + ofdma = kzalloc(sizeof(*ofdma), GFP_KERNEL); + if (!ofdma) + return -ENOMEM; + + nbcells = be32_to_cpup(of_get_property(np, "#dma-cells", NULL)); + if (!nbcells) { + pr_err("%s: #dma-cells property is missing or invalid\n", + __func__); + return -EINVAL; + } + + ofdma->of_node = np; + ofdma->of_dma_nbcells = nbcells; + ofdma->of_dma_xlate = of_dma_xlate; + ofdma->of_dma_data = data; + + /* Now queue of_dma controller structure in list */ + list_add_tail_rcu(&ofdma->of_dma_controllers, &of_dma_list); + + return 0; +} +EXPORT_SYMBOL_GPL(of_dma_controller_register); + +/** + * of_dma_controller_free - Remove a DMA controller from DT DMA helpers list + * @np: device node of DMA controller + * + * Memory allocated by of_dma_controller_register() is freed here. + */ +void of_dma_controller_free(struct device_node *np) +{ + struct of_dma *ofdma; + + ofdma = of_dma_find_controller(np); + if (ofdma) { + list_del_rcu(&ofdma->of_dma_controllers); + kfree(ofdma); + } +} +EXPORT_SYMBOL_GPL(of_dma_controller_free); + +/** + * of_dma_find_channel - Find a DMA channel by name + * @np: device node to look for DMA channels + * @name: name of desired channel + * @dma_spec: pointer to DMA specifier as found in the device tree + * + * Find a DMA channel by the name. Returns 0 on success or appropriate + * errno value on error. + */ +static int of_dma_find_channel(struct device_node *np, char *name, + struct of_phandle_args *dma_spec) +{ + int count, i; + const char *s; + + count = of_property_count_strings(np, "dma-names"); + if (count < 0) + return count; + + for (i = 0; i < count; i++) { + if (of_property_read_string_index(np, "dma-names", i, &s)) + continue; + + if (strcmp(name, s)) + continue; + + if (!of_parse_phandle_with_args(np, "dmas", "#dma-cells", i, + dma_spec)) + return 0; + } + + return -ENODEV; +} + +/** + * of_dma_request_slave_channel - Get the DMA slave channel + * @np: device node to get DMA request from + * @name: name of desired channel + * + * Returns pointer to appropriate dma channel on success or NULL on error. + */ +struct dma_chan *of_dma_request_slave_channel(struct device_node *np, + char *name) +{ + struct of_phandle_args dma_spec; + struct of_dma *ofdma; + struct dma_chan *chan; + int r; + + if (!np || !name) { + pr_err("%s: not enough information provided\n", __func__); + return NULL; + } + + do { + r = of_dma_find_channel(np, name, &dma_spec); + if (r) { + pr_err("%s: can't find DMA channel\n", np->full_name); + return NULL; + } + + ofdma = of_dma_find_controller(dma_spec.np); + if (!ofdma) { + pr_debug("%s: can't find DMA controller %s\n", + np->full_name, dma_spec.np->full_name); + continue; + } + + if (dma_spec.args_count != ofdma->of_dma_nbcells) { + pr_debug("%s: wrong #dma-cells for %s\n", np->full_name, + dma_spec.np->full_name); + continue; + } + + chan = ofdma->of_dma_xlate(&dma_spec, ofdma); + + of_node_put(dma_spec.np); + + } while (!chan); + + return chan; +} + +/** + * of_dma_simple_xlate - Simple DMA engine translation function + * @dma_spec: pointer to DMA specifier as found in the device tree + * @of_dma: pointer to DMA controller data + * + * A simple translation function for devices that use a 32-bit value for the + * filter_param when calling the DMA engine dma_request_channel() function. + * Note that this translation function requires that #dma-cells is equal to 1 + * and the argument of the dma specifier is the 32-bit filter_param. Returns + * pointer to appropriate dma channel on success or NULL on error. + */ +struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + int count = dma_spec->args_count; + struct of_dma_filter_info *info = ofdma->of_dma_data; + + if (!info || !info->filter_fn) + return NULL; + + if (count != 1) + return NULL; + + return dma_request_channel(info->dma_cap, info->filter_fn, + &dma_spec->args[0]); +} +EXPORT_SYMBOL_GPL(of_dma_simple_xlate); diff --git a/include/linux/of_dma.h b/include/linux/of_dma.h new file mode 100644 index 000000000000..337823dc6b90 --- /dev/null +++ b/include/linux/of_dma.h @@ -0,0 +1,45 @@ +/* + * OF helpers for DMA request / controller + * + * Based on of_gpio.h + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_OF_DMA_H +#define __LINUX_OF_DMA_H + +#include +#include + +struct device_node; + +struct of_dma { + struct list_head of_dma_controllers; + struct device_node *of_node; + int of_dma_nbcells; + struct dma_chan *(*of_dma_xlate) + (struct of_phandle_args *, struct of_dma *); + void *of_dma_data; +}; + +struct of_dma_filter_info { + dma_cap_mask_t dma_cap; + dma_filter_fn filter_fn; +}; + +extern int of_dma_controller_register(struct device_node *np, + struct dma_chan *(*of_dma_xlate) + (struct of_phandle_args *, struct of_dma *), + void *data); +extern void of_dma_controller_free(struct device_node *np); +extern struct dma_chan *of_dma_request_slave_channel(struct device_node *np, + char *name); +extern struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma); + +#endif /* __LINUX_OF_DMA_H */ -- cgit v1.2.3 From deef12443785add808af39ff7f836b54bc5c4253 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Wed, 19 Sep 2012 10:49:48 -0400 Subject: of: dma: fix typos in generic dma binding definition Some semicolons were left out in the examples. The #dma-channels and #dma-requests properties have a prefix that is, by convention, reserved for cell size properties. Rename those properties to dma-channels and dma-requests. Signed-off-by: Matt Porter Acked-by: Arnd Bergmann Acked-by: Jon Hunter Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/dma.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/dma/dma.txt b/Documentation/devicetree/bindings/dma/dma.txt index a4f59a5967d4..8f504e6bae14 100644 --- a/Documentation/devicetree/bindings/dma/dma.txt +++ b/Documentation/devicetree/bindings/dma/dma.txt @@ -13,22 +13,22 @@ Required property: more details. Optional properties: -- #dma-channels: Number of DMA channels supported by the controller. -- #dma-requests: Number of DMA requests signals supported by the +- dma-channels: Number of DMA channels supported by the controller. +- dma-requests: Number of DMA requests signals supported by the controller. Example: dma: dma@48000000 { - compatible = "ti,omap-sdma" + compatible = "ti,omap-sdma"; reg = <0x48000000 0x1000>; interrupts = <0 12 0x4 0 13 0x4 0 14 0x4 0 15 0x4>; #dma-cells = <1>; - #dma-channels = <32>; - #dma-requests = <127>; + dma-channels = <32>; + dma-requests = <127>; }; @@ -61,7 +61,7 @@ Examples: ... dmas = <&dma 2 /* read channel */ &dma 3>; /* write channel */ - dma-names = "rx", "tx" + dma-names = "rx", "tx"; ... }; @@ -70,7 +70,7 @@ Examples: dmas = <&dma1 5 &dma2 7 &dma3 2>; - dma-names = "rx-tx", "rx-tx", "rx-tx" + dma-names = "rx-tx", "rx-tx", "rx-tx"; 3. A device with three channels, one of which has two alternatives: -- cgit v1.2.3 From a9ddb575d6d6c58c39e8c44a22b84445fedb0521 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 16 Oct 2012 09:49:17 +0530 Subject: dmaengine: dw_dmac: Enhance device tree support dw_dmac driver already supports device tree but it used to have its platform data passed the non-DT way. This patch does following changes: - pass platform data via DT, non-DT way still takes precedence if both are used. - create generic filter routine - Earlier slave information was made available by slave specific filter routines in chan->private field. Now, this information would be passed from within dmac DT node. Slave drivers would now be required to pass bus_id (a string) as parameter to this generic filter(), which would be compared against the slave data passed from DT, by the generic filter routine. - Update binding document Signed-off-by: Viresh Kumar Reviewed-by: Andy Shevchenko [Fixed __devinit usage] Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/snps-dma.txt | 44 +++++++ drivers/dma/dw_dmac.c | 134 +++++++++++++++++++++ drivers/dma/dw_dmac_regs.h | 4 + include/linux/dw_dmac.h | 43 ++++--- 4 files changed, 208 insertions(+), 17 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/dma/snps-dma.txt b/Documentation/devicetree/bindings/dma/snps-dma.txt index c0d85dbcada5..5bb3dfb6f1d8 100644 --- a/Documentation/devicetree/bindings/dma/snps-dma.txt +++ b/Documentation/devicetree/bindings/dma/snps-dma.txt @@ -6,6 +6,26 @@ Required properties: - interrupt-parent: Should be the phandle for the interrupt controller that services interrupts for this device - interrupt: Should contain the DMAC interrupt number +- nr_channels: Number of channels supported by hardware +- is_private: The device channels should be marked as private and not for by the + general purpose DMA channel allocator. False if not passed. +- chan_allocation_order: order of allocation of channel, 0 (default): ascending, + 1: descending +- chan_priority: priority of channels. 0 (default): increase from chan 0->n, 1: + increase from chan n->0 +- block_size: Maximum block size supported by the controller +- nr_masters: Number of AHB masters supported by the controller +- data_width: Maximum data width supported by hardware per AHB master + (0 - 8bits, 1 - 16bits, ..., 5 - 256bits) +- slave_info: + - bus_id: name of this device channel, not just a device name since + devices may have more than one channel e.g. "foo_tx". For using the + dw_generic_filter(), slave drivers must pass exactly this string as + param to filter function. + - cfg_hi: Platform-specific initializer for the CFG_HI register + - cfg_lo: Platform-specific initializer for the CFG_LO register + - src_master: src master for transfers on allocated channel. + - dst_master: dest master for transfers on allocated channel. Example: @@ -14,4 +34,28 @@ Example: reg = <0xfc000000 0x1000>; interrupt-parent = <&vic1>; interrupts = <12>; + + nr_channels = <8>; + chan_allocation_order = <1>; + chan_priority = <1>; + block_size = <0xfff>; + nr_masters = <2>; + data_width = <3 3 0 0>; + + slave_info { + uart0-tx { + bus_id = "uart0-tx"; + cfg_hi = <0x4000>; /* 0x8 << 11 */ + cfg_lo = <0>; + src_master = <0>; + dst_master = <1>; + }; + spi0-tx { + bus_id = "spi0-tx"; + cfg_hi = <0x2000>; /* 0x4 << 11 */ + cfg_lo = <0>; + src_master = <0>; + dst_master = <0>; + }; + }; }; diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 8f0b111af4de..27b8e1d1845e 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1179,6 +1179,50 @@ static void dwc_free_chan_resources(struct dma_chan *chan) dev_vdbg(chan2dev(chan), "%s: done\n", __func__); } +bool dw_dma_generic_filter(struct dma_chan *chan, void *param) +{ + struct dw_dma *dw = to_dw_dma(chan->device); + static struct dw_dma *last_dw; + static char *last_bus_id; + int i = -1; + + /* + * dmaengine framework calls this routine for all channels of all dma + * controller, until true is returned. If 'param' bus_id is not + * registered with a dma controller (dw), then there is no need of + * running below function for all channels of dw. + * + * This block of code does this by saving the parameters of last + * failure. If dw and param are same, i.e. trying on same dw with + * different channel, return false. + */ + if ((last_dw == dw) && (last_bus_id == param)) + return false; + /* + * Return true: + * - If dw_dma's platform data is not filled with slave info, then all + * dma controllers are fine for transfer. + * - Or if param is NULL + */ + if (!dw->sd || !param) + return true; + + while (++i < dw->sd_count) { + if (!strcmp(dw->sd[i].bus_id, param)) { + chan->private = &dw->sd[i]; + last_dw = NULL; + last_bus_id = NULL; + + return true; + } + } + + last_dw = dw; + last_bus_id = param; + return false; +} +EXPORT_SYMBOL(dw_dma_generic_filter); + /* --------------------- Cyclic DMA API extensions -------------------- */ /** @@ -1462,6 +1506,91 @@ static void dw_dma_off(struct dw_dma *dw) dw->chan[i].initialized = false; } +#ifdef CONFIG_OF +static struct dw_dma_platform_data * +dw_dma_parse_dt(struct platform_device *pdev) +{ + struct device_node *sn, *cn, *np = pdev->dev.of_node; + struct dw_dma_platform_data *pdata; + struct dw_dma_slave *sd; + u32 tmp, arr[4]; + + if (!np) { + dev_err(&pdev->dev, "Missing DT data\n"); + return NULL; + } + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + if (of_property_read_u32(np, "nr_channels", &pdata->nr_channels)) + return NULL; + + if (of_property_read_bool(np, "is_private")) + pdata->is_private = true; + + if (!of_property_read_u32(np, "chan_allocation_order", &tmp)) + pdata->chan_allocation_order = (unsigned char)tmp; + + if (!of_property_read_u32(np, "chan_priority", &tmp)) + pdata->chan_priority = tmp; + + if (!of_property_read_u32(np, "block_size", &tmp)) + pdata->block_size = tmp; + + if (!of_property_read_u32(np, "nr_masters", &tmp)) { + if (tmp > 4) + return NULL; + + pdata->nr_masters = tmp; + } + + if (!of_property_read_u32_array(np, "data_width", arr, + pdata->nr_masters)) + for (tmp = 0; tmp < pdata->nr_masters; tmp++) + pdata->data_width[tmp] = arr[tmp]; + + /* parse slave data */ + sn = of_find_node_by_name(np, "slave_info"); + if (!sn) + return pdata; + + /* calculate number of slaves */ + tmp = of_get_child_count(sn); + if (!tmp) + return NULL; + + sd = devm_kzalloc(&pdev->dev, sizeof(*sd) * tmp, GFP_KERNEL); + if (!sd) + return NULL; + + pdata->sd = sd; + pdata->sd_count = tmp; + + for_each_child_of_node(sn, cn) { + sd->dma_dev = &pdev->dev; + of_property_read_string(cn, "bus_id", &sd->bus_id); + of_property_read_u32(cn, "cfg_hi", &sd->cfg_hi); + of_property_read_u32(cn, "cfg_lo", &sd->cfg_lo); + if (!of_property_read_u32(cn, "src_master", &tmp)) + sd->src_master = tmp; + + if (!of_property_read_u32(cn, "dst_master", &tmp)) + sd->dst_master = tmp; + sd++; + } + + return pdata; +} +#else +static inline struct dw_dma_platform_data * +dw_dma_parse_dt(struct platform_device *pdev) +{ + return NULL; +} +#endif + static int dw_probe(struct platform_device *pdev) { struct dw_dma_platform_data *pdata; @@ -1478,6 +1607,9 @@ static int dw_probe(struct platform_device *pdev) int i; pdata = dev_get_platdata(&pdev->dev); + if (!pdata) + pdata = dw_dma_parse_dt(pdev); + if (!pdata || pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) return -EINVAL; @@ -1512,6 +1644,8 @@ static int dw_probe(struct platform_device *pdev) clk_prepare_enable(dw->clk); dw->regs = regs; + dw->sd = pdata->sd; + dw->sd_count = pdata->sd_count; /* get hardware configuration parameters */ if (autocfg) { diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 88965597b7d0..88a069f66b89 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -239,6 +239,10 @@ struct dw_dma { struct tasklet_struct tasklet; struct clk *clk; + /* slave information */ + struct dw_dma_slave *sd; + unsigned int sd_count; + u8 all_chan_mask; /* hardware configuration */ diff --git a/include/linux/dw_dmac.h b/include/linux/dw_dmac.h index 62a6190dee22..41766de66e33 100644 --- a/include/linux/dw_dmac.h +++ b/include/linux/dw_dmac.h @@ -14,6 +14,26 @@ #include +/** + * struct dw_dma_slave - Controller-specific information about a slave + * + * @dma_dev: required DMA master device. Depricated. + * @bus_id: name of this device channel, not just a device name since + * devices may have more than one channel e.g. "foo_tx" + * @cfg_hi: Platform-specific initializer for the CFG_HI register + * @cfg_lo: Platform-specific initializer for the CFG_LO register + * @src_master: src master for transfers on allocated channel. + * @dst_master: dest master for transfers on allocated channel. + */ +struct dw_dma_slave { + struct device *dma_dev; + const char *bus_id; + u32 cfg_hi; + u32 cfg_lo; + u8 src_master; + u8 dst_master; +}; + /** * struct dw_dma_platform_data - Controller configuration parameters * @nr_channels: Number of channels supported by hardware (max 8) @@ -25,6 +45,8 @@ * @nr_masters: Number of AHB masters supported by the controller * @data_width: Maximum data width supported by hardware per AHB master * (0 - 8bits, 1 - 16bits, ..., 5 - 256bits) + * @sd: slave specific data. Used for configuring channels + * @sd_count: count of slave data structures passed. */ struct dw_dma_platform_data { unsigned int nr_channels; @@ -38,6 +60,9 @@ struct dw_dma_platform_data { unsigned short block_size; unsigned char nr_masters; unsigned char data_width[4]; + + struct dw_dma_slave *sd; + unsigned int sd_count; }; /* bursts size */ @@ -52,23 +77,6 @@ enum dw_dma_msize { DW_DMA_MSIZE_256, }; -/** - * struct dw_dma_slave - Controller-specific information about a slave - * - * @dma_dev: required DMA master device - * @cfg_hi: Platform-specific initializer for the CFG_HI register - * @cfg_lo: Platform-specific initializer for the CFG_LO register - * @src_master: src master for transfers on allocated channel. - * @dst_master: dest master for transfers on allocated channel. - */ -struct dw_dma_slave { - struct device *dma_dev; - u32 cfg_hi; - u32 cfg_lo; - u8 src_master; - u8 dst_master; -}; - /* Platform-configurable bits in CFG_HI */ #define DWC_CFGH_FCMODE (1 << 0) #define DWC_CFGH_FIFO_MODE (1 << 1) @@ -106,5 +114,6 @@ void dw_dma_cyclic_stop(struct dma_chan *chan); dma_addr_t dw_dma_get_src_addr(struct dma_chan *chan); dma_addr_t dw_dma_get_dst_addr(struct dma_chan *chan); +bool dw_dma_generic_filter(struct dma_chan *chan, void *param); #endif /* DW_DMAC_H */ -- cgit v1.2.3 From 9421bade0765d8ffb86b8a99213b611278a3542a Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Tue, 8 Jan 2013 16:36:42 +0100 Subject: pwm: atmel: add Timer Counter Block PWM driver This patch adds a PWM driver based on Atmel Timer Counter Block. The Timer Counter Block is used in Waveform generator mode. A Timer Counter Block provides up to 6 PWM devices grouped by 2: * group 0 = PWM 0 and 1 * group 1 = PWM 2 and 3 * group 2 = PMW 4 and 5 PWM devices in a given group must be configured with the same period value. If a PWM device in a group tries to change the period value and the other device is already configured with a different value an error will be returned. This driver requires device tree support. The Timer Counter Block number used to create a PWM chip is given by the tc-block field in an "atmel,tcb-pwm" compatible node. This patch was tested on kizbox board (at91sam9g20 SoC) with pwm-leds. Signed-off-by: Boris BREZILLON Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/atmel-tcb-pwm.txt | 18 + drivers/pwm/Kconfig | 12 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-atmel-tcb.c | 445 +++++++++++++++++++++ 4 files changed, 476 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/atmel-tcb-pwm.txt create mode 100644 drivers/pwm/pwm-atmel-tcb.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pwm/atmel-tcb-pwm.txt b/Documentation/devicetree/bindings/pwm/atmel-tcb-pwm.txt new file mode 100644 index 000000000000..de0eaed86651 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/atmel-tcb-pwm.txt @@ -0,0 +1,18 @@ +Atmel TCB PWM controller + +Required properties: +- compatible: should be "atmel,tcb-pwm" +- #pwm-cells: Should be 3. The first cell specifies the per-chip index + of the PWM to use, the second cell is the period in nanoseconds and + bit 0 in the third cell is used to encode the polarity of PWM output. + Set bit 0 of the third cell in PWM specifier to 1 for inverse polarity & + set to 0 for normal polarity. +- tc-block: The Timer Counter block to use as a PWM chip. + +Example: + +pwm { + compatible = "atmel,tcb-pwm"; + #pwm-cells = <3>; + tc-block = <1>; +}; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index e513cd998170..10b6afc94bc3 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -37,6 +37,18 @@ config PWM_AB8500 To compile this driver as a module, choose M here: the module will be called pwm-ab8500. +config PWM_ATMEL_TCB + tristate "TC Block PWM" + depends on ATMEL_TCLIB && OF + help + Generic PWM framework driver for Atmel Timer Counter Block. + + A Timer Counter Block provides 6 PWM devices grouped by 2. + Devices in a given group must have the same period. + + To compile this driver as a module, choose M here: the module + will be called pwm-atmel-tcb. + config PWM_BFIN tristate "Blackfin PWM support" depends on BFIN_GPTIMERS diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 62a2963cfe58..94ba21e24bd6 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_PWM) += core.o obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o +obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o obj-$(CONFIG_PWM_IMX) += pwm-imx.o obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c new file mode 100644 index 000000000000..16cb53092857 --- /dev/null +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -0,0 +1,445 @@ +/* + * Copyright (C) Overkiz SAS 2012 + * + * Author: Boris BREZILLON + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NPWM 6 + +#define ATMEL_TC_ACMR_MASK (ATMEL_TC_ACPA | ATMEL_TC_ACPC | \ + ATMEL_TC_AEEVT | ATMEL_TC_ASWTRG) + +#define ATMEL_TC_BCMR_MASK (ATMEL_TC_BCPB | ATMEL_TC_BCPC | \ + ATMEL_TC_BEEVT | ATMEL_TC_BSWTRG) + +struct atmel_tcb_pwm_device { + enum pwm_polarity polarity; /* PWM polarity */ + unsigned div; /* PWM clock divider */ + unsigned duty; /* PWM duty expressed in clk cycles */ + unsigned period; /* PWM period expressed in clk cycles */ +}; + +struct atmel_tcb_pwm_chip { + struct pwm_chip chip; + spinlock_t lock; + struct atmel_tc *tc; + struct atmel_tcb_pwm_device *pwms[NPWM]; +}; + +static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct atmel_tcb_pwm_chip, chip); +} + +static int atmel_tcb_pwm_set_polarity(struct pwm_chip *chip, + struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm); + + tcbpwm->polarity = polarity; + + return 0; +} + +static int atmel_tcb_pwm_request(struct pwm_chip *chip, + struct pwm_device *pwm) +{ + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); + struct atmel_tcb_pwm_device *tcbpwm; + struct atmel_tc *tc = tcbpwmc->tc; + void __iomem *regs = tc->regs; + unsigned group = pwm->hwpwm / 2; + unsigned index = pwm->hwpwm % 2; + unsigned cmr; + int ret; + + tcbpwm = devm_kzalloc(chip->dev, sizeof(*tcbpwm), GFP_KERNEL); + if (!tcbpwm) + return -ENOMEM; + + ret = clk_enable(tc->clk[group]); + if (ret) { + devm_kfree(chip->dev, tcbpwm); + return ret; + } + + pwm_set_chip_data(pwm, tcbpwm); + tcbpwm->polarity = PWM_POLARITY_NORMAL; + tcbpwm->duty = 0; + tcbpwm->period = 0; + tcbpwm->div = 0; + + spin_lock(&tcbpwmc->lock); + cmr = __raw_readl(regs + ATMEL_TC_REG(group, CMR)); + /* + * Get init config from Timer Counter registers if + * Timer Counter is already configured as a PWM generator. + */ + if (cmr & ATMEL_TC_WAVE) { + if (index == 0) + tcbpwm->duty = + __raw_readl(regs + ATMEL_TC_REG(group, RA)); + else + tcbpwm->duty = + __raw_readl(regs + ATMEL_TC_REG(group, RB)); + + tcbpwm->div = cmr & ATMEL_TC_TCCLKS; + tcbpwm->period = __raw_readl(regs + ATMEL_TC_REG(group, RC)); + cmr &= (ATMEL_TC_TCCLKS | ATMEL_TC_ACMR_MASK | + ATMEL_TC_BCMR_MASK); + } else + cmr = 0; + + cmr |= ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO | ATMEL_TC_EEVT_XC0; + __raw_writel(cmr, regs + ATMEL_TC_REG(group, CMR)); + spin_unlock(&tcbpwmc->lock); + + tcbpwmc->pwms[pwm->hwpwm] = tcbpwm; + + return 0; +} + +static void atmel_tcb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); + struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm); + struct atmel_tc *tc = tcbpwmc->tc; + + clk_disable(tc->clk[pwm->hwpwm / 2]); + tcbpwmc->pwms[pwm->hwpwm] = NULL; + devm_kfree(chip->dev, tcbpwm); +} + +static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); + struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm); + struct atmel_tc *tc = tcbpwmc->tc; + void __iomem *regs = tc->regs; + unsigned group = pwm->hwpwm / 2; + unsigned index = pwm->hwpwm % 2; + unsigned cmr; + enum pwm_polarity polarity = tcbpwm->polarity; + + /* + * If duty is 0 the timer will be stopped and we have to + * configure the output correctly on software trigger: + * - set output to high if PWM_POLARITY_INVERSED + * - set output to low if PWM_POLARITY_NORMAL + * + * This is why we're reverting polarity in this case. + */ + if (tcbpwm->duty == 0) + polarity = !polarity; + + spin_lock(&tcbpwmc->lock); + cmr = __raw_readl(regs + ATMEL_TC_REG(group, CMR)); + + /* flush old setting and set the new one */ + if (index == 0) { + cmr &= ~ATMEL_TC_ACMR_MASK; + if (polarity == PWM_POLARITY_INVERSED) + cmr |= ATMEL_TC_ASWTRG_CLEAR; + else + cmr |= ATMEL_TC_ASWTRG_SET; + } else { + cmr &= ~ATMEL_TC_BCMR_MASK; + if (polarity == PWM_POLARITY_INVERSED) + cmr |= ATMEL_TC_BSWTRG_CLEAR; + else + cmr |= ATMEL_TC_BSWTRG_SET; + } + + __raw_writel(cmr, regs + ATMEL_TC_REG(group, CMR)); + + /* + * Use software trigger to apply the new setting. + * If both PWM devices in this group are disabled we stop the clock. + */ + if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC))) + __raw_writel(ATMEL_TC_SWTRG | ATMEL_TC_CLKDIS, + regs + ATMEL_TC_REG(group, CCR)); + else + __raw_writel(ATMEL_TC_SWTRG, regs + + ATMEL_TC_REG(group, CCR)); + + spin_unlock(&tcbpwmc->lock); +} + +static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); + struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm); + struct atmel_tc *tc = tcbpwmc->tc; + void __iomem *regs = tc->regs; + unsigned group = pwm->hwpwm / 2; + unsigned index = pwm->hwpwm % 2; + u32 cmr; + enum pwm_polarity polarity = tcbpwm->polarity; + + /* + * If duty is 0 the timer will be stopped and we have to + * configure the output correctly on software trigger: + * - set output to high if PWM_POLARITY_INVERSED + * - set output to low if PWM_POLARITY_NORMAL + * + * This is why we're reverting polarity in this case. + */ + if (tcbpwm->duty == 0) + polarity = !polarity; + + spin_lock(&tcbpwmc->lock); + cmr = __raw_readl(regs + ATMEL_TC_REG(group, CMR)); + + /* flush old setting and set the new one */ + cmr &= ~ATMEL_TC_TCCLKS; + + if (index == 0) { + cmr &= ~ATMEL_TC_ACMR_MASK; + + /* Set CMR flags according to given polarity */ + if (polarity == PWM_POLARITY_INVERSED) + cmr |= ATMEL_TC_ASWTRG_CLEAR; + else + cmr |= ATMEL_TC_ASWTRG_SET; + } else { + cmr &= ~ATMEL_TC_BCMR_MASK; + if (polarity == PWM_POLARITY_INVERSED) + cmr |= ATMEL_TC_BSWTRG_CLEAR; + else + cmr |= ATMEL_TC_BSWTRG_SET; + } + + /* + * If duty is 0 or equal to period there's no need to register + * a specific action on RA/RB and RC compare. + * The output will be configured on software trigger and keep + * this config till next config call. + */ + if (tcbpwm->duty != tcbpwm->period && tcbpwm->duty > 0) { + if (index == 0) { + if (polarity == PWM_POLARITY_INVERSED) + cmr |= ATMEL_TC_ACPA_SET | ATMEL_TC_ACPC_CLEAR; + else + cmr |= ATMEL_TC_ACPA_CLEAR | ATMEL_TC_ACPC_SET; + } else { + if (polarity == PWM_POLARITY_INVERSED) + cmr |= ATMEL_TC_BCPB_SET | ATMEL_TC_BCPC_CLEAR; + else + cmr |= ATMEL_TC_BCPB_CLEAR | ATMEL_TC_BCPC_SET; + } + } + + __raw_writel(cmr, regs + ATMEL_TC_REG(group, CMR)); + + if (index == 0) + __raw_writel(tcbpwm->duty, regs + ATMEL_TC_REG(group, RA)); + else + __raw_writel(tcbpwm->duty, regs + ATMEL_TC_REG(group, RB)); + + __raw_writel(tcbpwm->period, regs + ATMEL_TC_REG(group, RC)); + + /* Use software trigger to apply the new setting */ + __raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, + regs + ATMEL_TC_REG(group, CCR)); + spin_unlock(&tcbpwmc->lock); + return 0; +} + +static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); + struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm); + unsigned group = pwm->hwpwm / 2; + unsigned index = pwm->hwpwm % 2; + struct atmel_tcb_pwm_device *atcbpwm = NULL; + struct atmel_tc *tc = tcbpwmc->tc; + int i; + int slowclk = 0; + unsigned period; + unsigned duty; + unsigned rate = clk_get_rate(tc->clk[group]); + unsigned long long min; + unsigned long long max; + + /* + * Find best clk divisor: + * the smallest divisor which can fulfill the period_ns requirements. + */ + for (i = 0; i < 5; ++i) { + if (atmel_tc_divisors[i] == 0) { + slowclk = i; + continue; + } + min = div_u64((u64)NSEC_PER_SEC * atmel_tc_divisors[i], rate); + max = min << tc->tcb_config->counter_width; + if (max >= period_ns) + break; + } + + /* + * If none of the divisor are small enough to represent period_ns + * take slow clock (32KHz). + */ + if (i == 5) { + i = slowclk; + rate = 32768; + min = div_u64(NSEC_PER_SEC, rate); + max = min << 16; + + /* If period is too big return ERANGE error */ + if (max < period_ns) + return -ERANGE; + } + + duty = div_u64(duty_ns, min); + period = div_u64(period_ns, min); + + if (index == 0) + atcbpwm = tcbpwmc->pwms[pwm->hwpwm + 1]; + else + atcbpwm = tcbpwmc->pwms[pwm->hwpwm - 1]; + + /* + * PWM devices provided by TCB driver are grouped by 2: + * - group 0: PWM 0 & 1 + * - group 1: PWM 2 & 3 + * - group 2: PWM 4 & 5 + * + * PWM devices in a given group must be configured with the + * same period_ns. + * + * We're checking the period value of the second PWM device + * in this group before applying the new config. + */ + if ((atcbpwm && atcbpwm->duty > 0 && + atcbpwm->duty != atcbpwm->period) && + (atcbpwm->div != i || atcbpwm->period != period)) { + dev_err(chip->dev, + "failed to configure period_ns: PWM group already configured with a different value\n"); + return -EINVAL; + } + + tcbpwm->period = period; + tcbpwm->div = i; + tcbpwm->duty = duty; + + /* If the PWM is enabled, call enable to apply the new conf */ + if (test_bit(PWMF_ENABLED, &pwm->flags)) + atmel_tcb_pwm_enable(chip, pwm); + + return 0; +} + +static const struct pwm_ops atmel_tcb_pwm_ops = { + .request = atmel_tcb_pwm_request, + .free = atmel_tcb_pwm_free, + .config = atmel_tcb_pwm_config, + .set_polarity = atmel_tcb_pwm_set_polarity, + .enable = atmel_tcb_pwm_enable, + .disable = atmel_tcb_pwm_disable, +}; + +static int atmel_tcb_pwm_probe(struct platform_device *pdev) +{ + struct atmel_tcb_pwm_chip *tcbpwm; + struct device_node *np = pdev->dev.of_node; + struct atmel_tc *tc; + int err; + int tcblock; + + err = of_property_read_u32(np, "tc-block", &tcblock); + if (err < 0) { + dev_err(&pdev->dev, + "failed to get Timer Counter Block number from device tree (error: %d)\n", + err); + return err; + } + + tc = atmel_tc_alloc(tcblock, "tcb-pwm"); + if (tc == NULL) { + dev_err(&pdev->dev, "failed to allocate Timer Counter Block\n"); + return -ENOMEM; + } + + tcbpwm = devm_kzalloc(&pdev->dev, sizeof(*tcbpwm), GFP_KERNEL); + if (tcbpwm == NULL) { + atmel_tc_free(tc); + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + tcbpwm->chip.dev = &pdev->dev; + tcbpwm->chip.ops = &atmel_tcb_pwm_ops; + tcbpwm->chip.of_xlate = of_pwm_xlate_with_flags; + tcbpwm->chip.of_pwm_n_cells = 3; + tcbpwm->chip.base = -1; + tcbpwm->chip.npwm = NPWM; + tcbpwm->tc = tc; + + spin_lock_init(&tcbpwm->lock); + + err = pwmchip_add(&tcbpwm->chip); + if (err < 0) { + atmel_tc_free(tc); + return err; + } + + platform_set_drvdata(pdev, tcbpwm); + + return 0; +} + +static int atmel_tcb_pwm_remove(struct platform_device *pdev) +{ + struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev); + int err; + + err = pwmchip_remove(&tcbpwm->chip); + if (err < 0) + return err; + + atmel_tc_free(tcbpwm->tc); + + return 0; +} + +static const struct of_device_id atmel_tcb_pwm_dt_ids[] = { + { .compatible = "atmel,tcb-pwm", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids); + +static struct platform_driver atmel_tcb_pwm_driver = { + .driver = { + .name = "atmel-tcb-pwm", + .of_match_table = atmel_tcb_pwm_dt_ids, + }, + .probe = atmel_tcb_pwm_probe, + .remove = atmel_tcb_pwm_remove, +}; +module_platform_driver(atmel_tcb_pwm_driver); + +MODULE_AUTHOR("Boris BREZILLON "); +MODULE_DESCRIPTION("Atmel Timer Counter Pulse Width Modulation Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 3ccb1c1702ed4bb07006d20c8173899a69dae242 Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Thu, 3 Jan 2013 08:44:16 +1300 Subject: pwm: vt8500: Add polarity support Add support to set polarity on PWM devices, allowing for inverted duty cycles. Also update the binding document to #pwm-cells = <3> to allow passing the flags from devicetree. Signed-off-by: Tony Prisk Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/vt8500-pwm.txt | 9 ++++++--- drivers/pwm/pwm-vt8500.c | 23 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt b/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt index bcc63678a9a5..d21d82d29855 100644 --- a/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt +++ b/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt @@ -3,14 +3,17 @@ VIA/Wondermedia VT8500/WM8xxx series SoC PWM controller Required properties: - compatible: should be "via,vt8500-pwm" - reg: physical base address and length of the controller's registers -- #pwm-cells: should be 2. The first cell specifies the per-chip index - of the PWM to use and the second cell is the period in nanoseconds. +- #pwm-cells: Should be 3. Number of cells being used to specify PWM property. + First cell specifies the per-chip index of the PWM to use, the second + cell is the period in nanoseconds and bit 0 in the third cell is used to + encode the polarity of PWM output. Set bit 0 of the third in PWM specifier + to 1 for inverse polarity & set to 0 for normal polarity. - clocks: phandle to the PWM source clock Example: pwm1: pwm@d8220000 { - #pwm-cells = <2>; + #pwm-cells = <3>; compatible = "via,vt8500-pwm"; reg = <0xd8220000 0x1000>; clocks = <&clkpwm>; diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index bbc37504103a..98d79e9f0144 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -164,10 +164,31 @@ static void vt8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) clk_disable(vt8500->clk); } +static int vt8500_pwm_set_polarity(struct pwm_chip *chip, + struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + struct vt8500_chip *vt8500 = to_vt8500_chip(chip); + u32 val; + + val = readl(vt8500->base + REG_CTRL(pwm->hwpwm)); + + if (polarity == PWM_POLARITY_INVERSED) + val |= CTRL_INVERT; + else + val &= ~CTRL_INVERT; + + writel(val, vt8500->base + REG_CTRL(pwm->hwpwm)); + pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); + + return 0; +} + static struct pwm_ops vt8500_pwm_ops = { .enable = vt8500_pwm_enable, .disable = vt8500_pwm_disable, .config = vt8500_pwm_config, + .set_polarity = vt8500_pwm_set_polarity, .owner = THIS_MODULE, }; @@ -197,6 +218,8 @@ static int vt8500_pwm_probe(struct platform_device *pdev) chip->chip.dev = &pdev->dev; chip->chip.ops = &vt8500_pwm_ops; + chip->chip.of_xlate = of_pwm_xlate_with_flags; + chip->chip.of_pwm_n_cells = 3; chip->chip.base = -1; chip->chip.npwm = VT8500_NR_PWMS; -- cgit v1.2.3 From bbf1453e28e4e3ee2cf5a0c34a20469b4d465f0f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Jan 2013 00:29:11 -0800 Subject: ASoC: ak4642: add Device Tree support Support for loading the ak4642 codec module via devicetree. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/ak4642.txt | 17 +++++++++++ sound/soc/codecs/ak4642.c | 33 ++++++++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/ak4642.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/ak4642.txt b/Documentation/devicetree/bindings/sound/ak4642.txt new file mode 100644 index 000000000000..623d4e70ae11 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ak4642.txt @@ -0,0 +1,17 @@ +AK4642 I2C transmitter + +This device supports I2C mode only. + +Required properties: + + - compatible : "asahi-kasei,ak4642" or "asahi-kasei,ak4643" or "asahi-kasei,ak4648" + - reg : The chip select number on the I2C bus + +Example: + +&i2c { + ak4648: ak4648@0x12 { + compatible = "asahi-kasei,ak4642"; + reg = <0x12>; + }; +}; diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 1f0cdab03294..c78794dc4b69 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -513,12 +514,31 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4648 = { }; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static struct of_device_id ak4642_of_match[]; static int ak4642_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct device_node *np = i2c->dev.of_node; + const struct snd_soc_codec_driver *driver; + + driver = NULL; + if (np) { + const struct of_device_id *of_id; + + of_id = of_match_device(ak4642_of_match, &i2c->dev); + if (of_id) + driver = of_id->data; + } else { + driver = (struct snd_soc_codec_driver *)id->driver_data; + } + + if (!driver) { + dev_err(&i2c->dev, "no driver\n"); + return -EINVAL; + } + return snd_soc_register_codec(&i2c->dev, - (struct snd_soc_codec_driver *)id->driver_data, - &ak4642_dai, 1); + driver, &ak4642_dai, 1); } static int ak4642_i2c_remove(struct i2c_client *client) @@ -527,6 +547,14 @@ static int ak4642_i2c_remove(struct i2c_client *client) return 0; } +static struct of_device_id ak4642_of_match[] __devinitconst = { + { .compatible = "asahi-kasei,ak4642", .data = &soc_codec_dev_ak4642}, + { .compatible = "asahi-kasei,ak4643", .data = &soc_codec_dev_ak4642}, + { .compatible = "asahi-kasei,ak4648", .data = &soc_codec_dev_ak4648}, + {}, +}; +MODULE_DEVICE_TABLE(of, ak4642_of_match); + static const struct i2c_device_id ak4642_i2c_id[] = { { "ak4642", (kernel_ulong_t)&soc_codec_dev_ak4642 }, { "ak4643", (kernel_ulong_t)&soc_codec_dev_ak4642 }, @@ -539,6 +567,7 @@ static struct i2c_driver ak4642_i2c_driver = { .driver = { .name = "ak4642-codec", .owner = THIS_MODULE, + .of_match_table = ak4642_of_match, }, .probe = ak4642_i2c_probe, .remove = ak4642_i2c_remove, -- cgit v1.2.3 From 65397edfa1bba622a8159bed4c8efcc4ddb8ed16 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 11 Dec 2012 17:28:27 +0000 Subject: ARM: psci: add devicetree binding for describing PSCI firmware This patch adds a new devicetree binding for describing PSCI firmware to Linux. Acked-by: Arnd Bergmann Acked-by: Nicolas Pitre Signed-off-by: Will Deacon --- Documentation/devicetree/bindings/arm/psci.txt | 55 ++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/psci.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/psci.txt b/Documentation/devicetree/bindings/arm/psci.txt new file mode 100644 index 000000000000..433afe9cb590 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/psci.txt @@ -0,0 +1,55 @@ +* Power State Coordination Interface (PSCI) + +Firmware implementing the PSCI functions described in ARM document number +ARM DEN 0022A ("Power State Coordination Interface System Software on ARM +processors") can be used by Linux to initiate various CPU-centric power +operations. + +Issue A of the specification describes functions for CPU suspend, hotplug +and migration of secure software. + +Functions are invoked by trapping to the privilege level of the PSCI +firmware (specified as part of the binding below) and passing arguments +in a manner similar to that specified by AAPCS: + + r0 => 32-bit Function ID / return value + {r1 - r3} => Parameters + +Note that the immediate field of the trapping instruction must be set +to #0. + + +Main node required properties: + + - compatible : Must be "arm,psci" + + - method : The method of calling the PSCI firmware. Permitted + values are: + + "smc" : SMC #0, with the register assignments specified + in this binding. + + "hvc" : HVC #0, with the register assignments specified + in this binding. + +Main node optional properties: + + - cpu_suspend : Function ID for CPU_SUSPEND operation + + - cpu_off : Function ID for CPU_OFF operation + + - cpu_on : Function ID for CPU_ON operation + + - migrate : Function ID for MIGRATE operation + + +Example: + + psci { + compatible = "arm,psci"; + method = "smc"; + cpu_suspend = <0x95c10000>; + cpu_off = <0x95c10001>; + cpu_on = <0x95c10002>; + migrate = <0x95c10003>; + }; -- cgit v1.2.3 From e32af889458837d11a1ec5ec98934a1e711b049d Mon Sep 17 00:00:00 2001 From: Gabriel Fernandez Date: Mon, 17 Dec 2012 15:53:24 +0100 Subject: pinctrl/nomadik: add device tree support This implements pin multiplexing and pin configuration for the Nomadik pin controller using the device tree. Signed-off-by: Gabriel Fernandez Reviewed-by: Stephen Warren Reviewed-by: Philippe Langlais Signed-off-by: Linus Walleij --- .../devicetree/bindings/pinctrl/ste,nomadik.txt | 140 +++++++++++ .../devicetree/bindings/vendor-prefixes.txt | 1 + arch/arm/mach-ux500/cpu-db8500.c | 2 +- drivers/pinctrl/pinctrl-nomadik.c | 277 ++++++++++++++++++++- 4 files changed, 418 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/pinctrl/ste,nomadik.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pinctrl/ste,nomadik.txt b/Documentation/devicetree/bindings/pinctrl/ste,nomadik.txt new file mode 100644 index 000000000000..9a2f3f420526 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/ste,nomadik.txt @@ -0,0 +1,140 @@ +ST Ericsson Nomadik pinmux controller + +Required properties: +- compatible: "stericsson,nmk-pinctrl", "stericsson,nmk-pinctrl-db8540", + "stericsson,nmk-pinctrl-stn8815" +- reg: Should contain the register physical address and length of the PRCMU. + +Please refer to pinctrl-bindings.txt in this directory for details of the +common pinctrl bindings used by client devices, including the meaning of the +phrase "pin configuration node". + +ST Ericsson's pin configuration nodes act as a container for an arbitrary number of +subnodes. Each of these subnodes represents some desired configuration for a +pin, a group, or a list of pins or groups. This configuration can include the +mux function to select on those pin(s)/group(s), and various pin configuration +parameters, such as input, output, pull up, pull down... + +The name of each subnode is not important; all subnodes should be enumerated +and processed purely based on their content. + +Required subnode-properties: +- ste,pins : An array of strings. Each string contains the name of a pin or + group. + +Optional subnode-properties: +- ste,function: A string containing the name of the function to mux to the + pin or group. + +- ste,config: Handle of pin configuration node (e.g. ste,config = <&slpm_in_wkup_pdis>) + +- ste,input : <0/1/2> + 0: input with no pull + 1: input with pull up, + 2: input with pull down, + +- ste,output: <0/1/2> + 0: output low, + 1: output high, + 2: output (value is not specified). + +- ste,sleep: <0/1> + 0: sleep mode disable, + 1: sleep mode enable. + +- ste,sleep-input: <0/1/2/3> + 0: sleep input with no pull, + 1: sleep input with pull up, + 2: sleep input with pull down. + 3: sleep input and keep last input configuration (no pull, pull up or pull down). + +- ste,sleep-output: <0/1/2> + 0: sleep output low, + 1: sleep output high, + 2: sleep output (value is not specified). + +- ste,sleep-gpio: <0/1> + 0: disable sleep gpio mode, + 1: enable sleep gpio mode. + +- ste,sleep-wakeup: <0/1> + 0: wake-up detection enabled, + 1: wake-up detection disabled. + +- ste,sleep-pull-disable: <0/1> + 0: GPIO pull-up or pull-down resistor is enabled, when pin is an input, + 1: GPIO pull-up and pull-down resistor are disabled. + +Example board file extract: + + pinctrl@80157000 { + compatible = "stericsson,nmk-pinctrl"; + reg = <0x80157000 0x2000>; + + pinctrl-names = "default"; + + slpm_in_wkup_pdis: slpm_in_wkup_pdis { + ste,sleep = <1>; + ste,sleep-input = <3>; + ste,sleep-wakeup = <1>; + ste,sleep-pull-disable = <0>; + }; + + slpm_out_hi_wkup_pdis: slpm_out_hi_wkup_pdis { + ste,sleep = <1>; + ste,sleep-output = <1>; + ste,sleep-wakeup = <1>; + ste,sleep-pull-disable = <0>; + }; + + slpm_out_wkup_pdis: slpm_out_wkup_pdis { + ste,sleep = <1>; + ste,sleep-output = <2>; + ste,sleep-wakeup = <1>; + ste,sleep-pull-disable = <0>; + }; + + uart0 { + uart0_default_mux: uart0_mux { + u0_default_mux { + ste,function = "u0"; + ste,pins = "u0_a_1"; + }; + }; + uart0_default_mode: uart0_default { + uart0_default_cfg1 { + ste,pins = "GPIO0", "GPIO2"; + ste,input = <1>; + }; + + uart0_default_cfg2 { + ste,pins = "GPIO1", "GPIO3"; + ste,output = <1>; + }; + }; + uart0_sleep_mode: uart0_sleep { + uart0_sleep_cfg1 { + ste,pins = "GPIO0", "GPIO2"; + ste,config = <&slpm_in_wkup_pdis>; + }; + uart0_sleep_cfg2 { + ste,pins = "GPIO1"; + ste,config = <&slpm_out_hi_wkup_pdis>; + }; + uart0_sleep_cfg3 { + ste,pins = "GPIO3"; + ste,config = <&slpm_out_wkup_pdis>; + }; + }; + }; + }; + + uart@80120000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x80120000 0x1000>; + interrupts = <0 11 0x4>; + + pinctrl-names = "default","sleep"; + pinctrl-0 = <&uart0_default_mux>, <&uart0_default_mode>; + pinctrl-1 = <&uart0_sleep_mode>; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 902b1b1f568e..15321373ec8d 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -50,6 +50,7 @@ simtek sirf SiRF Technology, Inc. snps Synopsys, Inc. st STMicroelectronics +ste ST-Ericsson stericsson ST-Ericsson ti Texas Instruments via VIA Technologies, Inc. diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c index 5b286e06474c..b80ad9610e97 100644 --- a/arch/arm/mach-ux500/cpu-db8500.c +++ b/arch/arm/mach-ux500/cpu-db8500.c @@ -285,7 +285,7 @@ static struct of_dev_auxdata u8500_auxdata_lookup[] __initdata = { OF_DEV_AUXDATA("st,nomadik-i2c", 0x80110000, "nmk-i2c.3", NULL), OF_DEV_AUXDATA("st,nomadik-i2c", 0x8012a000, "nmk-i2c.4", NULL), /* Requires device name bindings. */ - OF_DEV_AUXDATA("stericsson,nmk_pinctrl", U8500_PRCMU_BASE, + OF_DEV_AUXDATA("stericsson,nmk-pinctrl", U8500_PRCMU_BASE, "pinctrl-db8500", NULL), /* Requires clock name and DMA bindings. */ OF_DEV_AUXDATA("stericsson,ux500-msp-i2s", 0x80123000, diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c index f404e644a16e..ec3a5742e413 100644 --- a/drivers/pinctrl/pinctrl-nomadik.c +++ b/drivers/pinctrl/pinctrl-nomadik.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -1490,11 +1491,285 @@ static void nmk_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, nmk_gpio_dbg_show_one(s, pctldev, chip, offset - chip->base, offset); } +static void nmk_pinctrl_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps) +{ + int i; + + for (i = 0; i < num_maps; i++) + if (map[i].type == PIN_MAP_TYPE_CONFIGS_PIN) + kfree(map[i].data.configs.configs); + kfree(map); +} + +static int nmk_dt_reserve_map(struct pinctrl_map **map, unsigned *reserved_maps, + unsigned *num_maps, unsigned reserve) +{ + unsigned old_num = *reserved_maps; + unsigned new_num = *num_maps + reserve; + struct pinctrl_map *new_map; + + if (old_num >= new_num) + return 0; + + new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL); + if (!new_map) + return -ENOMEM; + + memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map)); + + *map = new_map; + *reserved_maps = new_num; + + return 0; +} + +static int nmk_dt_add_map_mux(struct pinctrl_map **map, unsigned *reserved_maps, + unsigned *num_maps, const char *group, + const char *function) +{ + if (*num_maps == *reserved_maps) + return -ENOSPC; + + (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP; + (*map)[*num_maps].data.mux.group = group; + (*map)[*num_maps].data.mux.function = function; + (*num_maps)++; + + return 0; +} + +static int nmk_dt_add_map_configs(struct pinctrl_map **map, + unsigned *reserved_maps, + unsigned *num_maps, const char *group, + unsigned long *configs, unsigned num_configs) +{ + unsigned long *dup_configs; + + if (*num_maps == *reserved_maps) + return -ENOSPC; + + dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs), + GFP_KERNEL); + if (!dup_configs) + return -ENOMEM; + + (*map)[*num_maps].type = PIN_MAP_TYPE_CONFIGS_PIN; + + (*map)[*num_maps].data.configs.group_or_pin = group; + (*map)[*num_maps].data.configs.configs = dup_configs; + (*map)[*num_maps].data.configs.num_configs = num_configs; + (*num_maps)++; + + return 0; +} + +#define NMK_CONFIG_PIN(x,y) { .property = x, .config = y, } +#define NMK_CONFIG_PIN_ARRAY(x,y) { .property = x, .choice = y, \ + .size = ARRAY_SIZE(y), } + +static const unsigned long nmk_pin_input_modes[] = { + PIN_INPUT_NOPULL, + PIN_INPUT_PULLUP, + PIN_INPUT_PULLDOWN, +}; + +static const unsigned long nmk_pin_output_modes[] = { + PIN_OUTPUT_LOW, + PIN_OUTPUT_HIGH, + PIN_DIR_OUTPUT, +}; + +static const unsigned long nmk_pin_sleep_modes[] = { + PIN_SLEEPMODE_DISABLED, + PIN_SLEEPMODE_ENABLED, +}; + +static const unsigned long nmk_pin_sleep_input_modes[] = { + PIN_SLPM_INPUT_NOPULL, + PIN_SLPM_INPUT_PULLUP, + PIN_SLPM_INPUT_PULLDOWN, + PIN_SLPM_DIR_INPUT, +}; + +static const unsigned long nmk_pin_sleep_output_modes[] = { + PIN_SLPM_OUTPUT_LOW, + PIN_SLPM_OUTPUT_HIGH, + PIN_SLPM_DIR_OUTPUT, +}; + +static const unsigned long nmk_pin_sleep_wakeup_modes[] = { + PIN_SLPM_WAKEUP_DISABLE, + PIN_SLPM_WAKEUP_ENABLE, +}; + +static const unsigned long nmk_pin_gpio_modes[] = { + PIN_GPIOMODE_DISABLED, + PIN_GPIOMODE_ENABLED, +}; + +static const unsigned long nmk_pin_sleep_pdis_modes[] = { + PIN_SLPM_PDIS_DISABLED, + PIN_SLPM_PDIS_ENABLED, +}; + +struct nmk_cfg_param { + const char *property; + unsigned long config; + const unsigned long *choice; + int size; +}; + +static const struct nmk_cfg_param nmk_cfg_params[] = { + NMK_CONFIG_PIN_ARRAY("ste,input", nmk_pin_input_modes), + NMK_CONFIG_PIN_ARRAY("ste,output", nmk_pin_output_modes), + NMK_CONFIG_PIN_ARRAY("ste,sleep", nmk_pin_sleep_modes), + NMK_CONFIG_PIN_ARRAY("ste,sleep-input", nmk_pin_sleep_input_modes), + NMK_CONFIG_PIN_ARRAY("ste,sleep-output", nmk_pin_sleep_output_modes), + NMK_CONFIG_PIN_ARRAY("ste,sleep-wakeup", nmk_pin_sleep_wakeup_modes), + NMK_CONFIG_PIN_ARRAY("ste,gpio", nmk_pin_gpio_modes), + NMK_CONFIG_PIN_ARRAY("ste,sleep-pull-disable", nmk_pin_sleep_pdis_modes), +}; + +static int nmk_dt_pin_config(int index, int val, unsigned long *config) +{ + int ret = 0; + + if (nmk_cfg_params[index].choice == NULL) + *config = nmk_cfg_params[index].config; + else { + /* test if out of range */ + if (val < nmk_cfg_params[index].size) { + *config = nmk_cfg_params[index].config | + nmk_cfg_params[index].choice[val]; + } + } + return ret; +} + +static const char *nmk_find_pin_name(struct pinctrl_dev *pctldev, const char *pin_name) +{ + int i, pin_number; + struct nmk_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev); + + if (sscanf((char *)pin_name, "GPIO%d", &pin_number) == 1) + for (i = 0; i < npct->soc->npins; i++) + if (npct->soc->pins[i].number == pin_number) + return npct->soc->pins[i].name; + return NULL; +} + +static bool nmk_pinctrl_dt_get_config(struct device_node *np, + unsigned long *configs) +{ + bool has_config = 0; + unsigned long cfg = 0; + int i, val, ret; + + for (i = 0; i < ARRAY_SIZE(nmk_cfg_params); i++) { + ret = of_property_read_u32(np, + nmk_cfg_params[i].property, &val); + if (ret != -EINVAL) { + if (nmk_dt_pin_config(i, val, &cfg) == 0) { + *configs |= cfg; + has_config = 1; + } + } + } + + return has_config; +} + +int nmk_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, + unsigned *reserved_maps, + unsigned *num_maps) +{ + int ret; + const char *function = NULL; + unsigned long configs = 0; + bool has_config = 0; + unsigned reserve = 0; + struct property *prop; + const char *group, *gpio_name; + struct device_node *np_config; + + ret = of_property_read_string(np, "ste,function", &function); + if (ret >= 0) + reserve = 1; + + has_config = nmk_pinctrl_dt_get_config(np, &configs); + + np_config = of_parse_phandle(np, "ste,config", 0); + if (np_config) + has_config |= nmk_pinctrl_dt_get_config(np_config, &configs); + + ret = of_property_count_strings(np, "ste,pins"); + if (ret < 0) + goto exit; + + if (has_config) + reserve++; + + reserve *= ret; + + ret = nmk_dt_reserve_map(map, reserved_maps, num_maps, reserve); + if (ret < 0) + goto exit; + + of_property_for_each_string(np, "ste,pins", prop, group) { + if (function) { + ret = nmk_dt_add_map_mux(map, reserved_maps, num_maps, + group, function); + if (ret < 0) + goto exit; + } + if (has_config) { + gpio_name = nmk_find_pin_name(pctldev, group); + + ret = nmk_dt_add_map_configs(map, reserved_maps, num_maps, + gpio_name, &configs, 1); + if (ret < 0) + goto exit; + } + + } +exit: + return ret; +} + +int nmk_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np_config, + struct pinctrl_map **map, unsigned *num_maps) +{ + unsigned reserved_maps; + struct device_node *np; + int ret; + + reserved_maps = 0; + *map = NULL; + *num_maps = 0; + + for_each_child_of_node(np_config, np) { + ret = nmk_pinctrl_dt_subnode_to_map(pctldev, np, map, + &reserved_maps, num_maps); + if (ret < 0) { + nmk_pinctrl_dt_free_map(pctldev, *map, *num_maps); + return ret; + } + } + + return 0; +} + static struct pinctrl_ops nmk_pinctrl_ops = { .get_groups_count = nmk_get_groups_cnt, .get_group_name = nmk_get_group_name, .get_group_pins = nmk_get_group_pins, .pin_dbg_show = nmk_pin_dbg_show, + .dt_node_to_map = nmk_pinctrl_dt_node_to_map, + .dt_free_map = nmk_pinctrl_dt_free_map, }; static int nmk_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev) @@ -1828,7 +2103,7 @@ static struct pinctrl_desc nmk_pinctrl_desc = { static const struct of_device_id nmk_pinctrl_match[] = { { - .compatible = "stericsson,nmk_pinctrl", + .compatible = "stericsson,nmk-pinctrl", .data = (void *)PINCTRL_NMK_DB8500, }, {}, -- cgit v1.2.3 From 8d31bfa551b9a1a61119e6d8fde08d0fa8cdfafb Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Sat, 12 Jan 2013 15:51:24 +1300 Subject: arm: vt8500: Add support for Wondermedia WM8750/WM8850 This patch adds support for the WM8750 (ARMv6) and WM8850 (ARMv7). Devicetree documentation is updated for new SoCs. Signed-off-by: Tony Prisk --- Documentation/devicetree/bindings/arm/vt8500.txt | 8 ++++++++ arch/arm/mach-vt8500/Kconfig | 16 ++++++++++++++++ arch/arm/mach-vt8500/vt8500.c | 2 ++ 3 files changed, 26 insertions(+) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/vt8500.txt b/Documentation/devicetree/bindings/arm/vt8500.txt index d657832c6819..87dc1ddf4770 100644 --- a/Documentation/devicetree/bindings/arm/vt8500.txt +++ b/Documentation/devicetree/bindings/arm/vt8500.txt @@ -12,3 +12,11 @@ compatible = "wm,wm8505"; Boards with the Wondermedia WM8650 SoC shall have the following properties: Required root node property: compatible = "wm,wm8650"; + +Boards with the Wondermedia WM8750 SoC shall have the following properties: +Required root node property: +compatible = "wm,wm8750"; + +Boards with the Wondermedia WM8850 SoC shall have the following properties: +Required root node property: +compatible = "wm,wm8850"; diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig index cdcc4329b4a8..747aa14d0475 100644 --- a/arch/arm/mach-vt8500/Kconfig +++ b/arch/arm/mach-vt8500/Kconfig @@ -15,3 +15,19 @@ config ARCH_WM8505 select ARCH_VT8500 select CPU_ARM926T help + +config ARCH_WM8750 + bool "WonderMedia WM8750" + depends on ARCH_MULTI_V6 + select ARCH_VT8500 + select CPU_V6 + help + Support for WonderMedia WM8750 System-on-Chip. + +config ARCH_WM8850 + bool "WonderMedia WM8850" + depends on ARCH_MULTI_V7 + select ARCH_VT8500 + select CPU_V7 + help + Support for WonderMedia WM8850 System-on-Chip. diff --git a/arch/arm/mach-vt8500/vt8500.c b/arch/arm/mach-vt8500/vt8500.c index 3c66d48ea082..55162ab1a943 100644 --- a/arch/arm/mach-vt8500/vt8500.c +++ b/arch/arm/mach-vt8500/vt8500.c @@ -183,6 +183,8 @@ static const char * const vt8500_dt_compat[] = { "via,vt8500", "wm,wm8650", "wm,wm8505", + "wm,wm8750", + "wm,wm8850", }; DT_MACHINE_START(WMT_DT, "VIA/Wondermedia SoC (Device Tree Support)") -- cgit v1.2.3 From 609dad9bdf970da0952cea29a4442318cd4a090e Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Sat, 5 Jan 2013 02:18:43 +0100 Subject: ASoC: tegra: add ac97 host driver This adds the driver for the Tegra 2x AC97 host controller. Signed-off-by: Lucas Stach Reviewed-by: Stephen Warren Signed-off-by: Mark Brown --- .../bindings/sound/nvidia,tegra20-ac97.txt | 22 + sound/soc/tegra/Kconfig | 10 + sound/soc/tegra/Makefile | 2 + sound/soc/tegra/tegra20_ac97.c | 480 +++++++++++++++++++++ sound/soc/tegra/tegra20_ac97.h | 95 ++++ 5 files changed, 609 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt create mode 100644 sound/soc/tegra/tegra20_ac97.c create mode 100644 sound/soc/tegra/tegra20_ac97.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt new file mode 100644 index 000000000000..c1454979c1ef --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt @@ -0,0 +1,22 @@ +NVIDIA Tegra 20 AC97 controller + +Required properties: +- compatible : "nvidia,tegra20-ac97" +- reg : Should contain AC97 controller registers location and length +- interrupts : Should contain AC97 interrupt +- nvidia,dma-request-selector : The Tegra DMA controller's phandle and + request selector for the AC97 controller +- nvidia,codec-reset-gpio : The Tegra GPIO controller's phandle and the number + of the GPIO used to reset the external AC97 codec +- nvidia,codec-sync-gpio : The Tegra GPIO controller's phandle and the number + of the GPIO corresponding with the AC97 DAP _FS line +Example: + +ac97@70002000 { + compatible = "nvidia,tegra20-ac97"; + reg = <0x70002000 0x200>; + interrupts = <0 81 0x04>; + nvidia,dma-request-selector = <&apbdma 12>; + nvidia,codec-reset-gpio = <&gpio 170 0>; + nvidia,codec-sync-gpio = <&gpio 120 0>; +}; diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 19e5fe7cc403..4b3a2b8cb788 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -6,6 +6,16 @@ config SND_SOC_TEGRA help Say Y or M here if you want support for SoC audio on Tegra. +config SND_SOC_TEGRA20_AC97 + tristate + depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC + select SND_SOC_AC97_BUS + select SND_SOC_TEGRA20_DAS + help + Say Y or M if you want to add support for codecs attached to the + Tegra20 AC97 interface. You will also need to select the individual + machine drivers to support below. + config SND_SOC_TEGRA20_DAS tristate depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 391e78a34c06..02513d9edf22 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -1,6 +1,7 @@ # Tegra platform Support snd-soc-tegra-pcm-objs := tegra_pcm.o snd-soc-tegra-utils-objs += tegra_asoc_utils.o +snd-soc-tegra20-ac97-objs := tegra20_ac97.o snd-soc-tegra20-das-objs := tegra20_das.o snd-soc-tegra20-i2s-objs := tegra20_i2s.o snd-soc-tegra20-spdif-objs := tegra20_spdif.o @@ -9,6 +10,7 @@ snd-soc-tegra30-i2s-objs := tegra30_i2s.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o +obj-$(CONFIG_SND_SOC_TEGRA20_AC97) += snd-soc-tegra20-ac97.o obj-$(CONFIG_SND_SOC_TEGRA20_DAS) += snd-soc-tegra20-das.o obj-$(CONFIG_SND_SOC_TEGRA20_I2S) += snd-soc-tegra20-i2s.o obj-$(CONFIG_SND_SOC_TEGRA20_SPDIF) += snd-soc-tegra20-spdif.o diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c new file mode 100644 index 000000000000..1bae73bf1c8a --- /dev/null +++ b/sound/soc/tegra/tegra20_ac97.c @@ -0,0 +1,480 @@ +/* + * tegra20_ac97.c - Tegra20 AC97 platform driver + * + * Copyright (c) 2012 Lucas Stach + * + * Partly based on code copyright/by: + * + * Copyright (c) 2011,2012 Toradex Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tegra_asoc_utils.h" +#include "tegra20_ac97.h" + +#define DRV_NAME "tegra20-ac97" + +static struct tegra20_ac97 *workdata; + +static void tegra20_ac97_codec_reset(struct snd_ac97 *ac97) +{ + u32 readback; + unsigned long timeout; + + /* reset line is not driven by DAC pad group, have to toggle GPIO */ + gpio_set_value(workdata->reset_gpio, 0); + udelay(2); + + gpio_set_value(workdata->reset_gpio, 1); + udelay(2); + + timeout = jiffies + msecs_to_jiffies(100); + + do { + regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback); + if (readback & TEGRA20_AC97_STATUS1_CODEC1_RDY) + break; + usleep_range(1000, 2000); + } while (!time_after(jiffies, timeout)); +} + +static void tegra20_ac97_codec_warm_reset(struct snd_ac97 *ac97) +{ + u32 readback; + unsigned long timeout; + + /* + * although sync line is driven by the DAC pad group warm reset using + * the controller cmd is not working, have to toggle sync line + * manually. + */ + gpio_request(workdata->sync_gpio, "codec-sync"); + + gpio_direction_output(workdata->sync_gpio, 1); + + udelay(2); + gpio_set_value(workdata->sync_gpio, 0); + udelay(2); + gpio_free(workdata->sync_gpio); + + timeout = jiffies + msecs_to_jiffies(100); + + do { + regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback); + if (readback & TEGRA20_AC97_STATUS1_CODEC1_RDY) + break; + usleep_range(1000, 2000); + } while (!time_after(jiffies, timeout)); +} + +static unsigned short tegra20_ac97_codec_read(struct snd_ac97 *ac97_snd, + unsigned short reg) +{ + u32 readback; + unsigned long timeout; + + regmap_write(workdata->regmap, TEGRA20_AC97_CMD, + (((reg | 0x80) << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) & + TEGRA20_AC97_CMD_CMD_ADDR_MASK) | + TEGRA20_AC97_CMD_BUSY); + + timeout = jiffies + msecs_to_jiffies(100); + + do { + regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback); + if (readback & TEGRA20_AC97_STATUS1_STA_VALID1) + break; + usleep_range(1000, 2000); + } while (!time_after(jiffies, timeout)); + + return ((readback & TEGRA20_AC97_STATUS1_STA_DATA1_MASK) >> + TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT); +} + +static void tegra20_ac97_codec_write(struct snd_ac97 *ac97_snd, + unsigned short reg, unsigned short val) +{ + u32 readback; + unsigned long timeout; + + regmap_write(workdata->regmap, TEGRA20_AC97_CMD, + ((reg << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) & + TEGRA20_AC97_CMD_CMD_ADDR_MASK) | + ((val << TEGRA20_AC97_CMD_CMD_DATA_SHIFT) & + TEGRA20_AC97_CMD_CMD_DATA_MASK) | + TEGRA20_AC97_CMD_BUSY); + + timeout = jiffies + msecs_to_jiffies(100); + + do { + regmap_read(workdata->regmap, TEGRA20_AC97_CMD, &readback); + if (!(readback & TEGRA20_AC97_CMD_BUSY)) + break; + usleep_range(1000, 2000); + } while (!time_after(jiffies, timeout)); +} + +struct snd_ac97_bus_ops soc_ac97_ops = { + .read = tegra20_ac97_codec_read, + .write = tegra20_ac97_codec_write, + .reset = tegra20_ac97_codec_reset, + .warm_reset = tegra20_ac97_codec_warm_reset, +}; +EXPORT_SYMBOL_GPL(soc_ac97_ops); + +static inline void tegra20_ac97_start_playback(struct tegra20_ac97 *ac97) +{ + regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR, + TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN, + TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN); + + regmap_update_bits(ac97->regmap, TEGRA20_AC97_CTRL, + TEGRA20_AC97_CTRL_PCM_DAC_EN | + TEGRA20_AC97_CTRL_STM_EN, + TEGRA20_AC97_CTRL_PCM_DAC_EN | + TEGRA20_AC97_CTRL_STM_EN); +} + +static inline void tegra20_ac97_stop_playback(struct tegra20_ac97 *ac97) +{ + regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR, + TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN, 0); + + regmap_update_bits(ac97->regmap, TEGRA20_AC97_CTRL, + TEGRA20_AC97_CTRL_PCM_DAC_EN, 0); +} + +static inline void tegra20_ac97_start_capture(struct tegra20_ac97 *ac97) +{ + regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR, + TEGRA20_AC97_FIFO_SCR_REC_FULL_EN, + TEGRA20_AC97_FIFO_SCR_REC_FULL_EN); +} + +static inline void tegra20_ac97_stop_capture(struct tegra20_ac97 *ac97) +{ + regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR, + TEGRA20_AC97_FIFO_SCR_REC_FULL_EN, 0); +} + +static int tegra20_ac97_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + tegra20_ac97_start_playback(ac97); + else + tegra20_ac97_start_capture(ac97); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + tegra20_ac97_stop_playback(ac97); + else + tegra20_ac97_stop_capture(ac97); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops tegra20_ac97_dai_ops = { + .trigger = tegra20_ac97_trigger, +}; + +static int tegra20_ac97_probe(struct snd_soc_dai *dai) +{ + struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai); + + dai->capture_dma_data = &ac97->capture_dma_data; + dai->playback_dma_data = &ac97->playback_dma_data; + + return 0; +} + +static struct snd_soc_dai_driver tegra20_ac97_dai = { + .name = "tegra-ac97-pcm", + .ac97_control = 1, + .probe = tegra20_ac97_probe, + .playback = { + .stream_name = "PCM Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "PCM Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tegra20_ac97_dai_ops, +}; + +static bool tegra20_ac97_wr_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA20_AC97_CTRL: + case TEGRA20_AC97_CMD: + case TEGRA20_AC97_STATUS1: + case TEGRA20_AC97_FIFO1_SCR: + case TEGRA20_AC97_FIFO_TX1: + case TEGRA20_AC97_FIFO_RX1: + return true; + default: + break; + } + + return false; +} + +static bool tegra20_ac97_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA20_AC97_STATUS1: + case TEGRA20_AC97_FIFO1_SCR: + case TEGRA20_AC97_FIFO_TX1: + case TEGRA20_AC97_FIFO_RX1: + return true; + default: + break; + } + + return false; +} + +static bool tegra20_ac97_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA20_AC97_FIFO_TX1: + case TEGRA20_AC97_FIFO_RX1: + return true; + default: + break; + } + + return false; +} + +static const struct regmap_config tegra20_ac97_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA20_AC97_FIFO_RX1, + .writeable_reg = tegra20_ac97_wr_rd_reg, + .readable_reg = tegra20_ac97_wr_rd_reg, + .volatile_reg = tegra20_ac97_volatile_reg, + .precious_reg = tegra20_ac97_precious_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static int tegra20_ac97_platform_probe(struct platform_device *pdev) +{ + struct tegra20_ac97 *ac97; + struct resource *mem, *memregion; + u32 of_dma[2]; + void __iomem *regs; + int ret = 0; + + ac97 = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_ac97), + GFP_KERNEL); + if (!ac97) { + dev_err(&pdev->dev, "Can't allocate tegra20_ac97\n"); + ret = -ENOMEM; + goto err; + } + dev_set_drvdata(&pdev->dev, ac97); + + ac97->clk_ac97 = clk_get(&pdev->dev, NULL); + if (IS_ERR(ac97->clk_ac97)) { + dev_err(&pdev->dev, "Can't retrieve ac97 clock\n"); + ret = PTR_ERR(ac97->clk_ac97); + goto err; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No memory resource\n"); + ret = -ENODEV; + goto err_clk_put; + } + + memregion = devm_request_mem_region(&pdev->dev, mem->start, + resource_size(mem), DRV_NAME); + if (!memregion) { + dev_err(&pdev->dev, "Memory region already claimed\n"); + ret = -EBUSY; + goto err_clk_put; + } + + regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); + if (!regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_clk_put; + } + + ac97->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &tegra20_ac97_regmap_config); + if (IS_ERR(ac97->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + ret = PTR_ERR(ac97->regmap); + goto err_clk_put; + } + + if (of_property_read_u32_array(pdev->dev.of_node, + "nvidia,dma-request-selector", + of_dma, 2) < 0) { + dev_err(&pdev->dev, "No DMA resource\n"); + ret = -ENODEV; + goto err_clk_put; + } + + ac97->reset_gpio = of_get_named_gpio(pdev->dev.of_node, + "nvidia,codec-reset-gpio", 0); + if (gpio_is_valid(ac97->reset_gpio)) { + ret = devm_gpio_request_one(&pdev->dev, ac97->reset_gpio, + GPIOF_OUT_INIT_HIGH, "codec-reset"); + if (ret) { + dev_err(&pdev->dev, "could not get codec-reset GPIO\n"); + goto err_clk_put; + } + } else { + dev_err(&pdev->dev, "no codec-reset GPIO supplied\n"); + goto err_clk_put; + } + + ac97->sync_gpio = of_get_named_gpio(pdev->dev.of_node, + "nvidia,codec-sync-gpio", 0); + if (!gpio_is_valid(ac97->sync_gpio)) { + dev_err(&pdev->dev, "no codec-sync GPIO supplied\n"); + goto err_clk_put; + } + + ac97->capture_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_RX1; + ac97->capture_dma_data.wrap = 4; + ac97->capture_dma_data.width = 32; + ac97->capture_dma_data.req_sel = of_dma[1]; + + ac97->playback_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_TX1; + ac97->playback_dma_data.wrap = 4; + ac97->playback_dma_data.width = 32; + ac97->playback_dma_data.req_sel = of_dma[1]; + + ret = snd_soc_register_dais(&pdev->dev, &tegra20_ac97_dai, 1); + if (ret) { + dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); + ret = -ENOMEM; + goto err_clk_put; + } + + ret = tegra_pcm_platform_register(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); + goto err_unregister_dai; + } + + ret = tegra_asoc_utils_init(&ac97->util_data, &pdev->dev); + if (ret) + goto err_unregister_pcm; + + ret = tegra_asoc_utils_set_ac97_rate(&ac97->util_data); + if (ret) + goto err_asoc_utils_fini; + + ret = clk_prepare_enable(ac97->clk_ac97); + if (ret) { + dev_err(&pdev->dev, "clk_enable failed: %d\n", ret); + goto err_asoc_utils_fini; + } + + /* XXX: crufty ASoC AC97 API - only one AC97 codec allowed */ + workdata = ac97; + + return 0; + +err_asoc_utils_fini: + tegra_asoc_utils_fini(&ac97->util_data); +err_unregister_pcm: + tegra_pcm_platform_unregister(&pdev->dev); +err_unregister_dai: + snd_soc_unregister_dai(&pdev->dev); +err_clk_put: + clk_put(ac97->clk_ac97); +err: + return ret; +} + +static int tegra20_ac97_platform_remove(struct platform_device *pdev) +{ + struct tegra20_ac97 *ac97 = dev_get_drvdata(&pdev->dev); + + tegra_pcm_platform_unregister(&pdev->dev); + snd_soc_unregister_dai(&pdev->dev); + + tegra_asoc_utils_fini(&ac97->util_data); + + clk_disable_unprepare(ac97->clk_ac97); + clk_put(ac97->clk_ac97); + + return 0; +} + +static const struct of_device_id tegra20_ac97_of_match[] __devinitconst = { + { .compatible = "nvidia,tegra20-ac97", }, + {}, +}; + +static struct platform_driver tegra20_ac97_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = tegra20_ac97_of_match, + }, + .probe = tegra20_ac97_platform_probe, + .remove = tegra20_ac97_platform_remove, +}; +module_platform_driver(tegra20_ac97_driver); + +MODULE_AUTHOR("Lucas Stach"); +MODULE_DESCRIPTION("Tegra20 AC97 ASoC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, tegra20_ac97_of_match); diff --git a/sound/soc/tegra/tegra20_ac97.h b/sound/soc/tegra/tegra20_ac97.h new file mode 100644 index 000000000000..dddc6828004e --- /dev/null +++ b/sound/soc/tegra/tegra20_ac97.h @@ -0,0 +1,95 @@ +/* + * tegra20_ac97.h - Definitions for the Tegra20 AC97 controller driver + * + * Copyright (c) 2012 Lucas Stach + * + * Partly based on code copyright/by: + * + * Copyright (c) 2011,2012 Toradex Inc. + * + * 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. + * + */ + +#ifndef __TEGRA20_AC97_H__ +#define __TEGRA20_AC97_H__ + +#include "tegra_pcm.h" + +#define TEGRA20_AC97_CTRL 0x00 +#define TEGRA20_AC97_CMD 0x04 +#define TEGRA20_AC97_STATUS1 0x08 +/* ... */ +#define TEGRA20_AC97_FIFO1_SCR 0x1c +/* ... */ +#define TEGRA20_AC97_FIFO_TX1 0x40 +#define TEGRA20_AC97_FIFO_RX1 0x80 + +/* TEGRA20_AC97_CTRL */ +#define TEGRA20_AC97_CTRL_STM2_EN (1 << 16) +#define TEGRA20_AC97_CTRL_DOUBLE_SAMPLING_EN (1 << 11) +#define TEGRA20_AC97_CTRL_IO_CNTRL_EN (1 << 10) +#define TEGRA20_AC97_CTRL_HSET_DAC_EN (1 << 9) +#define TEGRA20_AC97_CTRL_LINE2_DAC_EN (1 << 8) +#define TEGRA20_AC97_CTRL_PCM_LFE_EN (1 << 7) +#define TEGRA20_AC97_CTRL_PCM_SUR_EN (1 << 6) +#define TEGRA20_AC97_CTRL_PCM_CEN_DAC_EN (1 << 5) +#define TEGRA20_AC97_CTRL_LINE1_DAC_EN (1 << 4) +#define TEGRA20_AC97_CTRL_PCM_DAC_EN (1 << 3) +#define TEGRA20_AC97_CTRL_COLD_RESET (1 << 2) +#define TEGRA20_AC97_CTRL_WARM_RESET (1 << 1) +#define TEGRA20_AC97_CTRL_STM_EN (1 << 0) + +/* TEGRA20_AC97_CMD */ +#define TEGRA20_AC97_CMD_CMD_ADDR_SHIFT 24 +#define TEGRA20_AC97_CMD_CMD_ADDR_MASK (0xff << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) +#define TEGRA20_AC97_CMD_CMD_DATA_SHIFT 8 +#define TEGRA20_AC97_CMD_CMD_DATA_MASK (0xffff << TEGRA20_AC97_CMD_CMD_DATA_SHIFT) +#define TEGRA20_AC97_CMD_CMD_ID_SHIFT 2 +#define TEGRA20_AC97_CMD_CMD_ID_MASK (0x3 << TEGRA20_AC97_CMD_CMD_ID_SHIFT) +#define TEGRA20_AC97_CMD_BUSY (1 << 0) + +/* TEGRA20_AC97_STATUS1 */ +#define TEGRA20_AC97_STATUS1_STA_ADDR1_SHIFT 24 +#define TEGRA20_AC97_STATUS1_STA_ADDR1_MASK (0xff << TEGRA20_AC97_STATUS1_STA_ADDR1_SHIFT) +#define TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT 8 +#define TEGRA20_AC97_STATUS1_STA_DATA1_MASK (0xffff << TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT) +#define TEGRA20_AC97_STATUS1_STA_VALID1 (1 << 2) +#define TEGRA20_AC97_STATUS1_STANDBY1 (1 << 1) +#define TEGRA20_AC97_STATUS1_CODEC1_RDY (1 << 0) + +/* TEGRA20_AC97_FIFO1_SCR */ +#define TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_SHIFT 27 +#define TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_MASK (0x1f << TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_SHIFT) +#define TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_SHIFT 22 +#define TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_MASK (0x1f << TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_SHIFT) +#define TEGRA20_AC97_FIFO_SCR_REC_OVERRUN_INT_STA (1 << 19) +#define TEGRA20_AC97_FIFO_SCR_PB_UNDERRUN_INT_STA (1 << 18) +#define TEGRA20_AC97_FIFO_SCR_REC_FORCE_MT (1 << 17) +#define TEGRA20_AC97_FIFO_SCR_PB_FORCE_MT (1 << 16) +#define TEGRA20_AC97_FIFO_SCR_REC_FULL_EN (1 << 15) +#define TEGRA20_AC97_FIFO_SCR_REC_3QRT_FULL_EN (1 << 14) +#define TEGRA20_AC97_FIFO_SCR_REC_QRT_FULL_EN (1 << 13) +#define TEGRA20_AC97_FIFO_SCR_REC_EMPTY_EN (1 << 12) +#define TEGRA20_AC97_FIFO_SCR_PB_NOT_FULL_EN (1 << 11) +#define TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN (1 << 10) +#define TEGRA20_AC97_FIFO_SCR_PB_3QRT_MT_EN (1 << 9) +#define TEGRA20_AC97_FIFO_SCR_PB_EMPTY_MT_EN (1 << 8) + +struct tegra20_ac97 { + struct clk *clk_ac97; + struct tegra_pcm_dma_params capture_dma_data; + struct tegra_pcm_dma_params playback_dma_data; + struct regmap *regmap; + int reset_gpio; + int sync_gpio; + struct tegra_asoc_utils_data util_data; +}; +#endif /* __TEGRA20_AC97_H__ */ -- cgit v1.2.3 From 9e7b6d60d880a463b17e4eae0d61c9f9a12f22bb Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 10 Jan 2013 00:34:08 -0800 Subject: ASoC: fsi: add device tree support Support for loading the Renesas FSI driver via devicetree. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/renesas,fsi.txt | 26 ++++++++ sound/soc/sh/fsi.c | 71 +++++++++++++++++++--- 2 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/renesas,fsi.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/renesas,fsi.txt b/Documentation/devicetree/bindings/sound/renesas,fsi.txt new file mode 100644 index 000000000000..c5be003f413e --- /dev/null +++ b/Documentation/devicetree/bindings/sound/renesas,fsi.txt @@ -0,0 +1,26 @@ +Renesas FSI + +Required properties: +- compatible : "renesas,sh_fsi2" or "renesas,sh_fsi" +- reg : Should contain the register physical address and length +- interrupts : Should contain FSI interrupt + +- fsia,spdif-connection : FSI is connected by S/PDFI +- fsia,stream-mode-support : FSI supports 16bit stream mode. +- fsia,use-internal-clock : FSI uses internal clock when master mode. + +- fsib,spdif-connection : same as fsia +- fsib,stream-mode-support : same as fsia +- fsib,use-internal-clock : same as fsia + +Example: + +sh_fsi2: sh_fsi2@0xec230000 { + compatible = "renesas,sh_fsi2"; + reg = <0xec230000 0x400>; + interrupts = <0 146 0x4>; + + fsia,spdif-connection; + fsia,stream-mode-support; + fsia,use-internal-clock; +}; diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index ef34ef8e92ed..91576120cd47 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include #include @@ -297,7 +299,7 @@ struct fsi_master { int irq; struct fsi_priv fsia; struct fsi_priv fsib; - struct fsi_core *core; + const struct fsi_core *core; spinlock_t lock; }; @@ -1887,6 +1889,33 @@ static struct snd_soc_platform_driver fsi_soc_platform = { /* * platform function */ +static void fsi_of_parse(char *name, + struct device_node *np, + struct sh_fsi_port_info *info, + struct device *dev) +{ + int i; + char prop[128]; + unsigned long flags = 0; + struct { + char *name; + unsigned int val; + } of_parse_property[] = { + { "spdif-connection", SH_FSI_FMT_SPDIF }, + { "stream-mode-support", SH_FSI_ENABLE_STREAM_MODE }, + { "use-internal-clock", SH_FSI_CLK_CPG }, + }; + + for (i = 0; i < ARRAY_SIZE(of_parse_property); i++) { + sprintf(prop, "%s,%s", name, of_parse_property[i].name); + if (of_get_property(np, prop, NULL)) + flags |= of_parse_property[i].val; + } + info->flags = flags; + + dev_dbg(dev, "%s flags : %lx\n", name, info->flags); +} + static void fsi_port_info_init(struct fsi_priv *fsi, struct sh_fsi_port_info *info) { @@ -1914,22 +1943,40 @@ static void fsi_handler_init(struct fsi_priv *fsi, } } +static struct of_device_id fsi_of_match[]; static int fsi_probe(struct platform_device *pdev) { struct fsi_master *master; - const struct platform_device_id *id_entry; + struct device_node *np = pdev->dev.of_node; struct sh_fsi_platform_info info; + const struct fsi_core *core; struct fsi_priv *fsi; struct resource *res; unsigned int irq; int ret; memset(&info, 0, sizeof(info)); - if (pdev->dev.platform_data) - memcpy(&info, pdev->dev.platform_data, sizeof(info)); - id_entry = pdev->id_entry; - if (!id_entry) { + core = NULL; + if (np) { + const struct of_device_id *of_id; + + of_id = of_match_device(fsi_of_match, &pdev->dev); + if (of_id) { + core = of_id->data; + fsi_of_parse("fsia", np, &info.port_a, &pdev->dev); + fsi_of_parse("fsib", np, &info.port_b, &pdev->dev); + } + } else { + const struct platform_device_id *id_entry = pdev->id_entry; + if (id_entry) + core = (struct fsi_core *)id_entry->driver_data; + + if (pdev->dev.platform_data) + memcpy(&info, pdev->dev.platform_data, sizeof(info)); + } + + if (!core) { dev_err(&pdev->dev, "unknown fsi device\n"); return -ENODEV; } @@ -1956,7 +2003,7 @@ static int fsi_probe(struct platform_device *pdev) /* master setting */ master->irq = irq; - master->core = (struct fsi_core *)id_entry->driver_data; + master->core = core; spin_lock_init(&master->lock); /* FSI A setting */ @@ -1987,7 +2034,7 @@ static int fsi_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, master); ret = devm_request_irq(&pdev->dev, irq, &fsi_interrupt, 0, - id_entry->name, master); + dev_name(&pdev->dev), master); if (ret) { dev_err(&pdev->dev, "irq request err\n"); goto exit_fsib; @@ -2113,6 +2160,13 @@ static struct fsi_core fsi2_core = { .b_mclk = B_MST_CTLR, }; +static struct of_device_id fsi_of_match[] __devinitconst = { + { .compatible = "renesas,sh_fsi", .data = &fsi1_core}, + { .compatible = "renesas,sh_fsi2", .data = &fsi2_core}, + {}, +}; +MODULE_DEVICE_TABLE(of, fsi_of_match); + static struct platform_device_id fsi_id_table[] = { { "sh_fsi", (kernel_ulong_t)&fsi1_core }, { "sh_fsi2", (kernel_ulong_t)&fsi2_core }, @@ -2124,6 +2178,7 @@ static struct platform_driver fsi_driver = { .driver = { .name = "fsi-pcm-audio", .pm = &fsi_pm_ops, + .of_match_table = fsi_of_match, }, .probe = fsi_probe, .remove = fsi_remove, -- cgit v1.2.3 From bc6b1e7b86f5d8e4a6fc1c0189e64bba4077efe0 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 14 Dec 2012 11:36:44 +0100 Subject: ARM: OMAP: gpmc: add DT bindings for GPMC timings and NAND This patch adds basic DT bindings for OMAP GPMC. The actual peripherals are instantiated from child nodes within the GPMC node, and the only type of device that is currently supported is NAND. Code was added to parse the generic GPMC timing parameters and some documentation with examples on how to use them. Successfully tested on an AM33xx board. Signed-off-by: Daniel Mack Acked-by: Grant Likely [tony@atomide.com: updated to apply] Signed-off-by: Tony Lindgren --- Documentation/devicetree/bindings/bus/ti-gpmc.txt | 84 ++++++++++ .../devicetree/bindings/mtd/gpmc-nand.txt | 76 +++++++++ arch/arm/mach-omap2/gpmc.c | 173 +++++++++++++++++++++ 3 files changed, 333 insertions(+) create mode 100644 Documentation/devicetree/bindings/bus/ti-gpmc.txt create mode 100644 Documentation/devicetree/bindings/mtd/gpmc-nand.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/bus/ti-gpmc.txt b/Documentation/devicetree/bindings/bus/ti-gpmc.txt new file mode 100644 index 000000000000..5ddb2e9efaaa --- /dev/null +++ b/Documentation/devicetree/bindings/bus/ti-gpmc.txt @@ -0,0 +1,84 @@ +Device tree bindings for OMAP general purpose memory controllers (GPMC) + +The actual devices are instantiated from the child nodes of a GPMC node. + +Required properties: + + - compatible: Should be set to one of the following: + + ti,omap2420-gpmc (omap2420) + ti,omap2430-gpmc (omap2430) + ti,omap3430-gpmc (omap3430 & omap3630) + ti,omap4430-gpmc (omap4430 & omap4460 & omap543x) + ti,am3352-gpmc (am335x devices) + + - reg: A resource specifier for the register space + (see the example below) + - ti,hwmods: Should be set to "ti,gpmc" until the DT transition is + completed. + - #address-cells: Must be set to 2 to allow memory address translation + - #size-cells: Must be set to 1 to allow CS address passing + - gpmc,num-cs: The maximum number of chip-select lines that controller + can support. + - gpmc,num-waitpins: The maximum number of wait pins that controller can + support. + - ranges: Must be set up to reflect the memory layout with four + integer values for each chip-select line in use: + + 0 + + Currently, calculated values derived from the contents + of the per-CS register GPMC_CONFIG7 (as set up by the + bootloader) are used for the physical address decoding. + As this will change in the future, filling correct + values here is a requirement. + +Timing properties for child nodes. All are optional and default to 0. + + - gpmc,sync-clk: Minimum clock period for synchronous mode, in picoseconds + + Chip-select signal timings corresponding to GPMC_CONFIG2: + - gpmc,cs-on: Assertion time + - gpmc,cs-rd-off: Read deassertion time + - gpmc,cs-wr-off: Write deassertion time + + ADV signal timings corresponding to GPMC_CONFIG3: + - gpmc,adv-on: Assertion time + - gpmc,adv-rd-off: Read deassertion time + - gpmc,adv-wr-off: Write deassertion time + + WE signals timings corresponding to GPMC_CONFIG4: + - gpmc,we-on: Assertion time + - gpmc,we-off: Deassertion time + + OE signals timings corresponding to GPMC_CONFIG4: + - gpmc,oe-on: Assertion time + - gpmc,oe-off: Deassertion time + + Access time and cycle time timings corresponding to GPMC_CONFIG5: + - gpmc,page-burst-access: Multiple access word delay + - gpmc,access: Start-cycle to first data valid delay + - gpmc,rd-cycle: Total read cycle time + - gpmc,wr-cycle: Total write cycle time + +The following are only applicable to OMAP3+ and AM335x: + - gpmc,wr-access + - gpmc,wr-data-mux-bus + + +Example for an AM33xx board: + + gpmc: gpmc@50000000 { + compatible = "ti,am3352-gpmc"; + ti,hwmods = "gpmc"; + reg = <0x50000000 0x2000>; + interrupts = <100>; + + gpmc,num-cs = <8>; + gpmc,num-waitpins = <2>; + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0x08000000 0x10000000>; /* CS0 @addr 0x8000000, size 0x10000000 */ + + /* child nodes go here */ + }; diff --git a/Documentation/devicetree/bindings/mtd/gpmc-nand.txt b/Documentation/devicetree/bindings/mtd/gpmc-nand.txt new file mode 100644 index 000000000000..9f464f906ffb --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/gpmc-nand.txt @@ -0,0 +1,76 @@ +Device tree bindings for GPMC connected NANDs + +GPMC connected NAND (found on OMAP boards) are represented as child nodes of +the GPMC controller with a name of "nand". + +All timing relevant properties as well as generic gpmc child properties are +explained in a separate documents - please refer to +Documentation/devicetree/bindings/bus/ti-gpmc.txt + +For NAND specific properties such as ECC modes or bus width, please refer to +Documentation/devicetree/bindings/mtd/nand.txt + + +Required properties: + + - reg: The CS line the peripheral is connected to + +Optional properties: + + - nand-bus-width: Set this numeric value to 16 if the hardware + is wired that way. If not specified, a bus + width of 8 is assumed. + + - ti,nand-ecc-opt: A string setting the ECC layout to use. One of: + + "sw" Software method (default) + "hw" Hardware method + "hw-romcode" gpmc hamming mode method & romcode layout + "bch4" 4-bit BCH ecc code + "bch8" 8-bit BCH ecc code + +For inline partiton table parsing (optional): + + - #address-cells: should be set to 1 + - #size-cells: should be set to 1 + +Example for an AM33xx board: + + gpmc: gpmc@50000000 { + compatible = "ti,am3352-gpmc"; + ti,hwmods = "gpmc"; + reg = <0x50000000 0x1000000>; + interrupts = <100>; + gpmc,num-cs = <8>; + gpmc,num-waitpins = <2>; + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0x08000000 0x2000>; /* CS0: NAND */ + + nand@0,0 { + reg = <0 0 0>; /* CS0, offset 0 */ + nand-bus-width = <16>; + ti,nand-ecc-opt = "bch8"; + + gpmc,sync-clk = <0>; + gpmc,cs-on = <0>; + gpmc,cs-rd-off = <44>; + gpmc,cs-wr-off = <44>; + gpmc,adv-on = <6>; + gpmc,adv-rd-off = <34>; + gpmc,adv-wr-off = <44>; + gpmc,we-off = <40>; + gpmc,oe-off = <54>; + gpmc,access = <64>; + gpmc,rd-cycle = <82>; + gpmc,wr-cycle = <82>; + gpmc,wr-access = <40>; + gpmc,wr-data-mux-bus = <0>; + + #address-cells = <1>; + #size-cells = <1>; + + /* partitions go here */ + }; + }; + diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index 1f0ec79aabf1..01ce462e265d 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c @@ -25,6 +25,10 @@ #include #include #include +#include +#include +#include +#include #include @@ -34,6 +38,7 @@ #include "common.h" #include "omap_device.h" #include "gpmc.h" +#include "gpmc-nand.h" #define DEVICE_NAME "omap-gpmc" @@ -1121,6 +1126,165 @@ int gpmc_calc_timings(struct gpmc_timings *gpmc_t, return 0; } +#ifdef CONFIG_OF +static struct of_device_id gpmc_dt_ids[] = { + { .compatible = "ti,omap2420-gpmc" }, + { .compatible = "ti,omap2430-gpmc" }, + { .compatible = "ti,omap3430-gpmc" }, /* omap3430 & omap3630 */ + { .compatible = "ti,omap4430-gpmc" }, /* omap4430 & omap4460 & omap543x */ + { .compatible = "ti,am3352-gpmc" }, /* am335x devices */ + { } +}; +MODULE_DEVICE_TABLE(of, gpmc_dt_ids); + +static void __maybe_unused gpmc_read_timings_dt(struct device_node *np, + struct gpmc_timings *gpmc_t) +{ + u32 val; + + memset(gpmc_t, 0, sizeof(*gpmc_t)); + + /* minimum clock period for syncronous mode */ + if (!of_property_read_u32(np, "gpmc,sync-clk", &val)) + gpmc_t->sync_clk = val; + + /* chip select timtings */ + if (!of_property_read_u32(np, "gpmc,cs-on", &val)) + gpmc_t->cs_on = val; + + if (!of_property_read_u32(np, "gpmc,cs-rd-off", &val)) + gpmc_t->cs_rd_off = val; + + if (!of_property_read_u32(np, "gpmc,cs-wr-off", &val)) + gpmc_t->cs_wr_off = val; + + /* ADV signal timings */ + if (!of_property_read_u32(np, "gpmc,adv-on", &val)) + gpmc_t->adv_on = val; + + if (!of_property_read_u32(np, "gpmc,adv-rd-off", &val)) + gpmc_t->adv_rd_off = val; + + if (!of_property_read_u32(np, "gpmc,adv-wr-off", &val)) + gpmc_t->adv_wr_off = val; + + /* WE signal timings */ + if (!of_property_read_u32(np, "gpmc,we-on", &val)) + gpmc_t->we_on = val; + + if (!of_property_read_u32(np, "gpmc,we-off", &val)) + gpmc_t->we_off = val; + + /* OE signal timings */ + if (!of_property_read_u32(np, "gpmc,oe-on", &val)) + gpmc_t->oe_on = val; + + if (!of_property_read_u32(np, "gpmc,oe-off", &val)) + gpmc_t->oe_off = val; + + /* access and cycle timings */ + if (!of_property_read_u32(np, "gpmc,page-burst-access", &val)) + gpmc_t->page_burst_access = val; + + if (!of_property_read_u32(np, "gpmc,access", &val)) + gpmc_t->access = val; + + if (!of_property_read_u32(np, "gpmc,rd-cycle", &val)) + gpmc_t->rd_cycle = val; + + if (!of_property_read_u32(np, "gpmc,wr-cycle", &val)) + gpmc_t->wr_cycle = val; + + /* only for OMAP3430 */ + if (!of_property_read_u32(np, "gpmc,wr-access", &val)) + gpmc_t->wr_access = val; + + if (!of_property_read_u32(np, "gpmc,wr-data-mux-bus", &val)) + gpmc_t->wr_data_mux_bus = val; +} + +#ifdef CONFIG_MTD_NAND + +static const char * const nand_ecc_opts[] = { + [OMAP_ECC_HAMMING_CODE_DEFAULT] = "sw", + [OMAP_ECC_HAMMING_CODE_HW] = "hw", + [OMAP_ECC_HAMMING_CODE_HW_ROMCODE] = "hw-romcode", + [OMAP_ECC_BCH4_CODE_HW] = "bch4", + [OMAP_ECC_BCH8_CODE_HW] = "bch8", +}; + +static int gpmc_probe_nand_child(struct platform_device *pdev, + struct device_node *child) +{ + u32 val; + const char *s; + struct gpmc_timings gpmc_t; + struct omap_nand_platform_data *gpmc_nand_data; + + if (of_property_read_u32(child, "reg", &val) < 0) { + dev_err(&pdev->dev, "%s has no 'reg' property\n", + child->full_name); + return -ENODEV; + } + + gpmc_nand_data = devm_kzalloc(&pdev->dev, sizeof(*gpmc_nand_data), + GFP_KERNEL); + if (!gpmc_nand_data) + return -ENOMEM; + + gpmc_nand_data->cs = val; + gpmc_nand_data->of_node = child; + + if (!of_property_read_string(child, "ti,nand-ecc-opt", &s)) + for (val = 0; val < ARRAY_SIZE(nand_ecc_opts); val++) + if (!strcasecmp(s, nand_ecc_opts[val])) { + gpmc_nand_data->ecc_opt = val; + break; + } + + val = of_get_nand_bus_width(child); + if (val == 16) + gpmc_nand_data->devsize = NAND_BUSWIDTH_16; + + gpmc_read_timings_dt(child, &gpmc_t); + gpmc_nand_init(gpmc_nand_data, &gpmc_t); + + return 0; +} +#else +static int gpmc_probe_nand_child(struct platform_device *pdev, + struct device_node *child) +{ + return 0; +} +#endif + +static int gpmc_probe_dt(struct platform_device *pdev) +{ + int ret; + struct device_node *child; + const struct of_device_id *of_id = + of_match_device(gpmc_dt_ids, &pdev->dev); + + if (!of_id) + return 0; + + for_each_node_by_name(child, "nand") { + ret = gpmc_probe_nand_child(pdev, child); + of_node_put(child); + if (ret < 0) + return ret; + } + + return 0; +} +#else +static int gpmc_probe_dt(struct platform_device *pdev) +{ + return 0; +} +#endif + static int gpmc_probe(struct platform_device *pdev) { int rc; @@ -1174,6 +1338,14 @@ static int gpmc_probe(struct platform_device *pdev) if (IS_ERR_VALUE(gpmc_setup_irq())) dev_warn(gpmc_dev, "gpmc_setup_irq failed\n"); + rc = gpmc_probe_dt(pdev); + if (rc < 0) { + clk_disable_unprepare(gpmc_l3_clk); + clk_put(gpmc_l3_clk); + dev_err(gpmc_dev, "failed to probe DT parameters\n"); + return rc; + } + return 0; } @@ -1191,6 +1363,7 @@ static struct platform_driver gpmc_driver = { .driver = { .name = DEVICE_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(gpmc_dt_ids), }, }; -- cgit v1.2.3 From e9ea096dd2259454e0d09d0b0445fe8f0117add4 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 8 Jan 2013 16:27:44 +0530 Subject: serial: tegra: add serial driver NVIDIA's Tegra has multiple UART controller which supports: - APB DMA based controller fifo read/write. - End Of Data interrupt in incoming data to know whether end of frame achieve or not. - HW controlled RTS and CTS flow control to reduce SW overhead. Add serial driver to use all above feature. Signed-off-by: Laxman Dewangan Acked-by: Alan Cox Reviewed-by: Stephen Warren Signed-off-by: Greg Kroah-Hartman --- .../bindings/serial/nvidia,tegra20-hsuart.txt | 24 + drivers/tty/serial/Kconfig | 11 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/serial-tegra.c | 1399 ++++++++++++++++++++ 4 files changed, 1435 insertions(+) create mode 100644 Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt create mode 100644 drivers/tty/serial/serial-tegra.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt b/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt new file mode 100644 index 000000000000..392a4493eebd --- /dev/null +++ b/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt @@ -0,0 +1,24 @@ +NVIDIA Tegra20/Tegra30 high speed (DMA based) UART controller driver. + +Required properties: +- compatible : should be "nvidia,tegra30-hsuart", "nvidia,tegra20-hsuart". +- reg: Should contain UART controller registers location and length. +- interrupts: Should contain UART controller interrupts. +- nvidia,dma-request-selector : The Tegra DMA controller's phandle and + request selector for this UART controller. + +Optional properties: +- nvidia,enable-modem-interrupt: Enable modem interrupts. Should be enable + only if all 8 lines of UART controller are pinmuxed. + +Example: + +serial@70006000 { + compatible = "nvidia,tegra30-hsuart", "nvidia,tegra20-hsuart"; + reg = <0x70006000 0x40>; + reg-shift = <2>; + interrupts = <0 36 0x04>; + nvidia,dma-request-selector = <&apbdma 8>; + nvidia,enable-modem-interrupt; + status = "disabled"; +}; diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 59c23d038106..aff3cd356662 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -269,6 +269,17 @@ config SERIAL_SIRFSOC_CONSOLE your boot loader about how to pass options to the kernel at boot time.) +config SERIAL_TEGRA + tristate "NVIDIA Tegra20/30 SoC serial controller" + depends on ARCH_TEGRA && TEGRA20_APB_DMA + select SERIAL_CORE + help + Support for the on-chip UARTs on the NVIDIA Tegra series SOCs + providing /dev/ttyHS0, 1, 2, 3 and 4 (note, some machines may not + provide all of these ports, depending on how the serial port + are enabled). This driver uses the APB DMA to achieve higher baudrate + and better performance. + config SERIAL_MAX3100 tristate "MAX3100 support" depends on SPI diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index df1b998c436b..82e4306bf962 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o obj-$(CONFIG_SERIAL_LANTIQ) += lantiq.o obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o +obj-$(CONFIG_SERIAL_TEGRA) += serial-tegra.o obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o obj-$(CONFIG_SERIAL_ARC) += arc_uart.o diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c new file mode 100644 index 000000000000..cc4072f50352 --- /dev/null +++ b/drivers/tty/serial/serial-tegra.c @@ -0,0 +1,1399 @@ +/* + * serial_tegra.c + * + * High-speed serial driver for NVIDIA Tegra SoCs + * + * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved. + * + * Author: Laxman Dewangan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define TEGRA_UART_TYPE "TEGRA_UART" +#define TX_EMPTY_STATUS (UART_LSR_TEMT | UART_LSR_THRE) +#define BYTES_TO_ALIGN(x) ((unsigned long)(x) & 0x3) + +#define TEGRA_UART_RX_DMA_BUFFER_SIZE 4096 +#define TEGRA_UART_LSR_TXFIFO_FULL 0x100 +#define TEGRA_UART_IER_EORD 0x20 +#define TEGRA_UART_MCR_RTS_EN 0x40 +#define TEGRA_UART_MCR_CTS_EN 0x20 +#define TEGRA_UART_LSR_ANY (UART_LSR_OE | UART_LSR_BI | \ + UART_LSR_PE | UART_LSR_FE) +#define TEGRA_UART_IRDA_CSR 0x08 +#define TEGRA_UART_SIR_ENABLED 0x80 + +#define TEGRA_UART_TX_PIO 1 +#define TEGRA_UART_TX_DMA 2 +#define TEGRA_UART_MIN_DMA 16 +#define TEGRA_UART_FIFO_SIZE 32 + +/* + * Tx fifo trigger level setting in tegra uart is in + * reverse way then conventional uart. + */ +#define TEGRA_UART_TX_TRIG_16B 0x00 +#define TEGRA_UART_TX_TRIG_8B 0x10 +#define TEGRA_UART_TX_TRIG_4B 0x20 +#define TEGRA_UART_TX_TRIG_1B 0x30 + +#define TEGRA_UART_MAXIMUM 5 + +/* Default UART setting when started: 115200 no parity, stop, 8 data bits */ +#define TEGRA_UART_DEFAULT_BAUD 115200 +#define TEGRA_UART_DEFAULT_LSR UART_LCR_WLEN8 + +/* Tx transfer mode */ +#define TEGRA_TX_PIO 1 +#define TEGRA_TX_DMA 2 + +/** + * tegra_uart_chip_data: SOC specific data. + * + * @tx_fifo_full_status: Status flag available for checking tx fifo full. + * @allow_txfifo_reset_fifo_mode: allow_tx fifo reset with fifo mode or not. + * Tegra30 does not allow this. + * @support_clk_src_div: Clock source support the clock divider. + */ +struct tegra_uart_chip_data { + bool tx_fifo_full_status; + bool allow_txfifo_reset_fifo_mode; + bool support_clk_src_div; +}; + +struct tegra_uart_port { + struct uart_port uport; + const struct tegra_uart_chip_data *cdata; + + struct clk *uart_clk; + unsigned int current_baud; + + /* Register shadow */ + unsigned long fcr_shadow; + unsigned long mcr_shadow; + unsigned long lcr_shadow; + unsigned long ier_shadow; + bool rts_active; + + int tx_in_progress; + unsigned int tx_bytes; + + bool enable_modem_interrupt; + + bool rx_timeout; + int rx_in_progress; + int symb_bit; + int dma_req_sel; + + struct dma_chan *rx_dma_chan; + struct dma_chan *tx_dma_chan; + dma_addr_t rx_dma_buf_phys; + dma_addr_t tx_dma_buf_phys; + unsigned char *rx_dma_buf_virt; + unsigned char *tx_dma_buf_virt; + struct dma_async_tx_descriptor *tx_dma_desc; + struct dma_async_tx_descriptor *rx_dma_desc; + dma_cookie_t tx_cookie; + dma_cookie_t rx_cookie; + int tx_bytes_requested; + int rx_bytes_requested; +}; + +static void tegra_uart_start_next_tx(struct tegra_uart_port *tup); +static int tegra_uart_start_rx_dma(struct tegra_uart_port *tup); + +static inline unsigned long tegra_uart_read(struct tegra_uart_port *tup, + unsigned long reg) +{ + return readl(tup->uport.membase + (reg << tup->uport.regshift)); +} + +static inline void tegra_uart_write(struct tegra_uart_port *tup, unsigned val, + unsigned long reg) +{ + writel(val, tup->uport.membase + (reg << tup->uport.regshift)); +} + +static inline struct tegra_uart_port *to_tegra_uport(struct uart_port *u) +{ + return container_of(u, struct tegra_uart_port, uport); +} + +static unsigned int tegra_uart_get_mctrl(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + + /* + * RI - Ring detector is active + * CD/DCD/CAR - Carrier detect is always active. For some reason + * linux has different names for carrier detect. + * DSR - Data Set ready is active as the hardware doesn't support it. + * Don't know if the linux support this yet? + * CTS - Clear to send. Always set to active, as the hardware handles + * CTS automatically. + */ + if (tup->enable_modem_interrupt) + return TIOCM_RI | TIOCM_CD | TIOCM_DSR | TIOCM_CTS; + return TIOCM_CTS; +} + +static void set_rts(struct tegra_uart_port *tup, bool active) +{ + unsigned long mcr; + + mcr = tup->mcr_shadow; + if (active) + mcr |= TEGRA_UART_MCR_RTS_EN; + else + mcr &= ~TEGRA_UART_MCR_RTS_EN; + if (mcr != tup->mcr_shadow) { + tegra_uart_write(tup, mcr, UART_MCR); + tup->mcr_shadow = mcr; + } + return; +} + +static void set_dtr(struct tegra_uart_port *tup, bool active) +{ + unsigned long mcr; + + mcr = tup->mcr_shadow; + if (active) + mcr |= UART_MCR_DTR; + else + mcr &= ~UART_MCR_DTR; + if (mcr != tup->mcr_shadow) { + tegra_uart_write(tup, mcr, UART_MCR); + tup->mcr_shadow = mcr; + } + return; +} + +static void tegra_uart_set_mctrl(struct uart_port *u, unsigned int mctrl) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + unsigned long mcr; + int dtr_enable; + + mcr = tup->mcr_shadow; + tup->rts_active = !!(mctrl & TIOCM_RTS); + set_rts(tup, tup->rts_active); + + dtr_enable = !!(mctrl & TIOCM_DTR); + set_dtr(tup, dtr_enable); + return; +} + +static void tegra_uart_break_ctl(struct uart_port *u, int break_ctl) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + unsigned long lcr; + + lcr = tup->lcr_shadow; + if (break_ctl) + lcr |= UART_LCR_SBC; + else + lcr &= ~UART_LCR_SBC; + tegra_uart_write(tup, lcr, UART_LCR); + tup->lcr_shadow = lcr; +} + +/* Wait for a symbol-time. */ +static void tegra_uart_wait_sym_time(struct tegra_uart_port *tup, + unsigned int syms) +{ + if (tup->current_baud) + udelay(DIV_ROUND_UP(syms * tup->symb_bit * 1000000, + tup->current_baud)); +} + +static void tegra_uart_fifo_reset(struct tegra_uart_port *tup, u8 fcr_bits) +{ + unsigned long fcr = tup->fcr_shadow; + + if (tup->cdata->allow_txfifo_reset_fifo_mode) { + fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + tegra_uart_write(tup, fcr, UART_FCR); + } else { + fcr &= ~UART_FCR_ENABLE_FIFO; + tegra_uart_write(tup, fcr, UART_FCR); + udelay(60); + fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + tegra_uart_write(tup, fcr, UART_FCR); + fcr |= UART_FCR_ENABLE_FIFO; + tegra_uart_write(tup, fcr, UART_FCR); + } + + /* Dummy read to ensure the write is posted */ + tegra_uart_read(tup, UART_SCR); + + /* Wait for the flush to propagate. */ + tegra_uart_wait_sym_time(tup, 1); +} + +static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud) +{ + unsigned long rate; + unsigned int divisor; + unsigned long lcr; + int ret; + + if (tup->current_baud == baud) + return 0; + + if (tup->cdata->support_clk_src_div) { + rate = baud * 16; + ret = clk_set_rate(tup->uart_clk, rate); + if (ret < 0) { + dev_err(tup->uport.dev, + "clk_set_rate() failed for rate %lu\n", rate); + return ret; + } + divisor = 1; + } else { + rate = clk_get_rate(tup->uart_clk); + divisor = DIV_ROUND_CLOSEST(rate, baud * 16); + } + + lcr = tup->lcr_shadow; + lcr |= UART_LCR_DLAB; + tegra_uart_write(tup, lcr, UART_LCR); + + tegra_uart_write(tup, divisor & 0xFF, UART_TX); + tegra_uart_write(tup, ((divisor >> 8) & 0xFF), UART_IER); + + lcr &= ~UART_LCR_DLAB; + tegra_uart_write(tup, lcr, UART_LCR); + + /* Dummy read to ensure the write is posted */ + tegra_uart_read(tup, UART_SCR); + + tup->current_baud = baud; + + /* wait two character intervals at new rate */ + tegra_uart_wait_sym_time(tup, 2); + return 0; +} + +static char tegra_uart_decode_rx_error(struct tegra_uart_port *tup, + unsigned long lsr) +{ + char flag = TTY_NORMAL; + + if (unlikely(lsr & TEGRA_UART_LSR_ANY)) { + if (lsr & UART_LSR_OE) { + /* Overrrun error */ + flag |= TTY_OVERRUN; + tup->uport.icount.overrun++; + dev_err(tup->uport.dev, "Got overrun errors\n"); + } else if (lsr & UART_LSR_PE) { + /* Parity error */ + flag |= TTY_PARITY; + tup->uport.icount.parity++; + dev_err(tup->uport.dev, "Got Parity errors\n"); + } else if (lsr & UART_LSR_FE) { + flag |= TTY_FRAME; + tup->uport.icount.frame++; + dev_err(tup->uport.dev, "Got frame errors\n"); + } else if (lsr & UART_LSR_BI) { + dev_err(tup->uport.dev, "Got Break\n"); + tup->uport.icount.brk++; + /* If FIFO read error without any data, reset Rx FIFO */ + if (!(lsr & UART_LSR_DR) && (lsr & UART_LSR_FIFOE)) + tegra_uart_fifo_reset(tup, UART_FCR_CLEAR_RCVR); + } + } + return flag; +} + +static int tegra_uart_request_port(struct uart_port *u) +{ + return 0; +} + +static void tegra_uart_release_port(struct uart_port *u) +{ + /* Nothing to do here */ +} + +static void tegra_uart_fill_tx_fifo(struct tegra_uart_port *tup, int max_bytes) +{ + struct circ_buf *xmit = &tup->uport.state->xmit; + int i; + + for (i = 0; i < max_bytes; i++) { + BUG_ON(uart_circ_empty(xmit)); + if (tup->cdata->tx_fifo_full_status) { + unsigned long lsr = tegra_uart_read(tup, UART_LSR); + if ((lsr & TEGRA_UART_LSR_TXFIFO_FULL)) + break; + } + tegra_uart_write(tup, xmit->buf[xmit->tail], UART_TX); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + tup->uport.icount.tx++; + } +} + +static void tegra_uart_start_pio_tx(struct tegra_uart_port *tup, + unsigned int bytes) +{ + if (bytes > TEGRA_UART_MIN_DMA) + bytes = TEGRA_UART_MIN_DMA; + + tup->tx_in_progress = TEGRA_UART_TX_PIO; + tup->tx_bytes = bytes; + tup->ier_shadow |= UART_IER_THRI; + tegra_uart_write(tup, tup->ier_shadow, UART_IER); +} + +static void tegra_uart_tx_dma_complete(void *args) +{ + struct tegra_uart_port *tup = args; + struct circ_buf *xmit = &tup->uport.state->xmit; + struct dma_tx_state state; + unsigned long flags; + int count; + + dmaengine_tx_status(tup->tx_dma_chan, tup->rx_cookie, &state); + count = tup->tx_bytes_requested - state.residue; + async_tx_ack(tup->tx_dma_desc); + spin_lock_irqsave(&tup->uport.lock, flags); + xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); + tup->tx_in_progress = 0; + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&tup->uport); + tegra_uart_start_next_tx(tup); + spin_unlock_irqrestore(&tup->uport.lock, flags); +} + +static int tegra_uart_start_tx_dma(struct tegra_uart_port *tup, + unsigned long count) +{ + struct circ_buf *xmit = &tup->uport.state->xmit; + dma_addr_t tx_phys_addr; + + dma_sync_single_for_device(tup->uport.dev, tup->tx_dma_buf_phys, + UART_XMIT_SIZE, DMA_TO_DEVICE); + + tup->tx_bytes = count & ~(0xF); + tx_phys_addr = tup->tx_dma_buf_phys + xmit->tail; + tup->tx_dma_desc = dmaengine_prep_slave_single(tup->tx_dma_chan, + tx_phys_addr, tup->tx_bytes, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT); + if (!tup->tx_dma_desc) { + dev_err(tup->uport.dev, "Not able to get desc for Tx\n"); + return -EIO; + } + + tup->tx_dma_desc->callback = tegra_uart_tx_dma_complete; + tup->tx_dma_desc->callback_param = tup; + tup->tx_in_progress = TEGRA_UART_TX_DMA; + tup->tx_bytes_requested = tup->tx_bytes; + tup->tx_cookie = dmaengine_submit(tup->tx_dma_desc); + dma_async_issue_pending(tup->tx_dma_chan); + return 0; +} + +static void tegra_uart_start_next_tx(struct tegra_uart_port *tup) +{ + unsigned long tail; + unsigned long count; + struct circ_buf *xmit = &tup->uport.state->xmit; + + tail = (unsigned long)&xmit->buf[xmit->tail]; + count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + if (!count) + return; + + if (count < TEGRA_UART_MIN_DMA) + tegra_uart_start_pio_tx(tup, count); + else if (BYTES_TO_ALIGN(tail) > 0) + tegra_uart_start_pio_tx(tup, BYTES_TO_ALIGN(tail)); + else + tegra_uart_start_tx_dma(tup, count); +} + +/* Called by serial core driver with u->lock taken. */ +static void tegra_uart_start_tx(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + struct circ_buf *xmit = &u->state->xmit; + + if (!uart_circ_empty(xmit) && !tup->tx_in_progress) + tegra_uart_start_next_tx(tup); +} + +static unsigned int tegra_uart_tx_empty(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + unsigned int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&u->lock, flags); + if (!tup->tx_in_progress) { + unsigned long lsr = tegra_uart_read(tup, UART_LSR); + if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS) + ret = TIOCSER_TEMT; + } + spin_unlock_irqrestore(&u->lock, flags); + return ret; +} + +static void tegra_uart_stop_tx(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + struct circ_buf *xmit = &tup->uport.state->xmit; + struct dma_tx_state state; + int count; + + dmaengine_terminate_all(tup->tx_dma_chan); + dmaengine_tx_status(tup->tx_dma_chan, tup->tx_cookie, &state); + count = tup->tx_bytes_requested - state.residue; + async_tx_ack(tup->tx_dma_desc); + xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); + tup->tx_in_progress = 0; + return; +} + +static void tegra_uart_handle_tx_pio(struct tegra_uart_port *tup) +{ + struct circ_buf *xmit = &tup->uport.state->xmit; + + tegra_uart_fill_tx_fifo(tup, tup->tx_bytes); + tup->tx_in_progress = 0; + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&tup->uport); + tegra_uart_start_next_tx(tup); + return; +} + +static void tegra_uart_handle_rx_pio(struct tegra_uart_port *tup, + struct tty_struct *tty) +{ + do { + char flag = TTY_NORMAL; + unsigned long lsr = 0; + unsigned char ch; + + lsr = tegra_uart_read(tup, UART_LSR); + if (!(lsr & UART_LSR_DR)) + break; + + flag = tegra_uart_decode_rx_error(tup, lsr); + ch = (unsigned char) tegra_uart_read(tup, UART_RX); + tup->uport.icount.rx++; + + if (!uart_handle_sysrq_char(&tup->uport, ch) && tty) + tty_insert_flip_char(tty, ch, flag); + } while (1); + + return; +} + +static void tegra_uart_copy_rx_to_tty(struct tegra_uart_port *tup, + struct tty_struct *tty, int count) +{ + int copied; + + tup->uport.icount.rx += count; + if (!tty) { + dev_err(tup->uport.dev, "No tty port\n"); + return; + } + dma_sync_single_for_cpu(tup->uport.dev, tup->rx_dma_buf_phys, + TEGRA_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE); + copied = tty_insert_flip_string(tty, + ((unsigned char *)(tup->rx_dma_buf_virt)), count); + if (copied != count) { + WARN_ON(1); + dev_err(tup->uport.dev, "RxData copy to tty layer failed\n"); + } + dma_sync_single_for_device(tup->uport.dev, tup->rx_dma_buf_phys, + TEGRA_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE); +} + +static void tegra_uart_rx_dma_complete(void *args) +{ + struct tegra_uart_port *tup = args; + struct uart_port *u = &tup->uport; + int count = tup->rx_bytes_requested; + struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port); + unsigned long flags; + + async_tx_ack(tup->rx_dma_desc); + spin_lock_irqsave(&u->lock, flags); + + /* Deactivate flow control to stop sender */ + if (tup->rts_active) + set_rts(tup, false); + + /* If we are here, DMA is stopped */ + if (count) + tegra_uart_copy_rx_to_tty(tup, tty, count); + + tegra_uart_handle_rx_pio(tup, tty); + if (tty) { + tty_flip_buffer_push(tty); + tty_kref_put(tty); + } + tegra_uart_start_rx_dma(tup); + + /* Activate flow control to start transfer */ + if (tup->rts_active) + set_rts(tup, true); + + spin_unlock_irqrestore(&u->lock, flags); +} + +static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup) +{ + struct dma_tx_state state; + struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port); + int count; + + /* Deactivate flow control to stop sender */ + if (tup->rts_active) + set_rts(tup, false); + + dmaengine_terminate_all(tup->rx_dma_chan); + dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state); + count = tup->rx_bytes_requested - state.residue; + + /* If we are here, DMA is stopped */ + if (count) + tegra_uart_copy_rx_to_tty(tup, tty, count); + + tegra_uart_handle_rx_pio(tup, tty); + if (tty) { + tty_flip_buffer_push(tty); + tty_kref_put(tty); + } + tegra_uart_start_rx_dma(tup); + + if (tup->rts_active) + set_rts(tup, true); +} + +static int tegra_uart_start_rx_dma(struct tegra_uart_port *tup) +{ + unsigned int count = TEGRA_UART_RX_DMA_BUFFER_SIZE; + + tup->rx_dma_desc = dmaengine_prep_slave_single(tup->rx_dma_chan, + tup->rx_dma_buf_phys, count, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!tup->rx_dma_desc) { + dev_err(tup->uport.dev, "Not able to get desc for Rx\n"); + return -EIO; + } + + tup->rx_dma_desc->callback = tegra_uart_rx_dma_complete; + tup->rx_dma_desc->callback_param = tup; + dma_sync_single_for_device(tup->uport.dev, tup->rx_dma_buf_phys, + count, DMA_TO_DEVICE); + tup->rx_bytes_requested = count; + tup->rx_cookie = dmaengine_submit(tup->rx_dma_desc); + dma_async_issue_pending(tup->rx_dma_chan); + return 0; +} + +static void tegra_uart_handle_modem_signal_change(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + unsigned long msr; + + msr = tegra_uart_read(tup, UART_MSR); + if (!(msr & UART_MSR_ANY_DELTA)) + return; + + if (msr & UART_MSR_TERI) + tup->uport.icount.rng++; + if (msr & UART_MSR_DDSR) + tup->uport.icount.dsr++; + /* We may only get DDCD when HW init and reset */ + if (msr & UART_MSR_DDCD) + uart_handle_dcd_change(&tup->uport, msr & UART_MSR_DCD); + /* Will start/stop_tx accordingly */ + if (msr & UART_MSR_DCTS) + uart_handle_cts_change(&tup->uport, msr & UART_MSR_CTS); + return; +} + +static irqreturn_t tegra_uart_isr(int irq, void *data) +{ + struct tegra_uart_port *tup = data; + struct uart_port *u = &tup->uport; + unsigned long iir; + unsigned long ier; + bool is_rx_int = false; + unsigned long flags; + + spin_lock_irqsave(&u->lock, flags); + while (1) { + iir = tegra_uart_read(tup, UART_IIR); + if (iir & UART_IIR_NO_INT) { + if (is_rx_int) { + tegra_uart_handle_rx_dma(tup); + if (tup->rx_in_progress) { + ier = tup->ier_shadow; + ier |= (UART_IER_RLSI | UART_IER_RTOIE | + TEGRA_UART_IER_EORD); + tup->ier_shadow = ier; + tegra_uart_write(tup, ier, UART_IER); + } + } + spin_unlock_irqrestore(&u->lock, flags); + return IRQ_HANDLED; + } + + switch ((iir >> 1) & 0x7) { + case 0: /* Modem signal change interrupt */ + tegra_uart_handle_modem_signal_change(u); + break; + + case 1: /* Transmit interrupt only triggered when using PIO */ + tup->ier_shadow &= ~UART_IER_THRI; + tegra_uart_write(tup, tup->ier_shadow, UART_IER); + tegra_uart_handle_tx_pio(tup); + break; + + case 4: /* End of data */ + case 6: /* Rx timeout */ + case 2: /* Receive */ + if (!is_rx_int) { + is_rx_int = true; + /* Disable Rx interrupts */ + ier = tup->ier_shadow; + ier |= UART_IER_RDI; + tegra_uart_write(tup, ier, UART_IER); + ier &= ~(UART_IER_RDI | UART_IER_RLSI | + UART_IER_RTOIE | TEGRA_UART_IER_EORD); + tup->ier_shadow = ier; + tegra_uart_write(tup, ier, UART_IER); + } + break; + + case 3: /* Receive error */ + tegra_uart_decode_rx_error(tup, + tegra_uart_read(tup, UART_LSR)); + break; + + case 5: /* break nothing to handle */ + case 7: /* break nothing to handle */ + break; + } + } +} + +static void tegra_uart_stop_rx(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port); + struct dma_tx_state state; + unsigned long ier; + int count; + + if (tup->rts_active) + set_rts(tup, false); + + if (!tup->rx_in_progress) + return; + + tegra_uart_wait_sym_time(tup, 1); /* wait a character interval */ + + ier = tup->ier_shadow; + ier &= ~(UART_IER_RDI | UART_IER_RLSI | UART_IER_RTOIE | + TEGRA_UART_IER_EORD); + tup->ier_shadow = ier; + tegra_uart_write(tup, ier, UART_IER); + tup->rx_in_progress = 0; + if (tup->rx_dma_chan) { + dmaengine_terminate_all(tup->rx_dma_chan); + dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state); + async_tx_ack(tup->rx_dma_desc); + count = tup->rx_bytes_requested - state.residue; + tegra_uart_copy_rx_to_tty(tup, tty, count); + tegra_uart_handle_rx_pio(tup, tty); + } else { + tegra_uart_handle_rx_pio(tup, tty); + } + if (tty) { + tty_flip_buffer_push(tty); + tty_kref_put(tty); + } + return; +} + +static void tegra_uart_hw_deinit(struct tegra_uart_port *tup) +{ + unsigned long flags; + unsigned long char_time = DIV_ROUND_UP(10000000, tup->current_baud); + unsigned long fifo_empty_time = tup->uport.fifosize * char_time; + unsigned long wait_time; + unsigned long lsr; + unsigned long msr; + unsigned long mcr; + + /* Disable interrupts */ + tegra_uart_write(tup, 0, UART_IER); + + lsr = tegra_uart_read(tup, UART_LSR); + if ((lsr & UART_LSR_TEMT) != UART_LSR_TEMT) { + msr = tegra_uart_read(tup, UART_MSR); + mcr = tegra_uart_read(tup, UART_MCR); + if ((mcr & TEGRA_UART_MCR_CTS_EN) && (msr & UART_MSR_CTS)) + dev_err(tup->uport.dev, + "Tx Fifo not empty, CTS disabled, waiting\n"); + + /* Wait for Tx fifo to be empty */ + while ((lsr & UART_LSR_TEMT) != UART_LSR_TEMT) { + wait_time = min(fifo_empty_time, 100lu); + udelay(wait_time); + fifo_empty_time -= wait_time; + if (!fifo_empty_time) { + msr = tegra_uart_read(tup, UART_MSR); + mcr = tegra_uart_read(tup, UART_MCR); + if ((mcr & TEGRA_UART_MCR_CTS_EN) && + (msr & UART_MSR_CTS)) + dev_err(tup->uport.dev, + "Slave not ready\n"); + break; + } + lsr = tegra_uart_read(tup, UART_LSR); + } + } + + spin_lock_irqsave(&tup->uport.lock, flags); + /* Reset the Rx and Tx FIFOs */ + tegra_uart_fifo_reset(tup, UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR); + tup->current_baud = 0; + spin_unlock_irqrestore(&tup->uport.lock, flags); + + clk_disable_unprepare(tup->uart_clk); +} + +static int tegra_uart_hw_init(struct tegra_uart_port *tup) +{ + int ret; + + tup->fcr_shadow = 0; + tup->mcr_shadow = 0; + tup->lcr_shadow = 0; + tup->ier_shadow = 0; + tup->current_baud = 0; + + clk_prepare_enable(tup->uart_clk); + + /* Reset the UART controller to clear all previous status.*/ + tegra_periph_reset_assert(tup->uart_clk); + udelay(10); + tegra_periph_reset_deassert(tup->uart_clk); + + tup->rx_in_progress = 0; + tup->tx_in_progress = 0; + + /* + * Set the trigger level + * + * For PIO mode: + * + * For receive, this will interrupt the CPU after that many number of + * bytes are received, for the remaining bytes the receive timeout + * interrupt is received. Rx high watermark is set to 4. + * + * For transmit, if the trasnmit interrupt is enabled, this will + * interrupt the CPU when the number of entries in the FIFO reaches the + * low watermark. Tx low watermark is set to 16 bytes. + * + * For DMA mode: + * + * Set the Tx trigger to 16. This should match the DMA burst size that + * programmed in the DMA registers. + */ + tup->fcr_shadow = UART_FCR_ENABLE_FIFO; + tup->fcr_shadow |= UART_FCR_R_TRIG_01; + tup->fcr_shadow |= TEGRA_UART_TX_TRIG_16B; + tegra_uart_write(tup, tup->fcr_shadow, UART_FCR); + + /* + * Initialize the UART with default configuration + * (115200, N, 8, 1) so that the receive DMA buffer may be + * enqueued + */ + tup->lcr_shadow = TEGRA_UART_DEFAULT_LSR; + tegra_set_baudrate(tup, TEGRA_UART_DEFAULT_BAUD); + tup->fcr_shadow |= UART_FCR_DMA_SELECT; + tegra_uart_write(tup, tup->fcr_shadow, UART_FCR); + + ret = tegra_uart_start_rx_dma(tup); + if (ret < 0) { + dev_err(tup->uport.dev, "Not able to start Rx DMA\n"); + return ret; + } + tup->rx_in_progress = 1; + + /* + * Enable IE_RXS for the receive status interrupts like line errros. + * Enable IE_RX_TIMEOUT to get the bytes which cannot be DMA'd. + * + * If using DMA mode, enable EORD instead of receive interrupt which + * will interrupt after the UART is done with the receive instead of + * the interrupt when the FIFO "threshold" is reached. + * + * EORD is different interrupt than RX_TIMEOUT - RX_TIMEOUT occurs when + * the DATA is sitting in the FIFO and couldn't be transferred to the + * DMA as the DMA size alignment(4 bytes) is not met. EORD will be + * triggered when there is a pause of the incomming data stream for 4 + * characters long. + * + * For pauses in the data which is not aligned to 4 bytes, we get + * both the EORD as well as RX_TIMEOUT - SW sees RX_TIMEOUT first + * then the EORD. + */ + tup->ier_shadow = UART_IER_RLSI | UART_IER_RTOIE | TEGRA_UART_IER_EORD; + tegra_uart_write(tup, tup->ier_shadow, UART_IER); + return 0; +} + +static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup, + bool dma_to_memory) +{ + struct dma_chan *dma_chan; + unsigned char *dma_buf; + dma_addr_t dma_phys; + int ret; + struct dma_slave_config dma_sconfig; + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_chan = dma_request_channel(mask, NULL, NULL); + if (!dma_chan) { + dev_err(tup->uport.dev, + "Dma channel is not available, will try later\n"); + return -EPROBE_DEFER; + } + + if (dma_to_memory) { + dma_buf = dma_alloc_coherent(tup->uport.dev, + TEGRA_UART_RX_DMA_BUFFER_SIZE, + &dma_phys, GFP_KERNEL); + if (!dma_buf) { + dev_err(tup->uport.dev, + "Not able to allocate the dma buffer\n"); + dma_release_channel(dma_chan); + return -ENOMEM; + } + } else { + dma_phys = dma_map_single(tup->uport.dev, + tup->uport.state->xmit.buf, UART_XMIT_SIZE, + DMA_TO_DEVICE); + dma_buf = tup->uport.state->xmit.buf; + } + + dma_sconfig.slave_id = tup->dma_req_sel; + if (dma_to_memory) { + dma_sconfig.src_addr = tup->uport.mapbase; + dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_sconfig.src_maxburst = 4; + } else { + dma_sconfig.dst_addr = tup->uport.mapbase; + dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_sconfig.dst_maxburst = 16; + } + + ret = dmaengine_slave_config(dma_chan, &dma_sconfig); + if (ret < 0) { + dev_err(tup->uport.dev, + "Dma slave config failed, err = %d\n", ret); + goto scrub; + } + + if (dma_to_memory) { + tup->rx_dma_chan = dma_chan; + tup->rx_dma_buf_virt = dma_buf; + tup->rx_dma_buf_phys = dma_phys; + } else { + tup->tx_dma_chan = dma_chan; + tup->tx_dma_buf_virt = dma_buf; + tup->tx_dma_buf_phys = dma_phys; + } + return 0; + +scrub: + dma_release_channel(dma_chan); + return ret; +} + +static void tegra_uart_dma_channel_free(struct tegra_uart_port *tup, + bool dma_to_memory) +{ + struct dma_chan *dma_chan; + + if (dma_to_memory) { + dma_free_coherent(tup->uport.dev, TEGRA_UART_RX_DMA_BUFFER_SIZE, + tup->rx_dma_buf_virt, tup->rx_dma_buf_phys); + dma_chan = tup->rx_dma_chan; + tup->rx_dma_chan = NULL; + tup->rx_dma_buf_phys = 0; + tup->rx_dma_buf_virt = NULL; + } else { + dma_unmap_single(tup->uport.dev, tup->tx_dma_buf_phys, + UART_XMIT_SIZE, DMA_TO_DEVICE); + dma_chan = tup->tx_dma_chan; + tup->tx_dma_chan = NULL; + tup->tx_dma_buf_phys = 0; + tup->tx_dma_buf_virt = NULL; + } + dma_release_channel(dma_chan); +} + +static int tegra_uart_startup(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + int ret; + + ret = tegra_uart_dma_channel_allocate(tup, false); + if (ret < 0) { + dev_err(u->dev, "Tx Dma allocation failed, err = %d\n", ret); + return ret; + } + + ret = tegra_uart_dma_channel_allocate(tup, true); + if (ret < 0) { + dev_err(u->dev, "Rx Dma allocation failed, err = %d\n", ret); + goto fail_rx_dma; + } + + ret = tegra_uart_hw_init(tup); + if (ret < 0) { + dev_err(u->dev, "Uart HW init failed, err = %d\n", ret); + goto fail_hw_init; + } + + ret = request_irq(u->irq, tegra_uart_isr, IRQF_DISABLED, + dev_name(u->dev), tup); + if (ret < 0) { + dev_err(u->dev, "Failed to register ISR for IRQ %d\n", u->irq); + goto fail_hw_init; + } + return 0; + +fail_hw_init: + tegra_uart_dma_channel_free(tup, true); +fail_rx_dma: + tegra_uart_dma_channel_free(tup, false); + return ret; +} + +static void tegra_uart_shutdown(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + + tegra_uart_hw_deinit(tup); + + tup->rx_in_progress = 0; + tup->tx_in_progress = 0; + + tegra_uart_dma_channel_free(tup, true); + tegra_uart_dma_channel_free(tup, false); + free_irq(u->irq, tup); +} + +static void tegra_uart_enable_ms(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + + if (tup->enable_modem_interrupt) { + tup->ier_shadow |= UART_IER_MSI; + tegra_uart_write(tup, tup->ier_shadow, UART_IER); + } +} + +static void tegra_uart_set_termios(struct uart_port *u, + struct ktermios *termios, struct ktermios *oldtermios) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + unsigned int baud; + unsigned long flags; + unsigned int lcr; + int symb_bit = 1; + struct clk *parent_clk = clk_get_parent(tup->uart_clk); + unsigned long parent_clk_rate = clk_get_rate(parent_clk); + int max_divider = (tup->cdata->support_clk_src_div) ? 0x7FFF : 0xFFFF; + + max_divider *= 16; + spin_lock_irqsave(&u->lock, flags); + + /* Changing configuration, it is safe to stop any rx now */ + if (tup->rts_active) + set_rts(tup, false); + + /* Clear all interrupts as configuration is going to be change */ + tegra_uart_write(tup, tup->ier_shadow | UART_IER_RDI, UART_IER); + tegra_uart_read(tup, UART_IER); + tegra_uart_write(tup, 0, UART_IER); + tegra_uart_read(tup, UART_IER); + + /* Parity */ + lcr = tup->lcr_shadow; + lcr &= ~UART_LCR_PARITY; + + /* CMSPAR isn't supported by this driver */ + termios->c_cflag &= ~CMSPAR; + + if ((termios->c_cflag & PARENB) == PARENB) { + symb_bit++; + if (termios->c_cflag & PARODD) { + lcr |= UART_LCR_PARITY; + lcr &= ~UART_LCR_EPAR; + lcr &= ~UART_LCR_SPAR; + } else { + lcr |= UART_LCR_PARITY; + lcr |= UART_LCR_EPAR; + lcr &= ~UART_LCR_SPAR; + } + } + + lcr &= ~UART_LCR_WLEN8; + switch (termios->c_cflag & CSIZE) { + case CS5: + lcr |= UART_LCR_WLEN5; + symb_bit += 5; + break; + case CS6: + lcr |= UART_LCR_WLEN6; + symb_bit += 6; + break; + case CS7: + lcr |= UART_LCR_WLEN7; + symb_bit += 7; + break; + default: + lcr |= UART_LCR_WLEN8; + symb_bit += 8; + break; + } + + /* Stop bits */ + if (termios->c_cflag & CSTOPB) { + lcr |= UART_LCR_STOP; + symb_bit += 2; + } else { + lcr &= ~UART_LCR_STOP; + symb_bit++; + } + + tegra_uart_write(tup, lcr, UART_LCR); + tup->lcr_shadow = lcr; + tup->symb_bit = symb_bit; + + /* Baud rate. */ + baud = uart_get_baud_rate(u, termios, oldtermios, + parent_clk_rate/max_divider, + parent_clk_rate/16); + spin_unlock_irqrestore(&u->lock, flags); + tegra_set_baudrate(tup, baud); + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); + spin_lock_irqsave(&u->lock, flags); + + /* Flow control */ + if (termios->c_cflag & CRTSCTS) { + tup->mcr_shadow |= TEGRA_UART_MCR_CTS_EN; + tup->mcr_shadow &= ~TEGRA_UART_MCR_RTS_EN; + tegra_uart_write(tup, tup->mcr_shadow, UART_MCR); + /* if top layer has asked to set rts active then do so here */ + if (tup->rts_active) + set_rts(tup, true); + } else { + tup->mcr_shadow &= ~TEGRA_UART_MCR_CTS_EN; + tup->mcr_shadow &= ~TEGRA_UART_MCR_RTS_EN; + tegra_uart_write(tup, tup->mcr_shadow, UART_MCR); + } + + /* update the port timeout based on new settings */ + uart_update_timeout(u, termios->c_cflag, baud); + + /* Make sure all write has completed */ + tegra_uart_read(tup, UART_IER); + + /* Reenable interrupt */ + tegra_uart_write(tup, tup->ier_shadow, UART_IER); + tegra_uart_read(tup, UART_IER); + + spin_unlock_irqrestore(&u->lock, flags); + return; +} + +/* + * Flush any TX data submitted for DMA and PIO. Called when the + * TX circular buffer is reset. + */ +static void tegra_uart_flush_buffer(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + + tup->tx_bytes = 0; + if (tup->tx_dma_chan) + dmaengine_terminate_all(tup->tx_dma_chan); + return; +} + +static const char *tegra_uart_type(struct uart_port *u) +{ + return TEGRA_UART_TYPE; +} + +static struct uart_ops tegra_uart_ops = { + .tx_empty = tegra_uart_tx_empty, + .set_mctrl = tegra_uart_set_mctrl, + .get_mctrl = tegra_uart_get_mctrl, + .stop_tx = tegra_uart_stop_tx, + .start_tx = tegra_uart_start_tx, + .stop_rx = tegra_uart_stop_rx, + .flush_buffer = tegra_uart_flush_buffer, + .enable_ms = tegra_uart_enable_ms, + .break_ctl = tegra_uart_break_ctl, + .startup = tegra_uart_startup, + .shutdown = tegra_uart_shutdown, + .set_termios = tegra_uart_set_termios, + .type = tegra_uart_type, + .request_port = tegra_uart_request_port, + .release_port = tegra_uart_release_port, +}; + +static struct uart_driver tegra_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "tegra_hsuart", + .dev_name = "ttyTHS", + .cons = 0, + .nr = TEGRA_UART_MAXIMUM, +}; + +static int tegra_uart_parse_dt(struct platform_device *pdev, + struct tegra_uart_port *tup) +{ + struct device_node *np = pdev->dev.of_node; + u32 of_dma[2]; + int port; + + if (of_property_read_u32_array(np, "nvidia,dma-request-selector", + of_dma, 2) >= 0) { + tup->dma_req_sel = of_dma[1]; + } else { + dev_err(&pdev->dev, "missing dma requestor in device tree\n"); + return -EINVAL; + } + + port = of_alias_get_id(np, "serial"); + if (port < 0) { + dev_err(&pdev->dev, "failed to get alias id, errno %d\n", port); + return port; + } + tup->uport.line = port; + + tup->enable_modem_interrupt = of_property_read_bool(np, + "nvidia,enable-modem-interrupt"); + return 0; +} + +struct tegra_uart_chip_data tegra20_uart_chip_data = { + .tx_fifo_full_status = false, + .allow_txfifo_reset_fifo_mode = true, + .support_clk_src_div = false, +}; + +struct tegra_uart_chip_data tegra30_uart_chip_data = { + .tx_fifo_full_status = true, + .allow_txfifo_reset_fifo_mode = false, + .support_clk_src_div = true, +}; + +static struct of_device_id tegra_uart_of_match[] = { + { + .compatible = "nvidia,tegra30-hsuart", + .data = &tegra30_uart_chip_data, + }, { + .compatible = "nvidia,tegra20-hsuart", + .data = &tegra20_uart_chip_data, + }, { + }, +}; +MODULE_DEVICE_TABLE(of, tegra_uart_of_match); + +static int tegra_uart_probe(struct platform_device *pdev) +{ + struct tegra_uart_port *tup; + struct uart_port *u; + struct resource *resource; + int ret; + const struct tegra_uart_chip_data *cdata; + const struct of_device_id *match; + + match = of_match_device(of_match_ptr(tegra_uart_of_match), + &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + cdata = match->data; + + tup = devm_kzalloc(&pdev->dev, sizeof(*tup), GFP_KERNEL); + if (!tup) { + dev_err(&pdev->dev, "Failed to allocate memory for tup\n"); + return -ENOMEM; + } + + ret = tegra_uart_parse_dt(pdev, tup); + if (ret < 0) + return ret; + + u = &tup->uport; + u->dev = &pdev->dev; + u->ops = &tegra_uart_ops; + u->type = PORT_TEGRA; + u->fifosize = 32; + tup->cdata = cdata; + + platform_set_drvdata(pdev, tup); + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!resource) { + dev_err(&pdev->dev, "No IO memory resource\n"); + return -ENODEV; + } + + u->mapbase = resource->start; + u->membase = devm_request_and_ioremap(&pdev->dev, resource); + if (!u->membase) { + dev_err(&pdev->dev, "memregion/iomap address req failed\n"); + return -EADDRNOTAVAIL; + } + + tup->uart_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(tup->uart_clk)) { + dev_err(&pdev->dev, "Couldn't get the clock\n"); + return PTR_ERR(tup->uart_clk); + } + + u->iotype = UPIO_MEM32; + u->irq = platform_get_irq(pdev, 0); + u->regshift = 2; + ret = uart_add_one_port(&tegra_uart_driver, u); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to add uart port, err %d\n", ret); + return ret; + } + return ret; +} + +static int tegra_uart_remove(struct platform_device *pdev) +{ + struct tegra_uart_port *tup = platform_get_drvdata(pdev); + struct uart_port *u = &tup->uport; + + uart_remove_one_port(&tegra_uart_driver, u); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tegra_uart_suspend(struct device *dev) +{ + struct tegra_uart_port *tup = dev_get_drvdata(dev); + struct uart_port *u = &tup->uport; + + return uart_suspend_port(&tegra_uart_driver, u); +} + +static int tegra_uart_resume(struct device *dev) +{ + struct tegra_uart_port *tup = dev_get_drvdata(dev); + struct uart_port *u = &tup->uport; + + return uart_resume_port(&tegra_uart_driver, u); +} +#endif + +static const struct dev_pm_ops tegra_uart_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tegra_uart_suspend, tegra_uart_resume) +}; + +static struct platform_driver tegra_uart_platform_driver = { + .probe = tegra_uart_probe, + .remove = tegra_uart_remove, + .driver = { + .name = "serial-tegra", + .of_match_table = of_match_ptr(tegra_uart_of_match), + .pm = &tegra_uart_pm_ops, + }, +}; + +static int __init tegra_uart_init(void) +{ + int ret; + + ret = uart_register_driver(&tegra_uart_driver); + if (ret < 0) { + pr_err("Could not register %s driver\n", + tegra_uart_driver.driver_name); + return ret; + } + + ret = platform_driver_register(&tegra_uart_platform_driver); + if (ret < 0) { + pr_err("Uart platfrom driver register failed, e = %d\n", ret); + uart_unregister_driver(&tegra_uart_driver); + return ret; + } + return 0; +} + +static void __exit tegra_uart_exit(void) +{ + pr_info("Unloading tegra uart driver\n"); + platform_driver_unregister(&tegra_uart_platform_driver); + uart_unregister_driver(&tegra_uart_driver); +} + +module_init(tegra_uart_init); +module_exit(tegra_uart_exit); + +MODULE_ALIAS("platform:serial-tegra"); +MODULE_DESCRIPTION("High speed UART driver for tegra chipset"); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From ea28fd56fcde69af768135e428093f94c5ca6a88 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 11 Jan 2013 11:50:23 +0530 Subject: serial/arc-uart: switch to devicetree based probing * DT binding for arc-uart * With alll the bits in place we can now use DT probing. Note that there's a bit of kludge right now because earlyprintk portion of driver can't use the DT infrastrcuture to get resoures/plat_data. This requires some infrastructre changes to of_flat_ framework Signed-off-by: Vineet Gupta Cc: Grant Likely Cc: Arnd Bergmann Cc: Alan Cox Cc: devicetree-discuss@lists.ozlabs.org Cc: Rob Herring Cc: Rob Landley Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/tty/serial/arc-uart.txt | 26 +++++++++++++ drivers/tty/serial/arc_uart.c | 43 ++++++++++++++++++++-- 2 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/tty/serial/arc-uart.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/tty/serial/arc-uart.txt b/Documentation/devicetree/bindings/tty/serial/arc-uart.txt new file mode 100644 index 000000000000..c3bd8f9c9997 --- /dev/null +++ b/Documentation/devicetree/bindings/tty/serial/arc-uart.txt @@ -0,0 +1,26 @@ +* Synopsys ARC UART : Non standard UART used in some of the ARC FPGA boards + +Required properties: +- compatible : "snps,arc-uart" +- reg : offset and length of the register set for the device. +- interrupts : device interrupt +- clock-frequency : the input clock frequency for the UART +- baud : baud rate for UART + +e.g. + +arcuart0: serial@c0fc1000 { + compatible = "snps,arc-uart"; + reg = <0xc0fc1000 0x100>; + interrupts = <5>; + clock-frequency = <80000000>; + baud = <115200>; + status = "okay"; +}; + +Note: Each port should have an alias correctly numbered in "aliases" node. + +e.g. +aliases { + serial0 = &arcuart0; +}; diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index 2db64105677b..b46860104312 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -37,6 +37,8 @@ #include #include #include +#include +#include /************************************* * ARC UART Hardware Specs @@ -537,8 +539,26 @@ arc_uart_init_one(struct platform_device *pdev, int dev_id) return -ENODEV; uart->is_emulated = !!plat_data[0]; /* workaround ISS bug */ - uart->port.uartclk = plat_data[1]; - uart->baud = plat_data[2]; + + if (is_early_platform_device(pdev)) { + uart->port.uartclk = plat_data[1]; + uart->baud = plat_data[2]; + } else { + struct device_node *np = pdev->dev.of_node; + u32 val; + + if (of_property_read_u32(np, "clock-frequency", &val)) { + dev_err(&pdev->dev, "clock-frequency property NOTset\n"); + return -EINVAL; + } + uart->port.uartclk = val; + + if (of_property_read_u32(np, "baud", &val)) { + dev_err(&pdev->dev, "baud property NOT set\n"); + return -EINVAL; + } + uart->baud = val; + } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) @@ -673,8 +693,18 @@ static int __init arc_serial_probe_earlyprintk(struct platform_device *pdev) static int arc_serial_probe(struct platform_device *pdev) { int rc, dev_id; + struct device_node *np = pdev->dev.of_node; + + /* no device tree device */ + if (!np) + return -ENODEV; + + dev_id = of_alias_get_id(np, "serial"); + if (dev_id < 0) { + dev_err(&pdev->dev, "failed to get alias id: %d\n", dev_id); + return dev_id; + } - dev_id = pdev->id < 0 ? 0 : pdev->id; rc = arc_uart_init_one(pdev, dev_id); if (rc) return rc; @@ -689,12 +719,19 @@ static int arc_serial_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id arc_uart_dt_ids[] = { + { .compatible = "snps,arc-uart" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, arc_uart_dt_ids); + static struct platform_driver arc_platform_driver = { .probe = arc_serial_probe, .remove = arc_serial_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, + .of_match_table = arc_uart_dt_ids, }, }; -- cgit v1.2.3 From 6995b8cb9622bf574ac6f309e69288e7d0f76ece Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Wed, 16 Jan 2013 13:05:12 +0100 Subject: ASoC: tegra: add tegra machine driver using wm9712 codec This adds a very simple machine driver using the Wolfson wm9712 AC97 codec. Signed-off-by: Lucas Stach Reviewed-by: Stephen Warren Signed-off-by: Mark Brown --- .../bindings/sound/nvidia,tegra-audio-wm9712.txt | 51 ++++++ sound/soc/tegra/Kconfig | 9 ++ sound/soc/tegra/Makefile | 2 + sound/soc/tegra/tegra_wm9712.c | 176 +++++++++++++++++++++ 4 files changed, 238 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt create mode 100644 sound/soc/tegra/tegra_wm9712.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt new file mode 100644 index 000000000000..be35d34e8b26 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt @@ -0,0 +1,51 @@ +NVIDIA Tegra audio complex + +Required properties: +- compatible : "nvidia,tegra-audio-wm9712" +- nvidia,model : The user-visible name of this sound complex. +- nvidia,audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. Valid names for sources and + sinks are the WM9712's pins, and the jacks on the board: + + WM9712 pins: + + * MONOOUT + * HPOUTL + * HPOUTR + * LOUT2 + * ROUT2 + * OUT3 + * LINEINL + * LINEINR + * PHONE + * PCBEEP + * MIC1 + * MIC2 + * Mic Bias + + Board connectors: + + * Headphone + * LineIn + * Mic + +- nvidia,ac97-controller : The phandle of the Tegra AC97 controller + + +Example: + +sound { + compatible = "nvidia,tegra-audio-wm9712-colibri_t20", + "nvidia,tegra-audio-wm9712"; + nvidia,model = "Toradex Colibri T20"; + + nvidia,audio-routing = + "Headphone", "HPOUTL", + "Headphone", "HPOUTR", + "LineIn", "LINEINL", + "LineIn", "LINEINR", + "Mic", "MIC1"; + + nvidia,ac97-controller = <&ac97>; +}; diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 4b3a2b8cb788..dbc27ce1d4de 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -80,6 +80,15 @@ config SND_SOC_TEGRA_WM8903 boards using the WM8093 codec. Currently, the supported boards are Harmony, Ventana, Seaboard, Kaen, and Aebl. +config SND_SOC_TEGRA_WM9712 + tristate "SoC Audio support for Tegra boards using a WM9712 codec" + depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC + select SND_SOC_TEGRA20_AC97 + select SND_SOC_WM9712 + help + Say Y or M here if you want to add support for SoC audio on Tegra + boards using the WM9712 (or compatible) codec. + config SND_SOC_TEGRA_TRIMSLICE tristate "SoC Audio support for TrimSlice board" depends on SND_SOC_TEGRA && I2C diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 02513d9edf22..416a14bde41b 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -20,10 +20,12 @@ obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o # Tegra machine Support snd-soc-tegra-wm8753-objs := tegra_wm8753.o snd-soc-tegra-wm8903-objs := tegra_wm8903.o +snd-soc-tegra-wm9712-objs := tegra_wm9712.o snd-soc-tegra-trimslice-objs := trimslice.o snd-soc-tegra-alc5632-objs := tegra_alc5632.o obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o +obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o diff --git a/sound/soc/tegra/tegra_wm9712.c b/sound/soc/tegra/tegra_wm9712.c new file mode 100644 index 000000000000..cdbd2f0a23bc --- /dev/null +++ b/sound/soc/tegra/tegra_wm9712.c @@ -0,0 +1,176 @@ +/* + * tegra20_wm9712.c - Tegra machine ASoC driver for boards using WM9712 codec. + * + * Copyright 2012 Lucas Stach + * + * Partly based on code copyright/by: + * Copyright 2011,2012 Toradex Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define DRV_NAME "tegra-snd-wm9712" + +struct tegra_wm9712 { + struct platform_device *codec; +}; + +static const struct snd_soc_dapm_widget tegra_wm9712_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_LINE("LineIn", NULL), + SND_SOC_DAPM_MIC("Mic", NULL), +}; + +static int tegra_wm9712_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_codec *codec = codec_dai->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + + snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); + + return snd_soc_dapm_sync(dapm); +} + +static struct snd_soc_dai_link tegra_wm9712_dai = { + .name = "AC97 HiFi", + .stream_name = "AC97 HiFi", + .cpu_dai_name = "tegra-ac97-pcm", + .codec_dai_name = "wm9712-hifi", + .codec_name = "wm9712-codec", + .init = tegra_wm9712_init, +}; + +static struct snd_soc_card snd_soc_tegra_wm9712 = { + .name = "tegra-wm9712", + .owner = THIS_MODULE, + .dai_link = &tegra_wm9712_dai, + .num_links = 1, + + .dapm_widgets = tegra_wm9712_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra_wm9712_dapm_widgets), + .fully_routed = true, +}; + +static int tegra_wm9712_driver_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct snd_soc_card *card = &snd_soc_tegra_wm9712; + struct tegra_wm9712 *machine; + int ret; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "No platform data supplied\n"); + return -EINVAL; + } + + machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm9712), + GFP_KERNEL); + if (!machine) { + dev_err(&pdev->dev, "Can't allocate tegra_wm9712 struct\n"); + return -ENOMEM; + } + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, machine); + + machine->codec = platform_device_alloc("wm9712-codec", -1); + if (!machine->codec) { + dev_err(&pdev->dev, "Can't allocate wm9712 platform device\n"); + return -ENOMEM; + } + + ret = platform_device_add(machine->codec); + if (ret) + goto codec_put; + + ret = snd_soc_of_parse_card_name(card, "nvidia,model"); + if (ret) + goto codec_unregister; + + ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing"); + if (ret) + goto codec_unregister; + + tegra_wm9712_dai.cpu_of_node = of_parse_phandle(np, + "nvidia,ac97-controller", 0); + if (!tegra_wm9712_dai.cpu_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,ac97-controller' missing or invalid\n"); + ret = -EINVAL; + goto codec_unregister; + } + + tegra_wm9712_dai.platform_of_node = tegra_wm9712_dai.cpu_of_node; + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto codec_unregister; + } + + return 0; + +codec_unregister: + platform_device_del(machine->codec); +codec_put: + platform_device_put(machine->codec); + return ret; +} + +static int tegra_wm9712_driver_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct tegra_wm9712 *machine = snd_soc_card_get_drvdata(card); + + snd_soc_unregister_card(card); + + platform_device_unregister(machine->codec); + + return 0; +} + +static const struct of_device_id tegra_wm9712_of_match[] __devinitconst = { + { .compatible = "nvidia,tegra-audio-wm9712", }, + {}, +}; + +static struct platform_driver tegra_wm9712_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = tegra_wm9712_of_match, + }, + .probe = tegra_wm9712_driver_probe, + .remove = tegra_wm9712_driver_remove, +}; +module_platform_driver(tegra_wm9712_driver); + +MODULE_AUTHOR("Lucas Stach"); +MODULE_DESCRIPTION("Tegra+WM9712 machine ASoC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, tegra_wm9712_of_match); -- cgit v1.2.3 From 337dc3a7684cf6577b5595b9bb96e1af06baec41 Mon Sep 17 00:00:00 2001 From: Praveen Paneri Date: Fri, 23 Nov 2012 16:03:06 +0530 Subject: usb: phy: samsung: Introducing usb phy driver for hsotg This driver uses usb_phy interface to interact with s3c-hsotg. Supports phy_init and phy_shutdown functions to enable/disable usb phy. Support will be extended to host controllers and more Samsung SoCs. Signed-off-by: Praveen Paneri Acked-by: Heiko Stuebner Acked-by: Kyungmin Park Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/samsung-usbphy.txt | 11 + drivers/usb/phy/Kconfig | 8 + drivers/usb/phy/Makefile | 1 + drivers/usb/phy/samsung-usbphy.c | 354 +++++++++++++++++++++ include/linux/platform_data/samsung-usbphy.h | 27 ++ 5 files changed, 401 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/samsung-usbphy.txt create mode 100644 drivers/usb/phy/samsung-usbphy.c create mode 100644 include/linux/platform_data/samsung-usbphy.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt new file mode 100644 index 000000000000..7b26e2d6ea04 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt @@ -0,0 +1,11 @@ +* Samsung's usb phy transceiver + +The Samsung's phy transceiver is used for controlling usb otg phy for +s3c-hsotg usb device controller. +TODO: Adding the PHY binding with controller(s) according to the under +developement generic PHY driver. + +Required properties: +- compatible : should be "samsung,exynos4210-usbphy" +- reg : base physical address of the phy registers and length of memory mapped + region. diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 5de6e7f39f9c..36a85b675429 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -45,3 +45,11 @@ config USB_RCAR_PHY To compile this driver as a module, choose M here: the module will be called rcar-phy. + +config SAMSUNG_USBPHY + bool "Samsung USB PHY controller Driver" + depends on USB_S3C_HSOTG + select USB_OTG_UTILS + help + Enable this to support Samsung USB phy controller for samsung + SoCs. diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 1a579a860a03..ec304f642402 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_USB_ISP1301) += isp1301.o obj-$(CONFIG_MV_U3D_PHY) += mv_u3d_phy.o obj-$(CONFIG_USB_EHCI_TEGRA) += tegra_usb_phy.o obj-$(CONFIG_USB_RCAR_PHY) += rcar-phy.o +obj-$(CONFIG_SAMSUNG_USBPHY) += samsung-usbphy.o diff --git a/drivers/usb/phy/samsung-usbphy.c b/drivers/usb/phy/samsung-usbphy.c new file mode 100644 index 000000000000..5c5e1bb5de7b --- /dev/null +++ b/drivers/usb/phy/samsung-usbphy.c @@ -0,0 +1,354 @@ +/* linux/drivers/usb/phy/samsung-usbphy.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Praveen Paneri + * + * Samsung USB2.0 High-speed OTG transceiver, talks to S3C HS OTG controller + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions */ + +#define SAMSUNG_PHYPWR (0x00) + +#define PHYPWR_NORMAL_MASK (0x19 << 0) +#define PHYPWR_OTG_DISABLE (0x1 << 4) +#define PHYPWR_ANALOG_POWERDOWN (0x1 << 3) +#define PHYPWR_FORCE_SUSPEND (0x1 << 1) +/* For Exynos4 */ +#define PHYPWR_NORMAL_MASK_PHY0 (0x39 << 0) +#define PHYPWR_SLEEP_PHY0 (0x1 << 5) + +#define SAMSUNG_PHYCLK (0x04) + +#define PHYCLK_MODE_USB11 (0x1 << 6) +#define PHYCLK_EXT_OSC (0x1 << 5) +#define PHYCLK_COMMON_ON_N (0x1 << 4) +#define PHYCLK_ID_PULL (0x1 << 2) +#define PHYCLK_CLKSEL_MASK (0x3 << 0) +#define PHYCLK_CLKSEL_48M (0x0 << 0) +#define PHYCLK_CLKSEL_12M (0x2 << 0) +#define PHYCLK_CLKSEL_24M (0x3 << 0) + +#define SAMSUNG_RSTCON (0x08) + +#define RSTCON_PHYLINK_SWRST (0x1 << 2) +#define RSTCON_HLINK_SWRST (0x1 << 1) +#define RSTCON_SWRST (0x1 << 0) + +#ifndef MHZ +#define MHZ (1000*1000) +#endif + +enum samsung_cpu_type { + TYPE_S3C64XX, + TYPE_EXYNOS4210, +}; + +/* + * struct samsung_usbphy - transceiver driver state + * @phy: transceiver structure + * @plat: platform data + * @dev: The parent device supplied to the probe function + * @clk: usb phy clock + * @regs: usb phy register memory base + * @ref_clk_freq: reference clock frequency selection + * @cpu_type: machine identifier + */ +struct samsung_usbphy { + struct usb_phy phy; + struct samsung_usbphy_data *plat; + struct device *dev; + struct clk *clk; + void __iomem *regs; + int ref_clk_freq; + int cpu_type; +}; + +#define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) + +/* + * Returns reference clock frequency selection value + */ +static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) +{ + struct clk *ref_clk; + int refclk_freq = 0; + + ref_clk = clk_get(sphy->dev, "xusbxti"); + if (IS_ERR(ref_clk)) { + dev_err(sphy->dev, "Failed to get reference clock\n"); + return PTR_ERR(ref_clk); + } + + switch (clk_get_rate(ref_clk)) { + case 12 * MHZ: + refclk_freq = PHYCLK_CLKSEL_12M; + break; + case 24 * MHZ: + refclk_freq = PHYCLK_CLKSEL_24M; + break; + case 48 * MHZ: + refclk_freq = PHYCLK_CLKSEL_48M; + break; + default: + if (sphy->cpu_type == TYPE_S3C64XX) + refclk_freq = PHYCLK_CLKSEL_48M; + else + refclk_freq = PHYCLK_CLKSEL_24M; + break; + } + clk_put(ref_clk); + + return refclk_freq; +} + +static void samsung_usbphy_enable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phypwr; + u32 phyclk; + u32 rstcon; + + /* set clock frequency for PLL */ + phyclk = sphy->ref_clk_freq; + phypwr = readl(regs + SAMSUNG_PHYPWR); + rstcon = readl(regs + SAMSUNG_RSTCON); + + switch (sphy->cpu_type) { + case TYPE_S3C64XX: + phyclk &= ~PHYCLK_COMMON_ON_N; + phypwr &= ~PHYPWR_NORMAL_MASK; + rstcon |= RSTCON_SWRST; + break; + case TYPE_EXYNOS4210: + phypwr &= ~PHYPWR_NORMAL_MASK_PHY0; + rstcon |= RSTCON_SWRST; + default: + break; + } + + writel(phyclk, regs + SAMSUNG_PHYCLK); + /* Configure PHY0 for normal operation*/ + writel(phypwr, regs + SAMSUNG_PHYPWR); + /* reset all ports of PHY and Link */ + writel(rstcon, regs + SAMSUNG_RSTCON); + udelay(10); + rstcon &= ~RSTCON_SWRST; + writel(rstcon, regs + SAMSUNG_RSTCON); +} + +static void samsung_usbphy_disable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phypwr; + + phypwr = readl(regs + SAMSUNG_PHYPWR); + + switch (sphy->cpu_type) { + case TYPE_S3C64XX: + phypwr |= PHYPWR_NORMAL_MASK; + break; + case TYPE_EXYNOS4210: + phypwr |= PHYPWR_NORMAL_MASK_PHY0; + default: + break; + } + + /* Disable analog and otg block power */ + writel(phypwr, regs + SAMSUNG_PHYPWR); +} + +/* + * The function passed to the usb driver for phy initialization + */ +static int samsung_usbphy_init(struct usb_phy *phy) +{ + struct samsung_usbphy *sphy; + int ret = 0; + + sphy = phy_to_sphy(phy); + + /* Enable the phy clock */ + ret = clk_prepare_enable(sphy->clk); + if (ret) { + dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); + return ret; + } + + /* Disable phy isolation */ + if (sphy->plat && sphy->plat->pmu_isolation) + sphy->plat->pmu_isolation(false); + + /* Initialize usb phy registers */ + samsung_usbphy_enable(sphy); + + /* Disable the phy clock */ + clk_disable_unprepare(sphy->clk); + return ret; +} + +/* + * The function passed to the usb driver for phy shutdown + */ +static void samsung_usbphy_shutdown(struct usb_phy *phy) +{ + struct samsung_usbphy *sphy; + + sphy = phy_to_sphy(phy); + + if (clk_prepare_enable(sphy->clk)) { + dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); + return; + } + + /* De-initialize usb phy registers */ + samsung_usbphy_disable(sphy); + + /* Enable phy isolation */ + if (sphy->plat && sphy->plat->pmu_isolation) + sphy->plat->pmu_isolation(true); + + clk_disable_unprepare(sphy->clk); +} + +static const struct of_device_id samsung_usbphy_dt_match[]; + +static inline int samsung_usbphy_get_driver_data(struct platform_device *pdev) +{ + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(samsung_usbphy_dt_match, + pdev->dev.of_node); + return (int) match->data; + } + + return platform_get_device_id(pdev)->driver_data; +} + +static int __devinit samsung_usbphy_probe(struct platform_device *pdev) +{ + struct samsung_usbphy *sphy; + struct samsung_usbphy_data *pdata; + struct device *dev = &pdev->dev; + struct resource *phy_mem; + void __iomem *phy_base; + struct clk *clk; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "%s: no platform data defined\n", __func__); + return -EINVAL; + } + + phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!phy_mem) { + dev_err(dev, "%s: missing mem resource\n", __func__); + return -ENODEV; + } + + phy_base = devm_request_and_ioremap(dev, phy_mem); + if (!phy_base) { + dev_err(dev, "%s: register mapping failed\n", __func__); + return -ENXIO; + } + + sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); + if (!sphy) + return -ENOMEM; + + clk = devm_clk_get(dev, "otg"); + if (IS_ERR(clk)) { + dev_err(dev, "Failed to get otg clock\n"); + return PTR_ERR(clk); + } + + sphy->dev = &pdev->dev; + sphy->plat = pdata; + sphy->regs = phy_base; + sphy->clk = clk; + sphy->phy.dev = sphy->dev; + sphy->phy.label = "samsung-usbphy"; + sphy->phy.init = samsung_usbphy_init; + sphy->phy.shutdown = samsung_usbphy_shutdown; + sphy->cpu_type = samsung_usbphy_get_driver_data(pdev); + sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); + + platform_set_drvdata(pdev, sphy); + + return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2); +} + +static int __exit samsung_usbphy_remove(struct platform_device *pdev) +{ + struct samsung_usbphy *sphy = platform_get_drvdata(pdev); + + usb_remove_phy(&sphy->phy); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id samsung_usbphy_dt_match[] = { + { + .compatible = "samsung,s3c64xx-usbphy", + .data = (void *)TYPE_S3C64XX, + }, { + .compatible = "samsung,exynos4210-usbphy", + .data = (void *)TYPE_EXYNOS4210, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); +#endif + +static struct platform_device_id samsung_usbphy_driver_ids[] = { + { + .name = "s3c64xx-usbphy", + .driver_data = TYPE_S3C64XX, + }, { + .name = "exynos4210-usbphy", + .driver_data = TYPE_EXYNOS4210, + }, + {}, +}; + +MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids); + +static struct platform_driver samsung_usbphy_driver = { + .probe = samsung_usbphy_probe, + .remove = __devexit_p(samsung_usbphy_remove), + .id_table = samsung_usbphy_driver_ids, + .driver = { + .name = "samsung-usbphy", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(samsung_usbphy_dt_match), + }, +}; + +module_platform_driver(samsung_usbphy_driver); + +MODULE_DESCRIPTION("Samsung USB phy controller"); +MODULE_AUTHOR("Praveen Paneri "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:samsung-usbphy"); diff --git a/include/linux/platform_data/samsung-usbphy.h b/include/linux/platform_data/samsung-usbphy.h new file mode 100644 index 000000000000..1bd24cba982b --- /dev/null +++ b/include/linux/platform_data/samsung-usbphy.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * http://www.samsung.com/ + * Author: Praveen Paneri + * + * Defines platform data for samsung usb phy driver. + * + * 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. + */ + +#ifndef __SAMSUNG_USBPHY_PLATFORM_H +#define __SAMSUNG_USBPHY_PLATFORM_H + +/** + * samsung_usbphy_data - Platform data for USB PHY driver. + * @pmu_isolation: Function to control usb phy isolation in PMU. + */ +struct samsung_usbphy_data { + void (*pmu_isolation)(int on); +}; + +extern void samsung_usbphy_set_pdata(struct samsung_usbphy_data *pd); + +#endif /* __SAMSUNG_USBPHY_PLATFORM_H */ -- cgit v1.2.3 From 69f0946a8db75d6de377c6a5796aa2e417c3c83a Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Tue, 15 Jan 2013 11:40:25 +0530 Subject: usb: phy: samsung: Add support to set pmu isolation Adding support to parse device node data in order to get required properties to set pmu isolation for usb-phy. Signed-off-by: Vivek Gautam Acked-by: Kukjin Kim Reviewed-by: Sylwester Nawrocki Reviewed-by: Doug Anderson Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/samsung-usbphy.txt | 36 +++++ drivers/usb/phy/samsung-usbphy.c | 163 ++++++++++++++++++--- 2 files changed, 177 insertions(+), 22 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt index 7b26e2d6ea04..22d06cfdb076 100644 --- a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt +++ b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt @@ -9,3 +9,39 @@ Required properties: - compatible : should be "samsung,exynos4210-usbphy" - reg : base physical address of the phy registers and length of memory mapped region. + +Optional properties: +- #address-cells: should be '1' when usbphy node has a child node with 'reg' + property. +- #size-cells: should be '1' when usbphy node has a child node with 'reg' + property. +- ranges: allows valid translation between child's address space and parent's + address space. + +- The child node 'usbphy-sys' to the node 'usbphy' is for the system controller + interface for usb-phy. It should provide the following information required by + usb-phy controller to control phy. + - reg : base physical address of PHY_CONTROL registers. + The size of this register is the total sum of size of all PHY_CONTROL + registers that the SoC has. For example, the size will be + '0x4' in case we have only one PHY_CONTROL register (e.g. + OTHERS register in S3C64XX or USB_PHY_CONTROL register in S5PV210) + and, '0x8' in case we have two PHY_CONTROL registers (e.g. + USBDEVICE_PHY_CONTROL and USBHOST_PHY_CONTROL registers in exynos4x). + and so on. + +Example: + - Exynos4210 + + usbphy@125B0000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "samsung,exynos4210-usbphy"; + reg = <0x125B0000 0x100>; + ranges; + + usbphy-sys { + /* USB device and host PHY_CONTROL registers */ + reg = <0x10020704 0x8>; + }; + }; diff --git a/drivers/usb/phy/samsung-usbphy.c b/drivers/usb/phy/samsung-usbphy.c index 5c5e1bb5de7b..30aebb59d803 100644 --- a/drivers/usb/phy/samsung-usbphy.c +++ b/drivers/usb/phy/samsung-usbphy.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -60,20 +61,46 @@ #define MHZ (1000*1000) #endif +#define S3C64XX_USBPHY_ENABLE (0x1 << 16) +#define EXYNOS_USBPHY_ENABLE (0x1 << 0) + enum samsung_cpu_type { TYPE_S3C64XX, TYPE_EXYNOS4210, }; +/* + * struct samsung_usbphy_drvdata - driver data for various SoC variants + * @cpu_type: machine identifier + * @devphy_en_mask: device phy enable mask for PHY CONTROL register + * @devphy_reg_offset: offset to DEVICE PHY CONTROL register from + * mapped address of system controller. + * + * Here we have a separate mask for device type phy. + * Having different masks for host and device type phy helps + * in setting independent masks in case of SoCs like S5PV210, + * in which PHY0 and PHY1 enable bits belong to same register + * placed at position 0 and 1 respectively. + * Although for newer SoCs like exynos these bits belong to + * different registers altogether placed at position 0. + */ +struct samsung_usbphy_drvdata { + int cpu_type; + int devphy_en_mask; + u32 devphy_reg_offset; +}; + /* * struct samsung_usbphy - transceiver driver state * @phy: transceiver structure * @plat: platform data * @dev: The parent device supplied to the probe function * @clk: usb phy clock - * @regs: usb phy register memory base + * @regs: usb phy controller registers memory base + * @pmuregs: USB device PHY_CONTROL register memory base * @ref_clk_freq: reference clock frequency selection - * @cpu_type: machine identifier + * @drv_data: driver data available for different SoCs + * @lock: lock for phy operations */ struct samsung_usbphy { struct usb_phy phy; @@ -81,12 +108,66 @@ struct samsung_usbphy { struct device *dev; struct clk *clk; void __iomem *regs; + void __iomem *pmuregs; int ref_clk_freq; - int cpu_type; + const struct samsung_usbphy_drvdata *drv_data; + spinlock_t lock; }; #define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) +static int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) +{ + struct device_node *usbphy_sys; + + /* Getting node for system controller interface for usb-phy */ + usbphy_sys = of_get_child_by_name(sphy->dev->of_node, "usbphy-sys"); + if (!usbphy_sys) { + dev_err(sphy->dev, "No sys-controller interface for usb-phy\n"); + return -ENODEV; + } + + sphy->pmuregs = of_iomap(usbphy_sys, 0); + + of_node_put(usbphy_sys); + + if (sphy->pmuregs == NULL) { + dev_err(sphy->dev, "Can't get usb-phy pmu control register\n"); + return -ENODEV; + } + + return 0; +} + +/* + * Set isolation here for phy. + * Here 'on = true' would mean USB PHY block is isolated, hence + * de-activated and vice-versa. + */ +static void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) +{ + void __iomem *reg; + u32 reg_val; + u32 en_mask; + + if (!sphy->pmuregs) { + dev_warn(sphy->dev, "Can't set pmu isolation\n"); + return; + } + + reg = sphy->pmuregs + sphy->drv_data->devphy_reg_offset; + en_mask = sphy->drv_data->devphy_en_mask; + + reg_val = readl(reg); + + if (on) + reg_val &= ~en_mask; + else + reg_val |= en_mask; + + writel(reg_val, reg); +} + /* * Returns reference clock frequency selection value */ @@ -112,7 +193,7 @@ static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) refclk_freq = PHYCLK_CLKSEL_48M; break; default: - if (sphy->cpu_type == TYPE_S3C64XX) + if (sphy->drv_data->cpu_type == TYPE_S3C64XX) refclk_freq = PHYCLK_CLKSEL_48M; else refclk_freq = PHYCLK_CLKSEL_24M; @@ -135,7 +216,7 @@ static void samsung_usbphy_enable(struct samsung_usbphy *sphy) phypwr = readl(regs + SAMSUNG_PHYPWR); rstcon = readl(regs + SAMSUNG_RSTCON); - switch (sphy->cpu_type) { + switch (sphy->drv_data->cpu_type) { case TYPE_S3C64XX: phyclk &= ~PHYCLK_COMMON_ON_N; phypwr &= ~PHYPWR_NORMAL_MASK; @@ -165,7 +246,7 @@ static void samsung_usbphy_disable(struct samsung_usbphy *sphy) phypwr = readl(regs + SAMSUNG_PHYPWR); - switch (sphy->cpu_type) { + switch (sphy->drv_data->cpu_type) { case TYPE_S3C64XX: phypwr |= PHYPWR_NORMAL_MASK; break; @@ -185,6 +266,7 @@ static void samsung_usbphy_disable(struct samsung_usbphy *sphy) static int samsung_usbphy_init(struct usb_phy *phy) { struct samsung_usbphy *sphy; + unsigned long flags; int ret = 0; sphy = phy_to_sphy(phy); @@ -196,13 +278,19 @@ static int samsung_usbphy_init(struct usb_phy *phy) return ret; } + spin_lock_irqsave(&sphy->lock, flags); + /* Disable phy isolation */ if (sphy->plat && sphy->plat->pmu_isolation) sphy->plat->pmu_isolation(false); + else + samsung_usbphy_set_isolation(sphy, false); /* Initialize usb phy registers */ samsung_usbphy_enable(sphy); + spin_unlock_irqrestore(&sphy->lock, flags); + /* Disable the phy clock */ clk_disable_unprepare(sphy->clk); return ret; @@ -214,6 +302,7 @@ static int samsung_usbphy_init(struct usb_phy *phy) static void samsung_usbphy_shutdown(struct usb_phy *phy) { struct samsung_usbphy *sphy; + unsigned long flags; sphy = phy_to_sphy(phy); @@ -222,44 +311,47 @@ static void samsung_usbphy_shutdown(struct usb_phy *phy) return; } + spin_lock_irqsave(&sphy->lock, flags); + /* De-initialize usb phy registers */ samsung_usbphy_disable(sphy); /* Enable phy isolation */ if (sphy->plat && sphy->plat->pmu_isolation) sphy->plat->pmu_isolation(true); + else + samsung_usbphy_set_isolation(sphy, true); + + spin_unlock_irqrestore(&sphy->lock, flags); clk_disable_unprepare(sphy->clk); } static const struct of_device_id samsung_usbphy_dt_match[]; -static inline int samsung_usbphy_get_driver_data(struct platform_device *pdev) +static inline const struct samsung_usbphy_drvdata +*samsung_usbphy_get_driver_data(struct platform_device *pdev) { if (pdev->dev.of_node) { const struct of_device_id *match; match = of_match_node(samsung_usbphy_dt_match, pdev->dev.of_node); - return (int) match->data; + return match->data; } - return platform_get_device_id(pdev)->driver_data; + return (struct samsung_usbphy_drvdata *) + platform_get_device_id(pdev)->driver_data; } static int __devinit samsung_usbphy_probe(struct platform_device *pdev) { struct samsung_usbphy *sphy; - struct samsung_usbphy_data *pdata; + struct samsung_usbphy_data *pdata = pdev->dev.platform_data; struct device *dev = &pdev->dev; struct resource *phy_mem; void __iomem *phy_base; struct clk *clk; - - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "%s: no platform data defined\n", __func__); - return -EINVAL; - } + int ret; phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!phy_mem) { @@ -283,7 +375,19 @@ static int __devinit samsung_usbphy_probe(struct platform_device *pdev) return PTR_ERR(clk); } - sphy->dev = &pdev->dev; + sphy->dev = dev; + + if (dev->of_node) { + ret = samsung_usbphy_parse_dt(sphy); + if (ret < 0) + return ret; + } else { + if (!pdata) { + dev_err(dev, "no platform data specified\n"); + return -EINVAL; + } + } + sphy->plat = pdata; sphy->regs = phy_base; sphy->clk = clk; @@ -291,9 +395,11 @@ static int __devinit samsung_usbphy_probe(struct platform_device *pdev) sphy->phy.label = "samsung-usbphy"; sphy->phy.init = samsung_usbphy_init; sphy->phy.shutdown = samsung_usbphy_shutdown; - sphy->cpu_type = samsung_usbphy_get_driver_data(pdev); + sphy->drv_data = samsung_usbphy_get_driver_data(pdev); sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); + spin_lock_init(&sphy->lock); + platform_set_drvdata(pdev, sphy); return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2); @@ -305,17 +411,30 @@ static int __exit samsung_usbphy_remove(struct platform_device *pdev) usb_remove_phy(&sphy->phy); + if (sphy->pmuregs) + iounmap(sphy->pmuregs); + return 0; } +static const struct samsung_usbphy_drvdata usbphy_s3c64xx = { + .cpu_type = TYPE_S3C64XX, + .devphy_en_mask = S3C64XX_USBPHY_ENABLE, +}; + +static const struct samsung_usbphy_drvdata usbphy_exynos4 = { + .cpu_type = TYPE_EXYNOS4210, + .devphy_en_mask = EXYNOS_USBPHY_ENABLE, +}; + #ifdef CONFIG_OF static const struct of_device_id samsung_usbphy_dt_match[] = { { .compatible = "samsung,s3c64xx-usbphy", - .data = (void *)TYPE_S3C64XX, + .data = &usbphy_s3c64xx, }, { .compatible = "samsung,exynos4210-usbphy", - .data = (void *)TYPE_EXYNOS4210, + .data = &usbphy_exynos4, }, {}, }; @@ -325,10 +444,10 @@ MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); static struct platform_device_id samsung_usbphy_driver_ids[] = { { .name = "s3c64xx-usbphy", - .driver_data = TYPE_S3C64XX, + .driver_data = (unsigned long)&usbphy_s3c64xx, }, { .name = "exynos4210-usbphy", - .driver_data = TYPE_EXYNOS4210, + .driver_data = (unsigned long)&usbphy_exynos4, }, {}, }; -- cgit v1.2.3 From b6ae7a26535bb37c6c023c6f25b7f98904a8ab7c Mon Sep 17 00:00:00 2001 From: Pritesh Raithatha Date: Tue, 8 Jan 2013 13:02:37 +0530 Subject: pinctrl: tegra114: add pinctrl driver for NVIDIA's Tegra114 SoC This adds a driver for the Tegra114 pinmux, and required parameterization data for Tegra114. The driver uses the common Tegra pincontrol driver utility functions to implement the majority of the driver. This driver is not compatible with the earlier NVIDIA's SoCs, hence add new compatibile as "nvidia,tegra114-pinmux". Originally written by Pritesh. ldewangan: - cleanup the patches, - remove non-require tables. - Use module_platform_driver() for driver registartion. Signed-off-by: Pritesh Raithatha Signed-off-by: Laxman Dewangan Reviewed-by: Stephen Warren Signed-off-by: Linus Walleij --- .../bindings/pinctrl/nvidia,tegra114-pinmux.txt | 120 + drivers/pinctrl/Kconfig | 4 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-tegra114.c | 2757 ++++++++++++++++++++ 4 files changed, 2882 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/nvidia,tegra114-pinmux.txt create mode 100644 drivers/pinctrl/pinctrl-tegra114.c (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra114-pinmux.txt b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra114-pinmux.txt new file mode 100644 index 000000000000..e204d009f16c --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra114-pinmux.txt @@ -0,0 +1,120 @@ +NVIDIA Tegra114 pinmux controller + +The Tegra114 pinctrl binding is very similar to the Tegra20 and Tegra30 +pinctrl binding, as described in nvidia,tegra20-pinmux.txt and +nvidia,tegra30-pinmux.txt. In fact, this document assumes that binding as +a baseline, and only documents the differences between the two bindings. + +Required properties: +- compatible: "nvidia,tegra114-pinmux" +- reg: Should contain the register physical address and length for each of + the pad control and mux registers. The first bank of address must be the + driver strength pad control register address and second bank address must + be pinmux register address. + +Tegra114 adds the following optional properties for pin configuration subnodes: +- nvidia,enable-input: Integer. Enable the pin's input path. 0: no, 1: yes. +- nvidia,open-drain: Integer. Enable open drain mode. 0: no, 1: yes. +- nvidia,lock: Integer. Lock the pin configuration against further changes + until reset. 0: no, 1: yes. +- nvidia,io-reset: Integer. Reset the IO path. 0: no, 1: yes. +- nvidia,rcv-sel: Integer. Select VIL/VIH receivers. 0: normal, 1: high. +- nvidia,drive-type: Integer. Valid range 0...3. + +As with Tegra20 and Terga30, see the Tegra TRM for complete details regarding +which groups support which functionality. + +Valid values for pin and group names are: + + per-pin mux groups: + + These all support nvidia,function, nvidia,tristate, nvidia,pull, + nvidia,enable-input, nvidia,lock. Some support nvidia,open-drain, + nvidia,io-reset and nvidia,rcv-sel. + + ulpi_data0_po1, ulpi_data1_po2, ulpi_data2_po3, ulpi_data3_po4, + ulpi_data4_po5, ulpi_data5_po6, ulpi_data6_po7, ulpi_data7_po0, + ulpi_clk_py0, ulpi_dir_py1, ulpi_nxt_py2, ulpi_stp_py3, dap3_fs_pp0, + dap3_din_pp1, dap3_dout_pp2, dap3_sclk_pp3, pv0, pv1, sdmmc1_clk_pz0, + sdmmc1_cmd_pz1, sdmmc1_dat3_py4, sdmmc1_dat2_py5, sdmmc1_dat1_py6, + sdmmc1_dat0_py7, clk2_out_pw5, clk2_req_pcc5, hdmi_int_pn7, ddc_scl_pv4, + ddc_sda_pv5, uart2_rxd_pc3, uart2_txd_pc2, uart2_rts_n_pj6, + uart2_cts_n_pj5, uart3_txd_pw6, uart3_rxd_pw7, uart3_cts_n_pa1, + uart3_rts_n_pc0, pu0, pu1, pu2, pu3, pu4, pu5, pu6, gen1_i2c_sda_pc5, + gen1_i2c_scl_pc4, dap4_fs_pp4, dap4_din_pp5, dap4_dout_pp6, dap4_sclk_pp7, + clk3_out_pee0, clk3_req_pee1, gmi_wp_n_pc7, gmi_iordy_pi5, gmi_wait_pi7, + gmi_adv_n_pk0, gmi_clk_pk1, gmi_cs0_n_pj0, gmi_cs1_n_pj2, gmi_cs2_n_pk3, + gmi_cs3_n_pk4, gmi_cs4_n_pk2, gmi_cs6_n_pi3, gmi_cs7_n_pi6, gmi_ad0_pg0, + gmi_ad1_pg1, gmi_ad2_pg2, gmi_ad3_pg3, gmi_ad4_pg4, gmi_ad5_pg5, + gmi_ad6_pg6, gmi_ad7_pg7, gmi_ad8_ph0, gmi_ad9_ph1, gmi_ad10_ph2, + gmi_ad11_ph3, gmi_ad12_ph4, gmi_ad13_ph5, gmi_ad14_ph6, gmi_ad15_ph7, + gmi_a16_pj7, gmi_a17_pb0, gmi_a18_pb1, gmi_a19_pk7, gmi_wr_n_pi0, + gmi_oe_n_pi1, gmi_dqs_p_pj3, gmi_rst_n_pi4, gen2_i2c_scl_pt5, + gen2_i2c_sda_pt6, sdmmc4_clk_pcc4, sdmmc4_cmd_pt7, sdmmc4_dat0_paa0, + sdmmc4_dat1_paa1, sdmmc4_dat2_paa2, sdmmc4_dat3_paa3, sdmmc4_dat4_paa4, + sdmmc4_dat5_paa5, sdmmc4_dat6_paa6, sdmmc4_dat7_paa7, cam_mclk_pcc0, + pcc1, pbb0, cam_i2c_scl_pbb1, cam_i2c_sda_pbb2, pbb3, pbb4, pbb5, pbb6, + pbb7, pcc2, pwr_i2c_scl_pz6, pwr_i2c_sda_pz7, kb_row0_pr0, kb_row1_pr1, + kb_row2_pr2, kb_row3_pr3, kb_row4_pr4, kb_row5_pr5, kb_row6_pr6, + kb_row7_pr7, kb_row8_ps0, kb_row9_ps1, kb_row10_ps2, kb_col0_pq0, + kb_col1_pq1, kb_col2_pq2, kb_col3_pq3, kb_col4_pq4, kb_col5_pq5, + kb_col6_pq6, kb_col7_pq7, clk_32k_out_pa0, sys_clk_req_pz5, core_pwr_req, + cpu_pwr_req, pwr_int_n, owr, dap1_fs_pn0, dap1_din_pn1, dap1_dout_pn2, + dap1_sclk_pn3, clk1_req_pee2, clk1_out_pw4, spdif_in_pk6, spdif_out_pk5, + dap2_fs_pa2, dap2_din_pa4, dap2_dout_pa5, dap2_sclk_pa3, dvfs_pwm_px0, + gpio_x1_aud_px1, gpio_x3_aud_px3, dvfs_clk_px2, gpio_x4_aud_px4, + gpio_x5_aud_px5, gpio_x6_aud_px6, gpio_x7_aud_px7, sdmmc3_clk_pa6, + sdmmc3_cmd_pa7, sdmmc3_dat0_pb7, sdmmc3_dat1_pb6, sdmmc3_dat2_pb5, + sdmmc3_dat3_pb4, hdmi_cec_pee3, sdmmc1_wp_n_pv3, sdmmc3_cd_n_pv2, + gpio_w2_aud_pw2, gpio_w3_aud_pw3, usb_vbus_en0_pn4, usb_vbus_en1_pn5, + sdmmc3_clk_lb_in_pee5, sdmmc3_clk_lb_out_pee4, reset_out_n. + + drive groups: + + These all support nvidia,pull-down-strength, nvidia,pull-up-strength, + nvidia,slew-rate-rising, nvidia,slew-rate-falling. Most but not all + support nvidia,high-speed-mode, nvidia,schmitt, nvidia,low-power-mode + and nvidia,drive-type. + + ao1, ao2, at1, at2, at3, at4, at5, cdev1, cdev2, dap1, dap2, dap3, dap4, + dbg, sdio3, spi, uaa, uab, uart2, uart3, sdio1, ddc, gma, gme, gmf, gmg, + gmh, owr, uda. + +Example: + + pinmux: pinmux { + compatible = "nvidia,tegra114-pinmux"; + reg = <0x70000868 0x148 /* Pad control registers */ + 0x70003000 0x40c>; /* PinMux registers */ + }; + +Example board file extract: + + pinctrl { + sdmmc4_default: pinmux { + sdmmc4_clk_pcc4 { + nvidia,pins = "sdmmc4_clk_pcc4", + nvidia,function = "sdmmc4"; + nvidia,pull = <0>; + nvidia,tristate = <0>; + }; + sdmmc4_dat0_paa0 { + nvidia,pins = "sdmmc4_dat0_paa0", + "sdmmc4_dat1_paa1", + "sdmmc4_dat2_paa2", + "sdmmc4_dat3_paa3", + "sdmmc4_dat4_paa4", + "sdmmc4_dat5_paa5", + "sdmmc4_dat6_paa6", + "sdmmc4_dat7_paa7"; + nvidia,function = "sdmmc4"; + nvidia,pull = <2>; + nvidia,tristate = <0>; + }; + }; + }; + + sdhci@78000400 { + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc4_default>; + }; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index c31aeb01bb00..c9c165d08d8b 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -164,6 +164,10 @@ config PINCTRL_TEGRA30 bool select PINCTRL_TEGRA +config PINCTRL_TEGRA114 + bool + select PINCTRL_TEGRA + config PINCTRL_U300 bool "U300 pin controller driver" depends on ARCH_U300 diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index fc4606f27dc7..e5de64fd32ca 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_PINCTRL_SIRF) += pinctrl-sirf.o obj-$(CONFIG_PINCTRL_TEGRA) += pinctrl-tegra.o obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o +obj-$(CONFIG_PINCTRL_TEGRA114) += pinctrl-tegra114.o obj-$(CONFIG_PINCTRL_U300) += pinctrl-u300.o obj-$(CONFIG_PINCTRL_COH901) += pinctrl-coh901.o obj-$(CONFIG_PINCTRL_SAMSUNG) += pinctrl-samsung.o diff --git a/drivers/pinctrl/pinctrl-tegra114.c b/drivers/pinctrl/pinctrl-tegra114.c new file mode 100644 index 000000000000..053a8b1b12f1 --- /dev/null +++ b/drivers/pinctrl/pinctrl-tegra114.c @@ -0,0 +1,2757 @@ +/* + * Pinctrl data and driver for the NVIDIA Tegra114 pinmux + * + * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved. + * + * Arthur: Pritesh Raithatha + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "pinctrl-tegra.h" + +/* + * Most pins affected by the pinmux can also be GPIOs. Define these first. + * These must match how the GPIO driver names/numbers its pins. + */ +#define _GPIO(offset) (offset) + +#define TEGRA_PIN_CLK_32K_OUT_PA0 _GPIO(0) +#define TEGRA_PIN_UART3_CTS_N_PA1 _GPIO(1) +#define TEGRA_PIN_DAP2_FS_PA2 _GPIO(2) +#define TEGRA_PIN_DAP2_SCLK_PA3 _GPIO(3) +#define TEGRA_PIN_DAP2_DIN_PA4 _GPIO(4) +#define TEGRA_PIN_DAP2_DOUT_PA5 _GPIO(5) +#define TEGRA_PIN_SDMMC3_CLK_PA6 _GPIO(6) +#define TEGRA_PIN_SDMMC3_CMD_PA7 _GPIO(7) +#define TEGRA_PIN_GMI_A17_PB0 _GPIO(8) +#define TEGRA_PIN_GMI_A18_PB1 _GPIO(9) +#define TEGRA_PIN_SDMMC3_DAT3_PB4 _GPIO(12) +#define TEGRA_PIN_SDMMC3_DAT2_PB5 _GPIO(13) +#define TEGRA_PIN_SDMMC3_DAT1_PB6 _GPIO(14) +#define TEGRA_PIN_SDMMC3_DAT0_PB7 _GPIO(15) +#define TEGRA_PIN_UART3_RTS_N_PC0 _GPIO(16) +#define TEGRA_PIN_UART2_TXD_PC2 _GPIO(18) +#define TEGRA_PIN_UART2_RXD_PC3 _GPIO(19) +#define TEGRA_PIN_GEN1_I2C_SCL_PC4 _GPIO(20) +#define TEGRA_PIN_GEN1_I2C_SDA_PC5 _GPIO(21) +#define TEGRA_PIN_GMI_WP_N_PC7 _GPIO(23) +#define TEGRA_PIN_GMI_AD0_PG0 _GPIO(48) +#define TEGRA_PIN_GMI_AD1_PG1 _GPIO(49) +#define TEGRA_PIN_GMI_AD2_PG2 _GPIO(50) +#define TEGRA_PIN_GMI_AD3_PG3 _GPIO(51) +#define TEGRA_PIN_GMI_AD4_PG4 _GPIO(52) +#define TEGRA_PIN_GMI_AD5_PG5 _GPIO(53) +#define TEGRA_PIN_GMI_AD6_PG6 _GPIO(54) +#define TEGRA_PIN_GMI_AD7_PG7 _GPIO(55) +#define TEGRA_PIN_GMI_AD8_PH0 _GPIO(56) +#define TEGRA_PIN_GMI_AD9_PH1 _GPIO(57) +#define TEGRA_PIN_GMI_AD10_PH2 _GPIO(58) +#define TEGRA_PIN_GMI_AD11_PH3 _GPIO(59) +#define TEGRA_PIN_GMI_AD12_PH4 _GPIO(60) +#define TEGRA_PIN_GMI_AD13_PH5 _GPIO(61) +#define TEGRA_PIN_GMI_AD14_PH6 _GPIO(62) +#define TEGRA_PIN_GMI_AD15_PH7 _GPIO(63) +#define TEGRA_PIN_GMI_WR_N_PI0 _GPIO(64) +#define TEGRA_PIN_GMI_OE_N_PI1 _GPIO(65) +#define TEGRA_PIN_GMI_CS6_N_PI3 _GPIO(67) +#define TEGRA_PIN_GMI_RST_N_PI4 _GPIO(68) +#define TEGRA_PIN_GMI_IORDY_PI5 _GPIO(69) +#define TEGRA_PIN_GMI_CS7_N_PI6 _GPIO(70) +#define TEGRA_PIN_GMI_WAIT_PI7 _GPIO(71) +#define TEGRA_PIN_GMI_CS0_N_PJ0 _GPIO(72) +#define TEGRA_PIN_GMI_CS1_N_PJ2 _GPIO(74) +#define TEGRA_PIN_GMI_DQS_P_PJ3 _GPIO(75) +#define TEGRA_PIN_UART2_CTS_N_PJ5 _GPIO(77) +#define TEGRA_PIN_UART2_RTS_N_PJ6 _GPIO(78) +#define TEGRA_PIN_GMI_A16_PJ7 _GPIO(79) +#define TEGRA_PIN_GMI_ADV_N_PK0 _GPIO(80) +#define TEGRA_PIN_GMI_CLK_PK1 _GPIO(81) +#define TEGRA_PIN_GMI_CS4_N_PK2 _GPIO(82) +#define TEGRA_PIN_GMI_CS2_N_PK3 _GPIO(83) +#define TEGRA_PIN_GMI_CS3_N_PK4 _GPIO(84) +#define TEGRA_PIN_SPDIF_OUT_PK5 _GPIO(85) +#define TEGRA_PIN_SPDIF_IN_PK6 _GPIO(86) +#define TEGRA_PIN_GMI_A19_PK7 _GPIO(87) +#define TEGRA_PIN_DAP1_FS_PN0 _GPIO(104) +#define TEGRA_PIN_DAP1_DIN_PN1 _GPIO(105) +#define TEGRA_PIN_DAP1_DOUT_PN2 _GPIO(106) +#define TEGRA_PIN_DAP1_SCLK_PN3 _GPIO(107) +#define TEGRA_PIN_USB_VBUS_EN0_PN4 _GPIO(108) +#define TEGRA_PIN_USB_VBUS_EN1_PN5 _GPIO(109) +#define TEGRA_PIN_HDMI_INT_PN7 _GPIO(111) +#define TEGRA_PIN_ULPI_DATA7_PO0 _GPIO(112) +#define TEGRA_PIN_ULPI_DATA0_PO1 _GPIO(113) +#define TEGRA_PIN_ULPI_DATA1_PO2 _GPIO(114) +#define TEGRA_PIN_ULPI_DATA2_PO3 _GPIO(115) +#define TEGRA_PIN_ULPI_DATA3_PO4 _GPIO(116) +#define TEGRA_PIN_ULPI_DATA4_PO5 _GPIO(117) +#define TEGRA_PIN_ULPI_DATA5_PO6 _GPIO(118) +#define TEGRA_PIN_ULPI_DATA6_PO7 _GPIO(119) +#define TEGRA_PIN_DAP3_FS_PP0 _GPIO(120) +#define TEGRA_PIN_DAP3_DIN_PP1 _GPIO(121) +#define TEGRA_PIN_DAP3_DOUT_PP2 _GPIO(122) +#define TEGRA_PIN_DAP3_SCLK_PP3 _GPIO(123) +#define TEGRA_PIN_DAP4_FS_PP4 _GPIO(124) +#define TEGRA_PIN_DAP4_DIN_PP5 _GPIO(125) +#define TEGRA_PIN_DAP4_DOUT_PP6 _GPIO(126) +#define TEGRA_PIN_DAP4_SCLK_PP7 _GPIO(127) +#define TEGRA_PIN_KB_COL0_PQ0 _GPIO(128) +#define TEGRA_PIN_KB_COL1_PQ1 _GPIO(129) +#define TEGRA_PIN_KB_COL2_PQ2 _GPIO(130) +#define TEGRA_PIN_KB_COL3_PQ3 _GPIO(131) +#define TEGRA_PIN_KB_COL4_PQ4 _GPIO(132) +#define TEGRA_PIN_KB_COL5_PQ5 _GPIO(133) +#define TEGRA_PIN_KB_COL6_PQ6 _GPIO(134) +#define TEGRA_PIN_KB_COL7_PQ7 _GPIO(135) +#define TEGRA_PIN_KB_ROW0_PR0 _GPIO(136) +#define TEGRA_PIN_KB_ROW1_PR1 _GPIO(137) +#define TEGRA_PIN_KB_ROW2_PR2 _GPIO(138) +#define TEGRA_PIN_KB_ROW3_PR3 _GPIO(139) +#define TEGRA_PIN_KB_ROW4_PR4 _GPIO(140) +#define TEGRA_PIN_KB_ROW5_PR5 _GPIO(141) +#define TEGRA_PIN_KB_ROW6_PR6 _GPIO(142) +#define TEGRA_PIN_KB_ROW7_PR7 _GPIO(143) +#define TEGRA_PIN_KB_ROW8_PS0 _GPIO(144) +#define TEGRA_PIN_KB_ROW9_PS1 _GPIO(145) +#define TEGRA_PIN_KB_ROW10_PS2 _GPIO(146) +#define TEGRA_PIN_GEN2_I2C_SCL_PT5 _GPIO(157) +#define TEGRA_PIN_GEN2_I2C_SDA_PT6 _GPIO(158) +#define TEGRA_PIN_SDMMC4_CMD_PT7 _GPIO(159) +#define TEGRA_PIN_PU0 _GPIO(160) +#define TEGRA_PIN_PU1 _GPIO(161) +#define TEGRA_PIN_PU2 _GPIO(162) +#define TEGRA_PIN_PU3 _GPIO(163) +#define TEGRA_PIN_PU4 _GPIO(164) +#define TEGRA_PIN_PU5 _GPIO(165) +#define TEGRA_PIN_PU6 _GPIO(166) +#define TEGRA_PIN_PV0 _GPIO(168) +#define TEGRA_PIN_PV1 _GPIO(169) +#define TEGRA_PIN_SDMMC3_CD_N_PV2 _GPIO(170) +#define TEGRA_PIN_SDMMC1_WP_N_PV3 _GPIO(171) +#define TEGRA_PIN_DDC_SCL_PV4 _GPIO(172) +#define TEGRA_PIN_DDC_SDA_PV5 _GPIO(173) +#define TEGRA_PIN_GPIO_W2_AUD_PW2 _GPIO(178) +#define TEGRA_PIN_GPIO_W3_AUD_PW3 _GPIO(179) +#define TEGRA_PIN_CLK1_OUT_PW4 _GPIO(180) +#define TEGRA_PIN_CLK2_OUT_PW5 _GPIO(181) +#define TEGRA_PIN_UART3_TXD_PW6 _GPIO(182) +#define TEGRA_PIN_UART3_RXD_PW7 _GPIO(183) +#define TEGRA_PIN_DVFS_PWM_PX0 _GPIO(184) +#define TEGRA_PIN_GPIO_X1_AUD_PX1 _GPIO(185) +#define TEGRA_PIN_DVFS_CLK_PX2 _GPIO(186) +#define TEGRA_PIN_GPIO_X3_AUD_PX3 _GPIO(187) +#define TEGRA_PIN_GPIO_X4_AUD_PX4 _GPIO(188) +#define TEGRA_PIN_GPIO_X5_AUD_PX5 _GPIO(189) +#define TEGRA_PIN_GPIO_X6_AUD_PX6 _GPIO(190) +#define TEGRA_PIN_GPIO_X7_AUD_PX7 _GPIO(191) +#define TEGRA_PIN_ULPI_CLK_PY0 _GPIO(192) +#define TEGRA_PIN_ULPI_DIR_PY1 _GPIO(193) +#define TEGRA_PIN_ULPI_NXT_PY2 _GPIO(194) +#define TEGRA_PIN_ULPI_STP_PY3 _GPIO(195) +#define TEGRA_PIN_SDMMC1_DAT3_PY4 _GPIO(196) +#define TEGRA_PIN_SDMMC1_DAT2_PY5 _GPIO(197) +#define TEGRA_PIN_SDMMC1_DAT1_PY6 _GPIO(198) +#define TEGRA_PIN_SDMMC1_DAT0_PY7 _GPIO(199) +#define TEGRA_PIN_SDMMC1_CLK_PZ0 _GPIO(200) +#define TEGRA_PIN_SDMMC1_CMD_PZ1 _GPIO(201) +#define TEGRA_PIN_SYS_CLK_REQ_PZ5 _GPIO(205) +#define TEGRA_PIN_PWR_I2C_SCL_PZ6 _GPIO(206) +#define TEGRA_PIN_PWR_I2C_SDA_PZ7 _GPIO(207) +#define TEGRA_PIN_SDMMC4_DAT0_PAA0 _GPIO(208) +#define TEGRA_PIN_SDMMC4_DAT1_PAA1 _GPIO(209) +#define TEGRA_PIN_SDMMC4_DAT2_PAA2 _GPIO(210) +#define TEGRA_PIN_SDMMC4_DAT3_PAA3 _GPIO(211) +#define TEGRA_PIN_SDMMC4_DAT4_PAA4 _GPIO(212) +#define TEGRA_PIN_SDMMC4_DAT5_PAA5 _GPIO(213) +#define TEGRA_PIN_SDMMC4_DAT6_PAA6 _GPIO(214) +#define TEGRA_PIN_SDMMC4_DAT7_PAA7 _GPIO(215) +#define TEGRA_PIN_PBB0 _GPIO(216) +#define TEGRA_PIN_CAM_I2C_SCL_PBB1 _GPIO(217) +#define TEGRA_PIN_CAM_I2C_SDA_PBB2 _GPIO(218) +#define TEGRA_PIN_PBB3 _GPIO(219) +#define TEGRA_PIN_PBB4 _GPIO(220) +#define TEGRA_PIN_PBB5 _GPIO(221) +#define TEGRA_PIN_PBB6 _GPIO(222) +#define TEGRA_PIN_PBB7 _GPIO(223) +#define TEGRA_PIN_CAM_MCLK_PCC0 _GPIO(224) +#define TEGRA_PIN_PCC1 _GPIO(225) +#define TEGRA_PIN_PCC2 _GPIO(226) +#define TEGRA_PIN_SDMMC4_CLK_PCC4 _GPIO(228) +#define TEGRA_PIN_CLK2_REQ_PCC5 _GPIO(229) +#define TEGRA_PIN_CLK3_OUT_PEE0 _GPIO(240) +#define TEGRA_PIN_CLK3_REQ_PEE1 _GPIO(241) +#define TEGRA_PIN_CLK1_REQ_PEE2 _GPIO(242) +#define TEGRA_PIN_HDMI_CEC_PEE3 _GPIO(243) +#define TEGRA_PIN_SDMMC3_CLK_LB_OUT_PEE4 _GPIO(244) +#define TEGRA_PIN_SDMMC3_CLK_LB_IN_PEE5 _GPIO(245) + +/* All non-GPIO pins follow */ +#define NUM_GPIOS (TEGRA_PIN_SDMMC3_CLK_LB_IN_PEE5 + 1) +#define _PIN(offset) (NUM_GPIOS + (offset)) + +/* Non-GPIO pins */ +#define TEGRA_PIN_CORE_PWR_REQ _PIN(0) +#define TEGRA_PIN_CPU_PWR_REQ _PIN(1) +#define TEGRA_PIN_PWR_INT_N _PIN(2) +#define TEGRA_PIN_RESET_OUT_N _PIN(3) +#define TEGRA_PIN_OWR _PIN(4) + +static const struct pinctrl_pin_desc tegra114_pins[] = { + PINCTRL_PIN(TEGRA_PIN_CLK_32K_OUT_PA0, "CLK_32K_OUT PA0"), + PINCTRL_PIN(TEGRA_PIN_UART3_CTS_N_PA1, "UART3_CTS_N PA1"), + PINCTRL_PIN(TEGRA_PIN_DAP2_FS_PA2, "DAP2_FS PA2"), + PINCTRL_PIN(TEGRA_PIN_DAP2_SCLK_PA3, "DAP2_SCLK PA3"), + PINCTRL_PIN(TEGRA_PIN_DAP2_DIN_PA4, "DAP2_DIN PA4"), + PINCTRL_PIN(TEGRA_PIN_DAP2_DOUT_PA5, "DAP2_DOUT PA5"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_CLK_PA6, "SDMMC3_CLK PA6"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_CMD_PA7, "SDMMC3_CMD PA7"), + PINCTRL_PIN(TEGRA_PIN_GMI_A17_PB0, "GMI_A17 PB0"), + PINCTRL_PIN(TEGRA_PIN_GMI_A18_PB1, "GMI_A18 PB1"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT3_PB4, "SDMMC3_DAT3 PB4"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT2_PB5, "SDMMC3_DAT2 PB5"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT1_PB6, "SDMMC3_DAT1 PB6"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT0_PB7, "SDMMC3_DAT0 PB7"), + PINCTRL_PIN(TEGRA_PIN_UART3_RTS_N_PC0, "UART3_RTS_N PC0"), + PINCTRL_PIN(TEGRA_PIN_UART2_TXD_PC2, "UART2_TXD PC2"), + PINCTRL_PIN(TEGRA_PIN_UART2_RXD_PC3, "UART2_RXD PC3"), + PINCTRL_PIN(TEGRA_PIN_GEN1_I2C_SCL_PC4, "GEN1_I2C_SCL PC4"), + PINCTRL_PIN(TEGRA_PIN_GEN1_I2C_SDA_PC5, "GEN1_I2C_SDA PC5"), + PINCTRL_PIN(TEGRA_PIN_GMI_WP_N_PC7, "GMI_WP_N PC7"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD0_PG0, "GMI_AD0 PG0"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD1_PG1, "GMI_AD1 PG1"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD2_PG2, "GMI_AD2 PG2"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD3_PG3, "GMI_AD3 PG3"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD4_PG4, "GMI_AD4 PG4"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD5_PG5, "GMI_AD5 PG5"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD6_PG6, "GMI_AD6 PG6"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD7_PG7, "GMI_AD7 PG7"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD8_PH0, "GMI_AD8 PH0"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD9_PH1, "GMI_AD9 PH1"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD10_PH2, "GMI_AD10 PH2"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD11_PH3, "GMI_AD11 PH3"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD12_PH4, "GMI_AD12 PH4"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD13_PH5, "GMI_AD13 PH5"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD14_PH6, "GMI_AD14 PH6"), + PINCTRL_PIN(TEGRA_PIN_GMI_AD15_PH7, "GMI_AD15 PH7"), + PINCTRL_PIN(TEGRA_PIN_GMI_WR_N_PI0, "GMI_WR_N PI0"), + PINCTRL_PIN(TEGRA_PIN_GMI_OE_N_PI1, "GMI_OE_N PI1"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS6_N_PI3, "GMI_CS6_N PI3"), + PINCTRL_PIN(TEGRA_PIN_GMI_RST_N_PI4, "GMI_RST_N PI4"), + PINCTRL_PIN(TEGRA_PIN_GMI_IORDY_PI5, "GMI_IORDY PI5"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS7_N_PI6, "GMI_CS7_N PI6"), + PINCTRL_PIN(TEGRA_PIN_GMI_WAIT_PI7, "GMI_WAIT PI7"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS0_N_PJ0, "GMI_CS0_N PJ0"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS1_N_PJ2, "GMI_CS1_N PJ2"), + PINCTRL_PIN(TEGRA_PIN_GMI_DQS_P_PJ3, "GMI_DQS_P PJ3"), + PINCTRL_PIN(TEGRA_PIN_UART2_CTS_N_PJ5, "UART2_CTS_N PJ5"), + PINCTRL_PIN(TEGRA_PIN_UART2_RTS_N_PJ6, "UART2_RTS_N PJ6"), + PINCTRL_PIN(TEGRA_PIN_GMI_A16_PJ7, "GMI_A16 PJ7"), + PINCTRL_PIN(TEGRA_PIN_GMI_ADV_N_PK0, "GMI_ADV_N PK0"), + PINCTRL_PIN(TEGRA_PIN_GMI_CLK_PK1, "GMI_CLK PK1"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS4_N_PK2, "GMI_CS4_N PK2"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS2_N_PK3, "GMI_CS2_N PK3"), + PINCTRL_PIN(TEGRA_PIN_GMI_CS3_N_PK4, "GMI_CS3_N PK4"), + PINCTRL_PIN(TEGRA_PIN_SPDIF_OUT_PK5, "SPDIF_OUT PK5"), + PINCTRL_PIN(TEGRA_PIN_SPDIF_IN_PK6, "SPDIF_IN PK6"), + PINCTRL_PIN(TEGRA_PIN_GMI_A19_PK7, "GMI_A19 PK7"), + PINCTRL_PIN(TEGRA_PIN_DAP1_FS_PN0, "DAP1_FS PN0"), + PINCTRL_PIN(TEGRA_PIN_DAP1_DIN_PN1, "DAP1_DIN PN1"), + PINCTRL_PIN(TEGRA_PIN_DAP1_DOUT_PN2, "DAP1_DOUT PN2"), + PINCTRL_PIN(TEGRA_PIN_DAP1_SCLK_PN3, "DAP1_SCLK PN3"), + PINCTRL_PIN(TEGRA_PIN_USB_VBUS_EN0_PN4, "USB_VBUS_EN0 PN4"), + PINCTRL_PIN(TEGRA_PIN_USB_VBUS_EN1_PN5, "USB_VBUS_EN1 PN5"), + PINCTRL_PIN(TEGRA_PIN_HDMI_INT_PN7, "HDMI_INT PN7"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA7_PO0, "ULPI_DATA7 PO0"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA0_PO1, "ULPI_DATA0 PO1"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA1_PO2, "ULPI_DATA1 PO2"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA2_PO3, "ULPI_DATA2 PO3"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA3_PO4, "ULPI_DATA3 PO4"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA4_PO5, "ULPI_DATA4 PO5"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA5_PO6, "ULPI_DATA5 PO6"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DATA6_PO7, "ULPI_DATA6 PO7"), + PINCTRL_PIN(TEGRA_PIN_DAP3_FS_PP0, "DAP3_FS PP0"), + PINCTRL_PIN(TEGRA_PIN_DAP3_DIN_PP1, "DAP3_DIN PP1"), + PINCTRL_PIN(TEGRA_PIN_DAP3_DOUT_PP2, "DAP3_DOUT PP2"), + PINCTRL_PIN(TEGRA_PIN_DAP3_SCLK_PP3, "DAP3_SCLK PP3"), + PINCTRL_PIN(TEGRA_PIN_DAP4_FS_PP4, "DAP4_FS PP4"), + PINCTRL_PIN(TEGRA_PIN_DAP4_DIN_PP5, "DAP4_DIN PP5"), + PINCTRL_PIN(TEGRA_PIN_DAP4_DOUT_PP6, "DAP4_DOUT PP6"), + PINCTRL_PIN(TEGRA_PIN_DAP4_SCLK_PP7, "DAP4_SCLK PP7"), + PINCTRL_PIN(TEGRA_PIN_KB_COL0_PQ0, "KB_COL0 PQ0"), + PINCTRL_PIN(TEGRA_PIN_KB_COL1_PQ1, "KB_COL1 PQ1"), + PINCTRL_PIN(TEGRA_PIN_KB_COL2_PQ2, "KB_COL2 PQ2"), + PINCTRL_PIN(TEGRA_PIN_KB_COL3_PQ3, "KB_COL3 PQ3"), + PINCTRL_PIN(TEGRA_PIN_KB_COL4_PQ4, "KB_COL4 PQ4"), + PINCTRL_PIN(TEGRA_PIN_KB_COL5_PQ5, "KB_COL5 PQ5"), + PINCTRL_PIN(TEGRA_PIN_KB_COL6_PQ6, "KB_COL6 PQ6"), + PINCTRL_PIN(TEGRA_PIN_KB_COL7_PQ7, "KB_COL7 PQ7"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW0_PR0, "KB_ROW0 PR0"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW1_PR1, "KB_ROW1 PR1"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW2_PR2, "KB_ROW2 PR2"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW3_PR3, "KB_ROW3 PR3"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW4_PR4, "KB_ROW4 PR4"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW5_PR5, "KB_ROW5 PR5"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW6_PR6, "KB_ROW6 PR6"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW7_PR7, "KB_ROW7 PR7"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW8_PS0, "KB_ROW8 PS0"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW9_PS1, "KB_ROW9 PS1"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW10_PS2, "KB_ROW10 PS2"), + PINCTRL_PIN(TEGRA_PIN_GEN2_I2C_SCL_PT5, "GEN2_I2C_SCL PT5"), + PINCTRL_PIN(TEGRA_PIN_GEN2_I2C_SDA_PT6, "GEN2_I2C_SDA PT6"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_CMD_PT7, "SDMMC4_CMD PT7"), + PINCTRL_PIN(TEGRA_PIN_PU0, "PU0"), + PINCTRL_PIN(TEGRA_PIN_PU1, "PU1"), + PINCTRL_PIN(TEGRA_PIN_PU2, "PU2"), + PINCTRL_PIN(TEGRA_PIN_PU3, "PU3"), + PINCTRL_PIN(TEGRA_PIN_PU4, "PU4"), + PINCTRL_PIN(TEGRA_PIN_PU5, "PU5"), + PINCTRL_PIN(TEGRA_PIN_PU6, "PU6"), + PINCTRL_PIN(TEGRA_PIN_PV0, "PV0"), + PINCTRL_PIN(TEGRA_PIN_PV1, "PV1"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_CD_N_PV2, "SDMMC3_CD_N PV2"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_WP_N_PV3, "SDMMC1_WP_N PV3"), + PINCTRL_PIN(TEGRA_PIN_DDC_SCL_PV4, "DDC_SCL PV4"), + PINCTRL_PIN(TEGRA_PIN_DDC_SDA_PV5, "DDC_SDA PV5"), + PINCTRL_PIN(TEGRA_PIN_GPIO_W2_AUD_PW2, "GPIO_W2_AUD PW2"), + PINCTRL_PIN(TEGRA_PIN_GPIO_W3_AUD_PW3, "GPIO_W3_AUD PW3"), + PINCTRL_PIN(TEGRA_PIN_CLK1_OUT_PW4, "CLK1_OUT PW4"), + PINCTRL_PIN(TEGRA_PIN_CLK2_OUT_PW5, "CLK2_OUT PW5"), + PINCTRL_PIN(TEGRA_PIN_UART3_TXD_PW6, "UART3_TXD PW6"), + PINCTRL_PIN(TEGRA_PIN_UART3_RXD_PW7, "UART3_RXD PW7"), + PINCTRL_PIN(TEGRA_PIN_DVFS_PWM_PX0, "DVFS_PWM PX0"), + PINCTRL_PIN(TEGRA_PIN_GPIO_X1_AUD_PX1, "GPIO_X1_AUD PX1"), + PINCTRL_PIN(TEGRA_PIN_DVFS_CLK_PX2, "DVFS_CLK PX2"), + PINCTRL_PIN(TEGRA_PIN_GPIO_X3_AUD_PX3, "GPIO_X3_AUD PX3"), + PINCTRL_PIN(TEGRA_PIN_GPIO_X4_AUD_PX4, "GPIO_X4_AUD PX4"), + PINCTRL_PIN(TEGRA_PIN_GPIO_X5_AUD_PX5, "GPIO_X5_AUD PX5"), + PINCTRL_PIN(TEGRA_PIN_GPIO_X6_AUD_PX6, "GPIO_X6_AUD PX6"), + PINCTRL_PIN(TEGRA_PIN_GPIO_X7_AUD_PX7, "GPIO_X7_AUD PX7"), + PINCTRL_PIN(TEGRA_PIN_ULPI_CLK_PY0, "ULPI_CLK PY0"), + PINCTRL_PIN(TEGRA_PIN_ULPI_DIR_PY1, "ULPI_DIR PY1"), + PINCTRL_PIN(TEGRA_PIN_ULPI_NXT_PY2, "ULPI_NXT PY2"), + PINCTRL_PIN(TEGRA_PIN_ULPI_STP_PY3, "ULPI_STP PY3"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT3_PY4, "SDMMC1_DAT3 PY4"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT2_PY5, "SDMMC1_DAT2 PY5"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT1_PY6, "SDMMC1_DAT1 PY6"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT0_PY7, "SDMMC1_DAT0 PY7"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_CLK_PZ0, "SDMMC1_CLK PZ0"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_CMD_PZ1, "SDMMC1_CMD PZ1"), + PINCTRL_PIN(TEGRA_PIN_SYS_CLK_REQ_PZ5, "SYS_CLK_REQ PZ5"), + PINCTRL_PIN(TEGRA_PIN_PWR_I2C_SCL_PZ6, "PWR_I2C_SCL PZ6"), + PINCTRL_PIN(TEGRA_PIN_PWR_I2C_SDA_PZ7, "PWR_I2C_SDA PZ7"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT0_PAA0, "SDMMC4_DAT0 PAA0"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT1_PAA1, "SDMMC4_DAT1 PAA1"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT2_PAA2, "SDMMC4_DAT2 PAA2"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT3_PAA3, "SDMMC4_DAT3 PAA3"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT4_PAA4, "SDMMC4_DAT4 PAA4"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT5_PAA5, "SDMMC4_DAT5 PAA5"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT6_PAA6, "SDMMC4_DAT6 PAA6"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT7_PAA7, "SDMMC4_DAT7 PAA7"), + PINCTRL_PIN(TEGRA_PIN_PBB0, "PBB0"), + PINCTRL_PIN(TEGRA_PIN_CAM_I2C_SCL_PBB1, "CAM_I2C_SCL PBB1"), + PINCTRL_PIN(TEGRA_PIN_CAM_I2C_SDA_PBB2, "CAM_I2C_SDA PBB2"), + PINCTRL_PIN(TEGRA_PIN_PBB3, "PBB3"), + PINCTRL_PIN(TEGRA_PIN_PBB4, "PBB4"), + PINCTRL_PIN(TEGRA_PIN_PBB5, "PBB5"), + PINCTRL_PIN(TEGRA_PIN_PBB6, "PBB6"), + PINCTRL_PIN(TEGRA_PIN_PBB7, "PBB7"), + PINCTRL_PIN(TEGRA_PIN_CAM_MCLK_PCC0, "CAM_MCLK PCC0"), + PINCTRL_PIN(TEGRA_PIN_PCC1, "PCC1"), + PINCTRL_PIN(TEGRA_PIN_PCC2, "PCC2"), + PINCTRL_PIN(TEGRA_PIN_SDMMC4_CLK_PCC4, "SDMMC4_CLK PCC4"), + PINCTRL_PIN(TEGRA_PIN_CLK2_REQ_PCC5, "CLK2_REQ PCC5"), + PINCTRL_PIN(TEGRA_PIN_CLK3_OUT_PEE0, "CLK3_OUT PEE0"), + PINCTRL_PIN(TEGRA_PIN_CLK3_REQ_PEE1, "CLK3_REQ PEE1"), + PINCTRL_PIN(TEGRA_PIN_CLK1_REQ_PEE2, "CLK1_REQ PEE2"), + PINCTRL_PIN(TEGRA_PIN_HDMI_CEC_PEE3, "HDMI_CEC PEE3"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_CLK_LB_OUT_PEE4, "SDMMC3_CLK_LB_OUT PEE4"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_CLK_LB_IN_PEE5, "SDMMC3_CLK_LB_IN PEE5"), + PINCTRL_PIN(TEGRA_PIN_CORE_PWR_REQ, "CORE_PWR_REQ"), + PINCTRL_PIN(TEGRA_PIN_CPU_PWR_REQ, "CPU_PWR_REQ"), + PINCTRL_PIN(TEGRA_PIN_OWR, "OWR"), + PINCTRL_PIN(TEGRA_PIN_PWR_INT_N, "PWR_INT_N"), + PINCTRL_PIN(TEGRA_PIN_RESET_OUT_N, "RESET_OUT_N"), +}; + +static const unsigned clk_32k_out_pa0_pins[] = { + TEGRA_PIN_CLK_32K_OUT_PA0, +}; + +static const unsigned uart3_cts_n_pa1_pins[] = { + TEGRA_PIN_UART3_CTS_N_PA1, +}; + +static const unsigned dap2_fs_pa2_pins[] = { + TEGRA_PIN_DAP2_FS_PA2, +}; + +static const unsigned dap2_sclk_pa3_pins[] = { + TEGRA_PIN_DAP2_SCLK_PA3, +}; + +static const unsigned dap2_din_pa4_pins[] = { + TEGRA_PIN_DAP2_DIN_PA4, +}; + +static const unsigned dap2_dout_pa5_pins[] = { + TEGRA_PIN_DAP2_DOUT_PA5, +}; + +static const unsigned sdmmc3_clk_pa6_pins[] = { + TEGRA_PIN_SDMMC3_CLK_PA6, +}; + +static const unsigned sdmmc3_cmd_pa7_pins[] = { + TEGRA_PIN_SDMMC3_CMD_PA7, +}; + +static const unsigned gmi_a17_pb0_pins[] = { + TEGRA_PIN_GMI_A17_PB0, +}; + +static const unsigned gmi_a18_pb1_pins[] = { + TEGRA_PIN_GMI_A18_PB1, +}; + +static const unsigned sdmmc3_dat3_pb4_pins[] = { + TEGRA_PIN_SDMMC3_DAT3_PB4, +}; + +static const unsigned sdmmc3_dat2_pb5_pins[] = { + TEGRA_PIN_SDMMC3_DAT2_PB5, +}; + +static const unsigned sdmmc3_dat1_pb6_pins[] = { + TEGRA_PIN_SDMMC3_DAT1_PB6, +}; + +static const unsigned sdmmc3_dat0_pb7_pins[] = { + TEGRA_PIN_SDMMC3_DAT0_PB7, +}; + +static const unsigned uart3_rts_n_pc0_pins[] = { + TEGRA_PIN_UART3_RTS_N_PC0, +}; + +static const unsigned uart2_txd_pc2_pins[] = { + TEGRA_PIN_UART2_TXD_PC2, +}; + +static const unsigned uart2_rxd_pc3_pins[] = { + TEGRA_PIN_UART2_RXD_PC3, +}; + +static const unsigned gen1_i2c_scl_pc4_pins[] = { + TEGRA_PIN_GEN1_I2C_SCL_PC4, +}; + +static const unsigned gen1_i2c_sda_pc5_pins[] = { + TEGRA_PIN_GEN1_I2C_SDA_PC5, +}; + +static const unsigned gmi_wp_n_pc7_pins[] = { + TEGRA_PIN_GMI_WP_N_PC7, +}; + +static const unsigned gmi_ad0_pg0_pins[] = { + TEGRA_PIN_GMI_AD0_PG0, +}; + +static const unsigned gmi_ad1_pg1_pins[] = { + TEGRA_PIN_GMI_AD1_PG1, +}; + +static const unsigned gmi_ad2_pg2_pins[] = { + TEGRA_PIN_GMI_AD2_PG2, +}; + +static const unsigned gmi_ad3_pg3_pins[] = { + TEGRA_PIN_GMI_AD3_PG3, +}; + +static const unsigned gmi_ad4_pg4_pins[] = { + TEGRA_PIN_GMI_AD4_PG4, +}; + +static const unsigned gmi_ad5_pg5_pins[] = { + TEGRA_PIN_GMI_AD5_PG5, +}; + +static const unsigned gmi_ad6_pg6_pins[] = { + TEGRA_PIN_GMI_AD6_PG6, +}; + +static const unsigned gmi_ad7_pg7_pins[] = { + TEGRA_PIN_GMI_AD7_PG7, +}; + +static const unsigned gmi_ad8_ph0_pins[] = { + TEGRA_PIN_GMI_AD8_PH0, +}; + +static const unsigned gmi_ad9_ph1_pins[] = { + TEGRA_PIN_GMI_AD9_PH1, +}; + +static const unsigned gmi_ad10_ph2_pins[] = { + TEGRA_PIN_GMI_AD10_PH2, +}; + +static const unsigned gmi_ad11_ph3_pins[] = { + TEGRA_PIN_GMI_AD11_PH3, +}; + +static const unsigned gmi_ad12_ph4_pins[] = { + TEGRA_PIN_GMI_AD12_PH4, +}; + +static const unsigned gmi_ad13_ph5_pins[] = { + TEGRA_PIN_GMI_AD13_PH5, +}; + +static const unsigned gmi_ad14_ph6_pins[] = { + TEGRA_PIN_GMI_AD14_PH6, +}; + +static const unsigned gmi_ad15_ph7_pins[] = { + TEGRA_PIN_GMI_AD15_PH7, +}; + +static const unsigned gmi_wr_n_pi0_pins[] = { + TEGRA_PIN_GMI_WR_N_PI0, +}; + +static const unsigned gmi_oe_n_pi1_pins[] = { + TEGRA_PIN_GMI_OE_N_PI1, +}; + +static const unsigned gmi_cs6_n_pi3_pins[] = { + TEGRA_PIN_GMI_CS6_N_PI3, +}; + +static const unsigned gmi_rst_n_pi4_pins[] = { + TEGRA_PIN_GMI_RST_N_PI4, +}; + +static const unsigned gmi_iordy_pi5_pins[] = { + TEGRA_PIN_GMI_IORDY_PI5, +}; + +static const unsigned gmi_cs7_n_pi6_pins[] = { + TEGRA_PIN_GMI_CS7_N_PI6, +}; + +static const unsigned gmi_wait_pi7_pins[] = { + TEGRA_PIN_GMI_WAIT_PI7, +}; + +static const unsigned gmi_cs0_n_pj0_pins[] = { + TEGRA_PIN_GMI_CS0_N_PJ0, +}; + +static const unsigned gmi_cs1_n_pj2_pins[] = { + TEGRA_PIN_GMI_CS1_N_PJ2, +}; + +static const unsigned gmi_dqs_p_pj3_pins[] = { + TEGRA_PIN_GMI_DQS_P_PJ3, +}; + +static const unsigned uart2_cts_n_pj5_pins[] = { + TEGRA_PIN_UART2_CTS_N_PJ5, +}; + +static const unsigned uart2_rts_n_pj6_pins[] = { + TEGRA_PIN_UART2_RTS_N_PJ6, +}; + +static const unsigned gmi_a16_pj7_pins[] = { + TEGRA_PIN_GMI_A16_PJ7, +}; + +static const unsigned gmi_adv_n_pk0_pins[] = { + TEGRA_PIN_GMI_ADV_N_PK0, +}; + +static const unsigned gmi_clk_pk1_pins[] = { + TEGRA_PIN_GMI_CLK_PK1, +}; + +static const unsigned gmi_cs4_n_pk2_pins[] = { + TEGRA_PIN_GMI_CS4_N_PK2, +}; + +static const unsigned gmi_cs2_n_pk3_pins[] = { + TEGRA_PIN_GMI_CS2_N_PK3, +}; + +static const unsigned gmi_cs3_n_pk4_pins[] = { + TEGRA_PIN_GMI_CS3_N_PK4, +}; + +static const unsigned spdif_out_pk5_pins[] = { + TEGRA_PIN_SPDIF_OUT_PK5, +}; + +static const unsigned spdif_in_pk6_pins[] = { + TEGRA_PIN_SPDIF_IN_PK6, +}; + +static const unsigned gmi_a19_pk7_pins[] = { + TEGRA_PIN_GMI_A19_PK7, +}; + +static const unsigned dap1_fs_pn0_pins[] = { + TEGRA_PIN_DAP1_FS_PN0, +}; + +static const unsigned dap1_din_pn1_pins[] = { + TEGRA_PIN_DAP1_DIN_PN1, +}; + +static const unsigned dap1_dout_pn2_pins[] = { + TEGRA_PIN_DAP1_DOUT_PN2, +}; + +static const unsigned dap1_sclk_pn3_pins[] = { + TEGRA_PIN_DAP1_SCLK_PN3, +}; + +static const unsigned usb_vbus_en0_pn4_pins[] = { + TEGRA_PIN_USB_VBUS_EN0_PN4, +}; + +static const unsigned usb_vbus_en1_pn5_pins[] = { + TEGRA_PIN_USB_VBUS_EN1_PN5, +}; + +static const unsigned hdmi_int_pn7_pins[] = { + TEGRA_PIN_HDMI_INT_PN7, +}; + +static const unsigned ulpi_data7_po0_pins[] = { + TEGRA_PIN_ULPI_DATA7_PO0, +}; + +static const unsigned ulpi_data0_po1_pins[] = { + TEGRA_PIN_ULPI_DATA0_PO1, +}; + +static const unsigned ulpi_data1_po2_pins[] = { + TEGRA_PIN_ULPI_DATA1_PO2, +}; + +static const unsigned ulpi_data2_po3_pins[] = { + TEGRA_PIN_ULPI_DATA2_PO3, +}; + +static const unsigned ulpi_data3_po4_pins[] = { + TEGRA_PIN_ULPI_DATA3_PO4, +}; + +static const unsigned ulpi_data4_po5_pins[] = { + TEGRA_PIN_ULPI_DATA4_PO5, +}; + +static const unsigned ulpi_data5_po6_pins[] = { + TEGRA_PIN_ULPI_DATA5_PO6, +}; + +static const unsigned ulpi_data6_po7_pins[] = { + TEGRA_PIN_ULPI_DATA6_PO7, +}; + +static const unsigned dap3_fs_pp0_pins[] = { + TEGRA_PIN_DAP3_FS_PP0, +}; + +static const unsigned dap3_din_pp1_pins[] = { + TEGRA_PIN_DAP3_DIN_PP1, +}; + +static const unsigned dap3_dout_pp2_pins[] = { + TEGRA_PIN_DAP3_DOUT_PP2, +}; + +static const unsigned dap3_sclk_pp3_pins[] = { + TEGRA_PIN_DAP3_SCLK_PP3, +}; + +static const unsigned dap4_fs_pp4_pins[] = { + TEGRA_PIN_DAP4_FS_PP4, +}; + +static const unsigned dap4_din_pp5_pins[] = { + TEGRA_PIN_DAP4_DIN_PP5, +}; + +static const unsigned dap4_dout_pp6_pins[] = { + TEGRA_PIN_DAP4_DOUT_PP6, +}; + +static const unsigned dap4_sclk_pp7_pins[] = { + TEGRA_PIN_DAP4_SCLK_PP7, +}; + +static const unsigned kb_col0_pq0_pins[] = { + TEGRA_PIN_KB_COL0_PQ0, +}; + +static const unsigned kb_col1_pq1_pins[] = { + TEGRA_PIN_KB_COL1_PQ1, +}; + +static const unsigned kb_col2_pq2_pins[] = { + TEGRA_PIN_KB_COL2_PQ2, +}; + +static const unsigned kb_col3_pq3_pins[] = { + TEGRA_PIN_KB_COL3_PQ3, +}; + +static const unsigned kb_col4_pq4_pins[] = { + TEGRA_PIN_KB_COL4_PQ4, +}; + +static const unsigned kb_col5_pq5_pins[] = { + TEGRA_PIN_KB_COL5_PQ5, +}; + +static const unsigned kb_col6_pq6_pins[] = { + TEGRA_PIN_KB_COL6_PQ6, +}; + +static const unsigned kb_col7_pq7_pins[] = { + TEGRA_PIN_KB_COL7_PQ7, +}; + +static const unsigned kb_row0_pr0_pins[] = { + TEGRA_PIN_KB_ROW0_PR0, +}; + +static const unsigned kb_row1_pr1_pins[] = { + TEGRA_PIN_KB_ROW1_PR1, +}; + +static const unsigned kb_row2_pr2_pins[] = { + TEGRA_PIN_KB_ROW2_PR2, +}; + +static const unsigned kb_row3_pr3_pins[] = { + TEGRA_PIN_KB_ROW3_PR3, +}; + +static const unsigned kb_row4_pr4_pins[] = { + TEGRA_PIN_KB_ROW4_PR4, +}; + +static const unsigned kb_row5_pr5_pins[] = { + TEGRA_PIN_KB_ROW5_PR5, +}; + +static const unsigned kb_row6_pr6_pins[] = { + TEGRA_PIN_KB_ROW6_PR6, +}; + +static const unsigned kb_row7_pr7_pins[] = { + TEGRA_PIN_KB_ROW7_PR7, +}; + +static const unsigned kb_row8_ps0_pins[] = { + TEGRA_PIN_KB_ROW8_PS0, +}; + +static const unsigned kb_row9_ps1_pins[] = { + TEGRA_PIN_KB_ROW9_PS1, +}; + +static const unsigned kb_row10_ps2_pins[] = { + TEGRA_PIN_KB_ROW10_PS2, +}; + +static const unsigned gen2_i2c_scl_pt5_pins[] = { + TEGRA_PIN_GEN2_I2C_SCL_PT5, +}; + +static const unsigned gen2_i2c_sda_pt6_pins[] = { + TEGRA_PIN_GEN2_I2C_SDA_PT6, +}; + +static const unsigned sdmmc4_cmd_pt7_pins[] = { + TEGRA_PIN_SDMMC4_CMD_PT7, +}; + +static const unsigned pu0_pins[] = { + TEGRA_PIN_PU0, +}; + +static const unsigned pu1_pins[] = { + TEGRA_PIN_PU1, +}; + +static const unsigned pu2_pins[] = { + TEGRA_PIN_PU2, +}; + +static const unsigned pu3_pins[] = { + TEGRA_PIN_PU3, +}; + +static const unsigned pu4_pins[] = { + TEGRA_PIN_PU4, +}; + +static const unsigned pu5_pins[] = { + TEGRA_PIN_PU5, +}; + +static const unsigned pu6_pins[] = { + TEGRA_PIN_PU6, +}; + +static const unsigned pv0_pins[] = { + TEGRA_PIN_PV0, +}; + +static const unsigned pv1_pins[] = { + TEGRA_PIN_PV1, +}; + +static const unsigned sdmmc3_cd_n_pv2_pins[] = { + TEGRA_PIN_SDMMC3_CD_N_PV2, +}; + +static const unsigned sdmmc1_wp_n_pv3_pins[] = { + TEGRA_PIN_SDMMC1_WP_N_PV3, +}; + +static const unsigned ddc_scl_pv4_pins[] = { + TEGRA_PIN_DDC_SCL_PV4, +}; + +static const unsigned ddc_sda_pv5_pins[] = { + TEGRA_PIN_DDC_SDA_PV5, +}; + +static const unsigned gpio_w2_aud_pw2_pins[] = { + TEGRA_PIN_GPIO_W2_AUD_PW2, +}; + +static const unsigned gpio_w3_aud_pw3_pins[] = { + TEGRA_PIN_GPIO_W3_AUD_PW3, +}; + +static const unsigned clk1_out_pw4_pins[] = { + TEGRA_PIN_CLK1_OUT_PW4, +}; + +static const unsigned clk2_out_pw5_pins[] = { + TEGRA_PIN_CLK2_OUT_PW5, +}; + +static const unsigned uart3_txd_pw6_pins[] = { + TEGRA_PIN_UART3_TXD_PW6, +}; + +static const unsigned uart3_rxd_pw7_pins[] = { + TEGRA_PIN_UART3_RXD_PW7, +}; + +static const unsigned dvfs_pwm_px0_pins[] = { + TEGRA_PIN_DVFS_PWM_PX0, +}; + +static const unsigned gpio_x1_aud_px1_pins[] = { + TEGRA_PIN_GPIO_X1_AUD_PX1, +}; + +static const unsigned dvfs_clk_px2_pins[] = { + TEGRA_PIN_DVFS_CLK_PX2, +}; + +static const unsigned gpio_x3_aud_px3_pins[] = { + TEGRA_PIN_GPIO_X3_AUD_PX3, +}; + +static const unsigned gpio_x4_aud_px4_pins[] = { + TEGRA_PIN_GPIO_X4_AUD_PX4, +}; + +static const unsigned gpio_x5_aud_px5_pins[] = { + TEGRA_PIN_GPIO_X5_AUD_PX5, +}; + +static const unsigned gpio_x6_aud_px6_pins[] = { + TEGRA_PIN_GPIO_X6_AUD_PX6, +}; + +static const unsigned gpio_x7_aud_px7_pins[] = { + TEGRA_PIN_GPIO_X7_AUD_PX7, +}; + +static const unsigned ulpi_clk_py0_pins[] = { + TEGRA_PIN_ULPI_CLK_PY0, +}; + +static const unsigned ulpi_dir_py1_pins[] = { + TEGRA_PIN_ULPI_DIR_PY1, +}; + +static const unsigned ulpi_nxt_py2_pins[] = { + TEGRA_PIN_ULPI_NXT_PY2, +}; + +static const unsigned ulpi_stp_py3_pins[] = { + TEGRA_PIN_ULPI_STP_PY3, +}; + +static const unsigned sdmmc1_dat3_py4_pins[] = { + TEGRA_PIN_SDMMC1_DAT3_PY4, +}; + +static const unsigned sdmmc1_dat2_py5_pins[] = { + TEGRA_PIN_SDMMC1_DAT2_PY5, +}; + +static const unsigned sdmmc1_dat1_py6_pins[] = { + TEGRA_PIN_SDMMC1_DAT1_PY6, +}; + +static const unsigned sdmmc1_dat0_py7_pins[] = { + TEGRA_PIN_SDMMC1_DAT0_PY7, +}; + +static const unsigned sdmmc1_clk_pz0_pins[] = { + TEGRA_PIN_SDMMC1_CLK_PZ0, +}; + +static const unsigned sdmmc1_cmd_pz1_pins[] = { + TEGRA_PIN_SDMMC1_CMD_PZ1, +}; + +static const unsigned sys_clk_req_pz5_pins[] = { + TEGRA_PIN_SYS_CLK_REQ_PZ5, +}; + +static const unsigned pwr_i2c_scl_pz6_pins[] = { + TEGRA_PIN_PWR_I2C_SCL_PZ6, +}; + +static const unsigned pwr_i2c_sda_pz7_pins[] = { + TEGRA_PIN_PWR_I2C_SDA_PZ7, +}; + +static const unsigned sdmmc4_dat0_paa0_pins[] = { + TEGRA_PIN_SDMMC4_DAT0_PAA0, +}; + +static const unsigned sdmmc4_dat1_paa1_pins[] = { + TEGRA_PIN_SDMMC4_DAT1_PAA1, +}; + +static const unsigned sdmmc4_dat2_paa2_pins[] = { + TEGRA_PIN_SDMMC4_DAT2_PAA2, +}; + +static const unsigned sdmmc4_dat3_paa3_pins[] = { + TEGRA_PIN_SDMMC4_DAT3_PAA3, +}; + +static const unsigned sdmmc4_dat4_paa4_pins[] = { + TEGRA_PIN_SDMMC4_DAT4_PAA4, +}; + +static const unsigned sdmmc4_dat5_paa5_pins[] = { + TEGRA_PIN_SDMMC4_DAT5_PAA5, +}; + +static const unsigned sdmmc4_dat6_paa6_pins[] = { + TEGRA_PIN_SDMMC4_DAT6_PAA6, +}; + +static const unsigned sdmmc4_dat7_paa7_pins[] = { + TEGRA_PIN_SDMMC4_DAT7_PAA7, +}; + +static const unsigned pbb0_pins[] = { + TEGRA_PIN_PBB0, +}; + +static const unsigned cam_i2c_scl_pbb1_pins[] = { + TEGRA_PIN_CAM_I2C_SCL_PBB1, +}; + +static const unsigned cam_i2c_sda_pbb2_pins[] = { + TEGRA_PIN_CAM_I2C_SDA_PBB2, +}; + +static const unsigned pbb3_pins[] = { + TEGRA_PIN_PBB3, +}; + +static const unsigned pbb4_pins[] = { + TEGRA_PIN_PBB4, +}; + +static const unsigned pbb5_pins[] = { + TEGRA_PIN_PBB5, +}; + +static const unsigned pbb6_pins[] = { + TEGRA_PIN_PBB6, +}; + +static const unsigned pbb7_pins[] = { + TEGRA_PIN_PBB7, +}; + +static const unsigned cam_mclk_pcc0_pins[] = { + TEGRA_PIN_CAM_MCLK_PCC0, +}; + +static const unsigned pcc1_pins[] = { + TEGRA_PIN_PCC1, +}; + +static const unsigned pcc2_pins[] = { + TEGRA_PIN_PCC2, +}; + +static const unsigned sdmmc4_clk_pcc4_pins[] = { + TEGRA_PIN_SDMMC4_CLK_PCC4, +}; + +static const unsigned clk2_req_pcc5_pins[] = { + TEGRA_PIN_CLK2_REQ_PCC5, +}; + +static const unsigned clk3_out_pee0_pins[] = { + TEGRA_PIN_CLK3_OUT_PEE0, +}; + +static const unsigned clk3_req_pee1_pins[] = { + TEGRA_PIN_CLK3_REQ_PEE1, +}; + +static const unsigned clk1_req_pee2_pins[] = { + TEGRA_PIN_CLK1_REQ_PEE2, +}; + +static const unsigned hdmi_cec_pee3_pins[] = { + TEGRA_PIN_HDMI_CEC_PEE3, +}; + +static const unsigned sdmmc3_clk_lb_out_pee4_pins[] = { + TEGRA_PIN_SDMMC3_CLK_LB_OUT_PEE4, +}; + +static const unsigned sdmmc3_clk_lb_in_pee5_pins[] = { + TEGRA_PIN_SDMMC3_CLK_LB_IN_PEE5, +}; + +static const unsigned core_pwr_req_pins[] = { + TEGRA_PIN_CORE_PWR_REQ, +}; + +static const unsigned cpu_pwr_req_pins[] = { + TEGRA_PIN_CPU_PWR_REQ, +}; + +static const unsigned owr_pins[] = { + TEGRA_PIN_OWR, +}; + +static const unsigned pwr_int_n_pins[] = { + TEGRA_PIN_PWR_INT_N, +}; + +static const unsigned reset_out_n_pins[] = { + TEGRA_PIN_RESET_OUT_N, +}; + +static const unsigned drive_ao1_pins[] = { + TEGRA_PIN_KB_ROW0_PR0, + TEGRA_PIN_KB_ROW1_PR1, + TEGRA_PIN_KB_ROW2_PR2, + TEGRA_PIN_KB_ROW3_PR3, + TEGRA_PIN_KB_ROW4_PR4, + TEGRA_PIN_KB_ROW5_PR5, + TEGRA_PIN_KB_ROW6_PR6, + TEGRA_PIN_KB_ROW7_PR7, + TEGRA_PIN_PWR_I2C_SCL_PZ6, + TEGRA_PIN_PWR_I2C_SDA_PZ7, +}; + +static const unsigned drive_ao2_pins[] = { + TEGRA_PIN_CLK_32K_OUT_PA0, + TEGRA_PIN_KB_COL0_PQ0, + TEGRA_PIN_KB_COL1_PQ1, + TEGRA_PIN_KB_COL2_PQ2, + TEGRA_PIN_KB_COL3_PQ3, + TEGRA_PIN_KB_COL4_PQ4, + TEGRA_PIN_KB_COL5_PQ5, + TEGRA_PIN_KB_COL6_PQ6, + TEGRA_PIN_KB_COL7_PQ7, + TEGRA_PIN_KB_ROW8_PS0, + TEGRA_PIN_KB_ROW9_PS1, + TEGRA_PIN_KB_ROW10_PS2, + TEGRA_PIN_SYS_CLK_REQ_PZ5, + TEGRA_PIN_CORE_PWR_REQ, + TEGRA_PIN_CPU_PWR_REQ, + TEGRA_PIN_RESET_OUT_N, +}; + +static const unsigned drive_at1_pins[] = { + TEGRA_PIN_GMI_AD8_PH0, + TEGRA_PIN_GMI_AD9_PH1, + TEGRA_PIN_GMI_AD10_PH2, + TEGRA_PIN_GMI_AD11_PH3, + TEGRA_PIN_GMI_AD12_PH4, + TEGRA_PIN_GMI_AD13_PH5, + TEGRA_PIN_GMI_AD14_PH6, + TEGRA_PIN_GMI_AD15_PH7, + + TEGRA_PIN_GMI_IORDY_PI5, + TEGRA_PIN_GMI_CS7_N_PI6, +}; + +static const unsigned drive_at2_pins[] = { + TEGRA_PIN_GMI_AD0_PG0, + TEGRA_PIN_GMI_AD1_PG1, + TEGRA_PIN_GMI_AD2_PG2, + TEGRA_PIN_GMI_AD3_PG3, + TEGRA_PIN_GMI_AD4_PG4, + TEGRA_PIN_GMI_AD5_PG5, + TEGRA_PIN_GMI_AD6_PG6, + TEGRA_PIN_GMI_AD7_PG7, + + TEGRA_PIN_GMI_WR_N_PI0, + TEGRA_PIN_GMI_OE_N_PI1, + TEGRA_PIN_GMI_CS6_N_PI3, + TEGRA_PIN_GMI_RST_N_PI4, + TEGRA_PIN_GMI_WAIT_PI7, + + TEGRA_PIN_GMI_DQS_P_PJ3, + + TEGRA_PIN_GMI_ADV_N_PK0, + TEGRA_PIN_GMI_CLK_PK1, + TEGRA_PIN_GMI_CS4_N_PK2, + TEGRA_PIN_GMI_CS2_N_PK3, + TEGRA_PIN_GMI_CS3_N_PK4, +}; + +static const unsigned drive_at3_pins[] = { + TEGRA_PIN_GMI_WP_N_PC7, + TEGRA_PIN_GMI_CS0_N_PJ0, +}; + +static const unsigned drive_at4_pins[] = { + TEGRA_PIN_GMI_A17_PB0, + TEGRA_PIN_GMI_A18_PB1, + TEGRA_PIN_GMI_CS1_N_PJ2, + TEGRA_PIN_GMI_A16_PJ7, + TEGRA_PIN_GMI_A19_PK7, +}; + +static const unsigned drive_at5_pins[] = { + TEGRA_PIN_GEN2_I2C_SCL_PT5, + TEGRA_PIN_GEN2_I2C_SDA_PT6, +}; + +static const unsigned drive_cdev1_pins[] = { + TEGRA_PIN_CLK1_OUT_PW4, + TEGRA_PIN_CLK1_REQ_PEE2, +}; + +static const unsigned drive_cdev2_pins[] = { + TEGRA_PIN_CLK2_OUT_PW5, + TEGRA_PIN_CLK2_REQ_PCC5, + TEGRA_PIN_SDMMC1_WP_N_PV3, +}; + +static const unsigned drive_dap1_pins[] = { + TEGRA_PIN_DAP1_FS_PN0, + TEGRA_PIN_DAP1_DIN_PN1, + TEGRA_PIN_DAP1_DOUT_PN2, + TEGRA_PIN_DAP1_SCLK_PN3, +}; + +static const unsigned drive_dap2_pins[] = { + TEGRA_PIN_DAP2_FS_PA2, + TEGRA_PIN_DAP2_SCLK_PA3, + TEGRA_PIN_DAP2_DIN_PA4, + TEGRA_PIN_DAP2_DOUT_PA5, +}; + +static const unsigned drive_dap3_pins[] = { + TEGRA_PIN_DAP3_FS_PP0, + TEGRA_PIN_DAP3_DIN_PP1, + TEGRA_PIN_DAP3_DOUT_PP2, + TEGRA_PIN_DAP3_SCLK_PP3, +}; + +static const unsigned drive_dap4_pins[] = { + TEGRA_PIN_DAP4_FS_PP4, + TEGRA_PIN_DAP4_DIN_PP5, + TEGRA_PIN_DAP4_DOUT_PP6, + TEGRA_PIN_DAP4_SCLK_PP7, +}; + +static const unsigned drive_dbg_pins[] = { + TEGRA_PIN_GEN1_I2C_SCL_PC4, + TEGRA_PIN_GEN1_I2C_SDA_PC5, + TEGRA_PIN_PU0, + TEGRA_PIN_PU1, + TEGRA_PIN_PU2, + TEGRA_PIN_PU3, + TEGRA_PIN_PU4, + TEGRA_PIN_PU5, + TEGRA_PIN_PU6, +}; + +static const unsigned drive_sdio3_pins[] = { + TEGRA_PIN_SDMMC3_CLK_PA6, + TEGRA_PIN_SDMMC3_CMD_PA7, + TEGRA_PIN_SDMMC3_DAT3_PB4, + TEGRA_PIN_SDMMC3_DAT2_PB5, + TEGRA_PIN_SDMMC3_DAT1_PB6, + TEGRA_PIN_SDMMC3_DAT0_PB7, + TEGRA_PIN_SDMMC3_CLK_LB_OUT_PEE4, + TEGRA_PIN_SDMMC3_CLK_LB_IN_PEE5, +}; + +static const unsigned drive_spi_pins[] = { + TEGRA_PIN_DVFS_PWM_PX0, + TEGRA_PIN_GPIO_X1_AUD_PX1, + TEGRA_PIN_DVFS_CLK_PX2, + TEGRA_PIN_GPIO_X3_AUD_PX3, + TEGRA_PIN_GPIO_X4_AUD_PX4, + TEGRA_PIN_GPIO_X5_AUD_PX5, + TEGRA_PIN_GPIO_X6_AUD_PX6, + TEGRA_PIN_GPIO_X7_AUD_PX7, + TEGRA_PIN_GPIO_W2_AUD_PW2, + TEGRA_PIN_GPIO_W3_AUD_PW3, +}; + +static const unsigned drive_uaa_pins[] = { + TEGRA_PIN_ULPI_DATA0_PO1, + TEGRA_PIN_ULPI_DATA1_PO2, + TEGRA_PIN_ULPI_DATA2_PO3, + TEGRA_PIN_ULPI_DATA3_PO4, +}; + +static const unsigned drive_uab_pins[] = { + TEGRA_PIN_ULPI_DATA7_PO0, + TEGRA_PIN_ULPI_DATA4_PO5, + TEGRA_PIN_ULPI_DATA5_PO6, + TEGRA_PIN_ULPI_DATA6_PO7, + TEGRA_PIN_PV0, + TEGRA_PIN_PV1, +}; + +static const unsigned drive_uart2_pins[] = { + TEGRA_PIN_UART2_TXD_PC2, + TEGRA_PIN_UART2_RXD_PC3, + TEGRA_PIN_UART2_CTS_N_PJ5, + TEGRA_PIN_UART2_RTS_N_PJ6, +}; + +static const unsigned drive_uart3_pins[] = { + TEGRA_PIN_UART3_CTS_N_PA1, + TEGRA_PIN_UART3_RTS_N_PC0, + TEGRA_PIN_UART3_TXD_PW6, + TEGRA_PIN_UART3_RXD_PW7, +}; + +static const unsigned drive_sdio1_pins[] = { + TEGRA_PIN_SDMMC1_DAT3_PY4, + TEGRA_PIN_SDMMC1_DAT2_PY5, + TEGRA_PIN_SDMMC1_DAT1_PY6, + TEGRA_PIN_SDMMC1_DAT0_PY7, + TEGRA_PIN_SDMMC1_CLK_PZ0, + TEGRA_PIN_SDMMC1_CMD_PZ1, +}; + +static const unsigned drive_ddc_pins[] = { + TEGRA_PIN_DDC_SCL_PV4, + TEGRA_PIN_DDC_SDA_PV5, +}; + +static const unsigned drive_gma_pins[] = { + TEGRA_PIN_SDMMC4_CLK_PCC4, + TEGRA_PIN_SDMMC4_CMD_PT7, + TEGRA_PIN_SDMMC4_DAT0_PAA0, + TEGRA_PIN_SDMMC4_DAT1_PAA1, + TEGRA_PIN_SDMMC4_DAT2_PAA2, + TEGRA_PIN_SDMMC4_DAT3_PAA3, + TEGRA_PIN_SDMMC4_DAT4_PAA4, + TEGRA_PIN_SDMMC4_DAT5_PAA5, + TEGRA_PIN_SDMMC4_DAT6_PAA6, + TEGRA_PIN_SDMMC4_DAT7_PAA7, +}; + +static const unsigned drive_gme_pins[] = { + TEGRA_PIN_PBB0, + TEGRA_PIN_CAM_I2C_SCL_PBB1, + TEGRA_PIN_CAM_I2C_SDA_PBB2, + TEGRA_PIN_PBB3, + TEGRA_PIN_PCC2, +}; + +static const unsigned drive_gmf_pins[] = { + TEGRA_PIN_PBB4, + TEGRA_PIN_PBB5, + TEGRA_PIN_PBB6, + TEGRA_PIN_PBB7, +}; + +static const unsigned drive_gmg_pins[] = { + TEGRA_PIN_CAM_MCLK_PCC0, +}; + +static const unsigned drive_gmh_pins[] = { + TEGRA_PIN_PCC1, +}; + +static const unsigned drive_owr_pins[] = { + TEGRA_PIN_SDMMC3_CD_N_PV2, +}; + +static const unsigned drive_uda_pins[] = { + TEGRA_PIN_ULPI_CLK_PY0, + TEGRA_PIN_ULPI_DIR_PY1, + TEGRA_PIN_ULPI_NXT_PY2, + TEGRA_PIN_ULPI_STP_PY3, +}; + +static const unsigned drive_dev3_pins[] = { + TEGRA_PIN_CLK3_OUT_PEE0, + TEGRA_PIN_CLK3_REQ_PEE1, +}; + +enum tegra_mux { + TEGRA_MUX_BLINK, + TEGRA_MUX_CEC, + TEGRA_MUX_CLK12, + TEGRA_MUX_CPU, + TEGRA_MUX_DAP, + TEGRA_MUX_DAP1, + TEGRA_MUX_DAP2, + TEGRA_MUX_DEV3, + TEGRA_MUX_DISPLAYA, + TEGRA_MUX_DISPLAYA_ALT, + TEGRA_MUX_DISPLAYB, + TEGRA_MUX_DTV, + TEGRA_MUX_EMC_DLL, + TEGRA_MUX_EXTPERIPH1, + TEGRA_MUX_EXTPERIPH2, + TEGRA_MUX_EXTPERIPH3, + TEGRA_MUX_GMI, + TEGRA_MUX_GMI_ALT, + TEGRA_MUX_HDA, + TEGRA_MUX_HSI, + TEGRA_MUX_I2C1, + TEGRA_MUX_I2C2, + TEGRA_MUX_I2C3, + TEGRA_MUX_I2C4, + TEGRA_MUX_I2CPWR, + TEGRA_MUX_I2S0, + TEGRA_MUX_I2S1, + TEGRA_MUX_I2S2, + TEGRA_MUX_I2S3, + TEGRA_MUX_I2S4, + TEGRA_MUX_IRDA, + TEGRA_MUX_KBC, + TEGRA_MUX_NAND, + TEGRA_MUX_NAND_ALT, + TEGRA_MUX_OWR, + TEGRA_MUX_PMI, + TEGRA_MUX_PWM0, + TEGRA_MUX_PWM1, + TEGRA_MUX_PWM2, + TEGRA_MUX_PWM3, + TEGRA_MUX_PWRON, + TEGRA_MUX_RESET_OUT_N, + TEGRA_MUX_RSVD1, + TEGRA_MUX_RSVD2, + TEGRA_MUX_RSVD3, + TEGRA_MUX_RSVD4, + TEGRA_MUX_SDMMC1, + TEGRA_MUX_SDMMC2, + TEGRA_MUX_SDMMC3, + TEGRA_MUX_SDMMC4, + TEGRA_MUX_SOC, + TEGRA_MUX_SPDIF, + TEGRA_MUX_SPI1, + TEGRA_MUX_SPI2, + TEGRA_MUX_SPI3, + TEGRA_MUX_SPI4, + TEGRA_MUX_SPI5, + TEGRA_MUX_SPI6, + TEGRA_MUX_SYSCLK, + TEGRA_MUX_TRACE, + TEGRA_MUX_UARTA, + TEGRA_MUX_UARTB, + TEGRA_MUX_UARTC, + TEGRA_MUX_UARTD, + TEGRA_MUX_ULPI, + TEGRA_MUX_USB, + TEGRA_MUX_VGP1, + TEGRA_MUX_VGP2, + TEGRA_MUX_VGP3, + TEGRA_MUX_VGP4, + TEGRA_MUX_VGP5, + TEGRA_MUX_VGP6, + TEGRA_MUX_VI, + TEGRA_MUX_VI_ALT1, + TEGRA_MUX_VI_ALT3, +}; + +static const char * const blink_groups[] = { + "clk_32k_out_pa0", +}; + +static const char * const cec_groups[] = { + "hdmi_cec_pee3", +}; + +static const char * const clk12_groups[] = { + "sdmmc1_wp_n_pv3", + "sdmmc1_clk_pz0", +}; + +static const char * const cpu_groups[] = { + "cpu_pwr_req", +}; + +static const char * const dap_groups[] = { + "clk1_req_pee2", + "clk2_req_pcc5", +}; + +static const char * const dap1_groups[] = { + "clk1_req_pee2", +}; + +static const char * const dap2_groups[] = { + "clk1_out_pw4", + "gpio_x4_aud_px4", +}; + +static const char * const dev3_groups[] = { + "clk3_req_pee1", +}; + +static const char * const displaya_groups[] = { + "dap3_fs_pp0", + "dap3_din_pp1", + "dap3_dout_pp2", + "dap3_sclk_pp3", + "uart3_rts_n_pc0", + "pu3", + "pu4", + "pu5", + "pbb3", + "pbb4", + "pbb5", + "pbb6", + "kb_row3_pr3", + "kb_row4_pr4", + "kb_row5_pr5", + "kb_row6_pr6", + "kb_col3_pq3", + "sdmmc3_dat2_pb5", +}; + +static const char * const displaya_alt_groups[] = { + "kb_row6_pr6", +}; + +static const char * const displayb_groups[] = { + "dap3_fs_pp0", + "dap3_din_pp1", + "dap3_dout_pp2", + "dap3_sclk_pp3", + "pu3", + "pu4", + "pu5", + "pu6", + "pbb3", + "pbb4", + "pbb5", + "pbb6", + "kb_row3_pr3", + "kb_row4_pr4", + "kb_row5_pr5", + "kb_row6_pr6", + "sdmmc3_dat3_pb4", +}; + +static const char * const dtv_groups[] = { + "uart3_cts_n_pa1", + "uart3_rts_n_pc0", + "dap4_fs_pp4", + "dap4_dout_pp6", + "gmi_wait_pi7", + "gmi_ad8_ph0", + "gmi_ad14_ph6", + "gmi_ad15_ph7", +}; + +static const char * const emc_dll_groups[] = { + "kb_col0_pq0", + "kb_col1_pq1", +}; + +static const char * const extperiph1_groups[] = { + "clk1_out_pw4", +}; + +static const char * const extperiph2_groups[] = { + "clk2_out_pw5", +}; + +static const char * const extperiph3_groups[] = { + "clk3_out_pee0", +}; + +static const char * const gmi_groups[] = { + "gmi_wp_n_pc7", + + "gmi_ad0_pg0", + "gmi_ad1_pg1", + "gmi_ad2_pg2", + "gmi_ad3_pg3", + "gmi_ad4_pg4", + "gmi_ad5_pg5", + "gmi_ad6_pg6", + "gmi_ad7_pg7", + "gmi_ad8_ph0", + "gmi_ad9_ph1", + "gmi_ad10_ph2", + "gmi_ad11_ph3", + "gmi_ad12_ph4", + "gmi_ad13_ph5", + "gmi_ad14_ph6", + "gmi_ad15_ph7", + "gmi_wr_n_pi0", + "gmi_oe_n_pi1", + "gmi_cs6_n_pi3", + "gmi_rst_n_pi4", + "gmi_iordy_pi5", + "gmi_cs7_n_pi6", + "gmi_wait_pi7", + "gmi_cs0_n_pj0", + "gmi_cs1_n_pj2", + "gmi_dqs_p_pj3", + "gmi_adv_n_pk0", + "gmi_clk_pk1", + "gmi_cs4_n_pk2", + "gmi_cs2_n_pk3", + "gmi_cs3_n_pk4", + "gmi_a16_pj7", + "gmi_a17_pb0", + "gmi_a18_pb1", + "gmi_a19_pk7", + "gen2_i2c_scl_pt5", + "gen2_i2c_sda_pt6", + "sdmmc4_dat0_paa0", + "sdmmc4_dat1_paa1", + "sdmmc4_dat2_paa2", + "sdmmc4_dat3_paa3", + "sdmmc4_dat4_paa4", + "sdmmc4_dat5_paa5", + "sdmmc4_dat6_paa6", + "sdmmc4_dat7_paa7", + "sdmmc4_clk_pcc4", + "sdmmc4_cmd_pt7", + "dap1_fs_pn0", + "dap1_din_pn1", + "dap1_dout_pn2", + "dap1_sclk_pn3", +}; + +static const char * const gmi_alt_groups[] = { + "gmi_wp_n_pc7", + "gmi_cs3_n_pk4", + "gmi_a16_pj7", +}; + +static const char * const hda_groups[] = { + "dap1_fs_pn0", + "dap1_din_pn1", + "dap1_dout_pn2", + "dap1_sclk_pn3", + "dap2_fs_pa2", + "dap2_sclk_pa3", + "dap2_din_pa4", + "dap2_dout_pa5", +}; + +static const char * const hsi_groups[] = { + "ulpi_data0_po1", + "ulpi_data1_po2", + "ulpi_data2_po3", + "ulpi_data3_po4", + "ulpi_data4_po5", + "ulpi_data5_po6", + "ulpi_data6_po7", + "ulpi_data7_po0", +}; + +static const char * const i2c1_groups[] = { + "gen1_i2c_scl_pc4", + "gen1_i2c_sda_pc5", + "gpio_w2_aud_pw2", + "gpio_w3_aud_pw3", +}; + +static const char * const i2c2_groups[] = { + "gen2_i2c_scl_pt5", + "gen2_i2c_sda_pt6", +}; + +static const char * const i2c3_groups[] = { + "cam_i2c_scl_pbb1", + "cam_i2c_sda_pbb2", +}; + +static const char * const i2c4_groups[] = { + "ddc_scl_pv4", + "ddc_sda_pv5", +}; + +static const char * const i2cpwr_groups[] = { + "pwr_i2c_scl_pz6", + "pwr_i2c_sda_pz7", +}; + +static const char * const i2s0_groups[] = { + "dap1_fs_pn0", + "dap1_din_pn1", + "dap1_dout_pn2", + "dap1_sclk_pn3", +}; + +static const char * const i2s1_groups[] = { + "dap2_fs_pa2", + "dap2_sclk_pa3", + "dap2_din_pa4", + "dap2_dout_pa5", +}; + +static const char * const i2s2_groups[] = { + "dap3_fs_pp0", + "dap3_din_pp1", + "dap3_dout_pp2", + "dap3_sclk_pp3", +}; + +static const char * const i2s3_groups[] = { + "dap4_fs_pp4", + "dap4_din_pp5", + "dap4_dout_pp6", + "dap4_sclk_pp7", +}; + +static const char * const i2s4_groups[] = { + "pcc1", + "pbb0", + "pbb7", + "pcc2", +}; + +static const char * const irda_groups[] = { + "uart2_rxd_pc3", + "uart2_txd_pc2", +}; + +static const char * const kbc_groups[] = { + "kb_row0_pr0", + "kb_row1_pr1", + "kb_row2_pr2", + "kb_row3_pr3", + "kb_row4_pr4", + "kb_row5_pr5", + "kb_row6_pr6", + "kb_row7_pr7", + "kb_row8_ps0", + "kb_row9_ps1", + "kb_row10_ps2", + "kb_col0_pq0", + "kb_col1_pq1", + "kb_col2_pq2", + "kb_col3_pq3", + "kb_col4_pq4", + "kb_col5_pq5", + "kb_col6_pq6", + "kb_col7_pq7", +}; + +static const char * const nand_groups[] = { + "gmi_wp_n_pc7", + "gmi_wait_pi7", + "gmi_adv_n_pk0", + "gmi_clk_pk1", + "gmi_cs0_n_pj0", + "gmi_cs1_n_pj2", + "gmi_cs2_n_pk3", + "gmi_cs3_n_pk4", + "gmi_cs4_n_pk2", + "gmi_cs6_n_pi3", + "gmi_cs7_n_pi6", + "gmi_ad0_pg0", + "gmi_ad1_pg1", + "gmi_ad2_pg2", + "gmi_ad3_pg3", + "gmi_ad4_pg4", + "gmi_ad5_pg5", + "gmi_ad6_pg6", + "gmi_ad7_pg7", + "gmi_ad8_ph0", + "gmi_ad9_ph1", + "gmi_ad10_ph2", + "gmi_ad11_ph3", + "gmi_ad12_ph4", + "gmi_ad13_ph5", + "gmi_ad14_ph6", + "gmi_ad15_ph7", + "gmi_wr_n_pi0", + "gmi_oe_n_pi1", + "gmi_dqs_p_pj3", + "gmi_rst_n_pi4", +}; + +static const char * const nand_alt_groups[] = { + "gmi_cs6_n_pi3", + "gmi_cs7_n_pi6", + "gmi_rst_n_pi4", +}; + +static const char * const owr_groups[] = { + "pu0", + "kb_col4_pq4", + "owr", + "sdmmc3_cd_n_pv2", +}; + +static const char * const pmi_groups[] = { + "pwr_int_n", +}; + +static const char * const pwm0_groups[] = { + "sdmmc1_dat2_py5", + "uart3_rts_n_pc0", + "pu3", + "gmi_ad8_ph0", + "sdmmc3_dat3_pb4", +}; + +static const char * const pwm1_groups[] = { + "sdmmc1_dat1_py6", + "pu4", + "gmi_ad9_ph1", + "sdmmc3_dat2_pb5", +}; + +static const char * const pwm2_groups[] = { + "pu5", + "gmi_ad10_ph2", + "kb_col3_pq3", + "sdmmc3_dat1_pb6", +}; + +static const char * const pwm3_groups[] = { + "pu6", + "gmi_ad11_ph3", + "sdmmc3_cmd_pa7", +}; + +static const char * const pwron_groups[] = { + "core_pwr_req", +}; + +static const char * const reset_out_n_groups[] = { + "reset_out_n", +}; + +static const char * const rsvd1_groups[] = { + "pv1", + "hdmi_int_pn7", + "pu1", + "pu2", + "gmi_wp_n_pc7", + "gmi_adv_n_pk0", + "gmi_cs0_n_pj0", + "gmi_cs1_n_pj2", + "gmi_ad0_pg0", + "gmi_ad1_pg1", + "gmi_ad2_pg2", + "gmi_ad3_pg3", + "gmi_ad4_pg4", + "gmi_ad5_pg5", + "gmi_ad6_pg6", + "gmi_ad7_pg7", + "gmi_wr_n_pi0", + "gmi_oe_n_pi1", + "gpio_x4_aud_px4", + "gpio_x5_aud_px5", + "gpio_x7_aud_px7", + + "reset_out_n", +}; + +static const char * const rsvd2_groups[] = { + "pv0", + "pv1", + "sdmmc1_dat0_py7", + "clk2_out_pw5", + "clk2_req_pcc5", + "hdmi_int_pn7", + "ddc_scl_pv4", + "ddc_sda_pv5", + "uart3_txd_pw6", + "uart3_rxd_pw7", + "gen1_i2c_scl_pc4", + "gen1_i2c_sda_pc5", + "dap4_fs_pp4", + "dap4_din_pp5", + "dap4_dout_pp6", + "dap4_sclk_pp7", + "clk3_out_pee0", + "clk3_req_pee1", + "gmi_iordy_pi5", + "gmi_a17_pb0", + "gmi_a18_pb1", + "gen2_i2c_scl_pt5", + "gen2_i2c_sda_pt6", + "sdmmc4_clk_pcc4", + "sdmmc4_cmd_pt7", + "sdmmc4_dat7_paa7", + "pcc1", + "pbb7", + "pcc2", + "pwr_i2c_scl_pz6", + "pwr_i2c_sda_pz7", + "kb_row0_pr0", + "kb_row1_pr1", + "kb_row2_pr2", + "kb_row7_pr7", + "kb_row8_ps0", + "kb_row9_ps1", + "kb_row10_ps2", + "kb_col1_pq1", + "kb_col2_pq2", + "kb_col5_pq5", + "kb_col6_pq6", + "kb_col7_pq7", + "sys_clk_req_pz5", + "core_pwr_req", + "cpu_pwr_req", + "pwr_int_n", + "owr", + "spdif_out_pk5", + "gpio_x1_aud_px1", + "sdmmc3_clk_pa6", + "sdmmc3_dat0_pb7", + "gpio_w2_aud_pw2", + "usb_vbus_en0_pn4", + "usb_vbus_en1_pn5", + "sdmmc3_clk_lb_out_pee4", + "sdmmc3_clk_lb_in_pee5", + "reset_out_n", +}; + +static const char * const rsvd3_groups[] = { + "pv0", + "pv1", + "sdmmc1_clk_pz0", + "clk2_out_pw5", + "clk2_req_pcc5", + "hdmi_int_pn7", + "ddc_scl_pv4", + "ddc_sda_pv5", + "uart2_rts_n_pj6", + "uart2_cts_n_pj5", + "uart3_txd_pw6", + "uart3_rxd_pw7", + "pu0", + "pu1", + "pu2", + "gen1_i2c_scl_pc4", + "gen1_i2c_sda_pc5", + "dap4_din_pp5", + "dap4_sclk_pp7", + "clk3_out_pee0", + "clk3_req_pee1", + "pcc1", + "cam_i2c_scl_pbb1", + "cam_i2c_sda_pbb2", + "pbb7", + "pcc2", + "pwr_i2c_scl_pz6", + "pwr_i2c_sda_pz7", + "kb_row0_pr0", + "kb_row1_pr1", + "kb_row2_pr2", + "kb_row3_pr3", + "kb_row9_ps1", + "kb_row10_ps2", + "clk_32k_out_pa0", + "sys_clk_req_pz5", + "core_pwr_req", + "cpu_pwr_req", + "pwr_int_n", + "owr", + "clk1_req_pee2", + "clk1_out_pw4", + "spdif_out_pk5", + "spdif_in_pk6", + "dap2_fs_pa2", + "dap2_sclk_pa3", + "dap2_din_pa4", + "dap2_dout_pa5", + "dvfs_pwm_px0", + "gpio_x1_aud_px1", + "gpio_x3_aud_px3", + "dvfs_clk_px2", + "sdmmc3_clk_pa6", + "sdmmc3_dat0_pb7", + "hdmi_cec_pee3", + "sdmmc3_cd_n_pv2", + "usb_vbus_en0_pn4", + "usb_vbus_en1_pn5", + "sdmmc3_clk_lb_out_pee4", + "sdmmc3_clk_lb_in_pee5", + "reset_out_n", +}; + +static const char * const rsvd4_groups[] = { + "pv0", + "pv1", + "sdmmc1_clk_pz0", + "clk2_out_pw5", + "clk2_req_pcc5", + "hdmi_int_pn7", + "ddc_scl_pv4", + "ddc_sda_pv5", + "pu0", + "pu1", + "pu2", + "gen1_i2c_scl_pc4", + "gen1_i2c_sda_pc5", + "dap4_fs_pp4", + "dap4_din_pp5", + "dap4_dout_pp6", + "dap4_sclk_pp7", + "clk3_out_pee0", + "clk3_req_pee1", + "gmi_ad0_pg0", + "gmi_ad1_pg1", + "gmi_ad2_pg2", + "gmi_ad3_pg3", + "gmi_ad4_pg4", + "gmi_ad12_ph4", + "gmi_ad13_ph5", + "gmi_rst_n_pi4", + "gen2_i2c_scl_pt5", + "gen2_i2c_sda_pt6", + "sdmmc4_clk_pcc4", + "sdmmc4_cmd_pt7", + "sdmmc4_dat0_paa0", + "sdmmc4_dat1_paa1", + "sdmmc4_dat2_paa2", + "sdmmc4_dat3_paa3", + "sdmmc4_dat4_paa4", + "sdmmc4_dat5_paa5", + "sdmmc4_dat6_paa6", + "sdmmc4_dat7_paa7", + "cam_mclk_pcc0", + "pcc1", + "cam_i2c_scl_pbb1", + "cam_i2c_sda_pbb2", + "pbb3", + "pbb4", + "pbb5", + "pbb6", + "pbb7", + "pcc2", + "pwr_i2c_scl_pz6", + "pwr_i2c_sda_pz7", + "kb_row0_pr0", + "kb_row1_pr1", + "kb_row2_pr2", + "kb_col2_pq2", + "kb_col5_pq5", + "kb_col6_pq6", + "kb_col7_pq7", + "clk_32k_out_pa0", + "sys_clk_req_pz5", + "core_pwr_req", + "cpu_pwr_req", + "pwr_int_n", + "owr", + "dap1_fs_pn0", + "dap1_din_pn1", + "dap1_dout_pn2", + "dap1_sclk_pn3", + "clk1_req_pee2", + "clk1_out_pw4", + "spdif_in_pk6", + "spdif_out_pk5", + "dap2_fs_pa2", + "dap2_sclk_pa3", + "dap2_din_pa4", + "dap2_dout_pa5", + "dvfs_pwm_px0", + "gpio_x1_aud_px1", + "gpio_x3_aud_px3", + "dvfs_clk_px2", + "gpio_x5_aud_px5", + "gpio_x6_aud_px6", + "gpio_x7_aud_px7", + "sdmmc3_cd_n_pv2", + "usb_vbus_en0_pn4", + "usb_vbus_en1_pn5", + "sdmmc3_clk_lb_in_pee5", + "sdmmc3_clk_lb_out_pee4", +}; + +static const char * const sdmmc1_groups[] = { + + "sdmmc1_clk_pz0", + "sdmmc1_cmd_pz1", + "sdmmc1_dat3_py4", + "sdmmc1_dat2_py5", + "sdmmc1_dat1_py6", + "sdmmc1_dat0_py7", + "uart3_cts_n_pa1", + "kb_col5_pq5", + "sdmmc1_wp_n_pv3", +}; + +static const char * const sdmmc2_groups[] = { + "gmi_iordy_pi5", + "gmi_clk_pk1", + "gmi_cs2_n_pk3", + "gmi_cs3_n_pk4", + "gmi_cs7_n_pi6", + "gmi_ad12_ph4", + "gmi_ad13_ph5", + "gmi_ad14_ph6", + "gmi_ad15_ph7", + "gmi_dqs_p_pj3", +}; + +static const char * const sdmmc3_groups[] = { + "kb_col4_pq4", + "sdmmc3_clk_pa6", + "sdmmc3_cmd_pa7", + "sdmmc3_dat0_pb7", + "sdmmc3_dat1_pb6", + "sdmmc3_dat2_pb5", + "sdmmc3_dat3_pb4", + "hdmi_cec_pee3", + "sdmmc3_cd_n_pv2", + "sdmmc3_clk_lb_in_pee5", + "sdmmc3_clk_lb_out_pee4", +}; + +static const char * const sdmmc4_groups[] = { + "sdmmc4_clk_pcc4", + "sdmmc4_cmd_pt7", + "sdmmc4_dat0_paa0", + "sdmmc4_dat1_paa1", + "sdmmc4_dat2_paa2", + "sdmmc4_dat3_paa3", + "sdmmc4_dat4_paa4", + "sdmmc4_dat5_paa5", + "sdmmc4_dat6_paa6", + "sdmmc4_dat7_paa7", +}; + +static const char * const soc_groups[] = { + "gmi_cs1_n_pj2", + "gmi_oe_n_pi1", + "clk_32k_out_pa0", + "hdmi_cec_pee3", +}; + +static const char * const spdif_groups[] = { + "sdmmc1_cmd_pz1", + "sdmmc1_dat3_py4", + "uart2_rxd_pc3", + "uart2_txd_pc2", + "spdif_in_pk6", + "spdif_out_pk5", +}; + +static const char * const spi1_groups[] = { + "ulpi_clk_py0", + "ulpi_dir_py1", + "ulpi_nxt_py2", + "ulpi_stp_py3", + "gpio_x3_aud_px3", + "gpio_x4_aud_px4", + "gpio_x5_aud_px5", + "gpio_x6_aud_px6", + "gpio_x7_aud_px7", + "gpio_w3_aud_pw3", +}; + +static const char * const spi2_groups[] = { + "ulpi_data4_po5", + "ulpi_data5_po6", + "ulpi_data6_po7", + "ulpi_data7_po0", + "kb_row4_pr4", + "kb_row5_pr5", + "kb_col0_pq0", + "kb_col1_pq1", + "kb_col2_pq2", + "kb_col6_pq6", + "kb_col7_pq7", + "gpio_x4_aud_px4", + "gpio_x5_aud_px5", + "gpio_x6_aud_px6", + "gpio_x7_aud_px7", + "gpio_w2_aud_pw2", + "gpio_w3_aud_pw3", +}; + +static const char * const spi3_groups[] = { + "ulpi_data0_po1", + "ulpi_data1_po2", + "ulpi_data2_po3", + "ulpi_data3_po4", + "sdmmc4_dat0_paa0", + "sdmmc4_dat1_paa1", + "sdmmc4_dat2_paa2", + "sdmmc4_dat3_paa3", + "sdmmc4_dat4_paa4", + "sdmmc4_dat5_paa5", + "sdmmc4_dat6_paa6", + "sdmmc3_clk_pa6", + "sdmmc3_cmd_pa7", + "sdmmc3_dat0_pb7", + "sdmmc3_dat1_pb6", + "sdmmc3_dat2_pb5", + "sdmmc3_dat3_pb4", +}; + +static const char * const spi4_groups[] = { + "sdmmc1_cmd_pz1", + "sdmmc1_dat3_py4", + "sdmmc1_dat2_py5", + "sdmmc1_dat1_py6", + "sdmmc1_dat0_py7", + "uart2_rxd_pc3", + "uart2_txd_pc2", + "uart2_rts_n_pj6", + "uart2_cts_n_pj5", + "uart3_txd_pw6", + "uart3_rxd_pw7", + "uart3_cts_n_pa1", + "gmi_wait_pi7", + "gmi_cs6_n_pi3", + "gmi_ad5_pg5", + "gmi_ad6_pg6", + "gmi_ad7_pg7", + "gmi_a19_pk7", + "gmi_wr_n_pi0", + "sdmmc1_wp_n_pv3", +}; + +static const char * const spi5_groups[] = { + "ulpi_clk_py0", + "ulpi_dir_py1", + "ulpi_nxt_py2", + "ulpi_stp_py3", + "dap3_fs_pp0", + "dap3_din_pp1", + "dap3_dout_pp2", + "dap3_sclk_pp3", +}; + +static const char * const spi6_groups[] = { + "dvfs_pwm_px0", + "gpio_x1_aud_px1", + "gpio_x3_aud_px3", + "dvfs_clk_px2", + "gpio_x6_aud_px6", + "gpio_w2_aud_pw2", + "gpio_w3_aud_pw3", +}; + +static const char * const sysclk_groups[] = { + "sys_clk_req_pz5", +}; + +static const char * const trace_groups[] = { + "gmi_iordy_pi5", + "gmi_adv_n_pk0", + "gmi_clk_pk1", + "gmi_cs2_n_pk3", + "gmi_cs4_n_pk2", + "gmi_a16_pj7", + "gmi_a17_pb0", + "gmi_a18_pb1", + "gmi_a19_pk7", + "gmi_dqs_p_pj3", +}; + +static const char * const uarta_groups[] = { + "ulpi_data0_po1", + "ulpi_data1_po2", + "ulpi_data2_po3", + "ulpi_data3_po4", + "ulpi_data4_po5", + "ulpi_data5_po6", + "ulpi_data6_po7", + "ulpi_data7_po0", + "sdmmc1_cmd_pz1", + "sdmmc1_dat3_py4", + "sdmmc1_dat2_py5", + "sdmmc1_dat1_py6", + "sdmmc1_dat0_py7", + "uart2_rxd_pc3", + "uart2_txd_pc2", + "uart2_rts_n_pj6", + "uart2_cts_n_pj5", + "pu0", + "pu1", + "pu2", + "pu3", + "pu4", + "pu5", + "pu6", + "kb_row7_pr7", + "kb_row8_ps0", + "kb_row9_ps1", + "kb_row10_ps2", + "kb_col3_pq3", + "kb_col4_pq4", + "sdmmc3_cmd_pa7", + "sdmmc3_dat1_pb6", + "sdmmc1_wp_n_pv3", +}; + +static const char * const uartb_groups[] = { + "uart2_rts_n_pj6", + "uart2_cts_n_pj5", +}; + +static const char * const uartc_groups[] = { + "uart3_txd_pw6", + "uart3_rxd_pw7", + "uart3_cts_n_pa1", + "uart3_rts_n_pc0", +}; + +static const char * const uartd_groups[] = { + "ulpi_clk_py0", + "ulpi_dir_py1", + "ulpi_nxt_py2", + "ulpi_stp_py3", + "gmi_a16_pj7", + "gmi_a17_pb0", + "gmi_a18_pb1", + "gmi_a19_pk7", +}; + +static const char * const ulpi_groups[] = { + "ulpi_data0_po1", + "ulpi_data1_po2", + "ulpi_data2_po3", + "ulpi_data3_po4", + "ulpi_data4_po5", + "ulpi_data5_po6", + "ulpi_data6_po7", + "ulpi_data7_po0", + "ulpi_clk_py0", + "ulpi_dir_py1", + "ulpi_nxt_py2", + "ulpi_stp_py3", +}; + +static const char * const usb_groups[] = { + "pv0", + "pu6", + "gmi_cs0_n_pj0", + "gmi_cs4_n_pk2", + "gmi_ad11_ph3", + "kb_col0_pq0", + "spdif_in_pk6", + "usb_vbus_en0_pn4", + "usb_vbus_en1_pn5", +}; + +static const char * const vgp1_groups[] = { + "cam_i2c_scl_pbb1", +}; + +static const char * const vgp2_groups[] = { + "cam_i2c_sda_pbb2", +}; + +static const char * const vgp3_groups[] = { + "pbb3", +}; + +static const char * const vgp4_groups[] = { + "pbb4", +}; + +static const char * const vgp5_groups[] = { + "pbb5", +}; + +static const char * const vgp6_groups[] = { + "pbb6", +}; + +static const char * const vi_groups[] = { + "cam_mclk_pcc0", + "pbb0", +}; + +static const char * const vi_alt1_groups[] = { + "cam_mclk_pcc0", + "pbb0", +}; + +static const char * const vi_alt3_groups[] = { + "cam_mclk_pcc0", + "pbb0", +}; + +#define FUNCTION(fname) \ + { \ + .name = #fname, \ + .groups = fname##_groups, \ + .ngroups = ARRAY_SIZE(fname##_groups), \ + } + +static const struct tegra_function tegra114_functions[] = { + FUNCTION(blink), + FUNCTION(cec), + FUNCTION(clk12), + FUNCTION(cpu), + FUNCTION(dap), + FUNCTION(dap1), + FUNCTION(dap2), + FUNCTION(dev3), + FUNCTION(displaya), + FUNCTION(displaya_alt), + FUNCTION(displayb), + FUNCTION(dtv), + FUNCTION(emc_dll), + FUNCTION(extperiph1), + FUNCTION(extperiph2), + FUNCTION(extperiph3), + FUNCTION(gmi), + FUNCTION(gmi_alt), + FUNCTION(hda), + FUNCTION(hsi), + FUNCTION(i2c1), + FUNCTION(i2c2), + FUNCTION(i2c3), + FUNCTION(i2c4), + FUNCTION(i2cpwr), + FUNCTION(i2s0), + FUNCTION(i2s1), + FUNCTION(i2s2), + FUNCTION(i2s3), + FUNCTION(i2s4), + FUNCTION(irda), + FUNCTION(kbc), + FUNCTION(nand), + FUNCTION(nand_alt), + FUNCTION(owr), + FUNCTION(pmi), + FUNCTION(pwm0), + FUNCTION(pwm1), + FUNCTION(pwm2), + FUNCTION(pwm3), + FUNCTION(pwron), + FUNCTION(reset_out_n), + FUNCTION(rsvd1), + FUNCTION(rsvd2), + FUNCTION(rsvd3), + FUNCTION(rsvd4), + FUNCTION(sdmmc1), + FUNCTION(sdmmc2), + FUNCTION(sdmmc3), + FUNCTION(sdmmc4), + FUNCTION(soc), + FUNCTION(spdif), + FUNCTION(spi1), + FUNCTION(spi2), + FUNCTION(spi3), + FUNCTION(spi4), + FUNCTION(spi5), + FUNCTION(spi6), + FUNCTION(sysclk), + FUNCTION(trace), + FUNCTION(uarta), + FUNCTION(uartb), + FUNCTION(uartc), + FUNCTION(uartd), + FUNCTION(ulpi), + FUNCTION(usb), + FUNCTION(vgp1), + FUNCTION(vgp2), + FUNCTION(vgp3), + FUNCTION(vgp4), + FUNCTION(vgp5), + FUNCTION(vgp6), + FUNCTION(vi), + FUNCTION(vi_alt1), + FUNCTION(vi_alt3), +}; + +#define DRV_PINGROUP_REG_START 0x868 /* bank 0 */ +#define PINGROUP_REG_START 0x3000 /* bank 1 */ + +#define PINGROUP_REG_Y(r) ((r) - PINGROUP_REG_START) +#define PINGROUP_REG_N(r) -1 + +#define PINGROUP(pg_name, f0, f1, f2, f3, f_safe, r, od, ior, rcv_sel) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = ARRAY_SIZE(pg_name##_pins), \ + .funcs = { \ + TEGRA_MUX_##f0, \ + TEGRA_MUX_##f1, \ + TEGRA_MUX_##f2, \ + TEGRA_MUX_##f3, \ + }, \ + .func_safe = TEGRA_MUX_##f_safe, \ + .mux_reg = PINGROUP_REG_Y(r), \ + .mux_bank = 1, \ + .mux_bit = 0, \ + .pupd_reg = PINGROUP_REG_Y(r), \ + .pupd_bank = 1, \ + .pupd_bit = 2, \ + .tri_reg = PINGROUP_REG_Y(r), \ + .tri_bank = 1, \ + .tri_bit = 4, \ + .einput_reg = PINGROUP_REG_Y(r), \ + .einput_bank = 1, \ + .einput_bit = 5, \ + .odrain_reg = PINGROUP_REG_##od(r), \ + .odrain_bank = 1, \ + .odrain_bit = 6, \ + .lock_reg = PINGROUP_REG_Y(r), \ + .lock_bank = 1, \ + .lock_bit = 7, \ + .ioreset_reg = PINGROUP_REG_##ior(r), \ + .ioreset_bank = 1, \ + .ioreset_bit = 8, \ + .rcv_sel_reg = PINGROUP_REG_##rcv_sel(r), \ + .rcv_sel_bank = 1, \ + .rcv_sel_bit = 9, \ + .drv_reg = -1, \ + .drvtype_reg = -1, \ + } + +#define DRV_PINGROUP_DVRTYPE_Y(r) ((r) - DRV_PINGROUP_REG_START) +#define DRV_PINGROUP_DVRTYPE_N(r) -1 + +#define DRV_PINGROUP(pg_name, r, hsm_b, schmitt_b, lpmd_b, \ + drvdn_b, drvdn_w, drvup_b, drvup_w, \ + slwr_b, slwr_w, slwf_b, slwf_w, \ + drvtype) \ + { \ + .name = "drive_" #pg_name, \ + .pins = drive_##pg_name##_pins, \ + .npins = ARRAY_SIZE(drive_##pg_name##_pins), \ + .mux_reg = -1, \ + .pupd_reg = -1, \ + .tri_reg = -1, \ + .einput_reg = -1, \ + .odrain_reg = -1, \ + .lock_reg = -1, \ + .ioreset_reg = -1, \ + .rcv_sel_reg = -1, \ + .drv_reg = DRV_PINGROUP_DVRTYPE_Y(r), \ + .drv_bank = 0, \ + .hsm_bit = hsm_b, \ + .schmitt_bit = schmitt_b, \ + .lpmd_bit = lpmd_b, \ + .drvdn_bit = drvdn_b, \ + .drvdn_width = drvdn_w, \ + .drvup_bit = drvup_b, \ + .drvup_width = drvup_w, \ + .slwr_bit = slwr_b, \ + .slwr_width = slwr_w, \ + .slwf_bit = slwf_b, \ + .slwf_width = slwf_w, \ + .drvtype_reg = DRV_PINGROUP_DVRTYPE_##drvtype(r), \ + .drvtype_bank = 0, \ + .drvtype_bit = 6, \ + } + +static const struct tegra_pingroup tegra114_groups[] = { + /* pg_name, f0, f1, f2, f3, safe, r, od, ior, rcv_sel */ + /* FIXME: Fill in correct data in safe column */ + PINGROUP(ulpi_data0_po1, SPI3, HSI, UARTA, ULPI, ULPI, 0x3000, N, N, N), + PINGROUP(ulpi_data1_po2, SPI3, HSI, UARTA, ULPI, ULPI, 0x3004, N, N, N), + PINGROUP(ulpi_data2_po3, SPI3, HSI, UARTA, ULPI, ULPI, 0x3008, N, N, N), + PINGROUP(ulpi_data3_po4, SPI3, HSI, UARTA, ULPI, ULPI, 0x300c, N, N, N), + PINGROUP(ulpi_data4_po5, SPI2, HSI, UARTA, ULPI, ULPI, 0x3010, N, N, N), + PINGROUP(ulpi_data5_po6, SPI2, HSI, UARTA, ULPI, ULPI, 0x3014, N, N, N), + PINGROUP(ulpi_data6_po7, SPI2, HSI, UARTA, ULPI, ULPI, 0x3018, N, N, N), + PINGROUP(ulpi_data7_po0, SPI2, HSI, UARTA, ULPI, ULPI, 0x301c, N, N, N), + PINGROUP(ulpi_clk_py0, SPI1, SPI5, UARTD, ULPI, ULPI, 0x3020, N, N, N), + PINGROUP(ulpi_dir_py1, SPI1, SPI5, UARTD, ULPI, ULPI, 0x3024, N, N, N), + PINGROUP(ulpi_nxt_py2, SPI1, SPI5, UARTD, ULPI, ULPI, 0x3028, N, N, N), + PINGROUP(ulpi_stp_py3, SPI1, SPI5, UARTD, ULPI, ULPI, 0x302c, N, N, N), + PINGROUP(dap3_fs_pp0, I2S2, SPI5, DISPLAYA, DISPLAYB, I2S2, 0x3030, N, N, N), + PINGROUP(dap3_din_pp1, I2S2, SPI5, DISPLAYA, DISPLAYB, I2S2, 0x3034, N, N, N), + PINGROUP(dap3_dout_pp2, I2S2, SPI5, DISPLAYA, DISPLAYB, I2S2, 0x3038, N, N, N), + PINGROUP(dap3_sclk_pp3, I2S2, SPI5, DISPLAYA, DISPLAYB, I2S2, 0x303c, N, N, N), + PINGROUP(pv0, USB, RSVD2, RSVD3, RSVD4, RSVD4, 0x3040, N, N, N), + PINGROUP(pv1, RSVD1, RSVD2, RSVD3, RSVD4, RSVD4, 0x3044, N, N, N), + PINGROUP(sdmmc1_clk_pz0, SDMMC1, CLK12, RSVD3, RSVD4, RSVD4, 0x3048, N, N, N), + PINGROUP(sdmmc1_cmd_pz1, SDMMC1, SPDIF, SPI4, UARTA, SDMMC1, 0x304c, N, N, N), + PINGROUP(sdmmc1_dat3_py4, SDMMC1, SPDIF, SPI4, UARTA, SDMMC1, 0x3050, N, N, N), + PINGROUP(sdmmc1_dat2_py5, SDMMC1, PWM0, SPI4, UARTA, SDMMC1, 0x3054, N, N, N), + PINGROUP(sdmmc1_dat1_py6, SDMMC1, PWM1, SPI4, UARTA, SDMMC1, 0x3058, N, N, N), + PINGROUP(sdmmc1_dat0_py7, SDMMC1, RSVD2, SPI4, UARTA, RSVD2, 0x305c, N, N, N), + PINGROUP(clk2_out_pw5, EXTPERIPH2, RSVD2, RSVD3, RSVD4, RSVD4, 0x3068, N, N, N), + PINGROUP(clk2_req_pcc5, DAP, RSVD2, RSVD3, RSVD4, RSVD4, 0x306c, N, N, N), + PINGROUP(hdmi_int_pn7, RSVD1, RSVD2, RSVD3, RSVD4, RSVD4, 0x3110, N, N, Y), + PINGROUP(ddc_scl_pv4, I2C4, RSVD2, RSVD3, RSVD4, RSVD4, 0x3114, N, N, Y), + PINGROUP(ddc_sda_pv5, I2C4, RSVD2, RSVD3, RSVD4, RSVD4, 0x3118, N, N, Y), + PINGROUP(uart2_rxd_pc3, IRDA, SPDIF, UARTA, SPI4, IRDA, 0x3164, N, N, N), + PINGROUP(uart2_txd_pc2, IRDA, SPDIF, UARTA, SPI4, IRDA, 0x3168, N, N, N), + PINGROUP(uart2_rts_n_pj6, UARTA, UARTB, RSVD3, SPI4, RSVD3, 0x316c, N, N, N), + PINGROUP(uart2_cts_n_pj5, UARTA, UARTB, RSVD3, SPI4, RSVD3, 0x3170, N, N, N), + PINGROUP(uart3_txd_pw6, UARTC, RSVD2, RSVD3, SPI4, RSVD3, 0x3174, N, N, N), + PINGROUP(uart3_rxd_pw7, UARTC, RSVD2, RSVD3, SPI4, RSVD3, 0x3178, N, N, N), + PINGROUP(uart3_cts_n_pa1, UARTC, SDMMC1, DTV, SPI4, UARTC, 0x317c, N, N, N), + PINGROUP(uart3_rts_n_pc0, UARTC, PWM0, DTV, DISPLAYA, UARTC, 0x3180, N, N, N), + PINGROUP(pu0, OWR, UARTA, RSVD3, RSVD4, RSVD4, 0x3184, N, N, N), + PINGROUP(pu1, RSVD1, UARTA, RSVD3, RSVD4, RSVD4, 0x3188, N, N, N), + PINGROUP(pu2, RSVD1, UARTA, RSVD3, RSVD4, RSVD4, 0x318c, N, N, N), + PINGROUP(pu3, PWM0, UARTA, DISPLAYA, DISPLAYB, PWM0, 0x3190, N, N, N), + PINGROUP(pu4, PWM1, UARTA, DISPLAYA, DISPLAYB, PWM1, 0x3194, N, N, N), + PINGROUP(pu5, PWM2, UARTA, DISPLAYA, DISPLAYB, PWM2, 0x3198, N, N, N), + PINGROUP(pu6, PWM3, UARTA, USB, DISPLAYB, PWM3, 0x319c, N, N, N), + PINGROUP(gen1_i2c_sda_pc5, I2C1, RSVD2, RSVD3, RSVD4, RSVD4, 0x31a0, Y, N, N), + PINGROUP(gen1_i2c_scl_pc4, I2C1, RSVD2, RSVD3, RSVD4, RSVD4, 0x31a4, Y, N, N), + PINGROUP(dap4_fs_pp4, I2S3, RSVD2, DTV, RSVD4, RSVD4, 0x31a8, N, N, N), + PINGROUP(dap4_din_pp5, I2S3, RSVD2, RSVD3, RSVD4, RSVD4, 0x31ac, N, N, N), + PINGROUP(dap4_dout_pp6, I2S3, RSVD2, DTV, RSVD4, RSVD4, 0x31b0, N, N, N), + PINGROUP(dap4_sclk_pp7, I2S3, RSVD2, RSVD3, RSVD4, RSVD4, 0x31b4, N, N, N), + PINGROUP(clk3_out_pee0, EXTPERIPH3, RSVD2, RSVD3, RSVD4, RSVD4, 0x31b8, N, N, N), + PINGROUP(clk3_req_pee1, DEV3, RSVD2, RSVD3, RSVD4, RSVD4, 0x31bc, N, N, N), + PINGROUP(gmi_wp_n_pc7, RSVD1, NAND, GMI, GMI_ALT, RSVD1, 0x31c0, N, N, N), + PINGROUP(gmi_iordy_pi5, SDMMC2, RSVD2, GMI, TRACE, RSVD2, 0x31c4, N, N, N), + PINGROUP(gmi_wait_pi7, SPI4, NAND, GMI, DTV, NAND, 0x31c8, N, N, N), + PINGROUP(gmi_adv_n_pk0, RSVD1, NAND, GMI, TRACE, RSVD1, 0x31cc, N, N, N), + PINGROUP(gmi_clk_pk1, SDMMC2, NAND, GMI, TRACE, GMI, 0x31d0, N, N, N), + PINGROUP(gmi_cs0_n_pj0, RSVD1, NAND, GMI, USB, RSVD1, 0x31d4, N, N, N), + PINGROUP(gmi_cs1_n_pj2, RSVD1, NAND, GMI, SOC, RSVD1, 0x31d8, N, N, N), + PINGROUP(gmi_cs2_n_pk3, SDMMC2, NAND, GMI, TRACE, GMI, 0x31dc, N, N, N), + PINGROUP(gmi_cs3_n_pk4, SDMMC2, NAND, GMI, GMI_ALT, GMI, 0x31e0, N, N, N), + PINGROUP(gmi_cs4_n_pk2, USB, NAND, GMI, TRACE, GMI, 0x31e4, N, N, N), + PINGROUP(gmi_cs6_n_pi3, NAND, NAND_ALT, GMI, SPI4, NAND, 0x31e8, N, N, N), + PINGROUP(gmi_cs7_n_pi6, NAND, NAND_ALT, GMI, SDMMC2, NAND, 0x31ec, N, N, N), + PINGROUP(gmi_ad0_pg0, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31f0, N, N, N), + PINGROUP(gmi_ad1_pg1, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31f4, N, N, N), + PINGROUP(gmi_ad2_pg2, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31f8, N, N, N), + PINGROUP(gmi_ad3_pg3, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31fc, N, N, N), + PINGROUP(gmi_ad4_pg4, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x3200, N, N, N), + PINGROUP(gmi_ad5_pg5, RSVD1, NAND, GMI, SPI4, RSVD1, 0x3204, N, N, N), + PINGROUP(gmi_ad6_pg6, RSVD1, NAND, GMI, SPI4, RSVD1, 0x3208, N, N, N), + PINGROUP(gmi_ad7_pg7, RSVD1, NAND, GMI, SPI4, RSVD1, 0x320c, N, N, N), + PINGROUP(gmi_ad8_ph0, PWM0, NAND, GMI, DTV, GMI, 0x3210, N, N, N), + PINGROUP(gmi_ad9_ph1, PWM1, NAND, GMI, RSVD4, GMI, 0x3214, N, N, N), + PINGROUP(gmi_ad10_ph2, PWM2, NAND, GMI, RSVD4, GMI, 0x3218, N, N, N), + PINGROUP(gmi_ad11_ph3, PWM3, NAND, GMI, USB, GMI, 0x321c, N, N, N), + PINGROUP(gmi_ad12_ph4, SDMMC2, NAND, GMI, RSVD4, RSVD4, 0x3220, N, N, N), + PINGROUP(gmi_ad13_ph5, SDMMC2, NAND, GMI, RSVD4, RSVD4, 0x3224, N, N, N), + PINGROUP(gmi_ad14_ph6, SDMMC2, NAND, GMI, DTV, GMI, 0x3228, N, N, N), + PINGROUP(gmi_ad15_ph7, SDMMC2, NAND, GMI, DTV, GMI, 0x322c, N, N, N), + PINGROUP(gmi_a16_pj7, UARTD, TRACE, GMI, GMI_ALT, GMI, 0x3230, N, N, N), + PINGROUP(gmi_a17_pb0, UARTD, RSVD2, GMI, TRACE, RSVD2, 0x3234, N, N, N), + PINGROUP(gmi_a18_pb1, UARTD, RSVD2, GMI, TRACE, RSVD2, 0x3238, N, N, N), + PINGROUP(gmi_a19_pk7, UARTD, SPI4, GMI, TRACE, GMI, 0x323c, N, N, N), + PINGROUP(gmi_wr_n_pi0, RSVD1, NAND, GMI, SPI4, RSVD1, 0x3240, N, N, N), + PINGROUP(gmi_oe_n_pi1, RSVD1, NAND, GMI, SOC, RSVD1, 0x3244, N, N, N), + PINGROUP(gmi_dqs_p_pj3, SDMMC2, NAND, GMI, TRACE, NAND, 0x3248, N, N, N), + PINGROUP(gmi_rst_n_pi4, NAND, NAND_ALT, GMI, RSVD4, RSVD4, 0x324c, N, N, N), + PINGROUP(gen2_i2c_scl_pt5, I2C2, RSVD2, GMI, RSVD4, RSVD4, 0x3250, Y, N, N), + PINGROUP(gen2_i2c_sda_pt6, I2C2, RSVD2, GMI, RSVD4, RSVD4, 0x3254, Y, N, N), + PINGROUP(sdmmc4_clk_pcc4, SDMMC4, RSVD2, GMI, RSVD4, RSVD4, 0x3258, N, Y, N), + PINGROUP(sdmmc4_cmd_pt7, SDMMC4, RSVD2, GMI, RSVD4, RSVD4, 0x325c, N, Y, N), + PINGROUP(sdmmc4_dat0_paa0, SDMMC4, SPI3, GMI, RSVD4, RSVD4, 0x3260, N, Y, N), + PINGROUP(sdmmc4_dat1_paa1, SDMMC4, SPI3, GMI, RSVD4, RSVD4, 0x3264, N, Y, N), + PINGROUP(sdmmc4_dat2_paa2, SDMMC4, SPI3, GMI, RSVD4, RSVD4, 0x3268, N, Y, N), + PINGROUP(sdmmc4_dat3_paa3, SDMMC4, SPI3, GMI, RSVD4, RSVD4, 0x326c, N, Y, N), + PINGROUP(sdmmc4_dat4_paa4, SDMMC4, SPI3, GMI, RSVD4, RSVD4, 0x3270, N, Y, N), + PINGROUP(sdmmc4_dat5_paa5, SDMMC4, SPI3, GMI, RSVD4, RSVD4, 0x3274, N, Y, N), + PINGROUP(sdmmc4_dat6_paa6, SDMMC4, SPI3, GMI, RSVD4, RSVD4, 0x3278, N, Y, N), + PINGROUP(sdmmc4_dat7_paa7, SDMMC4, RSVD2, GMI, RSVD4, RSVD4, 0x327c, N, Y, N), + PINGROUP(cam_mclk_pcc0, VI, VI_ALT1, VI_ALT3, RSVD4, RSVD4, 0x3284, N, N, N), + PINGROUP(pcc1, I2S4, RSVD2, RSVD3, RSVD4, RSVD4, 0x3288, N, N, N), + PINGROUP(pbb0, I2S4, VI, VI_ALT1, VI_ALT3, I2S4, 0x328c, N, N, N), + PINGROUP(cam_i2c_scl_pbb1, VGP1, I2C3, RSVD3, RSVD4, RSVD4, 0x3290, Y, N, N), + PINGROUP(cam_i2c_sda_pbb2, VGP2, I2C3, RSVD3, RSVD4, RSVD4, 0x3294, Y, N, N), + PINGROUP(pbb3, VGP3, DISPLAYA, DISPLAYB, RSVD4, RSVD4, 0x3298, N, N, N), + PINGROUP(pbb4, VGP4, DISPLAYA, DISPLAYB, RSVD4, RSVD4, 0x329c, N, N, N), + PINGROUP(pbb5, VGP5, DISPLAYA, DISPLAYB, RSVD4, RSVD4, 0x32a0, N, N, N), + PINGROUP(pbb6, VGP6, DISPLAYA, DISPLAYB, RSVD4, RSVD4, 0x32a4, N, N, N), + PINGROUP(pbb7, I2S4, RSVD2, RSVD3, RSVD4, RSVD4, 0x32a8, N, N, N), + PINGROUP(pcc2, I2S4, RSVD2, RSVD3, RSVD4, RSVD4, 0x32ac, N, N, N), + PINGROUP(pwr_i2c_scl_pz6, I2CPWR, RSVD2, RSVD3, RSVD4, RSVD4, 0x32b4, Y, N, N), + PINGROUP(pwr_i2c_sda_pz7, I2CPWR, RSVD2, RSVD3, RSVD4, RSVD4, 0x32b8, Y, N, N), + PINGROUP(kb_row0_pr0, KBC, RSVD2, RSVD3, RSVD4, RSVD4, 0x32bc, N, N, N), + PINGROUP(kb_row1_pr1, KBC, RSVD2, RSVD3, RSVD4, RSVD4, 0x32c0, N, N, N), + PINGROUP(kb_row2_pr2, KBC, RSVD2, RSVD3, RSVD4, RSVD4, 0x32c4, N, N, N), + PINGROUP(kb_row3_pr3, KBC, DISPLAYA, RSVD3, DISPLAYB, RSVD3, 0x32c8, N, N, N), + PINGROUP(kb_row4_pr4, KBC, DISPLAYA, SPI2, DISPLAYB, KBC, 0x32cc, N, N, N), + PINGROUP(kb_row5_pr5, KBC, DISPLAYA, SPI2, DISPLAYB, KBC, 0x32d0, N, N, N), + PINGROUP(kb_row6_pr6, KBC, DISPLAYA, DISPLAYA_ALT, DISPLAYB, KBC, 0x32d4, N, N, N), + PINGROUP(kb_row7_pr7, KBC, RSVD2, RSVD3, UARTA, RSVD2, 0x32d8, N, N, N), + PINGROUP(kb_row8_ps0, KBC, RSVD2, RSVD3, UARTA, RSVD2, 0x32dc, N, N, N), + PINGROUP(kb_row9_ps1, KBC, RSVD2, RSVD3, UARTA, RSVD3, 0x32e0, N, N, N), + PINGROUP(kb_row10_ps2, KBC, RSVD2, RSVD3, UARTA, RSVD3, 0x32e4, N, N, N), + PINGROUP(kb_col0_pq0, KBC, USB, SPI2, EMC_DLL, KBC, 0x32fc, N, N, N), + PINGROUP(kb_col1_pq1, KBC, RSVD2, SPI2, EMC_DLL, RSVD2, 0x3300, N, N, N), + PINGROUP(kb_col2_pq2, KBC, RSVD2, SPI2, RSVD4, RSVD2, 0x3304, N, N, N), + PINGROUP(kb_col3_pq3, KBC, DISPLAYA, PWM2, UARTA, KBC, 0x3308, N, N, N), + PINGROUP(kb_col4_pq4, KBC, OWR, SDMMC3, UARTA, KBC, 0x330c, N, N, N), + PINGROUP(kb_col5_pq5, KBC, RSVD2, SDMMC1, RSVD4, RSVD4, 0x3310, N, N, N), + PINGROUP(kb_col6_pq6, KBC, RSVD2, SPI2, RSVD4, RSVD4, 0x3314, N, N, N), + PINGROUP(kb_col7_pq7, KBC, RSVD2, SPI2, RSVD4, RSVD4, 0x3318, N, N, N), + PINGROUP(clk_32k_out_pa0, BLINK, SOC, RSVD3, RSVD4, RSVD4, 0x331c, N, N, N), + PINGROUP(sys_clk_req_pz5, SYSCLK, RSVD2, RSVD3, RSVD4, RSVD4, 0x3320, N, N, N), + PINGROUP(core_pwr_req, PWRON, RSVD2, RSVD3, RSVD4, RSVD4, 0x3324, N, N, N), + PINGROUP(cpu_pwr_req, CPU, RSVD2, RSVD3, RSVD4, RSVD4, 0x3328, N, N, N), + PINGROUP(pwr_int_n, PMI, RSVD2, RSVD3, RSVD4, RSVD4, 0x332c, N, N, N), + PINGROUP(owr, OWR, RSVD2, RSVD3, RSVD4, RSVD4, 0x3334, N, N, Y), + PINGROUP(dap1_fs_pn0, I2S0, HDA, GMI, RSVD4, RSVD4, 0x3338, N, N, N), + PINGROUP(dap1_din_pn1, I2S0, HDA, GMI, RSVD4, RSVD4, 0x333c, N, N, N), + PINGROUP(dap1_dout_pn2, I2S0, HDA, GMI, RSVD4, RSVD4, 0x3340, N, N, N), + PINGROUP(dap1_sclk_pn3, I2S0, HDA, GMI, RSVD4, RSVD4, 0x3344, N, N, N), + PINGROUP(clk1_req_pee2, DAP, DAP1, RSVD3, RSVD4, RSVD4, 0x3348, N, N, N), + PINGROUP(clk1_out_pw4, EXTPERIPH1, DAP2, RSVD3, RSVD4, RSVD4, 0x334c, N, N, N), + PINGROUP(spdif_in_pk6, SPDIF, USB, RSVD3, RSVD4, RSVD4, 0x3350, N, N, N), + PINGROUP(spdif_out_pk5, SPDIF, RSVD2, RSVD3, RSVD4, RSVD4, 0x3354, N, N, N), + PINGROUP(dap2_fs_pa2, I2S1, HDA, RSVD3, RSVD4, RSVD4, 0x3358, N, N, N), + PINGROUP(dap2_din_pa4, I2S1, HDA, RSVD3, RSVD4, RSVD4, 0x335c, N, N, N), + PINGROUP(dap2_dout_pa5, I2S1, HDA, RSVD3, RSVD4, RSVD4, 0x3360, N, N, N), + PINGROUP(dap2_sclk_pa3, I2S1, HDA, RSVD3, RSVD4, RSVD4, 0x3364, N, N, N), + PINGROUP(dvfs_pwm_px0, SPI6, RSVD2, RSVD3, RSVD4, RSVD4, 0x3368, N, N, N), + PINGROUP(gpio_x1_aud_px1, SPI6, RSVD2, RSVD3, RSVD4, RSVD4, 0x336c, N, N, N), + PINGROUP(gpio_x3_aud_px3, SPI6, SPI1, RSVD3, RSVD4, RSVD4, 0x3370, N, N, N), + PINGROUP(gpio_x4_aud_px4, RSVD1, SPI1, SPI2, DAP2, RSVD1, 0x3378, N, N, N), + PINGROUP(gpio_x5_aud_px5, RSVD1, SPI1, SPI2, RSVD4, RSVD1, 0x337c, N, N, N), + PINGROUP(gpio_x6_aud_px6, SPI6, SPI1, SPI2, RSVD4, RSVD4, 0x3380, N, N, N), + PINGROUP(gpio_x7_aud_px7, RSVD1, SPI1, SPI2, RSVD4, RSVD4, 0x3384, N, N, N), + PINGROUP(sdmmc3_clk_pa6, SDMMC3, RSVD2, RSVD3, SPI3, RSVD3, 0x3390, N, N, N), + PINGROUP(sdmmc3_cmd_pa7, SDMMC3, PWM3, UARTA, SPI3, SDMMC3, 0x3394, N, N, N), + PINGROUP(sdmmc3_dat0_pb7, SDMMC3, RSVD2, RSVD3, SPI3, RSVD3, 0x3398, N, N, N), + PINGROUP(sdmmc3_dat1_pb6, SDMMC3, PWM2, UARTA, SPI3, SDMMC3, 0x339c, N, N, N), + PINGROUP(sdmmc3_dat2_pb5, SDMMC3, PWM1, DISPLAYA, SPI3, SDMMC3, 0x33a0, N, N, N), + PINGROUP(sdmmc3_dat3_pb4, SDMMC3, PWM0, DISPLAYB, SPI3, SDMMC3, 0x33a4, N, N, N), + PINGROUP(hdmi_cec_pee3, CEC, SDMMC3, RSVD3, SOC, RSVD3, 0x33e0, Y, N, N), + PINGROUP(sdmmc1_wp_n_pv3, SDMMC1, CLK12, SPI4, UARTA, SDMMC1, 0x33e4, N, N, N), + PINGROUP(sdmmc3_cd_n_pv2, SDMMC3, OWR, RSVD3, RSVD4, RSVD4, 0x33e8, N, N, N), + PINGROUP(gpio_w2_aud_pw2, SPI6, RSVD2, SPI2, I2C1, RSVD2, 0x33ec, N, N, N), + PINGROUP(gpio_w3_aud_pw3, SPI6, SPI1, SPI2, I2C1, SPI6, 0x33f0, N, N, N), + PINGROUP(usb_vbus_en0_pn4, USB, RSVD2, RSVD3, RSVD4, RSVD4, 0x33f4, Y, N, N), + PINGROUP(usb_vbus_en1_pn5, USB, RSVD2, RSVD3, RSVD4, RSVD4, 0x33f8, Y, N, N), + PINGROUP(sdmmc3_clk_lb_in_pee5, SDMMC3, RSVD2, RSVD3, RSVD4, RSVD4, 0x33fc, N, N, N), + PINGROUP(sdmmc3_clk_lb_out_pee4, SDMMC3, RSVD2, RSVD3, RSVD4, RSVD4, 0x3400, N, N, N), + PINGROUP(reset_out_n, RSVD1, RSVD2, RSVD3, RESET_OUT_N, RSVD3, 0x3408, N, N, N), + + /* pg_name, r, hsm_b, schmitt_b, lpmd_b, drvdn_b, drvdn_w, drvup_b, drvup_w, slwr_b, slwr_w, slwf_b, slwf_w, drvtype */ + DRV_PINGROUP(ao1, 0x868, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(ao2, 0x86c, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(at1, 0x870, 2, 3, 4, 12, 7, 20, 7, 28, 2, 30, 2, Y), + DRV_PINGROUP(at2, 0x874, 2, 3, 4, 12, 7, 20, 7, 28, 2, 30, 2, Y), + DRV_PINGROUP(at3, 0x878, 2, 3, 4, 12, 7, 20, 7, 28, 2, 30, 2, Y), + DRV_PINGROUP(at4, 0x87c, 2, 3, 4, 12, 7, 20, 7, 28, 2, 30, 2, Y), + DRV_PINGROUP(at5, 0x880, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(cdev1, 0x884, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(cdev2, 0x888, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(dap1, 0x890, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(dap2, 0x894, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(dap3, 0x898, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(dap4, 0x89c, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(dbg, 0x8a0, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(sdio3, 0x8b0, 2, 3, -1, 12, 7, 20, 7, 28, 2, 30, 2, N), + DRV_PINGROUP(spi, 0x8b4, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(uaa, 0x8b8, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(uab, 0x8bc, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(uart2, 0x8c0, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(uart3, 0x8c4, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(sdio1, 0x8ec, 2, 3, -1, 12, 7, 20, 7, 28, 2, 30, 2, N), + DRV_PINGROUP(ddc, 0x8fc, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(gma, 0x900, 2, 3, 4, 14, 5, 20, 5, 28, 2, 30, 2, Y), + DRV_PINGROUP(gme, 0x910, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(gmf, 0x914, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(gmg, 0x918, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(gmh, 0x91c, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(owr, 0x920, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(uda, 0x924, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), +}; + +static const struct tegra_pinctrl_soc_data tegra114_pinctrl = { + .ngpios = NUM_GPIOS, + .pins = tegra114_pins, + .npins = ARRAY_SIZE(tegra114_pins), + .functions = tegra114_functions, + .nfunctions = ARRAY_SIZE(tegra114_functions), + .groups = tegra114_groups, + .ngroups = ARRAY_SIZE(tegra114_groups), +}; + +static int tegra114_pinctrl_probe(struct platform_device *pdev) +{ + return tegra_pinctrl_probe(pdev, &tegra114_pinctrl); +} + +static struct of_device_id tegra114_pinctrl_of_match[] = { + { .compatible = "nvidia,tegra114-pinmux", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra114_pinctrl_of_match); + +static struct platform_driver tegra114_pinctrl_driver = { + .driver = { + .name = "tegra114-pinctrl", + .owner = THIS_MODULE, + .of_match_table = tegra114_pinctrl_of_match, + }, + .probe = tegra114_pinctrl_probe, + .remove = tegra_pinctrl_remove, +}; +module_platform_driver(tegra114_pinctrl_driver); + +MODULE_ALIAS("platform:tegra114-pinctrl"); +MODULE_AUTHOR("Pritesh Raithatha "); +MODULE_DESCRIPTION("NVIDIA Tegra114 pincontrol driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From eb8b8f2e7164b819ca35a3d6240198fdb98d0ef3 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Thu, 20 Dec 2012 16:51:31 +0800 Subject: clk: prima2: enable dt-binding clkdev mapping this patche deletes hard code that registers clkdev by things like: clk_register_clkdev(clk, NULL, "b0030000.nand"); clk_register_clkdev(clk, NULL, "b0040000.audio"); clk_register_clkdev(clk, NULL, "b0080000.usp"); prima2 clock controller becomes a clock provider and every dt node just declares its clock sources by dt prop. it also makes us easier to extend this driver to support both prima2 and marco as marco has different address mapping with prima2. Signed-off-by: Barry Song Signed-off-by: Mike Turquette --- .../devicetree/bindings/clock/prima2-clock.txt | 73 ++++++++ arch/arm/boot/dts/prima2.dtsi | 31 +++- drivers/clk/clk-prima2.c | 205 ++++++++------------- 3 files changed, 183 insertions(+), 126 deletions(-) create mode 100644 Documentation/devicetree/bindings/clock/prima2-clock.txt (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/clock/prima2-clock.txt b/Documentation/devicetree/bindings/clock/prima2-clock.txt new file mode 100644 index 000000000000..5016979c0f78 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/prima2-clock.txt @@ -0,0 +1,73 @@ +* Clock bindings for CSR SiRFprimaII + +Required properties: +- compatible: Should be "sirf,prima2-clkc" +- reg: Address and length of the register set +- interrupts: Should contain clock controller interrupt +- #clock-cells: Should be <1> + +The clock consumer should specify the desired clock by having the clock +ID in its "clocks" phandle cell. The following is a full list of prima2 +clocks and IDs. + + Clock ID + --------------------------- + rtc 0 + osc 1 + pll1 2 + pll2 3 + pll3 4 + mem 5 + sys 6 + security 7 + dsp 8 + gps 9 + mf 10 + io 11 + cpu 12 + uart0 13 + uart1 14 + uart2 15 + tsc 16 + i2c0 17 + i2c1 18 + spi0 19 + spi1 20 + pwmc 21 + efuse 22 + pulse 23 + dmac0 24 + dmac1 25 + nand 26 + audio 27 + usp0 28 + usp1 29 + usp2 30 + vip 31 + gfx 32 + mm 33 + lcd 34 + vpp 35 + mmc01 36 + mmc23 37 + mmc45 38 + usbpll 39 + usb0 40 + usb1 41 + +Examples: + +clks: clock-controller@88000000 { + compatible = "sirf,prima2-clkc"; + reg = <0x88000000 0x1000>; + interrupts = <3>; + #clock-cells = <1>; +}; + +i2c0: i2c@b00e0000 { + cell-index = <0>; + compatible = "sirf,prima2-i2c"; + reg = <0xb00e0000 0x10000>; + interrupts = <24>; + clocks = <&clks 17>; +}; diff --git a/arch/arm/boot/dts/prima2.dtsi b/arch/arm/boot/dts/prima2.dtsi index 055fca542120..3329719a9412 100644 --- a/arch/arm/boot/dts/prima2.dtsi +++ b/arch/arm/boot/dts/prima2.dtsi @@ -58,10 +58,11 @@ #size-cells = <1>; ranges = <0x88000000 0x88000000 0x40000>; - clock-controller@88000000 { + clks: clock-controller@88000000 { compatible = "sirf,prima2-clkc"; reg = <0x88000000 0x1000>; interrupts = <3>; + #clock-cells = <1>; }; reset-controller@88010000 { @@ -85,6 +86,7 @@ compatible = "sirf,prima2-memc"; reg = <0x90000000 0x10000>; interrupts = <27>; + clocks = <&clks 5>; }; }; @@ -104,6 +106,7 @@ compatible = "sirf,prima2-vpp"; reg = <0x90020000 0x10000>; interrupts = <31>; + clocks = <&clks 35>; }; }; @@ -117,6 +120,7 @@ compatible = "powervr,sgx531"; reg = <0x98000000 0x8000000>; interrupts = <6>; + clocks = <&clks 32>; }; }; @@ -130,6 +134,7 @@ compatible = "sirf,prima2-video-codec"; reg = <0xa0000000 0x8000000>; interrupts = <5>; + clocks = <&clks 33>; }; }; @@ -149,12 +154,14 @@ compatible = "sirf,prima2-gps"; reg = <0xa8010000 0x10000>; interrupts = <7>; + clocks = <&clks 9>; }; dsp@a9000000 { compatible = "sirf,prima2-dsp"; reg = <0xa9000000 0x1000000>; interrupts = <8>; + clocks = <&clks 8>; }; }; @@ -174,12 +181,14 @@ compatible = "sirf,prima2-nand"; reg = <0xb0030000 0x10000>; interrupts = <41>; + clocks = <&clks 26>; }; audio@b0040000 { compatible = "sirf,prima2-audio"; reg = <0xb0040000 0x10000>; interrupts = <35>; + clocks = <&clks 27>; }; uart0: uart@b0050000 { @@ -187,6 +196,7 @@ compatible = "sirf,prima2-uart"; reg = <0xb0050000 0x10000>; interrupts = <17>; + clocks = <&clks 13>; }; uart1: uart@b0060000 { @@ -194,6 +204,7 @@ compatible = "sirf,prima2-uart"; reg = <0xb0060000 0x10000>; interrupts = <18>; + clocks = <&clks 14>; }; uart2: uart@b0070000 { @@ -201,6 +212,7 @@ compatible = "sirf,prima2-uart"; reg = <0xb0070000 0x10000>; interrupts = <19>; + clocks = <&clks 15>; }; usp0: usp@b0080000 { @@ -208,6 +220,7 @@ compatible = "sirf,prima2-usp"; reg = <0xb0080000 0x10000>; interrupts = <20>; + clocks = <&clks 28>; }; usp1: usp@b0090000 { @@ -215,6 +228,7 @@ compatible = "sirf,prima2-usp"; reg = <0xb0090000 0x10000>; interrupts = <21>; + clocks = <&clks 29>; }; usp2: usp@b00a0000 { @@ -222,6 +236,7 @@ compatible = "sirf,prima2-usp"; reg = <0xb00a0000 0x10000>; interrupts = <22>; + clocks = <&clks 30>; }; dmac0: dma-controller@b00b0000 { @@ -229,6 +244,7 @@ compatible = "sirf,prima2-dmac"; reg = <0xb00b0000 0x10000>; interrupts = <12>; + clocks = <&clks 24>; }; dmac1: dma-controller@b0160000 { @@ -236,11 +252,13 @@ compatible = "sirf,prima2-dmac"; reg = <0xb0160000 0x10000>; interrupts = <13>; + clocks = <&clks 25>; }; vip@b00C0000 { compatible = "sirf,prima2-vip"; reg = <0xb00C0000 0x10000>; + clocks = <&clks 31>; }; spi0: spi@b00d0000 { @@ -248,6 +266,7 @@ compatible = "sirf,prima2-spi"; reg = <0xb00d0000 0x10000>; interrupts = <15>; + clocks = <&clks 19>; }; spi1: spi@b0170000 { @@ -255,6 +274,7 @@ compatible = "sirf,prima2-spi"; reg = <0xb0170000 0x10000>; interrupts = <16>; + clocks = <&clks 20>; }; i2c0: i2c@b00e0000 { @@ -262,6 +282,7 @@ compatible = "sirf,prima2-i2c"; reg = <0xb00e0000 0x10000>; interrupts = <24>; + clocks = <&clks 17>; }; i2c1: i2c@b00f0000 { @@ -269,12 +290,14 @@ compatible = "sirf,prima2-i2c"; reg = <0xb00f0000 0x10000>; interrupts = <25>; + clocks = <&clks 18>; }; tsc@b0110000 { compatible = "sirf,prima2-tsc"; reg = <0xb0110000 0x10000>; interrupts = <33>; + clocks = <&clks 16>; }; gpio: pinctrl@b0120000 { @@ -507,17 +530,20 @@ pwm@b0130000 { compatible = "sirf,prima2-pwm"; reg = <0xb0130000 0x10000>; + clocks = <&clks 21>; }; efusesys@b0140000 { compatible = "sirf,prima2-efuse"; reg = <0xb0140000 0x10000>; + clocks = <&clks 22>; }; pulsec@b0150000 { compatible = "sirf,prima2-pulsec"; reg = <0xb0150000 0x10000>; interrupts = <48>; + clocks = <&clks 23>; }; pci-iobg { @@ -616,12 +642,14 @@ compatible = "chipidea,ci13611a-prima2"; reg = <0xb8000000 0x10000>; interrupts = <10>; + clocks = <&clks 40>; }; usb1: usb@b00f0000 { compatible = "chipidea,ci13611a-prima2"; reg = <0xb8010000 0x10000>; interrupts = <11>; + clocks = <&clks 41>; }; sata@b00f0000 { @@ -634,6 +662,7 @@ compatible = "sirf,prima2-security"; reg = <0xb8030000 0x10000>; interrupts = <42>; + clocks = <&clks 7>; }; }; }; diff --git a/drivers/clk/clk-prima2.c b/drivers/clk/clk-prima2.c index a203ecccdc4f..f8e9d0c27be2 100644 --- a/drivers/clk/clk-prima2.c +++ b/drivers/clk/clk-prima2.c @@ -1025,20 +1025,67 @@ static struct of_device_id rsc_ids[] = { {}, }; +enum prima2_clk_index { + /* 0 1 2 3 4 5 6 7 8 9 */ + rtc, osc, pll1, pll2, pll3, mem, sys, security, dsp, gps, + mf, io, cpu, uart0, uart1, uart2, tsc, i2c0, i2c1, spi0, + spi1, pwmc, efuse, pulse, dmac0, dmac1, nand, audio, usp0, usp1, + usp2, vip, gfx, mm, lcd, vpp, mmc01, mmc23, mmc45, usbpll, + usb0, usb1, maxclk, +}; + +static __initdata struct clk_hw* prima2_clk_hw_array[maxclk] = { + NULL, /* dummy */ + NULL, + &clk_pll1.hw, + &clk_pll2.hw, + &clk_pll3.hw, + &clk_mem.hw, + &clk_sys.hw, + &clk_security.hw, + &clk_dsp.hw, + &clk_gps.hw, + &clk_mf.hw, + &clk_io.hw, + &clk_cpu.hw, + &clk_uart0.hw, + &clk_uart1.hw, + &clk_uart2.hw, + &clk_tsc.hw, + &clk_i2c0.hw, + &clk_i2c1.hw, + &clk_spi0.hw, + &clk_spi1.hw, + &clk_pwmc.hw, + &clk_efuse.hw, + &clk_pulse.hw, + &clk_dmac0.hw, + &clk_dmac1.hw, + &clk_nand.hw, + &clk_audio.hw, + &clk_usp0.hw, + &clk_usp1.hw, + &clk_usp2.hw, + &clk_vip.hw, + &clk_gfx.hw, + &clk_mm.hw, + &clk_lcd.hw, + &clk_vpp.hw, + &clk_mmc01.hw, + &clk_mmc23.hw, + &clk_mmc45.hw, + &usb_pll_clk_hw, + &clk_usb0.hw, + &clk_usb1.hw, +}; + +static struct clk *prima2_clks[maxclk]; +static struct clk_onecell_data clk_data; + void __init sirfsoc_of_clk_init(void) { - struct clk *clk; struct device_node *np; - - np = of_find_matching_node(NULL, clkc_ids); - if (!np) - panic("unable to find compatible clkc node in dtb\n"); - - sirfsoc_clk_vbase = of_iomap(np, 0); - if (!sirfsoc_clk_vbase) - panic("unable to map clkc registers\n"); - - of_node_put(np); + int i; np = of_find_matching_node(NULL, rsc_ids); if (!np) @@ -1050,122 +1097,30 @@ void __init sirfsoc_of_clk_init(void) of_node_put(np); + np = of_find_matching_node(NULL, clkc_ids); + if (!np) + return; + + sirfsoc_clk_vbase = of_iomap(np, 0); + if (!sirfsoc_clk_vbase) + panic("unable to map clkc registers\n"); /* These are always available (RTC and 26MHz OSC)*/ - clk = clk_register_fixed_rate(NULL, "rtc", NULL, + prima2_clks[rtc] = clk_register_fixed_rate(NULL, "rtc", NULL, CLK_IS_ROOT, 32768); - BUG_ON(IS_ERR(clk)); - clk = clk_register_fixed_rate(NULL, "osc", NULL, + prima2_clks[osc]= clk_register_fixed_rate(NULL, "osc", NULL, CLK_IS_ROOT, 26000000); - BUG_ON(IS_ERR(clk)); - - clk = clk_register(NULL, &clk_pll1.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_pll2.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_pll3.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_mem.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_sys.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_security.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b8030000.security"); - clk = clk_register(NULL, &clk_dsp.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_gps.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "a8010000.gps"); - clk = clk_register(NULL, &clk_mf.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_io.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "io"); - clk = clk_register(NULL, &clk_cpu.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "cpu"); - clk = clk_register(NULL, &clk_uart0.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0050000.uart"); - clk = clk_register(NULL, &clk_uart1.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0060000.uart"); - clk = clk_register(NULL, &clk_uart2.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0070000.uart"); - clk = clk_register(NULL, &clk_tsc.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0110000.tsc"); - clk = clk_register(NULL, &clk_i2c0.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00e0000.i2c"); - clk = clk_register(NULL, &clk_i2c1.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00f0000.i2c"); - clk = clk_register(NULL, &clk_spi0.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00d0000.spi"); - clk = clk_register(NULL, &clk_spi1.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0170000.spi"); - clk = clk_register(NULL, &clk_pwmc.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0130000.pwm"); - clk = clk_register(NULL, &clk_efuse.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0140000.efusesys"); - clk = clk_register(NULL, &clk_pulse.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0150000.pulsec"); - clk = clk_register(NULL, &clk_dmac0.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00b0000.dma-controller"); - clk = clk_register(NULL, &clk_dmac1.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0160000.dma-controller"); - clk = clk_register(NULL, &clk_nand.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0030000.nand"); - clk = clk_register(NULL, &clk_audio.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0040000.audio"); - clk = clk_register(NULL, &clk_usp0.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0080000.usp"); - clk = clk_register(NULL, &clk_usp1.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b0090000.usp"); - clk = clk_register(NULL, &clk_usp2.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00a0000.usp"); - clk = clk_register(NULL, &clk_vip.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00c0000.vip"); - clk = clk_register(NULL, &clk_gfx.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "98000000.graphics"); - clk = clk_register(NULL, &clk_mm.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "a0000000.multimedia"); - clk = clk_register(NULL, &clk_lcd.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "90010000.display"); - clk = clk_register(NULL, &clk_vpp.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "90020000.vpp"); - clk = clk_register(NULL, &clk_mmc01.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_mmc23.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_mmc45.hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &usb_pll_clk_hw); - BUG_ON(IS_ERR(clk)); - clk = clk_register(NULL, &clk_usb0.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00e0000.usb"); - clk = clk_register(NULL, &clk_usb1.hw); - BUG_ON(IS_ERR(clk)); - clk_register_clkdev(clk, NULL, "b00f0000.usb"); + + for (i = pll1; i < maxclk; i++) { + prima2_clks[i] = clk_register(NULL, prima2_clk_hw_array[i]); + BUG_ON(!prima2_clks[i]); + } + clk_register_clkdev(prima2_clks[cpu], NULL, "cpu"); + clk_register_clkdev(prima2_clks[io], NULL, "io"); + clk_register_clkdev(prima2_clks[mem], NULL, "mem"); + + clk_data.clks = prima2_clks; + clk_data.clk_num = maxclk; + + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); } -- cgit v1.2.3 From c098020d0368ded1c6ba8d7b612feffe067509a6 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 21 Jan 2013 14:22:56 +0100 Subject: serial/efm32: parse location property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The non-dt probing allowed passing the location via platform data from the beginning. So make up leeway for device tree probing. Signed-off-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/tty/serial/efm32-uart.txt | 6 +++++ drivers/tty/serial/efm32-uart.c | 31 +++++++++++++++++----- 2 files changed, 30 insertions(+), 7 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/tty/serial/efm32-uart.txt b/Documentation/devicetree/bindings/tty/serial/efm32-uart.txt index 6588b6950a7f..8e080b893b49 100644 --- a/Documentation/devicetree/bindings/tty/serial/efm32-uart.txt +++ b/Documentation/devicetree/bindings/tty/serial/efm32-uart.txt @@ -5,10 +5,16 @@ Required properties: - reg : Address and length of the register set - interrupts : Should contain uart interrupt +Optional properties: +- location : Decides the location of the USART I/O pins. + Allowed range : [0 .. 5] + Default: 0 + Example: uart@0x4000c400 { compatible = "efm32,uart"; reg = <0x4000c400 0x400>; interrupts = <15>; + location = <0>; }; diff --git a/drivers/tty/serial/efm32-uart.c b/drivers/tty/serial/efm32-uart.c index de14bd7dce10..7d199c8e1a75 100644 --- a/drivers/tty/serial/efm32-uart.c +++ b/drivers/tty/serial/efm32-uart.c @@ -81,6 +81,7 @@ struct efm32_uart_port { struct uart_port port; unsigned int txirq; struct clk *clk; + struct efm32_uart_pdata pdata; }; #define to_efm_port(_port) container_of(_port, struct efm32_uart_port, port) #define efm_debug(efm_port, format, arg...) \ @@ -293,13 +294,8 @@ static irqreturn_t efm32_uart_txirq(int irq, void *data) static int efm32_uart_startup(struct uart_port *port) { struct efm32_uart_port *efm_port = to_efm_port(port); - u32 location = 0; - struct efm32_uart_pdata *pdata = dev_get_platdata(port->dev); int ret; - if (pdata) - location = UARTn_ROUTE_LOCATION(pdata->location); - ret = clk_enable(efm_port->clk); if (ret) { efm_debug(efm_port, "failed to enable clk\n"); @@ -308,7 +304,9 @@ static int efm32_uart_startup(struct uart_port *port) port->uartclk = clk_get_rate(efm_port->clk); /* Enable pins at configured location */ - efm32_uart_write32(efm_port, location | UARTn_ROUTE_RXPEN | UARTn_ROUTE_TXPEN, + efm32_uart_write32(efm_port, + UARTn_ROUTE_LOCATION(efm_port->pdata.location) | + UARTn_ROUTE_RXPEN | UARTn_ROUTE_TXPEN, UARTn_ROUTE); ret = request_irq(port->irq, efm32_uart_rxirq, 0, @@ -667,11 +665,24 @@ static int efm32_uart_probe_dt(struct platform_device *pdev, struct efm32_uart_port *efm_port) { struct device_node *np = pdev->dev.of_node; + u32 location; int ret; if (!np) return 1; + ret = of_property_read_u32(np, "location", &location); + if (!ret) { + if (location > 5) { + dev_err(&pdev->dev, "invalid location\n"); + return -EINVAL; + } + efm_debug(efm_port, "using location %u\n", location); + efm_port->pdata.location = location; + } else { + efm_debug(efm_port, "fall back to location 0\n"); + } + ret = of_alias_get_id(np, "serial"); if (ret < 0) { dev_err(&pdev->dev, "failed to get alias id: %d\n", ret); @@ -731,10 +742,16 @@ static int efm32_uart_probe(struct platform_device *pdev) efm_port->port.flags = UPF_BOOT_AUTOCONF; ret = efm32_uart_probe_dt(pdev, efm_port); - if (ret > 0) + if (ret > 0) { /* not created by device tree */ + const struct efm32_uart_pdata *pdata = dev_get_platdata(&pdev->dev); + efm_port->port.line = pdev->id; + if (pdata) + efm_port->pdata = *pdata; + } + if (efm_port->port.line >= 0 && efm_port->port.line < ARRAY_SIZE(efm32_uart_ports)) efm32_uart_ports[efm_port->port.line] = efm_port; -- cgit v1.2.3 From 09180e5b4e673a92f1a8f47061948f524ceba8df Mon Sep 17 00:00:00 2001 From: Barry Song Date: Thu, 20 Dec 2012 17:07:38 +0800 Subject: ARM: PRIMA2: add CSR SiRFmarco device tree .dts SiRFmarco is a dual-core cortex-a9 SMP SoC from CSR. this patch adds the .dtsi and a basic evb board .dts for it. Signed-off-by: Barry Song Reviewed-by: Mark Rutland --- Documentation/devicetree/bindings/arm/sirf.txt | 10 +- arch/arm/boot/dts/marco-evb.dts | 54 ++ arch/arm/boot/dts/marco.dtsi | 756 +++++++++++++++++++++++++ 3 files changed, 818 insertions(+), 2 deletions(-) create mode 100644 arch/arm/boot/dts/marco-evb.dts create mode 100644 arch/arm/boot/dts/marco.dtsi (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/arm/sirf.txt b/Documentation/devicetree/bindings/arm/sirf.txt index 1881e1c6dda5..c6ba6d3c747f 100644 --- a/Documentation/devicetree/bindings/arm/sirf.txt +++ b/Documentation/devicetree/bindings/arm/sirf.txt @@ -1,3 +1,9 @@ -prima2 "cb" evaluation board +CSR SiRFprimaII and SiRFmarco device tree bindings. +======================================== + Required root node properties: - - compatible = "sirf,prima2-cb", "sirf,prima2"; + - compatible: + - "sirf,prima2-cb" : prima2 "cb" evaluation board + - "sirf,marco-cb" : marco "cb" evaluation board + - "sirf,prima2" : prima2 device based board + - "sirf,marco" : marco device based board diff --git a/arch/arm/boot/dts/marco-evb.dts b/arch/arm/boot/dts/marco-evb.dts new file mode 100644 index 000000000000..5130aeacfca5 --- /dev/null +++ b/arch/arm/boot/dts/marco-evb.dts @@ -0,0 +1,54 @@ +/* + * DTS file for CSR SiRFmarco Evaluation Board + * + * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +/dts-v1/; + +/include/ "marco.dtsi" + +/ { + model = "CSR SiRFmarco Evaluation Board"; + compatible = "sirf,marco-cb", "sirf,marco"; + + memory { + reg = <0x40000000 0x60000000>; + }; + + axi { + peri-iobg { + uart1: uart@cc060000 { + status = "okay"; + }; + uart2: uart@cc070000 { + status = "okay"; + }; + i2c0: i2c@cc0e0000 { + status = "okay"; + fpga-cpld@4d { + compatible = "sirf,fpga-cpld"; + reg = <0x4d>; + }; + }; + spi1: spi@cc170000 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&spi1_pins_a>; + spi@0 { + compatible = "spidev"; + reg = <0>; + spi-max-frequency = <1000000>; + }; + }; + pci-iobg { + sd0: sdhci@cd000000 { + bus-width = <8>; + status = "okay"; + }; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/marco.dtsi b/arch/arm/boot/dts/marco.dtsi new file mode 100644 index 000000000000..1579c3491ccd --- /dev/null +++ b/arch/arm/boot/dts/marco.dtsi @@ -0,0 +1,756 @@ +/* + * DTS file for CSR SiRFmarco SoC + * + * Copyright (c) 2012 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +/include/ "skeleton.dtsi" +/ { + compatible = "sirf,marco"; + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&gic>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a9"; + reg = <0>; + }; + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a9"; + reg = <1>; + }; + }; + + axi { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x40000000 0x40000000 0xa0000000>; + + l2-cache-controller@c0030000 { + compatible = "sirf,marco-pl310-cache", "arm,pl310-cache"; + reg = <0xc0030000 0x1000>; + interrupts = <0 59 0>; + arm,tag-latency = <1 1 1>; + arm,data-latency = <1 1 1>; + arm,filter-ranges = <0x40000000 0x80000000>; + }; + + gic: interrupt-controller@c0011000 { + compatible = "arm,cortex-a9-gic"; + interrupt-controller; + #interrupt-cells = <3>; + reg = <0xc0011000 0x1000>, + <0xc0010100 0x0100>; + }; + + rstc-iobg { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0xc2000000 0xc2000000 0x1000000>; + + reset-controller@c2000000 { + compatible = "sirf,marco-rstc"; + reg = <0xc2000000 0x10000>; + }; + }; + + sys-iobg { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0xc3000000 0xc3000000 0x1000000>; + + clock-controller@c3000000 { + compatible = "sirf,marco-clkc"; + reg = <0xc3000000 0x1000>; + interrupts = <0 3 0>; + }; + + rsc-controller@c3010000 { + compatible = "sirf,marco-rsc"; + reg = <0xc3010000 0x1000>; + }; + }; + + mem-iobg { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0xc4000000 0xc4000000 0x1000000>; + + memory-controller@c4000000 { + compatible = "sirf,marco-memc"; + reg = <0xc4000000 0x10000>; + interrupts = <0 27 0>; + }; + }; + + disp-iobg0 { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0xc5000000 0xc5000000 0x1000000>; + + display0@c5000000 { + compatible = "sirf,marco-lcd"; + reg = <0xc5000000 0x10000>; + interrupts = <0 30 0>; + }; + + vpp0@c5010000 { + compatible = "sirf,marco-vpp"; + reg = <0xc5010000 0x10000>; + interrupts = <0 31 0>; + }; + }; + + disp-iobg1 { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0xc6000000 0xc6000000 0x1000000>; + + display1@c6000000 { + compatible = "sirf,marco-lcd"; + reg = <0xc6000000 0x10000>; + interrupts = <0 62 0>; + }; + + vpp1@c6010000 { + compatible = "sirf,marco-vpp"; + reg = <0xc6010000 0x10000>; + interrupts = <0 63 0>; + }; + }; + + graphics-iobg { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0xc8000000 0xc8000000 0x1000000>; + + graphics@c8000000 { + compatible = "powervr,sgx540"; + reg = <0xc8000000 0x1000000>; + interrupts = <0 6 0>; + }; + }; + + multimedia-iobg { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0xc9000000 0xc9000000 0x1000000>; + + multimedia@a0000000 { + compatible = "sirf,marco-video-codec"; + reg = <0xc9000000 0x1000000>; + interrupts = <0 5 0>; + }; + }; + + dsp-iobg { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0xca000000 0xca000000 0x2000000>; + + dspif@ca000000 { + compatible = "sirf,marco-dspif"; + reg = <0xca000000 0x10000>; + interrupts = <0 9 0>; + }; + + gps@ca010000 { + compatible = "sirf,marco-gps"; + reg = <0xca010000 0x10000>; + interrupts = <0 7 0>; + }; + + dsp@cb000000 { + compatible = "sirf,marco-dsp"; + reg = <0xcb000000 0x1000000>; + interrupts = <0 8 0>; + }; + }; + + peri-iobg { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0xcc000000 0xcc000000 0x2000000>; + + timer@cc020000 { + compatible = "sirf,marco-tick"; + reg = <0xcc020000 0x1000>; + interrupts = <0 0 0>, + <0 1 0>, + <0 2 0>, + <0 49 0>, + <0 50 0>, + <0 51 0>; + }; + + nand@cc030000 { + compatible = "sirf,marco-nand"; + reg = <0xcc030000 0x10000>; + interrupts = <0 41 0>; + }; + + audio@cc040000 { + compatible = "sirf,marco-audio"; + reg = <0xcc040000 0x10000>; + interrupts = <0 35 0>; + }; + + uart0: uart@cc050000 { + cell-index = <0>; + compatible = "sirf,marco-uart"; + reg = <0xcc050000 0x1000>; + interrupts = <0 17 0>; + fifosize = <128>; + status = "disabled"; + }; + + uart1: uart@cc060000 { + cell-index = <1>; + compatible = "sirf,marco-uart"; + reg = <0xcc060000 0x1000>; + interrupts = <0 18 0>; + fifosize = <32>; + status = "disabled"; + }; + + uart2: uart@cc070000 { + cell-index = <2>; + compatible = "sirf,marco-uart"; + reg = <0xcc070000 0x1000>; + interrupts = <0 19 0>; + fifosize = <128>; + status = "disabled"; + }; + + uart3: uart@cc190000 { + cell-index = <3>; + compatible = "sirf,marco-uart"; + reg = <0xcc190000 0x1000>; + interrupts = <0 66 0>; + fifosize = <128>; + status = "disabled"; + }; + + uart4: uart@cc1a0000 { + cell-index = <4>; + compatible = "sirf,marco-uart"; + reg = <0xcc1a0000 0x1000>; + interrupts = <0 69 0>; + fifosize = <128>; + status = "disabled"; + }; + + usp0: usp@cc080000 { + cell-index = <0>; + compatible = "sirf,marco-usp"; + reg = <0xcc080000 0x10000>; + interrupts = <0 20 0>; + status = "disabled"; + }; + + usp1: usp@cc090000 { + cell-index = <1>; + compatible = "sirf,marco-usp"; + reg = <0xcc090000 0x10000>; + interrupts = <0 21 0>; + status = "disabled"; + }; + + usp2: usp@cc0a0000 { + cell-index = <2>; + compatible = "sirf,marco-usp"; + reg = <0xcc0a0000 0x10000>; + interrupts = <0 22 0>; + status = "disabled"; + }; + + dmac0: dma-controller@cc0b0000 { + cell-index = <0>; + compatible = "sirf,marco-dmac"; + reg = <0xcc0b0000 0x10000>; + interrupts = <0 12 0>; + }; + + dmac1: dma-controller@cc160000 { + cell-index = <1>; + compatible = "sirf,marco-dmac"; + reg = <0xcc160000 0x10000>; + interrupts = <0 13 0>; + }; + + vip@cc0c0000 { + compatible = "sirf,marco-vip"; + reg = <0xcc0c0000 0x10000>; + }; + + spi0: spi@cc0d0000 { + cell-index = <0>; + compatible = "sirf,marco-spi"; + reg = <0xcc0d0000 0x10000>; + interrupts = <0 15 0>; + sirf,spi-num-chipselects = <1>; + cs-gpios = <&gpio 0 0>; + sirf,spi-dma-rx-channel = <25>; + sirf,spi-dma-tx-channel = <20>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + spi1: spi@cc170000 { + cell-index = <1>; + compatible = "sirf,marco-spi"; + reg = <0xcc170000 0x10000>; + interrupts = <0 16 0>; + sirf,spi-num-chipselects = <1>; + cs-gpios = <&gpio 0 0>; + sirf,spi-dma-rx-channel = <12>; + sirf,spi-dma-tx-channel = <13>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c0: i2c@cc0e0000 { + cell-index = <0>; + compatible = "sirf,marco-i2c"; + reg = <0xcc0e0000 0x10000>; + interrupts = <0 24 0>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c1: i2c@cc0f0000 { + cell-index = <1>; + compatible = "sirf,marco-i2c"; + reg = <0xcc0f0000 0x10000>; + interrupts = <0 25 0>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + tsc@cc110000 { + compatible = "sirf,marco-tsc"; + reg = <0xcc110000 0x10000>; + interrupts = <0 33 0>; + }; + + gpio: pinctrl@cc120000 { + #gpio-cells = <2>; + #interrupt-cells = <2>; + compatible = "sirf,marco-pinctrl"; + reg = <0xcc120000 0x10000>; + interrupts = <0 43 0>, + <0 44 0>, + <0 45 0>, + <0 46 0>, + <0 47 0>; + gpio-controller; + interrupt-controller; + + lcd_16pins_a: lcd0_0 { + lcd { + sirf,pins = "lcd_16bitsgrp"; + sirf,function = "lcd_16bits"; + }; + }; + lcd_18pins_a: lcd0_1 { + lcd { + sirf,pins = "lcd_18bitsgrp"; + sirf,function = "lcd_18bits"; + }; + }; + lcd_24pins_a: lcd0_2 { + lcd { + sirf,pins = "lcd_24bitsgrp"; + sirf,function = "lcd_24bits"; + }; + }; + lcdrom_pins_a: lcdrom0_0 { + lcd { + sirf,pins = "lcdromgrp"; + sirf,function = "lcdrom"; + }; + }; + uart0_pins_a: uart0_0 { + uart { + sirf,pins = "uart0grp"; + sirf,function = "uart0"; + }; + }; + uart1_pins_a: uart1_0 { + uart { + sirf,pins = "uart1grp"; + sirf,function = "uart1"; + }; + }; + uart2_pins_a: uart2_0 { + uart { + sirf,pins = "uart2grp"; + sirf,function = "uart2"; + }; + }; + uart2_noflow_pins_a: uart2_1 { + uart { + sirf,pins = "uart2_nostreamctrlgrp"; + sirf,function = "uart2_nostreamctrl"; + }; + }; + spi0_pins_a: spi0_0 { + spi { + sirf,pins = "spi0grp"; + sirf,function = "spi0"; + }; + }; + spi1_pins_a: spi1_0 { + spi { + sirf,pins = "spi1grp"; + sirf,function = "spi1"; + }; + }; + i2c0_pins_a: i2c0_0 { + i2c { + sirf,pins = "i2c0grp"; + sirf,function = "i2c0"; + }; + }; + i2c1_pins_a: i2c1_0 { + i2c { + sirf,pins = "i2c1grp"; + sirf,function = "i2c1"; + }; + }; + pwm0_pins_a: pwm0_0 { + pwm { + sirf,pins = "pwm0grp"; + sirf,function = "pwm0"; + }; + }; + pwm1_pins_a: pwm1_0 { + pwm { + sirf,pins = "pwm1grp"; + sirf,function = "pwm1"; + }; + }; + pwm2_pins_a: pwm2_0 { + pwm { + sirf,pins = "pwm2grp"; + sirf,function = "pwm2"; + }; + }; + pwm3_pins_a: pwm3_0 { + pwm { + sirf,pins = "pwm3grp"; + sirf,function = "pwm3"; + }; + }; + gps_pins_a: gps_0 { + gps { + sirf,pins = "gpsgrp"; + sirf,function = "gps"; + }; + }; + vip_pins_a: vip_0 { + vip { + sirf,pins = "vipgrp"; + sirf,function = "vip"; + }; + }; + sdmmc0_pins_a: sdmmc0_0 { + sdmmc0 { + sirf,pins = "sdmmc0grp"; + sirf,function = "sdmmc0"; + }; + }; + sdmmc1_pins_a: sdmmc1_0 { + sdmmc1 { + sirf,pins = "sdmmc1grp"; + sirf,function = "sdmmc1"; + }; + }; + sdmmc2_pins_a: sdmmc2_0 { + sdmmc2 { + sirf,pins = "sdmmc2grp"; + sirf,function = "sdmmc2"; + }; + }; + sdmmc3_pins_a: sdmmc3_0 { + sdmmc3 { + sirf,pins = "sdmmc3grp"; + sirf,function = "sdmmc3"; + }; + }; + sdmmc4_pins_a: sdmmc4_0 { + sdmmc4 { + sirf,pins = "sdmmc4grp"; + sirf,function = "sdmmc4"; + }; + }; + sdmmc5_pins_a: sdmmc5_0 { + sdmmc5 { + sirf,pins = "sdmmc5grp"; + sirf,function = "sdmmc5"; + }; + }; + i2s_pins_a: i2s_0 { + i2s { + sirf,pins = "i2sgrp"; + sirf,function = "i2s"; + }; + }; + ac97_pins_a: ac97_0 { + ac97 { + sirf,pins = "ac97grp"; + sirf,function = "ac97"; + }; + }; + nand_pins_a: nand_0 { + nand { + sirf,pins = "nandgrp"; + sirf,function = "nand"; + }; + }; + usp0_pins_a: usp0_0 { + usp0 { + sirf,pins = "usp0grp"; + sirf,function = "usp0"; + }; + }; + usp1_pins_a: usp1_0 { + usp1 { + sirf,pins = "usp1grp"; + sirf,function = "usp1"; + }; + }; + usp2_pins_a: usp2_0 { + usp2 { + sirf,pins = "usp2grp"; + sirf,function = "usp2"; + }; + }; + usb0_utmi_drvbus_pins_a: usb0_utmi_drvbus_0 { + usb0_utmi_drvbus { + sirf,pins = "usb0_utmi_drvbusgrp"; + sirf,function = "usb0_utmi_drvbus"; + }; + }; + usb1_utmi_drvbus_pins_a: usb1_utmi_drvbus_0 { + usb1_utmi_drvbus { + sirf,pins = "usb1_utmi_drvbusgrp"; + sirf,function = "usb1_utmi_drvbus"; + }; + }; + warm_rst_pins_a: warm_rst_0 { + warm_rst { + sirf,pins = "warm_rstgrp"; + sirf,function = "warm_rst"; + }; + }; + pulse_count_pins_a: pulse_count_0 { + pulse_count { + sirf,pins = "pulse_countgrp"; + sirf,function = "pulse_count"; + }; + }; + cko0_rst_pins_a: cko0_rst_0 { + cko0_rst { + sirf,pins = "cko0_rstgrp"; + sirf,function = "cko0_rst"; + }; + }; + cko1_rst_pins_a: cko1_rst_0 { + cko1_rst { + sirf,pins = "cko1_rstgrp"; + sirf,function = "cko1_rst"; + }; + }; + }; + + pwm@cc130000 { + compatible = "sirf,marco-pwm"; + reg = <0xcc130000 0x10000>; + }; + + efusesys@cc140000 { + compatible = "sirf,marco-efuse"; + reg = <0xcc140000 0x10000>; + }; + + pulsec@cc150000 { + compatible = "sirf,marco-pulsec"; + reg = <0xcc150000 0x10000>; + interrupts = <0 48 0>; + }; + + pci-iobg { + compatible = "sirf,marco-pciiobg", "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0xcd000000 0xcd000000 0x1000000>; + + sd0: sdhci@cd000000 { + cell-index = <0>; + compatible = "sirf,marco-sdhc"; + reg = <0xcd000000 0x100000>; + interrupts = <0 38 0>; + status = "disabled"; + }; + + sd1: sdhci@cd100000 { + cell-index = <1>; + compatible = "sirf,marco-sdhc"; + reg = <0xcd100000 0x100000>; + interrupts = <0 38 0>; + status = "disabled"; + }; + + sd2: sdhci@cd200000 { + cell-index = <2>; + compatible = "sirf,marco-sdhc"; + reg = <0xcd200000 0x100000>; + interrupts = <0 23 0>; + status = "disabled"; + }; + + sd3: sdhci@cd300000 { + cell-index = <3>; + compatible = "sirf,marco-sdhc"; + reg = <0xcd300000 0x100000>; + interrupts = <0 23 0>; + status = "disabled"; + }; + + sd4: sdhci@cd400000 { + cell-index = <4>; + compatible = "sirf,marco-sdhc"; + reg = <0xcd400000 0x100000>; + interrupts = <0 39 0>; + status = "disabled"; + }; + + sd5: sdhci@cd500000 { + cell-index = <5>; + compatible = "sirf,marco-sdhc"; + reg = <0xcd500000 0x100000>; + interrupts = <0 39 0>; + status = "disabled"; + }; + + pci-copy@cd900000 { + compatible = "sirf,marco-pcicp"; + reg = <0xcd900000 0x100000>; + interrupts = <0 40 0>; + }; + + rom-interface@cda00000 { + compatible = "sirf,marco-romif"; + reg = <0xcda00000 0x100000>; + }; + }; + }; + + rtc-iobg { + compatible = "sirf,marco-rtciobg", "sirf-marco-rtciobg-bus"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0xc1000000 0x10000>; + + gpsrtc@1000 { + compatible = "sirf,marco-gpsrtc"; + reg = <0x1000 0x1000>; + interrupts = <0 55 0>, + <0 56 0>, + <0 57 0>; + }; + + sysrtc@2000 { + compatible = "sirf,marco-sysrtc"; + reg = <0x2000 0x1000>; + interrupts = <0 52 0>, + <0 53 0>, + <0 54 0>; + }; + + pwrc@3000 { + compatible = "sirf,marco-pwrc"; + reg = <0x3000 0x1000>; + interrupts = <0 32 0>; + }; + }; + + uus-iobg { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0xce000000 0xce000000 0x1000000>; + + usb0: usb@ce000000 { + compatible = "chipidea,ci13611a-marco"; + reg = <0xce000000 0x10000>; + interrupts = <0 10 0>; + }; + + usb1: usb@ce010000 { + compatible = "chipidea,ci13611a-marco"; + reg = <0xce010000 0x10000>; + interrupts = <0 11 0>; + }; + + security@ce020000 { + compatible = "sirf,marco-security"; + reg = <0xce020000 0x10000>; + interrupts = <0 42 0>; + }; + }; + + can-iobg { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0xd0000000 0xd0000000 0x1000000>; + + can0: can@d0000000 { + compatible = "sirf,marco-can"; + reg = <0xd0000000 0x10000>; + }; + + can1: can@d0010000 { + compatible = "sirf,marco-can"; + reg = <0xd0010000 0x10000>; + }; + }; + + lvds-iobg { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0xd1000000 0xd1000000 0x1000000>; + + lvds@d1000000 { + compatible = "sirf,marco-lvds"; + reg = <0xd1000000 0x10000>; + interrupts = <0 64 0>; + }; + }; + }; +}; -- cgit v1.2.3 From 0e37f88d9ad800f5dd94c9fc9dc304b4e9cb7d2c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 18 Jan 2013 22:30:34 +0100 Subject: ARM: sunxi: Add pinctrl driver for Allwinner SoCs The Allwinner SoCs have an IP module that handle both the muxing and the GPIOs. This IP has 8 banks of 32 bits, with a number of pins actually useful for each of these banks varying from one to another, and depending on the SoC used on the board. This driver only implements the pinctrl part, the gpio part will come eventually. Acked-by: Arnd Bergmann Acked-by: Olof Johansson Signed-off-by: Maxime Ripard Signed-off-by: Linus Walleij --- .../bindings/pinctrl/allwinner,sunxi-pinctrl.txt | 60 +++ arch/arm/mach-sunxi/Kconfig | 1 + drivers/pinctrl/Kconfig | 5 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-sunxi.c | 548 +++++++++++++++++++++ drivers/pinctrl/pinctrl-sunxi.h | 387 +++++++++++++++ 6 files changed, 1002 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt create mode 100644 drivers/pinctrl/pinctrl-sunxi.c create mode 100644 drivers/pinctrl/pinctrl-sunxi.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt new file mode 100644 index 000000000000..dff0e5f995e2 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt @@ -0,0 +1,60 @@ +* Allwinner A1X Pin Controller + +The pins controlled by sunXi pin controller are organized in banks, +each bank has 32 pins. Each pin has 7 multiplexing functions, with +the first two functions being GPIO in and out. The configuration on +the pins includes drive strength and pull-up. + +Required properties: +- compatible: "allwinner,-pinctrl". Supported SoCs for now are: + sun5i-a13. +- reg: Should contain the register physical address and length for the + pin controller. + +Please refer to pinctrl-bindings.txt in this directory for details of the +common pinctrl bindings used by client devices. + +A pinctrl node should contain at least one subnodes representing the +pinctrl groups available on the machine. Each subnode will list the +pins it needs, and how they should be configured, with regard to muxer +configuration, drive strength and pullups. If one of these options is +not set, its actual value will be unspecified. + +Required subnode-properties: + +- allwinner,pins: List of strings containing the pin name. +- allwinner,function: Function to mux the pins listed above to. + +Optional subnode-properties: +- allwinner,drive: Integer. Represents the current sent to the pin + 0: 10 mA + 1: 20 mA + 2: 30 mA + 3: 40 mA +- allwinner,pull: Integer. + 0: No resistor + 1: Pull-up resistor + 2: Pull-down resistor + +Examples: + +pinctrl@01c20800 { + compatible = "allwinner,sun5i-a13-pinctrl"; + reg = <0x01c20800 0x400>; + #address-cells = <1>; + #size-cells = <0>; + + uart1_pins_a: uart1@0 { + allwinner,pins = "PE10", "PE11"; + allwinner,function = "uart1"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + uart1_pins_b: uart1@1 { + allwinner,pins = "PG3", "PG4"; + allwinner,function = "uart1"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; +}; diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index 3fdd0085e306..8709a39bd34c 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -7,3 +7,4 @@ config ARCH_SUNXI select PINCTRL select SPARSE_IRQ select SUNXI_TIMER + select PINCTRL_SUNXI \ No newline at end of file diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index c31aeb01bb00..88840a421c6e 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -151,6 +151,11 @@ config PINCTRL_SIRF depends on ARCH_SIRF select PINMUX +config PINCTRL_SUNXI + bool + select PINMUX + select GENERIC_PINCONF + config PINCTRL_TEGRA bool select PINMUX diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index fc4606f27dc7..a2427da4d58e 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_PINCTRL_PXA168) += pinctrl-pxa168.o obj-$(CONFIG_PINCTRL_PXA910) += pinctrl-pxa910.o obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o obj-$(CONFIG_PINCTRL_SIRF) += pinctrl-sirf.o +obj-$(CONFIG_PINCTRL_SUNXI) += pinctrl-sunxi.o obj-$(CONFIG_PINCTRL_TEGRA) += pinctrl-tegra.o obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o diff --git a/drivers/pinctrl/pinctrl-sunxi.c b/drivers/pinctrl/pinctrl-sunxi.c new file mode 100644 index 000000000000..1a81613e8f77 --- /dev/null +++ b/drivers/pinctrl/pinctrl-sunxi.c @@ -0,0 +1,548 @@ +/* + * Allwinner A1X SoCs pinctrl driver. + * + * Copyright (C) 2012 Maxime Ripard + * + * Maxime Ripard + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "pinctrl-sunxi.h" + +static struct sunxi_pinctrl_group * +sunxi_pinctrl_find_group_by_name(struct sunxi_pinctrl *pctl, const char *group) +{ + int i; + + for (i = 0; i < pctl->ngroups; i++) { + struct sunxi_pinctrl_group *grp = pctl->groups + i; + + if (!strcmp(grp->name, group)) + return grp; + } + + return NULL; +} + +static struct sunxi_pinctrl_function * +sunxi_pinctrl_find_function_by_name(struct sunxi_pinctrl *pctl, + const char *name) +{ + struct sunxi_pinctrl_function *func = pctl->functions; + int i; + + for (i = 0; i < pctl->nfunctions; i++) { + if (!func[i].name) + break; + + if (!strcmp(func[i].name, name)) + return func + i; + } + + return NULL; +} + +static struct sunxi_desc_function * +sunxi_pinctrl_desc_find_function_by_name(struct sunxi_pinctrl *pctl, + const char *pin_name, + const char *func_name) +{ + int i; + + for (i = 0; i < pctl->desc->npins; i++) { + const struct sunxi_desc_pin *pin = pctl->desc->pins + i; + + if (!strcmp(pin->pin.name, pin_name)) { + struct sunxi_desc_function *func = pin->functions; + + while (func->name) { + if (!strcmp(func->name, func_name)) + return func; + + func++; + } + } + } + + return NULL; +} + +static int sunxi_pctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + return pctl->ngroups; +} + +static const char *sunxi_pctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned group) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + return pctl->groups[group].name; +} + +static int sunxi_pctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned group, + const unsigned **pins, + unsigned *num_pins) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + *pins = (unsigned *)&pctl->groups[group].pin; + *num_pins = 1; + + return 0; +} + +static int sunxi_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *node, + struct pinctrl_map **map, + unsigned *num_maps) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + unsigned long *pinconfig; + struct property *prop; + const char *function; + const char *group; + int ret, nmaps, i = 0; + u32 val; + + *map = NULL; + *num_maps = 0; + + ret = of_property_read_string(node, "allwinner,function", &function); + if (ret) { + dev_err(pctl->dev, + "missing allwinner,function property in node %s\n", + node->name); + return -EINVAL; + } + + nmaps = of_property_count_strings(node, "allwinner,pins") * 2; + if (nmaps < 0) { + dev_err(pctl->dev, + "missing allwinner,pins property in node %s\n", + node->name); + return -EINVAL; + } + + *map = kmalloc(nmaps * sizeof(struct pinctrl_map), GFP_KERNEL); + if (!map) + return -ENOMEM; + + of_property_for_each_string(node, "allwinner,pins", prop, group) { + struct sunxi_pinctrl_group *grp = + sunxi_pinctrl_find_group_by_name(pctl, group); + int j = 0, configlen = 0; + + if (!grp) { + dev_err(pctl->dev, "unknown pin %s", group); + continue; + } + + if (!sunxi_pinctrl_desc_find_function_by_name(pctl, + grp->name, + function)) { + dev_err(pctl->dev, "unsupported function %s on pin %s", + function, group); + continue; + } + + (*map)[i].type = PIN_MAP_TYPE_MUX_GROUP; + (*map)[i].data.mux.group = group; + (*map)[i].data.mux.function = function; + + i++; + + (*map)[i].type = PIN_MAP_TYPE_CONFIGS_GROUP; + (*map)[i].data.configs.group_or_pin = group; + + if (of_find_property(node, "allwinner,drive", NULL)) + configlen++; + if (of_find_property(node, "allwinner,pull", NULL)) + configlen++; + + pinconfig = kzalloc(configlen * sizeof(*pinconfig), GFP_KERNEL); + + if (!of_property_read_u32(node, "allwinner,drive", &val)) { + u16 strength = (val + 1) * 10; + pinconfig[j++] = + pinconf_to_config_packed(PIN_CONFIG_DRIVE_STRENGTH, + strength); + } + + if (!of_property_read_u32(node, "allwinner,pull", &val)) { + enum pin_config_param pull = PIN_CONFIG_END; + if (val == 1) + pull = PIN_CONFIG_BIAS_PULL_UP; + else if (val == 2) + pull = PIN_CONFIG_BIAS_PULL_DOWN; + pinconfig[j++] = pinconf_to_config_packed(pull, 0); + } + + (*map)[i].data.configs.configs = pinconfig; + (*map)[i].data.configs.num_configs = configlen; + + i++; + } + + *num_maps = nmaps; + + return 0; +} + +static void sunxi_pctrl_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, + unsigned num_maps) +{ + int i; + + for (i = 0; i < num_maps; i++) { + if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP) + kfree(map[i].data.configs.configs); + } + + kfree(map); +} + +static struct pinctrl_ops sunxi_pctrl_ops = { + .dt_node_to_map = sunxi_pctrl_dt_node_to_map, + .dt_free_map = sunxi_pctrl_dt_free_map, + .get_groups_count = sunxi_pctrl_get_groups_count, + .get_group_name = sunxi_pctrl_get_group_name, + .get_group_pins = sunxi_pctrl_get_group_pins, +}; + +static int sunxi_pconf_group_get(struct pinctrl_dev *pctldev, + unsigned group, + unsigned long *config) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + *config = pctl->groups[group].config; + + return 0; +} + +static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, + unsigned group, + unsigned long config) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + struct sunxi_pinctrl_group *g = &pctl->groups[group]; + u32 val, mask; + u16 strength; + u8 dlevel; + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_DRIVE_STRENGTH: + strength = pinconf_to_config_argument(config); + if (strength > 40) + return -EINVAL; + /* + * We convert from mA to what the register expects: + * 0: 10mA + * 1: 20mA + * 2: 30mA + * 3: 40mA + */ + dlevel = strength / 10 - 1; + val = readl(pctl->membase + sunxi_dlevel_reg(g->pin)); + mask = DLEVEL_PINS_MASK << sunxi_dlevel_offset(g->pin); + writel((val & ~mask) | dlevel << sunxi_dlevel_offset(g->pin), + pctl->membase + sunxi_dlevel_reg(g->pin)); + break; + case PIN_CONFIG_BIAS_PULL_UP: + val = readl(pctl->membase + sunxi_pull_reg(g->pin)); + mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin); + writel((val & ~mask) | 1 << sunxi_pull_offset(g->pin), + pctl->membase + sunxi_pull_reg(g->pin)); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + val = readl(pctl->membase + sunxi_pull_reg(g->pin)); + mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin); + writel((val & ~mask) | 2 << sunxi_pull_offset(g->pin), + pctl->membase + sunxi_pull_reg(g->pin)); + break; + default: + break; + } + + /* cache the config value */ + g->config = config; + + return 0; +} + +static struct pinconf_ops sunxi_pconf_ops = { + .pin_config_group_get = sunxi_pconf_group_get, + .pin_config_group_set = sunxi_pconf_group_set, +}; + +static int sunxi_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + return pctl->nfunctions; +} + +static const char *sunxi_pmx_get_func_name(struct pinctrl_dev *pctldev, + unsigned function) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + return pctl->functions[function].name; +} + +static int sunxi_pmx_get_func_groups(struct pinctrl_dev *pctldev, + unsigned function, + const char * const **groups, + unsigned * const num_groups) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + *groups = pctl->functions[function].groups; + *num_groups = pctl->functions[function].ngroups; + + return 0; +} + +static void sunxi_pmx_set(struct pinctrl_dev *pctldev, + unsigned pin, + u8 config) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + u32 val = readl(pctl->membase + sunxi_mux_reg(pin)); + u32 mask = MUX_PINS_MASK << sunxi_mux_offset(pin); + writel((val & ~mask) | config << sunxi_mux_offset(pin), + pctl->membase + sunxi_mux_reg(pin)); +} + +static int sunxi_pmx_enable(struct pinctrl_dev *pctldev, + unsigned function, + unsigned group) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + struct sunxi_pinctrl_group *g = pctl->groups + group; + struct sunxi_pinctrl_function *func = pctl->functions + function; + struct sunxi_desc_function *desc = + sunxi_pinctrl_desc_find_function_by_name(pctl, + g->name, + func->name); + + if (!desc) + return -EINVAL; + + sunxi_pmx_set(pctldev, g->pin, desc->muxval); + + return 0; +} + +static struct pinmux_ops sunxi_pmx_ops = { + .get_functions_count = sunxi_pmx_get_funcs_cnt, + .get_function_name = sunxi_pmx_get_func_name, + .get_function_groups = sunxi_pmx_get_func_groups, + .enable = sunxi_pmx_enable, +}; + +static struct pinctrl_desc sunxi_pctrl_desc = { + .confops = &sunxi_pconf_ops, + .pctlops = &sunxi_pctrl_ops, + .pmxops = &sunxi_pmx_ops, +}; + +static struct of_device_id sunxi_pinctrl_match[] = { + {} +}; +MODULE_DEVICE_TABLE(of, sunxi_pinctrl_match); + +static int sunxi_pinctrl_add_function(struct sunxi_pinctrl *pctl, + const char *name) +{ + struct sunxi_pinctrl_function *func = pctl->functions; + + while (func->name) { + /* function already there */ + if (strcmp(func->name, name) == 0) { + func->ngroups++; + return -EEXIST; + } + func++; + } + + func->name = name; + func->ngroups = 1; + + pctl->nfunctions++; + + return 0; +} + +static int sunxi_pinctrl_build_state(struct platform_device *pdev) +{ + struct sunxi_pinctrl *pctl = platform_get_drvdata(pdev); + int i; + + pctl->ngroups = pctl->desc->npins; + + /* Allocate groups */ + pctl->groups = devm_kzalloc(&pdev->dev, + pctl->ngroups * sizeof(*pctl->groups), + GFP_KERNEL); + if (!pctl->groups) + return -ENOMEM; + + for (i = 0; i < pctl->desc->npins; i++) { + const struct sunxi_desc_pin *pin = pctl->desc->pins + i; + struct sunxi_pinctrl_group *group = pctl->groups + i; + + group->name = pin->pin.name; + group->pin = pin->pin.number; + } + + /* + * We suppose that we won't have any more functions than pins, + * we'll reallocate that later anyway + */ + pctl->functions = devm_kzalloc(&pdev->dev, + pctl->desc->npins * sizeof(*pctl->functions), + GFP_KERNEL); + if (!pctl->functions) + return -ENOMEM; + + /* Count functions and their associated groups */ + for (i = 0; i < pctl->desc->npins; i++) { + const struct sunxi_desc_pin *pin = pctl->desc->pins + i; + struct sunxi_desc_function *func = pin->functions; + + while (func->name) { + sunxi_pinctrl_add_function(pctl, func->name); + func++; + } + } + + pctl->functions = krealloc(pctl->functions, + pctl->nfunctions * sizeof(*pctl->functions), + GFP_KERNEL); + + for (i = 0; i < pctl->desc->npins; i++) { + const struct sunxi_desc_pin *pin = pctl->desc->pins + i; + struct sunxi_desc_function *func = pin->functions; + + while (func->name) { + struct sunxi_pinctrl_function *func_item; + const char **func_grp; + + func_item = sunxi_pinctrl_find_function_by_name(pctl, + func->name); + if (!func_item) + return -EINVAL; + + if (!func_item->groups) { + func_item->groups = + devm_kzalloc(&pdev->dev, + func_item->ngroups * sizeof(*func_item->groups), + GFP_KERNEL); + if (!func_item->groups) + return -ENOMEM; + } + + func_grp = func_item->groups; + while (*func_grp) + func_grp++; + + *func_grp = pin->pin.name; + func++; + } + } + + return 0; +} + +static int sunxi_pinctrl_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + const struct of_device_id *device; + struct pinctrl_pin_desc *pins; + struct sunxi_pinctrl *pctl; + int i, ret; + + pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL); + if (!pctl) + return -ENOMEM; + platform_set_drvdata(pdev, pctl); + + pctl->membase = of_iomap(node, 0); + if (!pctl->membase) + return -ENOMEM; + + device = of_match_device(sunxi_pinctrl_match, &pdev->dev); + if (!device) + return -ENODEV; + + pctl->desc = (struct sunxi_pinctrl_desc *)device->data; + + ret = sunxi_pinctrl_build_state(pdev); + if (ret) { + dev_err(&pdev->dev, "dt probe failed: %d\n", ret); + return ret; + } + + pins = devm_kzalloc(&pdev->dev, + pctl->desc->npins * sizeof(*pins), + GFP_KERNEL); + if (!pins) + return -ENOMEM; + + for (i = 0; i < pctl->desc->npins; i++) + pins[i] = pctl->desc->pins[i].pin; + + sunxi_pctrl_desc.name = dev_name(&pdev->dev); + sunxi_pctrl_desc.owner = THIS_MODULE; + sunxi_pctrl_desc.pins = pins; + sunxi_pctrl_desc.npins = pctl->desc->npins; + pctl->dev = &pdev->dev; + pctl->pctl_dev = pinctrl_register(&sunxi_pctrl_desc, + &pdev->dev, pctl); + if (!pctl->pctl_dev) { + dev_err(&pdev->dev, "couldn't register pinctrl driver\n"); + return -EINVAL; + } + + dev_info(&pdev->dev, "initialized sunXi pin control driver\n"); + + return 0; +} + +static struct platform_driver sunxi_pinctrl_driver = { + .probe = sunxi_pinctrl_probe, + .driver = { + .name = "sunxi-pinctrl", + .owner = THIS_MODULE, + .of_match_table = sunxi_pinctrl_match, + }, +}; +module_platform_driver(sunxi_pinctrl_driver); + +MODULE_AUTHOR("Maxime Ripard + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __PINCTRL_SUNXI_H +#define __PINCTRL_SUNXI_H + +#include + +#define PA_BASE 0 +#define PB_BASE 32 +#define PC_BASE 64 +#define PD_BASE 96 +#define PE_BASE 128 +#define PF_BASE 160 +#define PG_BASE 192 + +#define SUNXI_PINCTRL_PIN_PA0 PINCTRL_PIN(PA_BASE + 0, "PA0") +#define SUNXI_PINCTRL_PIN_PA1 PINCTRL_PIN(PA_BASE + 1, "PA1") +#define SUNXI_PINCTRL_PIN_PA2 PINCTRL_PIN(PA_BASE + 2, "PA2") +#define SUNXI_PINCTRL_PIN_PA3 PINCTRL_PIN(PA_BASE + 3, "PA3") +#define SUNXI_PINCTRL_PIN_PA4 PINCTRL_PIN(PA_BASE + 4, "PA4") +#define SUNXI_PINCTRL_PIN_PA5 PINCTRL_PIN(PA_BASE + 5, "PA5") +#define SUNXI_PINCTRL_PIN_PA6 PINCTRL_PIN(PA_BASE + 6, "PA6") +#define SUNXI_PINCTRL_PIN_PA7 PINCTRL_PIN(PA_BASE + 7, "PA7") +#define SUNXI_PINCTRL_PIN_PA8 PINCTRL_PIN(PA_BASE + 8, "PA8") +#define SUNXI_PINCTRL_PIN_PA9 PINCTRL_PIN(PA_BASE + 9, "PA9") +#define SUNXI_PINCTRL_PIN_PA10 PINCTRL_PIN(PA_BASE + 10, "PA10") +#define SUNXI_PINCTRL_PIN_PA11 PINCTRL_PIN(PA_BASE + 11, "PA11") +#define SUNXI_PINCTRL_PIN_PA12 PINCTRL_PIN(PA_BASE + 12, "PA12") +#define SUNXI_PINCTRL_PIN_PA13 PINCTRL_PIN(PA_BASE + 13, "PA13") +#define SUNXI_PINCTRL_PIN_PA14 PINCTRL_PIN(PA_BASE + 14, "PA14") +#define SUNXI_PINCTRL_PIN_PA15 PINCTRL_PIN(PA_BASE + 15, "PA15") +#define SUNXI_PINCTRL_PIN_PA16 PINCTRL_PIN(PA_BASE + 16, "PA16") +#define SUNXI_PINCTRL_PIN_PA17 PINCTRL_PIN(PA_BASE + 17, "PA17") +#define SUNXI_PINCTRL_PIN_PA18 PINCTRL_PIN(PA_BASE + 18, "PA18") +#define SUNXI_PINCTRL_PIN_PA19 PINCTRL_PIN(PA_BASE + 19, "PA19") +#define SUNXI_PINCTRL_PIN_PA20 PINCTRL_PIN(PA_BASE + 20, "PA20") +#define SUNXI_PINCTRL_PIN_PA21 PINCTRL_PIN(PA_BASE + 21, "PA21") +#define SUNXI_PINCTRL_PIN_PA22 PINCTRL_PIN(PA_BASE + 22, "PA22") +#define SUNXI_PINCTRL_PIN_PA23 PINCTRL_PIN(PA_BASE + 23, "PA23") +#define SUNXI_PINCTRL_PIN_PA24 PINCTRL_PIN(PA_BASE + 24, "PA24") +#define SUNXI_PINCTRL_PIN_PA25 PINCTRL_PIN(PA_BASE + 25, "PA25") +#define SUNXI_PINCTRL_PIN_PA26 PINCTRL_PIN(PA_BASE + 26, "PA26") +#define SUNXI_PINCTRL_PIN_PA27 PINCTRL_PIN(PA_BASE + 27, "PA27") +#define SUNXI_PINCTRL_PIN_PA28 PINCTRL_PIN(PA_BASE + 28, "PA28") +#define SUNXI_PINCTRL_PIN_PA29 PINCTRL_PIN(PA_BASE + 29, "PA29") +#define SUNXI_PINCTRL_PIN_PA30 PINCTRL_PIN(PA_BASE + 30, "PA30") +#define SUNXI_PINCTRL_PIN_PA31 PINCTRL_PIN(PA_BASE + 31, "PA31") + +#define SUNXI_PINCTRL_PIN_PB0 PINCTRL_PIN(PB_BASE + 0, "PB0") +#define SUNXI_PINCTRL_PIN_PB1 PINCTRL_PIN(PB_BASE + 1, "PB1") +#define SUNXI_PINCTRL_PIN_PB2 PINCTRL_PIN(PB_BASE + 2, "PB2") +#define SUNXI_PINCTRL_PIN_PB3 PINCTRL_PIN(PB_BASE + 3, "PB3") +#define SUNXI_PINCTRL_PIN_PB4 PINCTRL_PIN(PB_BASE + 4, "PB4") +#define SUNXI_PINCTRL_PIN_PB5 PINCTRL_PIN(PB_BASE + 5, "PB5") +#define SUNXI_PINCTRL_PIN_PB6 PINCTRL_PIN(PB_BASE + 6, "PB6") +#define SUNXI_PINCTRL_PIN_PB7 PINCTRL_PIN(PB_BASE + 7, "PB7") +#define SUNXI_PINCTRL_PIN_PB8 PINCTRL_PIN(PB_BASE + 8, "PB8") +#define SUNXI_PINCTRL_PIN_PB9 PINCTRL_PIN(PB_BASE + 9, "PB9") +#define SUNXI_PINCTRL_PIN_PB10 PINCTRL_PIN(PB_BASE + 10, "PB10") +#define SUNXI_PINCTRL_PIN_PB11 PINCTRL_PIN(PB_BASE + 11, "PB11") +#define SUNXI_PINCTRL_PIN_PB12 PINCTRL_PIN(PB_BASE + 12, "PB12") +#define SUNXI_PINCTRL_PIN_PB13 PINCTRL_PIN(PB_BASE + 13, "PB13") +#define SUNXI_PINCTRL_PIN_PB14 PINCTRL_PIN(PB_BASE + 14, "PB14") +#define SUNXI_PINCTRL_PIN_PB15 PINCTRL_PIN(PB_BASE + 15, "PB15") +#define SUNXI_PINCTRL_PIN_PB16 PINCTRL_PIN(PB_BASE + 16, "PB16") +#define SUNXI_PINCTRL_PIN_PB17 PINCTRL_PIN(PB_BASE + 17, "PB17") +#define SUNXI_PINCTRL_PIN_PB18 PINCTRL_PIN(PB_BASE + 18, "PB18") +#define SUNXI_PINCTRL_PIN_PB19 PINCTRL_PIN(PB_BASE + 19, "PB19") +#define SUNXI_PINCTRL_PIN_PB20 PINCTRL_PIN(PB_BASE + 20, "PB20") +#define SUNXI_PINCTRL_PIN_PB21 PINCTRL_PIN(PB_BASE + 21, "PB21") +#define SUNXI_PINCTRL_PIN_PB22 PINCTRL_PIN(PB_BASE + 22, "PB22") +#define SUNXI_PINCTRL_PIN_PB23 PINCTRL_PIN(PB_BASE + 23, "PB23") +#define SUNXI_PINCTRL_PIN_PB24 PINCTRL_PIN(PB_BASE + 24, "PB24") +#define SUNXI_PINCTRL_PIN_PB25 PINCTRL_PIN(PB_BASE + 25, "PB25") +#define SUNXI_PINCTRL_PIN_PB26 PINCTRL_PIN(PB_BASE + 26, "PB26") +#define SUNXI_PINCTRL_PIN_PB27 PINCTRL_PIN(PB_BASE + 27, "PB27") +#define SUNXI_PINCTRL_PIN_PB28 PINCTRL_PIN(PB_BASE + 28, "PB28") +#define SUNXI_PINCTRL_PIN_PB29 PINCTRL_PIN(PB_BASE + 29, "PB29") +#define SUNXI_PINCTRL_PIN_PB30 PINCTRL_PIN(PB_BASE + 30, "PB30") +#define SUNXI_PINCTRL_PIN_PB31 PINCTRL_PIN(PB_BASE + 31, "PB31") + +#define SUNXI_PINCTRL_PIN_PC0 PINCTRL_PIN(PC_BASE + 0, "PC0") +#define SUNXI_PINCTRL_PIN_PC1 PINCTRL_PIN(PC_BASE + 1, "PC1") +#define SUNXI_PINCTRL_PIN_PC2 PINCTRL_PIN(PC_BASE + 2, "PC2") +#define SUNXI_PINCTRL_PIN_PC3 PINCTRL_PIN(PC_BASE + 3, "PC3") +#define SUNXI_PINCTRL_PIN_PC4 PINCTRL_PIN(PC_BASE + 4, "PC4") +#define SUNXI_PINCTRL_PIN_PC5 PINCTRL_PIN(PC_BASE + 5, "PC5") +#define SUNXI_PINCTRL_PIN_PC6 PINCTRL_PIN(PC_BASE + 6, "PC6") +#define SUNXI_PINCTRL_PIN_PC7 PINCTRL_PIN(PC_BASE + 7, "PC7") +#define SUNXI_PINCTRL_PIN_PC8 PINCTRL_PIN(PC_BASE + 8, "PC8") +#define SUNXI_PINCTRL_PIN_PC9 PINCTRL_PIN(PC_BASE + 9, "PC9") +#define SUNXI_PINCTRL_PIN_PC10 PINCTRL_PIN(PC_BASE + 10, "PC10") +#define SUNXI_PINCTRL_PIN_PC11 PINCTRL_PIN(PC_BASE + 11, "PC11") +#define SUNXI_PINCTRL_PIN_PC12 PINCTRL_PIN(PC_BASE + 12, "PC12") +#define SUNXI_PINCTRL_PIN_PC13 PINCTRL_PIN(PC_BASE + 13, "PC13") +#define SUNXI_PINCTRL_PIN_PC14 PINCTRL_PIN(PC_BASE + 14, "PC14") +#define SUNXI_PINCTRL_PIN_PC15 PINCTRL_PIN(PC_BASE + 15, "PC15") +#define SUNXI_PINCTRL_PIN_PC16 PINCTRL_PIN(PC_BASE + 16, "PC16") +#define SUNXI_PINCTRL_PIN_PC17 PINCTRL_PIN(PC_BASE + 17, "PC17") +#define SUNXI_PINCTRL_PIN_PC18 PINCTRL_PIN(PC_BASE + 18, "PC18") +#define SUNXI_PINCTRL_PIN_PC19 PINCTRL_PIN(PC_BASE + 19, "PC19") +#define SUNXI_PINCTRL_PIN_PC20 PINCTRL_PIN(PC_BASE + 20, "PC20") +#define SUNXI_PINCTRL_PIN_PC21 PINCTRL_PIN(PC_BASE + 21, "PC21") +#define SUNXI_PINCTRL_PIN_PC22 PINCTRL_PIN(PC_BASE + 22, "PC22") +#define SUNXI_PINCTRL_PIN_PC23 PINCTRL_PIN(PC_BASE + 23, "PC23") +#define SUNXI_PINCTRL_PIN_PC24 PINCTRL_PIN(PC_BASE + 24, "PC24") +#define SUNXI_PINCTRL_PIN_PC25 PINCTRL_PIN(PC_BASE + 25, "PC25") +#define SUNXI_PINCTRL_PIN_PC26 PINCTRL_PIN(PC_BASE + 26, "PC26") +#define SUNXI_PINCTRL_PIN_PC27 PINCTRL_PIN(PC_BASE + 27, "PC27") +#define SUNXI_PINCTRL_PIN_PC28 PINCTRL_PIN(PC_BASE + 28, "PC28") +#define SUNXI_PINCTRL_PIN_PC29 PINCTRL_PIN(PC_BASE + 29, "PC29") +#define SUNXI_PINCTRL_PIN_PC30 PINCTRL_PIN(PC_BASE + 30, "PC30") +#define SUNXI_PINCTRL_PIN_PC31 PINCTRL_PIN(PC_BASE + 31, "PC31") + +#define SUNXI_PINCTRL_PIN_PD0 PINCTRL_PIN(PD_BASE + 0, "PD0") +#define SUNXI_PINCTRL_PIN_PD1 PINCTRL_PIN(PD_BASE + 1, "PD1") +#define SUNXI_PINCTRL_PIN_PD2 PINCTRL_PIN(PD_BASE + 2, "PD2") +#define SUNXI_PINCTRL_PIN_PD3 PINCTRL_PIN(PD_BASE + 3, "PD3") +#define SUNXI_PINCTRL_PIN_PD4 PINCTRL_PIN(PD_BASE + 4, "PD4") +#define SUNXI_PINCTRL_PIN_PD5 PINCTRL_PIN(PD_BASE + 5, "PD5") +#define SUNXI_PINCTRL_PIN_PD6 PINCTRL_PIN(PD_BASE + 6, "PD6") +#define SUNXI_PINCTRL_PIN_PD7 PINCTRL_PIN(PD_BASE + 7, "PD7") +#define SUNXI_PINCTRL_PIN_PD8 PINCTRL_PIN(PD_BASE + 8, "PD8") +#define SUNXI_PINCTRL_PIN_PD9 PINCTRL_PIN(PD_BASE + 9, "PD9") +#define SUNXI_PINCTRL_PIN_PD10 PINCTRL_PIN(PD_BASE + 10, "PD10") +#define SUNXI_PINCTRL_PIN_PD11 PINCTRL_PIN(PD_BASE + 11, "PD11") +#define SUNXI_PINCTRL_PIN_PD12 PINCTRL_PIN(PD_BASE + 12, "PD12") +#define SUNXI_PINCTRL_PIN_PD13 PINCTRL_PIN(PD_BASE + 13, "PD13") +#define SUNXI_PINCTRL_PIN_PD14 PINCTRL_PIN(PD_BASE + 14, "PD14") +#define SUNXI_PINCTRL_PIN_PD15 PINCTRL_PIN(PD_BASE + 15, "PD15") +#define SUNXI_PINCTRL_PIN_PD16 PINCTRL_PIN(PD_BASE + 16, "PD16") +#define SUNXI_PINCTRL_PIN_PD17 PINCTRL_PIN(PD_BASE + 17, "PD17") +#define SUNXI_PINCTRL_PIN_PD18 PINCTRL_PIN(PD_BASE + 18, "PD18") +#define SUNXI_PINCTRL_PIN_PD19 PINCTRL_PIN(PD_BASE + 19, "PD19") +#define SUNXI_PINCTRL_PIN_PD20 PINCTRL_PIN(PD_BASE + 20, "PD20") +#define SUNXI_PINCTRL_PIN_PD21 PINCTRL_PIN(PD_BASE + 21, "PD21") +#define SUNXI_PINCTRL_PIN_PD22 PINCTRL_PIN(PD_BASE + 22, "PD22") +#define SUNXI_PINCTRL_PIN_PD23 PINCTRL_PIN(PD_BASE + 23, "PD23") +#define SUNXI_PINCTRL_PIN_PD24 PINCTRL_PIN(PD_BASE + 24, "PD24") +#define SUNXI_PINCTRL_PIN_PD25 PINCTRL_PIN(PD_BASE + 25, "PD25") +#define SUNXI_PINCTRL_PIN_PD26 PINCTRL_PIN(PD_BASE + 26, "PD26") +#define SUNXI_PINCTRL_PIN_PD27 PINCTRL_PIN(PD_BASE + 27, "PD27") +#define SUNXI_PINCTRL_PIN_PD28 PINCTRL_PIN(PD_BASE + 28, "PD28") +#define SUNXI_PINCTRL_PIN_PD29 PINCTRL_PIN(PD_BASE + 29, "PD29") +#define SUNXI_PINCTRL_PIN_PD30 PINCTRL_PIN(PD_BASE + 30, "PD30") +#define SUNXI_PINCTRL_PIN_PD31 PINCTRL_PIN(PD_BASE + 31, "PD31") + +#define SUNXI_PINCTRL_PIN_PE0 PINCTRL_PIN(PE_BASE + 0, "PE0") +#define SUNXI_PINCTRL_PIN_PE1 PINCTRL_PIN(PE_BASE + 1, "PE1") +#define SUNXI_PINCTRL_PIN_PE2 PINCTRL_PIN(PE_BASE + 2, "PE2") +#define SUNXI_PINCTRL_PIN_PE3 PINCTRL_PIN(PE_BASE + 3, "PE3") +#define SUNXI_PINCTRL_PIN_PE4 PINCTRL_PIN(PE_BASE + 4, "PE4") +#define SUNXI_PINCTRL_PIN_PE5 PINCTRL_PIN(PE_BASE + 5, "PE5") +#define SUNXI_PINCTRL_PIN_PE6 PINCTRL_PIN(PE_BASE + 6, "PE6") +#define SUNXI_PINCTRL_PIN_PE7 PINCTRL_PIN(PE_BASE + 7, "PE7") +#define SUNXI_PINCTRL_PIN_PE8 PINCTRL_PIN(PE_BASE + 8, "PE8") +#define SUNXI_PINCTRL_PIN_PE9 PINCTRL_PIN(PE_BASE + 9, "PE9") +#define SUNXI_PINCTRL_PIN_PE10 PINCTRL_PIN(PE_BASE + 10, "PE10") +#define SUNXI_PINCTRL_PIN_PE11 PINCTRL_PIN(PE_BASE + 11, "PE11") +#define SUNXI_PINCTRL_PIN_PE12 PINCTRL_PIN(PE_BASE + 12, "PE12") +#define SUNXI_PINCTRL_PIN_PE13 PINCTRL_PIN(PE_BASE + 13, "PE13") +#define SUNXI_PINCTRL_PIN_PE14 PINCTRL_PIN(PE_BASE + 14, "PE14") +#define SUNXI_PINCTRL_PIN_PE15 PINCTRL_PIN(PE_BASE + 15, "PE15") +#define SUNXI_PINCTRL_PIN_PE16 PINCTRL_PIN(PE_BASE + 16, "PE16") +#define SUNXI_PINCTRL_PIN_PE17 PINCTRL_PIN(PE_BASE + 17, "PE17") +#define SUNXI_PINCTRL_PIN_PE18 PINCTRL_PIN(PE_BASE + 18, "PE18") +#define SUNXI_PINCTRL_PIN_PE19 PINCTRL_PIN(PE_BASE + 19, "PE19") +#define SUNXI_PINCTRL_PIN_PE20 PINCTRL_PIN(PE_BASE + 20, "PE20") +#define SUNXI_PINCTRL_PIN_PE21 PINCTRL_PIN(PE_BASE + 21, "PE21") +#define SUNXI_PINCTRL_PIN_PE22 PINCTRL_PIN(PE_BASE + 22, "PE22") +#define SUNXI_PINCTRL_PIN_PE23 PINCTRL_PIN(PE_BASE + 23, "PE23") +#define SUNXI_PINCTRL_PIN_PE24 PINCTRL_PIN(PE_BASE + 24, "PE24") +#define SUNXI_PINCTRL_PIN_PE25 PINCTRL_PIN(PE_BASE + 25, "PE25") +#define SUNXI_PINCTRL_PIN_PE26 PINCTRL_PIN(PE_BASE + 26, "PE26") +#define SUNXI_PINCTRL_PIN_PE27 PINCTRL_PIN(PE_BASE + 27, "PE27") +#define SUNXI_PINCTRL_PIN_PE28 PINCTRL_PIN(PE_BASE + 28, "PE28") +#define SUNXI_PINCTRL_PIN_PE29 PINCTRL_PIN(PE_BASE + 29, "PE29") +#define SUNXI_PINCTRL_PIN_PE30 PINCTRL_PIN(PE_BASE + 30, "PE30") +#define SUNXI_PINCTRL_PIN_PE31 PINCTRL_PIN(PE_BASE + 31, "PE31") + +#define SUNXI_PINCTRL_PIN_PF0 PINCTRL_PIN(PF_BASE + 0, "PF0") +#define SUNXI_PINCTRL_PIN_PF1 PINCTRL_PIN(PF_BASE + 1, "PF1") +#define SUNXI_PINCTRL_PIN_PF2 PINCTRL_PIN(PF_BASE + 2, "PF2") +#define SUNXI_PINCTRL_PIN_PF3 PINCTRL_PIN(PF_BASE + 3, "PF3") +#define SUNXI_PINCTRL_PIN_PF4 PINCTRL_PIN(PF_BASE + 4, "PF4") +#define SUNXI_PINCTRL_PIN_PF5 PINCTRL_PIN(PF_BASE + 5, "PF5") +#define SUNXI_PINCTRL_PIN_PF6 PINCTRL_PIN(PF_BASE + 6, "PF6") +#define SUNXI_PINCTRL_PIN_PF7 PINCTRL_PIN(PF_BASE + 7, "PF7") +#define SUNXI_PINCTRL_PIN_PF8 PINCTRL_PIN(PF_BASE + 8, "PF8") +#define SUNXI_PINCTRL_PIN_PF9 PINCTRL_PIN(PF_BASE + 9, "PF9") +#define SUNXI_PINCTRL_PIN_PF10 PINCTRL_PIN(PF_BASE + 10, "PF10") +#define SUNXI_PINCTRL_PIN_PF11 PINCTRL_PIN(PF_BASE + 11, "PF11") +#define SUNXI_PINCTRL_PIN_PF12 PINCTRL_PIN(PF_BASE + 12, "PF12") +#define SUNXI_PINCTRL_PIN_PF13 PINCTRL_PIN(PF_BASE + 13, "PF13") +#define SUNXI_PINCTRL_PIN_PF14 PINCTRL_PIN(PF_BASE + 14, "PF14") +#define SUNXI_PINCTRL_PIN_PF15 PINCTRL_PIN(PF_BASE + 15, "PF15") +#define SUNXI_PINCTRL_PIN_PF16 PINCTRL_PIN(PF_BASE + 16, "PF16") +#define SUNXI_PINCTRL_PIN_PF17 PINCTRL_PIN(PF_BASE + 17, "PF17") +#define SUNXI_PINCTRL_PIN_PF18 PINCTRL_PIN(PF_BASE + 18, "PF18") +#define SUNXI_PINCTRL_PIN_PF19 PINCTRL_PIN(PF_BASE + 19, "PF19") +#define SUNXI_PINCTRL_PIN_PF20 PINCTRL_PIN(PF_BASE + 20, "PF20") +#define SUNXI_PINCTRL_PIN_PF21 PINCTRL_PIN(PF_BASE + 21, "PF21") +#define SUNXI_PINCTRL_PIN_PF22 PINCTRL_PIN(PF_BASE + 22, "PF22") +#define SUNXI_PINCTRL_PIN_PF23 PINCTRL_PIN(PF_BASE + 23, "PF23") +#define SUNXI_PINCTRL_PIN_PF24 PINCTRL_PIN(PF_BASE + 24, "PF24") +#define SUNXI_PINCTRL_PIN_PF25 PINCTRL_PIN(PF_BASE + 25, "PF25") +#define SUNXI_PINCTRL_PIN_PF26 PINCTRL_PIN(PF_BASE + 26, "PF26") +#define SUNXI_PINCTRL_PIN_PF27 PINCTRL_PIN(PF_BASE + 27, "PF27") +#define SUNXI_PINCTRL_PIN_PF28 PINCTRL_PIN(PF_BASE + 28, "PF28") +#define SUNXI_PINCTRL_PIN_PF29 PINCTRL_PIN(PF_BASE + 29, "PF29") +#define SUNXI_PINCTRL_PIN_PF30 PINCTRL_PIN(PF_BASE + 30, "PF30") +#define SUNXI_PINCTRL_PIN_PF31 PINCTRL_PIN(PF_BASE + 31, "PF31") + +#define SUNXI_PINCTRL_PIN_PG0 PINCTRL_PIN(PG_BASE + 0, "PG0") +#define SUNXI_PINCTRL_PIN_PG1 PINCTRL_PIN(PG_BASE + 1, "PG1") +#define SUNXI_PINCTRL_PIN_PG2 PINCTRL_PIN(PG_BASE + 2, "PG2") +#define SUNXI_PINCTRL_PIN_PG3 PINCTRL_PIN(PG_BASE + 3, "PG3") +#define SUNXI_PINCTRL_PIN_PG4 PINCTRL_PIN(PG_BASE + 4, "PG4") +#define SUNXI_PINCTRL_PIN_PG5 PINCTRL_PIN(PG_BASE + 5, "PG5") +#define SUNXI_PINCTRL_PIN_PG6 PINCTRL_PIN(PG_BASE + 6, "PG6") +#define SUNXI_PINCTRL_PIN_PG7 PINCTRL_PIN(PG_BASE + 7, "PG7") +#define SUNXI_PINCTRL_PIN_PG8 PINCTRL_PIN(PG_BASE + 8, "PG8") +#define SUNXI_PINCTRL_PIN_PG9 PINCTRL_PIN(PG_BASE + 9, "PG9") +#define SUNXI_PINCTRL_PIN_PG10 PINCTRL_PIN(PG_BASE + 10, "PG10") +#define SUNXI_PINCTRL_PIN_PG11 PINCTRL_PIN(PG_BASE + 11, "PG11") +#define SUNXI_PINCTRL_PIN_PG12 PINCTRL_PIN(PG_BASE + 12, "PG12") +#define SUNXI_PINCTRL_PIN_PG13 PINCTRL_PIN(PG_BASE + 13, "PG13") +#define SUNXI_PINCTRL_PIN_PG14 PINCTRL_PIN(PG_BASE + 14, "PG14") +#define SUNXI_PINCTRL_PIN_PG15 PINCTRL_PIN(PG_BASE + 15, "PG15") +#define SUNXI_PINCTRL_PIN_PG16 PINCTRL_PIN(PG_BASE + 16, "PG16") +#define SUNXI_PINCTRL_PIN_PG17 PINCTRL_PIN(PG_BASE + 17, "PG17") +#define SUNXI_PINCTRL_PIN_PG18 PINCTRL_PIN(PG_BASE + 18, "PG18") +#define SUNXI_PINCTRL_PIN_PG19 PINCTRL_PIN(PG_BASE + 19, "PG19") +#define SUNXI_PINCTRL_PIN_PG20 PINCTRL_PIN(PG_BASE + 20, "PG20") +#define SUNXI_PINCTRL_PIN_PG21 PINCTRL_PIN(PG_BASE + 21, "PG21") +#define SUNXI_PINCTRL_PIN_PG22 PINCTRL_PIN(PG_BASE + 22, "PG22") +#define SUNXI_PINCTRL_PIN_PG23 PINCTRL_PIN(PG_BASE + 23, "PG23") +#define SUNXI_PINCTRL_PIN_PG24 PINCTRL_PIN(PG_BASE + 24, "PG24") +#define SUNXI_PINCTRL_PIN_PG25 PINCTRL_PIN(PG_BASE + 25, "PG25") +#define SUNXI_PINCTRL_PIN_PG26 PINCTRL_PIN(PG_BASE + 26, "PG26") +#define SUNXI_PINCTRL_PIN_PG27 PINCTRL_PIN(PG_BASE + 27, "PG27") +#define SUNXI_PINCTRL_PIN_PG28 PINCTRL_PIN(PG_BASE + 28, "PG28") +#define SUNXI_PINCTRL_PIN_PG29 PINCTRL_PIN(PG_BASE + 29, "PG29") +#define SUNXI_PINCTRL_PIN_PG30 PINCTRL_PIN(PG_BASE + 30, "PG30") +#define SUNXI_PINCTRL_PIN_PG31 PINCTRL_PIN(PG_BASE + 31, "PG31") + +#define BANK_MEM_SIZE 0x24 +#define MUX_REGS_OFFSET 0x0 +#define DLEVEL_REGS_OFFSET 0x14 +#define PULL_REGS_OFFSET 0x1c + +#define PINS_PER_BANK 32 +#define MUX_PINS_PER_REG 8 +#define MUX_PINS_BITS 4 +#define MUX_PINS_MASK 0x0f +#define DLEVEL_PINS_PER_REG 16 +#define DLEVEL_PINS_BITS 2 +#define DLEVEL_PINS_MASK 0x03 +#define PULL_PINS_PER_REG 16 +#define PULL_PINS_BITS 2 +#define PULL_PINS_MASK 0x03 + +struct sunxi_desc_function { + const char *name; + u8 muxval; +}; + +struct sunxi_desc_pin { + struct pinctrl_pin_desc pin; + struct sunxi_desc_function *functions; +}; + +struct sunxi_pinctrl_desc { + const struct sunxi_desc_pin *pins; + int npins; +}; + +struct sunxi_pinctrl_function { + const char *name; + const char **groups; + unsigned ngroups; +}; + +struct sunxi_pinctrl_group { + const char *name; + unsigned long config; + unsigned pin; +}; + +struct sunxi_pinctrl { + void __iomem *membase; + struct sunxi_pinctrl_desc *desc; + struct device *dev; + struct sunxi_pinctrl_function *functions; + unsigned nfunctions; + struct sunxi_pinctrl_group *groups; + unsigned ngroups; + struct pinctrl_dev *pctl_dev; +}; + +#define SUNXI_PIN(_pin, ...) \ + { \ + .pin = _pin, \ + .functions = (struct sunxi_desc_function[]){ \ + __VA_ARGS__, { } }, \ + } + +#define SUNXI_FUNCTION(_val, _name) \ + { \ + .name = _name, \ + .muxval = _val, \ + } + + +/* + * The sunXi PIO registers are organized as is: + * 0x00 - 0x0c Muxing values. + * 8 pins per register, each pin having a 4bits value + * 0x10 Pin values + * 32 bits per register, each pin corresponding to one bit + * 0x14 - 0x18 Drive level + * 16 pins per register, each pin having a 2bits value + * 0x1c - 0x20 Pull-Up values + * 16 pins per register, each pin having a 2bits value + * + * This is for the first bank. Each bank will have the same layout, + * with an offset being a multiple of 0x24. + * + * The following functions calculate from the pin number the register + * and the bit offset that we should access. + */ +static inline u32 sunxi_mux_reg(u16 pin) +{ + u8 bank = pin / PINS_PER_BANK; + u32 offset = bank * BANK_MEM_SIZE; + offset += MUX_REGS_OFFSET; + offset += pin % PINS_PER_BANK / MUX_PINS_PER_REG * 0x04; + return round_down(offset, 4); +} + +static inline u32 sunxi_mux_offset(u16 pin) +{ + u32 pin_num = pin % MUX_PINS_PER_REG; + return pin_num * MUX_PINS_BITS; +} + +static inline u32 sunxi_dlevel_reg(u16 pin) +{ + u8 bank = pin / PINS_PER_BANK; + u32 offset = bank * BANK_MEM_SIZE; + offset += DLEVEL_REGS_OFFSET; + offset += pin % PINS_PER_BANK / DLEVEL_PINS_PER_REG * 0x04; + return round_down(offset, 4); +} + +static inline u32 sunxi_dlevel_offset(u16 pin) +{ + u32 pin_num = pin % DLEVEL_PINS_PER_REG; + return pin_num * DLEVEL_PINS_BITS; +} + +static inline u32 sunxi_pull_reg(u16 pin) +{ + u8 bank = pin / PINS_PER_BANK; + u32 offset = bank * BANK_MEM_SIZE; + offset += PULL_REGS_OFFSET; + offset += pin % PINS_PER_BANK / PULL_PINS_PER_REG * 0x04; + return round_down(offset, 4); +} + +static inline u32 sunxi_pull_offset(u16 pin) +{ + u32 pin_num = pin % PULL_PINS_PER_REG; + return pin_num * PULL_PINS_BITS; +} + +#endif /* __PINCTRL_SUNXI_H */ -- cgit v1.2.3 From 45976c01b7be338efc9d3c911293491a981ed87a Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 18 Jan 2013 16:38:43 +0000 Subject: ARM: at91/at91-pinctrl documentation: fix typo and add some details The relation between PIN_BANK numbers and pio letters wasn't made very clear. Signed-off-by: Richard Genoud Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Nicolas Ferre --- Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt index 3a268127b054..bc50899e0c81 100644 --- a/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/atmel,at91-pinctrl.txt @@ -81,7 +81,8 @@ PA31 TXD4 Required properties for pin configuration node: - atmel,pins: 4 integers array, represents a group of pins mux and config setting. The format is atmel,pins = . - The PERIPH 0 means gpio. + The PERIPH 0 means gpio, PERIPH 1 is periph A, PERIPH 2 is periph B... + PIN_BANK 0 is pioA, PIN_BANK 1 is pioB... Bits used for CONFIG: PULL_UP (1 << 0): indicate this pin need a pull up. @@ -126,7 +127,7 @@ pinctrl@fffff400 { pinctrl_dbgu: dbgu-0 { atmel,pins = <1 14 0x1 0x0 /* PB14 periph A */ - 1 15 0x1 0x1>; /* PB15 periph with pullup */ + 1 15 0x1 0x1>; /* PB15 periph A with pullup */ }; }; }; -- cgit v1.2.3 From 8c1b3e16e902b010f79e2d299927ec43b495f1c7 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Tue, 22 Jan 2013 18:30:41 +0530 Subject: usb: phy: samsung: Add host phy support to samsung-phy driver This patch adds host phy support to samsung-usbphy driver and further adds support for samsung's exynos5250 usb-phy. Signed-off-by: Praveen Paneri Signed-off-by: Vivek Gautam Acked-by: Kukjin Kim Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/samsung-usbphy.txt | 12 +- drivers/usb/phy/Kconfig | 2 +- drivers/usb/phy/samsung-usbphy.c | 513 +++++++++++++++++++-- 3 files changed, 496 insertions(+), 31 deletions(-) (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt index 22d06cfdb076..033194934f64 100644 --- a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt +++ b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt @@ -1,15 +1,23 @@ * Samsung's usb phy transceiver -The Samsung's phy transceiver is used for controlling usb otg phy for -s3c-hsotg usb device controller. +The Samsung's phy transceiver is used for controlling usb phy for +s3c-hsotg as well as ehci-s5p and ohci-exynos usb controllers +across Samsung SOCs. TODO: Adding the PHY binding with controller(s) according to the under developement generic PHY driver. Required properties: + +Exynos4210: - compatible : should be "samsung,exynos4210-usbphy" - reg : base physical address of the phy registers and length of memory mapped region. +Exynos5250: +- compatible : should be "samsung,exynos5250-usbphy" +- reg : base physical address of the phy registers and length of memory mapped + region. + Optional properties: - #address-cells: should be '1' when usbphy node has a child node with 'reg' property. diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 36a85b675429..fae4d08c0ddd 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -48,7 +48,7 @@ config USB_RCAR_PHY config SAMSUNG_USBPHY bool "Samsung USB PHY controller Driver" - depends on USB_S3C_HSOTG + depends on USB_S3C_HSOTG || USB_EHCI_S5P || USB_OHCI_EXYNOS select USB_OTG_UTILS help Enable this to support Samsung USB phy controller for samsung diff --git a/drivers/usb/phy/samsung-usbphy.c b/drivers/usb/phy/samsung-usbphy.c index 30aebb59d803..9e9861c673ab 100644 --- a/drivers/usb/phy/samsung-usbphy.c +++ b/drivers/usb/phy/samsung-usbphy.c @@ -5,7 +5,8 @@ * * Author: Praveen Paneri * - * Samsung USB2.0 High-speed OTG transceiver, talks to S3C HS OTG controller + * Samsung USB2.0 PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and + * OHCI-EXYNOS controllers. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -21,11 +22,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include /* Register definitions */ @@ -57,24 +60,132 @@ #define RSTCON_HLINK_SWRST (0x1 << 1) #define RSTCON_SWRST (0x1 << 0) +/* EXYNOS5 */ +#define EXYNOS5_PHY_HOST_CTRL0 (0x00) + +#define HOST_CTRL0_PHYSWRSTALL (0x1 << 31) + +#define HOST_CTRL0_REFCLKSEL_MASK (0x3 << 19) +#define HOST_CTRL0_REFCLKSEL_XTAL (0x0 << 19) +#define HOST_CTRL0_REFCLKSEL_EXTL (0x1 << 19) +#define HOST_CTRL0_REFCLKSEL_CLKCORE (0x2 << 19) + +#define HOST_CTRL0_FSEL_MASK (0x7 << 16) +#define HOST_CTRL0_FSEL(_x) ((_x) << 16) + +#define FSEL_CLKSEL_50M (0x7) +#define FSEL_CLKSEL_24M (0x5) +#define FSEL_CLKSEL_20M (0x4) +#define FSEL_CLKSEL_19200K (0x3) +#define FSEL_CLKSEL_12M (0x2) +#define FSEL_CLKSEL_10M (0x1) +#define FSEL_CLKSEL_9600K (0x0) + +#define HOST_CTRL0_TESTBURNIN (0x1 << 11) +#define HOST_CTRL0_RETENABLE (0x1 << 10) +#define HOST_CTRL0_COMMONON_N (0x1 << 9) +#define HOST_CTRL0_SIDDQ (0x1 << 6) +#define HOST_CTRL0_FORCESLEEP (0x1 << 5) +#define HOST_CTRL0_FORCESUSPEND (0x1 << 4) +#define HOST_CTRL0_WORDINTERFACE (0x1 << 3) +#define HOST_CTRL0_UTMISWRST (0x1 << 2) +#define HOST_CTRL0_LINKSWRST (0x1 << 1) +#define HOST_CTRL0_PHYSWRST (0x1 << 0) + +#define EXYNOS5_PHY_HOST_TUNE0 (0x04) + +#define EXYNOS5_PHY_HSIC_CTRL1 (0x10) + +#define EXYNOS5_PHY_HSIC_TUNE1 (0x14) + +#define EXYNOS5_PHY_HSIC_CTRL2 (0x20) + +#define EXYNOS5_PHY_HSIC_TUNE2 (0x24) + +#define HSIC_CTRL_REFCLKSEL_MASK (0x3 << 23) +#define HSIC_CTRL_REFCLKSEL (0x2 << 23) + +#define HSIC_CTRL_REFCLKDIV_MASK (0x7f << 16) +#define HSIC_CTRL_REFCLKDIV(_x) ((_x) << 16) +#define HSIC_CTRL_REFCLKDIV_12 (0x24 << 16) +#define HSIC_CTRL_REFCLKDIV_15 (0x1c << 16) +#define HSIC_CTRL_REFCLKDIV_16 (0x1a << 16) +#define HSIC_CTRL_REFCLKDIV_19_2 (0x15 << 16) +#define HSIC_CTRL_REFCLKDIV_20 (0x14 << 16) + +#define HSIC_CTRL_SIDDQ (0x1 << 6) +#define HSIC_CTRL_FORCESLEEP (0x1 << 5) +#define HSIC_CTRL_FORCESUSPEND (0x1 << 4) +#define HSIC_CTRL_WORDINTERFACE (0x1 << 3) +#define HSIC_CTRL_UTMISWRST (0x1 << 2) +#define HSIC_CTRL_PHYSWRST (0x1 << 0) + +#define EXYNOS5_PHY_HOST_EHCICTRL (0x30) + +#define HOST_EHCICTRL_ENAINCRXALIGN (0x1 << 29) +#define HOST_EHCICTRL_ENAINCR4 (0x1 << 28) +#define HOST_EHCICTRL_ENAINCR8 (0x1 << 27) +#define HOST_EHCICTRL_ENAINCR16 (0x1 << 26) + +#define EXYNOS5_PHY_HOST_OHCICTRL (0x34) + +#define HOST_OHCICTRL_SUSPLGCY (0x1 << 3) +#define HOST_OHCICTRL_APPSTARTCLK (0x1 << 2) +#define HOST_OHCICTRL_CNTSEL (0x1 << 1) +#define HOST_OHCICTRL_CLKCKTRST (0x1 << 0) + +#define EXYNOS5_PHY_OTG_SYS (0x38) + +#define OTG_SYS_PHYLINK_SWRESET (0x1 << 14) +#define OTG_SYS_LINKSWRST_UOTG (0x1 << 13) +#define OTG_SYS_PHY0_SWRST (0x1 << 12) + +#define OTG_SYS_REFCLKSEL_MASK (0x3 << 9) +#define OTG_SYS_REFCLKSEL_XTAL (0x0 << 9) +#define OTG_SYS_REFCLKSEL_EXTL (0x1 << 9) +#define OTG_SYS_REFCLKSEL_CLKCORE (0x2 << 9) + +#define OTG_SYS_IDPULLUP_UOTG (0x1 << 8) +#define OTG_SYS_COMMON_ON (0x1 << 7) + +#define OTG_SYS_FSEL_MASK (0x7 << 4) +#define OTG_SYS_FSEL(_x) ((_x) << 4) + +#define OTG_SYS_FORCESLEEP (0x1 << 3) +#define OTG_SYS_OTGDISABLE (0x1 << 2) +#define OTG_SYS_SIDDQ_UOTG (0x1 << 1) +#define OTG_SYS_FORCESUSPEND (0x1 << 0) + +#define EXYNOS5_PHY_OTG_TUNE (0x40) + #ifndef MHZ #define MHZ (1000*1000) #endif +#ifndef KHZ +#define KHZ (1000) +#endif + +#define EXYNOS_USBHOST_PHY_CTRL_OFFSET (0x4) #define S3C64XX_USBPHY_ENABLE (0x1 << 16) #define EXYNOS_USBPHY_ENABLE (0x1 << 0) +#define EXYNOS_USB20PHY_CFG_HOST_LINK (0x1 << 0) enum samsung_cpu_type { TYPE_S3C64XX, TYPE_EXYNOS4210, + TYPE_EXYNOS5250, }; /* * struct samsung_usbphy_drvdata - driver data for various SoC variants * @cpu_type: machine identifier * @devphy_en_mask: device phy enable mask for PHY CONTROL register + * @hostphy_en_mask: host phy enable mask for PHY CONTROL register * @devphy_reg_offset: offset to DEVICE PHY CONTROL register from * mapped address of system controller. + * @hostphy_reg_offset: offset to HOST PHY CONTROL register from + * mapped address of system controller. * * Here we have a separate mask for device type phy. * Having different masks for host and device type phy helps @@ -87,7 +198,9 @@ enum samsung_cpu_type { struct samsung_usbphy_drvdata { int cpu_type; int devphy_en_mask; + int hostphy_en_mask; u32 devphy_reg_offset; + u32 hostphy_reg_offset; }; /* @@ -98,8 +211,12 @@ struct samsung_usbphy_drvdata { * @clk: usb phy clock * @regs: usb phy controller registers memory base * @pmuregs: USB device PHY_CONTROL register memory base + * @sysreg: USB2.0 PHY_CFG register memory base * @ref_clk_freq: reference clock frequency selection * @drv_data: driver data available for different SoCs + * @phy_type: Samsung SoCs specific phy types: #HOST + * #DEVICE + * @phy_usage: usage count for phy * @lock: lock for phy operations */ struct samsung_usbphy { @@ -109,13 +226,27 @@ struct samsung_usbphy { struct clk *clk; void __iomem *regs; void __iomem *pmuregs; + void __iomem *sysreg; int ref_clk_freq; const struct samsung_usbphy_drvdata *drv_data; + enum samsung_usb_phy_type phy_type; + atomic_t phy_usage; spinlock_t lock; }; #define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) +int samsung_usbphy_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + if (!otg) + return -ENODEV; + + if (!otg->host) + otg->host = host; + + return 0; +} + static int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) { struct device_node *usbphy_sys; @@ -129,14 +260,27 @@ static int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) sphy->pmuregs = of_iomap(usbphy_sys, 0); - of_node_put(usbphy_sys); - if (sphy->pmuregs == NULL) { dev_err(sphy->dev, "Can't get usb-phy pmu control register\n"); - return -ENODEV; + goto err0; } + sphy->sysreg = of_iomap(usbphy_sys, 1); + + /* + * Not returning error code here, since this situation is not fatal. + * Few SoCs may not have this switch available + */ + if (sphy->sysreg == NULL) + dev_warn(sphy->dev, "Can't get usb-phy sysreg cfg register\n"); + + of_node_put(usbphy_sys); + return 0; + +err0: + of_node_put(usbphy_sys); + return -ENXIO; } /* @@ -146,17 +290,42 @@ static int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) */ static void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) { - void __iomem *reg; + void __iomem *reg = NULL; u32 reg_val; - u32 en_mask; + u32 en_mask = 0; if (!sphy->pmuregs) { dev_warn(sphy->dev, "Can't set pmu isolation\n"); return; } - reg = sphy->pmuregs + sphy->drv_data->devphy_reg_offset; - en_mask = sphy->drv_data->devphy_en_mask; + switch (sphy->drv_data->cpu_type) { + case TYPE_S3C64XX: + /* + * Do nothing: We will add here once S3C64xx goes for DT support + */ + break; + case TYPE_EXYNOS4210: + /* + * Fall through since exynos4210 and exynos5250 have similar + * register architecture: two separate registers for host and + * device phy control with enable bit at position 0. + */ + case TYPE_EXYNOS5250: + if (sphy->phy_type == USB_PHY_TYPE_DEVICE) { + reg = sphy->pmuregs + + sphy->drv_data->devphy_reg_offset; + en_mask = sphy->drv_data->devphy_en_mask; + } else if (sphy->phy_type == USB_PHY_TYPE_HOST) { + reg = sphy->pmuregs + + sphy->drv_data->hostphy_reg_offset; + en_mask = sphy->drv_data->hostphy_en_mask; + } + break; + default: + dev_err(sphy->dev, "Invalid SoC type\n"); + return; + } reg_val = readl(reg); @@ -168,6 +337,43 @@ static void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) writel(reg_val, reg); } +/* + * Configure the mode of working of usb-phy here: HOST/DEVICE. + */ +static void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy) +{ + u32 reg; + + if (!sphy->sysreg) { + dev_warn(sphy->dev, "Can't configure specified phy mode\n"); + return; + } + + reg = readl(sphy->sysreg); + + if (sphy->phy_type == USB_PHY_TYPE_DEVICE) + reg &= ~EXYNOS_USB20PHY_CFG_HOST_LINK; + else if (sphy->phy_type == USB_PHY_TYPE_HOST) + reg |= EXYNOS_USB20PHY_CFG_HOST_LINK; + + writel(reg, sphy->sysreg); +} + +/* + * PHYs are different for USB Device and USB Host. + * This make sure that correct PHY type is selected before + * any operation on PHY. + */ +static int samsung_usbphy_set_type(struct usb_phy *phy, + enum samsung_usb_phy_type phy_type) +{ + struct samsung_usbphy *sphy = phy_to_sphy(phy); + + sphy->phy_type = phy_type; + + return 0; +} + /* * Returns reference clock frequency selection value */ @@ -176,34 +382,185 @@ static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) struct clk *ref_clk; int refclk_freq = 0; - ref_clk = clk_get(sphy->dev, "xusbxti"); + /* + * In exynos5250 USB host and device PHY use + * external crystal clock XXTI + */ + if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) + ref_clk = clk_get(sphy->dev, "ext_xtal"); + else + ref_clk = clk_get(sphy->dev, "xusbxti"); if (IS_ERR(ref_clk)) { dev_err(sphy->dev, "Failed to get reference clock\n"); return PTR_ERR(ref_clk); } - switch (clk_get_rate(ref_clk)) { - case 12 * MHZ: - refclk_freq = PHYCLK_CLKSEL_12M; - break; - case 24 * MHZ: - refclk_freq = PHYCLK_CLKSEL_24M; - break; - case 48 * MHZ: - refclk_freq = PHYCLK_CLKSEL_48M; - break; - default: - if (sphy->drv_data->cpu_type == TYPE_S3C64XX) - refclk_freq = PHYCLK_CLKSEL_48M; - else + if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) { + /* set clock frequency for PLL */ + switch (clk_get_rate(ref_clk)) { + case 9600 * KHZ: + refclk_freq = FSEL_CLKSEL_9600K; + break; + case 10 * MHZ: + refclk_freq = FSEL_CLKSEL_10M; + break; + case 12 * MHZ: + refclk_freq = FSEL_CLKSEL_12M; + break; + case 19200 * KHZ: + refclk_freq = FSEL_CLKSEL_19200K; + break; + case 20 * MHZ: + refclk_freq = FSEL_CLKSEL_20M; + break; + case 50 * MHZ: + refclk_freq = FSEL_CLKSEL_50M; + break; + case 24 * MHZ: + default: + /* default reference clock */ + refclk_freq = FSEL_CLKSEL_24M; + break; + } + } else { + switch (clk_get_rate(ref_clk)) { + case 12 * MHZ: + refclk_freq = PHYCLK_CLKSEL_12M; + break; + case 24 * MHZ: refclk_freq = PHYCLK_CLKSEL_24M; - break; + break; + case 48 * MHZ: + refclk_freq = PHYCLK_CLKSEL_48M; + break; + default: + if (sphy->drv_data->cpu_type == TYPE_S3C64XX) + refclk_freq = PHYCLK_CLKSEL_48M; + else + refclk_freq = PHYCLK_CLKSEL_24M; + break; + } } clk_put(ref_clk); return refclk_freq; } +static bool exynos5_phyhost_is_on(void *regs) +{ + u32 reg; + + reg = readl(regs + EXYNOS5_PHY_HOST_CTRL0); + + return !(reg & HOST_CTRL0_SIDDQ); +} + +static void samsung_exynos5_usbphy_enable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phyclk = sphy->ref_clk_freq; + u32 phyhost; + u32 phyotg; + u32 phyhsic; + u32 ehcictrl; + u32 ohcictrl; + + /* + * phy_usage helps in keeping usage count for phy + * so that the first consumer enabling the phy is also + * the last consumer to disable it. + */ + + atomic_inc(&sphy->phy_usage); + + if (exynos5_phyhost_is_on(regs)) { + dev_info(sphy->dev, "Already power on PHY\n"); + return; + } + + /* Host configuration */ + phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); + + /* phy reference clock configuration */ + phyhost &= ~HOST_CTRL0_FSEL_MASK; + phyhost |= HOST_CTRL0_FSEL(phyclk); + + /* host phy reset */ + phyhost &= ~(HOST_CTRL0_PHYSWRST | + HOST_CTRL0_PHYSWRSTALL | + HOST_CTRL0_SIDDQ | + /* Enable normal mode of operation */ + HOST_CTRL0_FORCESUSPEND | + HOST_CTRL0_FORCESLEEP); + + /* Link reset */ + phyhost |= (HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST | + /* COMMON Block configuration during suspend */ + HOST_CTRL0_COMMONON_N); + writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); + udelay(10); + phyhost &= ~(HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST); + writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); + + /* OTG configuration */ + phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); + + /* phy reference clock configuration */ + phyotg &= ~OTG_SYS_FSEL_MASK; + phyotg |= OTG_SYS_FSEL(phyclk); + + /* Enable normal mode of operation */ + phyotg &= ~(OTG_SYS_FORCESUSPEND | + OTG_SYS_SIDDQ_UOTG | + OTG_SYS_FORCESLEEP | + OTG_SYS_REFCLKSEL_MASK | + /* COMMON Block configuration during suspend */ + OTG_SYS_COMMON_ON); + + /* OTG phy & link reset */ + phyotg |= (OTG_SYS_PHY0_SWRST | + OTG_SYS_LINKSWRST_UOTG | + OTG_SYS_PHYLINK_SWRESET | + OTG_SYS_OTGDISABLE | + /* Set phy refclk */ + OTG_SYS_REFCLKSEL_CLKCORE); + + writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); + udelay(10); + phyotg &= ~(OTG_SYS_PHY0_SWRST | + OTG_SYS_LINKSWRST_UOTG | + OTG_SYS_PHYLINK_SWRESET); + writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); + + /* HSIC phy configuration */ + phyhsic = (HSIC_CTRL_REFCLKDIV_12 | + HSIC_CTRL_REFCLKSEL | + HSIC_CTRL_PHYSWRST); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); + udelay(10); + phyhsic &= ~HSIC_CTRL_PHYSWRST; + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); + + udelay(80); + + /* enable EHCI DMA burst */ + ehcictrl = readl(regs + EXYNOS5_PHY_HOST_EHCICTRL); + ehcictrl |= (HOST_EHCICTRL_ENAINCRXALIGN | + HOST_EHCICTRL_ENAINCR4 | + HOST_EHCICTRL_ENAINCR8 | + HOST_EHCICTRL_ENAINCR16); + writel(ehcictrl, regs + EXYNOS5_PHY_HOST_EHCICTRL); + + /* set ohci_suspend_on_n */ + ohcictrl = readl(regs + EXYNOS5_PHY_HOST_OHCICTRL); + ohcictrl |= HOST_OHCICTRL_SUSPLGCY; + writel(ohcictrl, regs + EXYNOS5_PHY_HOST_OHCICTRL); +} + static void samsung_usbphy_enable(struct samsung_usbphy *sphy) { void __iomem *regs = sphy->regs; @@ -239,6 +596,41 @@ static void samsung_usbphy_enable(struct samsung_usbphy *sphy) writel(rstcon, regs + SAMSUNG_RSTCON); } +static void samsung_exynos5_usbphy_disable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phyhost; + u32 phyotg; + u32 phyhsic; + + if (atomic_dec_return(&sphy->phy_usage) > 0) { + dev_info(sphy->dev, "still being used\n"); + return; + } + + phyhsic = (HSIC_CTRL_REFCLKDIV_12 | + HSIC_CTRL_REFCLKSEL | + HSIC_CTRL_SIDDQ | + HSIC_CTRL_FORCESLEEP | + HSIC_CTRL_FORCESUSPEND); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); + + phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); + phyhost |= (HOST_CTRL0_SIDDQ | + HOST_CTRL0_FORCESUSPEND | + HOST_CTRL0_FORCESLEEP | + HOST_CTRL0_PHYSWRST | + HOST_CTRL0_PHYSWRSTALL); + writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); + + phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); + phyotg |= (OTG_SYS_FORCESUSPEND | + OTG_SYS_SIDDQ_UOTG | + OTG_SYS_FORCESLEEP); + writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); +} + static void samsung_usbphy_disable(struct samsung_usbphy *sphy) { void __iomem *regs = sphy->regs; @@ -266,11 +658,14 @@ static void samsung_usbphy_disable(struct samsung_usbphy *sphy) static int samsung_usbphy_init(struct usb_phy *phy) { struct samsung_usbphy *sphy; + struct usb_bus *host = NULL; unsigned long flags; int ret = 0; sphy = phy_to_sphy(phy); + host = phy->otg->host; + /* Enable the phy clock */ ret = clk_prepare_enable(sphy->clk); if (ret) { @@ -280,19 +675,35 @@ static int samsung_usbphy_init(struct usb_phy *phy) spin_lock_irqsave(&sphy->lock, flags); + if (host) { + /* setting default phy-type for USB 2.0 */ + if (!strstr(dev_name(host->controller), "ehci") || + !strstr(dev_name(host->controller), "ohci")) + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); + } else { + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); + } + /* Disable phy isolation */ if (sphy->plat && sphy->plat->pmu_isolation) sphy->plat->pmu_isolation(false); else samsung_usbphy_set_isolation(sphy, false); + /* Selecting Host/OTG mode; After reset USB2.0PHY_CFG: HOST */ + samsung_usbphy_cfg_sel(sphy); + /* Initialize usb phy registers */ - samsung_usbphy_enable(sphy); + if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) + samsung_exynos5_usbphy_enable(sphy); + else + samsung_usbphy_enable(sphy); spin_unlock_irqrestore(&sphy->lock, flags); /* Disable the phy clock */ clk_disable_unprepare(sphy->clk); + return ret; } @@ -302,10 +713,13 @@ static int samsung_usbphy_init(struct usb_phy *phy) static void samsung_usbphy_shutdown(struct usb_phy *phy) { struct samsung_usbphy *sphy; + struct usb_bus *host = NULL; unsigned long flags; sphy = phy_to_sphy(phy); + host = phy->otg->host; + if (clk_prepare_enable(sphy->clk)) { dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); return; @@ -313,8 +727,20 @@ static void samsung_usbphy_shutdown(struct usb_phy *phy) spin_lock_irqsave(&sphy->lock, flags); + if (host) { + /* setting default phy-type for USB 2.0 */ + if (!strstr(dev_name(host->controller), "ehci") || + !strstr(dev_name(host->controller), "ohci")) + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); + } else { + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); + } + /* De-initialize usb phy registers */ - samsung_usbphy_disable(sphy); + if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) + samsung_exynos5_usbphy_disable(sphy); + else + samsung_usbphy_disable(sphy); /* Enable phy isolation */ if (sphy->plat && sphy->plat->pmu_isolation) @@ -346,7 +772,9 @@ static inline const struct samsung_usbphy_drvdata static int __devinit samsung_usbphy_probe(struct platform_device *pdev) { struct samsung_usbphy *sphy; + struct usb_otg *otg; struct samsung_usbphy_data *pdata = pdev->dev.platform_data; + const struct samsung_usbphy_drvdata *drv_data; struct device *dev = &pdev->dev; struct resource *phy_mem; void __iomem *phy_base; @@ -369,7 +797,17 @@ static int __devinit samsung_usbphy_probe(struct platform_device *pdev) if (!sphy) return -ENOMEM; - clk = devm_clk_get(dev, "otg"); + otg = devm_kzalloc(dev, sizeof(*otg), GFP_KERNEL); + if (!otg) + return -ENOMEM; + + drv_data = samsung_usbphy_get_driver_data(pdev); + + if (drv_data->cpu_type == TYPE_EXYNOS5250) + clk = devm_clk_get(dev, "usbhost"); + else + clk = devm_clk_get(dev, "otg"); + if (IS_ERR(clk)) { dev_err(dev, "Failed to get otg clock\n"); return PTR_ERR(clk); @@ -391,13 +829,17 @@ static int __devinit samsung_usbphy_probe(struct platform_device *pdev) sphy->plat = pdata; sphy->regs = phy_base; sphy->clk = clk; + sphy->drv_data = drv_data; sphy->phy.dev = sphy->dev; sphy->phy.label = "samsung-usbphy"; sphy->phy.init = samsung_usbphy_init; sphy->phy.shutdown = samsung_usbphy_shutdown; - sphy->drv_data = samsung_usbphy_get_driver_data(pdev); sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); + sphy->phy.otg = otg; + sphy->phy.otg->phy = &sphy->phy; + sphy->phy.otg->set_host = samsung_usbphy_set_host; + spin_lock_init(&sphy->lock); platform_set_drvdata(pdev, sphy); @@ -413,6 +855,8 @@ static int __exit samsung_usbphy_remove(struct platform_device *pdev) if (sphy->pmuregs) iounmap(sphy->pmuregs); + if (sphy->sysreg) + iounmap(sphy->sysreg); return 0; } @@ -425,6 +869,13 @@ static const struct samsung_usbphy_drvdata usbphy_s3c64xx = { static const struct samsung_usbphy_drvdata usbphy_exynos4 = { .cpu_type = TYPE_EXYNOS4210, .devphy_en_mask = EXYNOS_USBPHY_ENABLE, + .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, +}; + +static struct samsung_usbphy_drvdata usbphy_exynos5 = { + .cpu_type = TYPE_EXYNOS5250, + .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, + .hostphy_reg_offset = EXYNOS_USBHOST_PHY_CTRL_OFFSET, }; #ifdef CONFIG_OF @@ -435,6 +886,9 @@ static const struct of_device_id samsung_usbphy_dt_match[] = { }, { .compatible = "samsung,exynos4210-usbphy", .data = &usbphy_exynos4, + }, { + .compatible = "samsung,exynos5250-usbphy", + .data = &usbphy_exynos5 }, {}, }; @@ -448,6 +902,9 @@ static struct platform_device_id samsung_usbphy_driver_ids[] = { }, { .name = "exynos4210-usbphy", .driver_data = (unsigned long)&usbphy_exynos4, + }, { + .name = "exynos5250-usbphy", + .driver_data = (unsigned long)&usbphy_exynos5, }, {}, }; -- cgit v1.2.3 From cc3f414cf2e404130584b63d373161ba6fd24bc2 Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Thu, 4 Oct 2012 15:32:52 +0200 Subject: video: add of helper for display timings/videomode This adds support for reading display timings from DT into a struct display_timings. The of_display_timing implementation supports multiple subnodes. All children are read into an array, that can be queried. If no native mode is specified, the first subnode will be used. For cases where the graphics driver knows there can be only one mode description or where the driver only supports one mode, a helper function of_get_videomode is added, that gets a struct videomode from DT. Signed-off-by: Steffen Trumtrar Signed-off-by: Philipp Zabel Acked-by: Stephen Warren Reviewed-by: Thierry Reding Acked-by: Thierry Reding Tested-by: Thierry Reding Tested-by: Philipp Zabel Reviewed-by: Laurent Pinchart Acked-by: Laurent Pinchart Tested-by: Afzal Mohammed Tested-by: Rob Clark Tested-by: Leela Krishna Amudala --- .../devicetree/bindings/video/display-timing.txt | 109 ++++++++++ drivers/video/Kconfig | 15 ++ drivers/video/Makefile | 2 + drivers/video/of_display_timing.c | 239 +++++++++++++++++++++ drivers/video/of_videomode.c | 54 +++++ include/video/of_display_timing.h | 20 ++ include/video/of_videomode.h | 18 ++ 7 files changed, 457 insertions(+) create mode 100644 Documentation/devicetree/bindings/video/display-timing.txt create mode 100644 drivers/video/of_display_timing.c create mode 100644 drivers/video/of_videomode.c create mode 100644 include/video/of_display_timing.h create mode 100644 include/video/of_videomode.h (limited to 'Documentation/devicetree') diff --git a/Documentation/devicetree/bindings/video/display-timing.txt b/Documentation/devicetree/bindings/video/display-timing.txt new file mode 100644 index 000000000000..150038552bc3 --- /dev/null +++ b/Documentation/devicetree/bindings/video/display-timing.txt @@ -0,0 +1,109 @@ +display-timing bindings +======================= + +display-timings node +-------------------- + +required properties: + - none + +optional properties: + - native-mode: The native mode for the display, in case multiple modes are + provided. When omitted, assume the first node is the native. + +timing subnode +-------------- + +required properties: + - hactive, vactive: display resolution + - hfront-porch, hback-porch, hsync-len: horizontal display timing parameters + in pixels + vfront-porch, vback-porch, vsync-len: vertical display timing parameters in + lines + - clock-frequency: display clock in Hz + +optional properties: + - hsync-active: hsync pulse is active low/high/ignored + - vsync-active: vsync pulse is active low/high/ignored + - de-active: data-enable pulse is active low/high/ignored + - pixelclk-active: with + - active high = drive pixel data on rising edge/ + sample data on falling edge + - active low = drive pixel data on falling edge/ + sample data on rising edge + - ignored = ignored + - interlaced (bool): boolean to enable interlaced mode + - doublescan (bool): boolean to enable doublescan mode + +All the optional properties that are not bool follow the following logic: + <1>: high active + <0>: low active + omitted: not used on hardware + +There are different ways of describing the capabilities of a display. The +devicetree representation corresponds to the one commonly found in datasheets +for displays. If a display supports multiple signal timings, the native-mode +can be specified. + +The parameters are defined as: + + +----------+-------------------------------------+----------+-------+ + | | ↑ | | | + | | |vback_porch | | | + | | ↓ | | | + +----------#######################################----------+-------+ + | # ↑ # | | + | # | # | | + | hback # | # hfront | hsync | + | porch # | hactive # porch | len | + |<-------->#<-------+--------------------------->#<-------->|<----->| + | # | # | | + | # |vactive # | | + | # | # | | + | # ↓ # | | + +----------#######################################----------+-------+ + | | ↑ | | | + | | |vfront_porch | | | + | | ↓ | | | + +----------+-------------------------------------+----------+-------+ + | | ↑ | | | + | | |vsync_len | | | + | | ↓ | | | + +----------+-------------------------------------+----------+-------+ + +Example: + + display-timings { + native-mode = <&timing0>; + timing0: 1080p24 { + /* 1920x1080p24 */ + clock-frequency = <52000000>; + hactive = <1920>; + vactive = <1080>; + hfront-porch = <25>; + hback-porch = <25>; + hsync-len = <25>; + vback-porch = <2>; + vfront-porch = <2>; + vsync-len = <2>; + hsync-active = <1>; + }; + }; + +Every required property also supports the use of ranges, so the commonly used +datasheet description with minimum, typical and maximum values can be used. + +Example: + + timing1: timing { + /* 1920x1080p24 */ + clock-frequency = <148500000>; + hactive = <1920>; + vactive = <1080>; + hsync-len = <0 44 60>; + hfront-porch = <80 88 95>; + hback-porch = <100 148 160>; + vfront-porch = <0 4 6>; + vback-porch = <0 36 50>; + vsync-len = <0 5 6>; + }; diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 09a8f0d8a3d4..807c7fa689fa 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -39,6 +39,21 @@ config DISPLAY_TIMING config VIDEOMODE bool +config OF_DISPLAY_TIMING + bool "Enable device tree display timing support" + depends on OF + select DISPLAY_TIMING + help + helper to parse display timings from the devicetree + +config OF_VIDEOMODE + bool "Enable device tree videomode support" + depends on OF + select VIDEOMODE + select OF_DISPLAY_TIMING + help + helper to get videomodes from the devicetree + menuconfig FB tristate "Support for frame buffer devices" ---help--- diff --git a/drivers/video/Makefile b/drivers/video/Makefile index e0dd8202365f..f592f3b32ec7 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -169,4 +169,6 @@ obj-$(CONFIG_FB_VIRTUAL) += vfb.o #video output switch sysfs driver obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o obj-$(CONFIG_DISPLAY_TIMING) += display_timing.o +obj-$(CONFIG_OF_DISPLAY_TIMING) += of_display_timing.o obj-$(CONFIG_VIDEOMODE) += videomode.o +obj-$(CONFIG_OF_VIDEOMODE) += of_videomode.o diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c new file mode 100644 index 000000000000..13ecd9897010 --- /dev/null +++ b/drivers/video/of_display_timing.c @@ -0,0 +1,239 @@ +/* + * OF helpers for parsing display timings + * + * Copyright (c) 2012 Steffen Trumtrar , Pengutronix + * + * based on of_videomode.c by Sascha Hauer + * + * This file is released under the GPLv2 + */ +#include +#include +#include +#include