Skip to content

Instantly share code, notes, and snippets.

@notro
Created October 19, 2013 17:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save notro/7059077 to your computer and use it in GitHub Desktop.
Save notro/7059077 to your computer and use it in GitHub Desktop.
BCM2708 with Device Tree support pr. 2013/10/19
/dts-v1/;
/memreserve/ 0x1c000000 0x04000000;
/*
/include/ "skeleton.dtsi"
*/
/include/ "bcm2708.dtsi"
/ {
compatible = "brcm,bcm2708";
model = "v31 Raspberry Pi Model B";
/*
memreserve = <0x00000000 0x00000000>;
*/
memory {
reg = <0 0>;
};
chosen {
bootargs = "debug earlyprintk hello from dtb dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait";
/*
bootargs = "hello from dtb earlyprintk loglevel=8 memblock=debug dma.dmachans=0x7f35 bcm2708_fb.fbwidth=656 bcm2708_fb.fbheight=416 bcm2708.boardrev=0xe bcm2708.serial=0x4939788f smsc95xx.macaddr=B8:27:EB:39:78:8F sdhci-bcm2708.emmc_clock_freq=100000000 vc_mem.mem_base=0x1ec00000 vc_mem.mem_size=0x20000000 dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait";
*/
};
leds {
compatible = "gpio-leds";
pinctrl-names = "default";
pinctrl-0 = <&act_pin>;
act {
label = "ACT";
gpios = <&gpio 16 1>;
default-state = "keep";
linux,default-trigger = "mmc0";
};
};
gpio_keys_polled {
compatible = "gpio-keys-polled";
#address-cells = <1>;
#size-cells = <0>;
poll-interval = <1000>;
autorepeat;
button@a {
label = "GPIO Key A";
linux,code = <30>;
gpios = <&gpio 24 1>;
};
};
gpio_keys {
compatible = "gpio-keys";
#address-cells = <1>;
#size-cells = <0>;
autorepeat;
button@a {
label = "GPIO Key A";
linux,code = <30>;
gpios = <&gpio 25 1>;
};
};
system {
linux,serial = <0x00000000 0x00000000>;
linux,revision = <0x00000000>;
};
display {
compatible = "brcm,bcm2708-fb";
broadcom,width = <0x00000000>;
broadcom,height = <0x00000000>;
broadcom,depth = <0x00000000>;
};
axi {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
vc_mem {
reg = <0x1ec00000 0x20000000>;
};
usb {
hub {
ethernet {
mac-address = [00 00 00 00 00 00];
};
};
};
dma {
broadcom,channels = <0x00000000>;
};
sdhci {
clock-frequency = <0x00000000>;
};
uart0 {
clock-frequency = <0x00000000>;
};
};
onewire {
compatible = "w1-gpio";
gpios = <&gpio 4 0>;
};
aliases {
spi0 = &spi;
i2c1 = &i2c1;
};
};
&gpio {
pinctrl-names = "default";
pinctrl-0 = <&lan_pin &sdcard_pins &uart0_pins &audio_pins &spi0_pins &i2c_pins &gpiokeys>;
// alt0: alt0 { /* don't know what this is */
// brcm,pins = <4 5>;
// brcm,function = <4>; /* alt0 */
// };
audio_pins: audio {
brcm,pins = <40 45>;
brcm,function = <4>; /* alt0 */
};
sdcard_pins: sdcard {
brcm,pins = <48 49 50 51 52 53>;
brcm,function = <7>; /* alt3 */
};
/* LAN_RUN/n_reset */
lan_pin: lan {
brcm,pins = <6>;
brcm,function = <1>; /* GPIO out */
};
uart0_pins: uart0 {
brcm,pins = <14 15>;
brcm,function = <4>; /* alt0 */
};
spi0_pins: spi0 {
brcm,pins = <7 8 9 10 11>;
brcm,function = <4>; /* alt0 */
};
i2c_pins: i2c {
brcm,pins = <0 1 2 3>;
brcm,function = <4>; /* alt0 */
};
gpiokeys: gpiokeys {
brcm,pins = <25 24>;
brcm,function = <0>; /* GPIO in */
brcm,pull = <2>; /* pull up */
};
act_pin: act { /* ACT led */
brcm,pins = <16>;
brcm,function = <1>; /* GPIO out */
};
};
&spi {
status = "okay";
spidev@0{
compatible = "spidev";
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
spi-max-frequency = <500000>;
// Mode 0 (CPOL = 0, CPHA = 0)
};
tsc2046@1 {
reg = <1>; /* CE1 */
compatible = "ti,tsc2046";
interrupts = <3 17>; /* bank 3 is GPIO interrupts */
spi-max-frequency = <2000000>;
pendown-gpio = <&gpio 17 0>;
ti,x-min = /bits/ 16 <230>;
ti,x-max = /bits/ 16 <3850>;
ti,y-min = /bits/ 16 <190>;
ti,y-max = /bits/ 16 <3850>;
ti,x-plate-ohms = /bits/ 16 <60>;
ti,pressure-max = /bits/ 16 <255>;
linux,wakeup;
};
};
&i2c1 {
status = "okay";
clock-frequency = <100000>;
};
&sdhci {
status = "okay";
bus-width = <4>;
};
/*
Another: https://community.freescale.com/thread/301274
spidev@0x00 {
compatible = "spidev";
spi-max-frequency = <30000000>;
reg = <0>;
};
*/
/*
/include/ "testcases/tests.dtsi"
*/
/include/ "skeleton.dtsi"
/ {
compatible = "brcm,bcm2708";
model = "BCM2708";
interrupt-parent = <&intc>;
chosen {
bootargs = "earlyprintk console=ttyAMA0";
};
axi {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0x7e000000 0x20000000 0x02000000>;
intc: interrupt-controller {
compatible = "brcm,bcm2708-armctrl-ic";
reg = <0x7e00b200 0x200>;
interrupt-controller;
#interrupt-cells = <2>;
};
uart@20201000 {
compatible = "brcm,bcm2835-pl011", "arm,pl011", "arm,primecell";
reg = <0x7e201000 0x1000>;
interrupts = <2 25>;
clock-frequency = <3000000>;
arm,primecell-periphid = <0x00241011>;
};
gpio: gpio {
compatible = "brcm,bcm2708-pinctrl";
reg = <0x7e200000 0xb4>;
gpio-controller;
#gpio-cells = <2>;
};
sdhci: sdhci {
compatible = "brcm,bcm2708-sdhci";
reg = <0x7e300000 0x100>;
interrupts = <2 30>;
clocks = <&clk_mmc>;
status = "disabled";
};
spi: spi-bcm2708 {
compatible = "brcm,bcm2708-spi";
reg = <0x7e204000 0x1000>;
interrupts = <2 22>;
clocks = <&clk_spi>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
};
i2c0: i2c@20205000 {
compatible = "brcm,bcm2708-i2c";
reg = <0x7e205000 0x1000>;
interrupts = <2 21>;
clocks = <&clk_i2c>;
status = "disabled";
};
i2c1: i2c@20804000 {
compatible = "brcm,bcm2708-i2c";
reg = <0x7e804000 0x1000>;
interrupts = <2 21>;
clocks = <&clk_i2c>;
status = "disabled";
};
hwmon {
compatible = "brcm,bcm2835-hwmon";
};
thermal {
compatible = "brcm,bcm2835-thermal";
};
// /* fiq_fix_enable == true */
// usb {
//// compatible = "brcm,bcm2835-usb", "snps,dwc2";
// compatible = "brcm,bcm2835-usb", "snps,dwc";
//// reg = <0x7e980000 0x10000>;
// reg = <0x7e980000 0x20000>, <0x7e006000 0x1000>;
//// interrupts = <1 9>;
// interrupts = <2 0>;
// };
};
clocks {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
clk_mmc: mmc {
compatible = "fixed-clock";
reg = <0>;
#clock-cells = <0>;
clock-frequency = <100000000>;
};
clk_i2c: i2c {
compatible = "fixed-clock";
reg = <1>;
#clock-cells = <0>;
clock-frequency = <250000000>;
};
clk_spi: spi {
compatible = "fixed-clock";
reg = <2>;
#clock-cells = <0>;
clock-frequency = <250000000>;
};
};
};
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 4e36ccd..7707c27 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -367,6 +367,7 @@ config ARCH_BCM2708
select ARM_AMBA
select HAVE_CLK
select HAVE_SCHED_CLOCK
+ select NEED_MACH_GPIO_H
select NEED_MACH_MEMORY_H
select CLKDEV_LOOKUP
select ARCH_HAS_CPUFREQ
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index f0895c5..014305d 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -39,6 +39,7 @@ dtb-$(CONFIG_ARCH_AT91) += sama5d33ek.dtb
dtb-$(CONFIG_ARCH_AT91) += sama5d34ek.dtb
dtb-$(CONFIG_ARCH_AT91) += sama5d35ek.dtb
+dtb-$(CONFIG_ARCH_BCM2708) += bcm2708-rpi-b.dtb
dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb
dtb-$(CONFIG_ARCH_BCM) += bcm11351-brt.dtb
dtb-$(CONFIG_ARCH_DAVINCI) += da850-enbw-cmc.dtb \
diff --git a/arch/arm/mach-bcm2708/Kconfig b/arch/arm/mach-bcm2708/Kconfig
index 1dfd0a4..81b4265 100644
--- a/arch/arm/mach-bcm2708/Kconfig
+++ b/arch/arm/mach-bcm2708/Kconfig
@@ -45,4 +45,22 @@ config BCM2708_SPIDEV
default y
help
Binds spidev driver to the SPI0 master
+
+config PINCTRL_BCM2708
+ bool
+ select PINMUX
+ select PINCONF
+
+config BCM2708_DT
+ bool "Use Device Tree"
+ depends on MACH_BCM2708
+ default n
+ select USE_OF
+ select COMMON_CLK
+ select COMMON_CLK_DEBUG
+ select ARCH_REQUIRE_GPIOLIB
+ select PINCTRL
+ select PINCTRL_BCM2708
+ help
+ Uses pinctrl and Device Tree
endmenu
diff --git a/arch/arm/mach-bcm2708/Makefile b/arch/arm/mach-bcm2708/Makefile
index 0da162c..78386a2 100644
--- a/arch/arm/mach-bcm2708/Makefile
+++ b/arch/arm/mach-bcm2708/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o
obj-$(CONFIG_BCM2708_DMAER) += dmaer_master.o
dmaer_master-objs := dmaer.o vc_support.o
+obj-$(CONFIG_PINCTRL_BCM2708) += pinctrl-bcm2708.o
diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c
index 5662c1a..8c79b5f 100644
--- a/arch/arm/mach-bcm2708/bcm2708.c
+++ b/arch/arm/mach-bcm2708/bcm2708.c
@@ -34,6 +34,12 @@
#include <linux/spi/spi.h>
#include <linux/w1-gpio.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/bcm2835.h>
+#include <linux/irqdomain.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+
#include <linux/version.h>
#include <linux/clkdev.h>
#include <asm/system.h>
@@ -690,10 +696,188 @@ static void bcm2708_power_off(void)
}
}
+
+
+
+
+
+
+
+#define NR_IRQS_BANK0 21
+#define NR_BANKS 3 + 1
+#define IRQS_PER_BANK 32
+
+/* from drivers/irqchip/irq-bcm2835.c */
+static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr,
+ const u32 *intspec, unsigned int intsize,
+ unsigned long *out_hwirq, unsigned int *out_type)
+{
+ if (WARN_ON(intsize != 2))
+ return -EINVAL;
+
+ if (WARN_ON(intspec[0] >= NR_BANKS))
+ return -EINVAL;
+
+ if (WARN_ON(intspec[1] >= IRQS_PER_BANK))
+ return -EINVAL;
+
+ if (WARN_ON(intspec[0] == 0 && intspec[1] >= NR_IRQS_BANK0))
+ return -EINVAL;
+
+ /* map banks */
+ if (intspec[0] == 0)
+ *out_hwirq = ARM_IRQ0_BASE + intspec[1];
+ else if (intspec[0] == 1)
+ *out_hwirq = ARM_IRQ1_BASE + intspec[1];
+ else if (intspec[0] == 2)
+ *out_hwirq = ARM_IRQ2_BASE + intspec[1];
+ else /* use bank 3 for GPIO interrupts */
+ *out_hwirq = GPIO_IRQ_START + intspec[1];
+
+ /* make BCM2835 interrupt numbers work with BCM2708 */
+ /* reverse remap_irqs[] in arch/arm/mach-bcm2708/armctrl.c */
+ switch (*out_hwirq) {
+ case INTERRUPT_VC_JPEG:
+ *out_hwirq = INTERRUPT_JPEG;
+ break;
+ case INTERRUPT_VC_USB:
+ *out_hwirq = INTERRUPT_USB;
+ break;
+ case INTERRUPT_VC_3D:
+ *out_hwirq = INTERRUPT_3D;
+ break;
+ case INTERRUPT_VC_DMA2:
+ *out_hwirq = INTERRUPT_DMA2;
+ break;
+ case INTERRUPT_VC_DMA3:
+ *out_hwirq = INTERRUPT_DMA3;
+ break;
+ case INTERRUPT_VC_I2C:
+ *out_hwirq = INTERRUPT_I2C;
+ break;
+ case INTERRUPT_VC_SPI:
+ *out_hwirq = INTERRUPT_SPI;
+ break;
+ case INTERRUPT_VC_I2SPCM:
+ *out_hwirq = INTERRUPT_I2SPCM;
+ break;
+ case INTERRUPT_VC_SDIO:
+ *out_hwirq = INTERRUPT_SDIO;
+ break;
+ case INTERRUPT_VC_UART:
+ *out_hwirq = INTERRUPT_UART;
+ break;
+ case INTERRUPT_VC_ARASANSDIO:
+ *out_hwirq = INTERRUPT_ARASANSDIO;
+ break;
+ }
+
+ *out_type = IRQ_TYPE_NONE;
+ return 0;
+}
+
+static struct irq_domain_ops armctrl_ops = {
+ .xlate = armctrl_xlate
+};
+
+void __init bcm2708_dt_init_irq(void)
+{
+ struct device_node *np;
+ struct irq_domain *domain;
+
+ early_printk("bcm2708: %s()\n", __func__);
+ np = of_find_compatible_node(NULL, NULL, "brcm,bcm2708-armctrl-ic");
+ if (!np)
+ return;
+
+ domain = irq_domain_add_legacy(np, NR_IRQS, IRQ_ARMCTRL_START, 0,
+ &armctrl_ops, NULL);
+ WARN_ON(!domain);
+
+/*
+ printk("\n\n");
+ unsigned int data;
+ int i;
+ for (i=0; i < MAXIRQNUM; i++) {
+ data = (unsigned int) irq_get_chip_data(i);
+ printk("irq=%d => data=%d\n", i, data);
+ }
+ printk("\n\n");
+*/
+
+}
+
+
+
+static bool __init bcm2708_dt_init(void)
+{
+ struct device_node *np;
+ u32 linux_revision;
+ u64 linux_serial;
+// u32 clock_frequency;
+ int i;
+ int ret;
+
+ early_printk("\n\nbcm2708: %s()\n", __func__);
+
+ if (!of_have_populated_dt()) {
+early_printk("No Device Tree?\n");
+ pr_warn("No Device Tree?\n");
+ return false;
+ }
+
+#if defined(CONFIG_BCM_VC_CMA)
+ vc_cma_early_init();
+#endif
+ pm_power_off = bcm2708_power_off;
+
+ bcm2835_init_clocks();
+ bcm2708_dt_init_irq();
+
+ bcm_register_device(&bcm2708_dmaman_device);
+ bcm_register_device(&bcm2708_vcio_device);
+ bcm_register_device(&bcm2708_gpio_device);
+ bcm_register_device(&bcm2708_systemtimer_device);
+ if (!fiq_fix_enable)
+ {
+ bcm2708_usb_device.resource = bcm2708_usb_resources_no_fiq_fix;
+ bcm2708_usb_device.num_resources = ARRAY_SIZE(bcm2708_usb_resources_no_fiq_fix);
+ }
+ bcm_register_device(&bcm2708_usb_device);
+ bcm_register_device(&bcm2708_uart1_device);
+ bcm_register_device(&bcm2708_powerman_device);
+
+ for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++)
+ bcm_register_device(&bcm2708_alsa_devices[i]);
+
+ early_printk("bcm2708: calling of_platform_populate()\n");
+ ret = of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+ if (ret) {
+ pr_err("bcm2708: of_platform_populate failed: %d\n", ret);
+// BUG();
+ }
+
+ np = of_find_node_by_path("/system");
+ if (np) {
+ if (!of_property_read_u32(np, "linux,revision", &linux_revision))
+ system_rev = linux_revision;
+ if (!of_property_read_u64(np, "linux,serial", &linux_serial))
+ system_serial_low = (u32)linux_serial;
+ }
+// np = of_find_node_by_path("/axi/uart0");
+// if (np && !of_property_read_u32(np, "clock-frequency", &clock_frequency))
+// uart_clock = clock_frequency;
+
+ early_printk("bcm2708: %s: OUT\n", __func__);
+ return true;
+}
+
void __init bcm2708_init(void)
{
int i;
+ if (bcm2708_dt_init())
+ return;
#if defined(CONFIG_BCM_VC_CMA)
vc_cma_early_init();
#endif
@@ -739,10 +923,12 @@ void __init bcm2708_init(void)
bcm_register_device(&bcm2835_hwmon_device);
bcm_register_device(&bcm2835_thermal_device);
+early_printk("bcm2708: before amba_device_register\n");
for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
struct amba_device *d = amba_devs[i];
amba_device_register(d, &iomem_resource);
}
+early_printk("bcm2708: after amba_device_register\n");
system_rev = boardrev;
system_serial_low = serial;
@@ -900,6 +1086,11 @@ static void __init board_reserve(void)
#endif
}
+static const char * const bcm2708_compat[] = {
+ "brcm,bcm2708",
+ NULL
+};
+
MACHINE_START(BCM2708, "BCM2708")
/* Maintainer: Broadcom Europe Ltd. */
.map_io = bcm2708_map_io,
@@ -909,6 +1100,7 @@ MACHINE_START(BCM2708, "BCM2708")
.init_early = bcm2708_init_early,
.reserve = board_reserve,
.restart = bcm2708_restart,
+ .dt_compat = bcm2708_compat
MACHINE_END
module_param(boardrev, uint, 0644);
diff --git a/arch/arm/mach-bcm2708/clock.c b/arch/arm/mach-bcm2708/clock.c
index 4fc556e..d4bae02 100644
--- a/arch/arm/mach-bcm2708/clock.c
+++ b/arch/arm/mach-bcm2708/clock.c
@@ -31,6 +31,7 @@
#include "clock.h"
+#ifndef CONFIG_BCM2708_DT
int clk_enable(struct clk *clk)
{
return 0;
@@ -59,3 +60,4 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
return -EIO;
}
EXPORT_SYMBOL(clk_set_rate);
+#endif
diff --git a/arch/arm/mach-bcm2708/dma.c b/arch/arm/mach-bcm2708/dma.c
index 51d147a..2e45f6f 100644
--- a/arch/arm/mach-bcm2708/dma.c
+++ b/arch/arm/mach-bcm2708/dma.c
@@ -13,6 +13,7 @@
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/scatterlist.h>
+#include <linux/of_device.h>
#include <mach/dma.h>
#include <mach/irqs.h>
@@ -281,6 +282,17 @@ static void dev_dmaman_deregister(const char *dev_name, struct device *dev)
static int dmachans = -1; /* module parameter */
+static void bcm_dmaman_of_parse(void)
+{
+ u32 channels;
+ struct device_node *np = of_find_node_by_path("/axi/dma");
+
+ if (np && !of_property_read_u32(np, "broadcom,channels", &channels)) {
+ printk(DRIVER_NAME ": /axi/dma/broadcom,channels = 0x%x\n", channels);
+ dmachans = channels;
+ }
+}
+
static int bcm_dmaman_probe(struct platform_device *pdev)
{
int ret = 0;
@@ -289,6 +301,8 @@ static int bcm_dmaman_probe(struct platform_device *pdev)
void __iomem *dma_base = NULL;
int have_dma_region = 0;
+ bcm_dmaman_of_parse();
+
dmaman = kzalloc(sizeof(*dmaman), GFP_KERNEL);
if (NULL == dmaman) {
printk(KERN_ERR DRIVER_NAME ": failed to allocate "
diff --git a/arch/arm/mach-bcm2708/vc_mem.c b/arch/arm/mach-bcm2708/vc_mem.c
index aeae4d5..411b7a7 100644
--- a/arch/arm/mach-bcm2708/vc_mem.c
+++ b/arch/arm/mach-bcm2708/vc_mem.c
@@ -22,6 +22,7 @@
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/dma-mapping.h>
+#include <linux/of_device.h>
#ifdef CONFIG_ARCH_KONA
#include <chal/chal_ipc.h>
@@ -355,6 +356,19 @@ vc_mem_proc_write(struct file *file, const char __user * buffer,
*
***************************************************************************/
+static void __init
+vc_mem_of_parse(void)
+{
+ u32 reg[2];
+ struct device_node *np = of_find_node_by_path("/axi/vc_mem");
+
+ if (np && !of_property_read_u32_array(np, "reg", reg, 2)) {
+ pr_debug("/axi/vc_mem/reg = <0x%04X 0x%04X>\n", reg[0], reg[1]);
+ mem_base = reg[0];
+ mem_size = reg[1];
+ }
+}
+
static int __init
vc_mem_init(void)
{
@@ -363,6 +377,7 @@ vc_mem_init(void)
LOG_DBG("%s: called", __func__);
+ vc_mem_of_parse();
mm_vc_mem_phys_addr = phys_addr;
mm_vc_mem_size = mem_size;
mm_vc_mem_base = mem_base;
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 137d3e7..18d7916 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-composite.o
# SoCs specific
obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
+obj-$(CONFIG_BCM2708_DT) += clk-bcm2835.o
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
obj-$(CONFIG_ARCH_MXS) += mxs/
diff --git a/drivers/hwmon/bcm2835-hwmon.c b/drivers/hwmon/bcm2835-hwmon.c
index 5bbed45..d0257c8 100644
--- a/drivers/hwmon/bcm2835-hwmon.c
+++ b/drivers/hwmon/bcm2835-hwmon.c
@@ -202,6 +202,12 @@ static int bcm2835_hwmon_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id bcm2835_hwmon_of_match[] = {
+ { .compatible = "brcm,bcm2835-hwmon" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bcm2835_hwmon_match);
+
/* Hwmon Driver */
static struct platform_driver bcm2835_hwmon_driver = {
.probe = bcm2835_hwmon_probe,
@@ -209,6 +215,7 @@ static struct platform_driver bcm2835_hwmon_driver = {
.driver = {
.name = "bcm2835_hwmon",
.owner = THIS_MODULE,
+ .of_match_table = bcm2835_hwmon_of_match,
},
};
diff --git a/drivers/i2c/busses/i2c-bcm2708.c b/drivers/i2c/busses/i2c-bcm2708.c
index 33f4e7d..e2786ff 100644
--- a/drivers/i2c/busses/i2c-bcm2708.c
+++ b/drivers/i2c/busses/i2c-bcm2708.c
@@ -26,6 +26,7 @@
#include <linux/spinlock.h>
#include <linux/clk.h>
#include <linux/err.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/slab.h>
@@ -268,6 +269,18 @@ static int bcm2708_i2c_probe(struct platform_device *pdev)
struct clk *clk;
struct bcm2708_i2c *bi;
struct i2c_adapter *adap;
+ u32 bus_clk_rate;
+
+ if (pdev->dev.of_node) {
+ pdev->id = of_alias_get_id(pdev->dev.of_node, "i2c");
+ if (pdev->id < 0) {
+ dev_err(&pdev->dev, "alias is missing\n");
+ return -EINVAL;
+ }
+ if (!of_property_read_u32(pdev->dev.of_node,
+ "clock-frequency", &bus_clk_rate))
+ baudrate = bus_clk_rate;
+ }
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs) {
@@ -375,10 +388,17 @@ static int bcm2708_i2c_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id bcm2708_i2c_of_match[] = {
+ { .compatible = "brcm,bcm2708-i2c" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bcm2708_i2c_of_match);
+
static struct platform_driver bcm2708_i2c_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
+ .of_match_table = bcm2708_i2c_of_match,
},
.probe = bcm2708_i2c_probe,
.remove = bcm2708_i2c_remove,
diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c
index 4770680..458ba8d 100644
--- a/drivers/mmc/host/sdhci-bcm2708.c
+++ b/drivers/mmc/host/sdhci-bcm2708.c
@@ -29,6 +29,7 @@
#include <linux/mmc/mmc.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sd.h>
+#include <linux/of_device.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
@@ -1165,6 +1166,17 @@ static struct sdhci_ops sdhci_bcm2708_ops = {
* *
\*****************************************************************************/
+static void sdhci_bcm2708_of_parse(void)
+{
+ u32 clock;
+ struct device_node *np = of_find_node_by_path("/axi/sdhci");
+
+ if (np && !of_property_read_u32(np, "clock-frequency", &clock)) {
+ pr_info(DRIVER_NAME": /axi/sdhci/clock-frequency: %d\n", clock);
+ emmc_clock_freq = clock;
+ }
+}
+
static int sdhci_bcm2708_probe(struct platform_device *pdev)
{
struct sdhci_host *host;
@@ -1174,6 +1186,8 @@ static int sdhci_bcm2708_probe(struct platform_device *pdev)
BUG_ON(pdev == NULL);
+ sdhci_bcm2708_of_parse();
+
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!iomem) {
ret = -ENOMEM;
@@ -1361,10 +1375,17 @@ static int sdhci_bcm2708_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id sdhci_bcm2708_of_match[] = {
+ { .compatible = "brcm,bcm2708-sdhci" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sdhci_bcm2708_of_match);
+
static struct platform_driver sdhci_bcm2708_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
+ .of_match_table = sdhci_bcm2708_of_match,
},
.probe = sdhci_bcm2708_probe,
.remove = sdhci_bcm2708_remove,
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index 60076fe..8da9245 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -29,6 +29,7 @@
#include <linux/bitrev.h>
#include <linux/crc16.h>
#include <linux/crc32.h>
+#include <linux/of_device.h>
#include <linux/usb/usbnet.h>
#include <linux/slab.h>
#include "smsc95xx.h"
@@ -817,12 +818,31 @@ static int smsc95xx_is_macaddr_param(struct usbnet *dev, u8 *dev_mac)
}
}
+static int smsc95xx_is_macaddr_of(struct usbnet *dev, u8 *dev_mac)
+{
+ u8 mtbl[MAC_ADDR_LEN];
+ int i;
+ struct device_node *np = of_find_node_by_path("/axi/usb/hub/ethernet");
+
+ if (!np || of_property_read_u8_array(np, "mac-address", mtbl, MAC_ADDR_LEN))
+ return 0;
+ for (i = 0; i < MAC_ADDR_LEN; i++)
+ dev_mac[i] = mtbl[i];
+ netif_dbg(dev, ifup, dev->net, "Overriding MAC address with: "
+ "%02x:%02x:%02x:%02x:%02x:%02x\n", mtbl[0], mtbl[1], mtbl[2],
+ mtbl[3], mtbl[4], mtbl[5]);
+ return 1;
+}
+
static void smsc95xx_init_mac_address(struct usbnet *dev)
{
/* Check module parameters */
if (smsc95xx_is_macaddr_param(dev, dev->net->dev_addr))
return;
+ if (smsc95xx_is_macaddr_of(dev, dev->net->dev_addr))
+ return;
+
/* try reading mac address from EEPROM */
if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN,
dev->net->dev_addr) == 0) {
diff --git a/drivers/spi/spi-bcm2708.c b/drivers/spi/spi-bcm2708.c
index abaa5a6..bec9fcc 100644
--- a/drivers/spi/spi-bcm2708.c
+++ b/drivers/spi/spi-bcm2708.c
@@ -27,6 +27,7 @@
#include <linux/spinlock.h>
#include <linux/clk.h>
#include <linux/err.h>
+#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/spi/spi.h>
@@ -512,6 +513,7 @@ static int bcm2708_spi_probe(struct platform_device *pdev)
master->setup = bcm2708_spi_setup;
master->transfer = bcm2708_spi_transfer;
master->cleanup = bcm2708_spi_cleanup;
+ master->dev.of_node = pdev->dev.of_node;
platform_set_drvdata(pdev, master);
bs = spi_master_get_devdata(master);
@@ -545,7 +547,7 @@ static int bcm2708_spi_probe(struct platform_device *pdev)
}
/* initialise the hardware */
- clk_enable(clk);
+ clk_prepare_enable(clk);
bcm2708_wr(bs, SPI_CS, SPI_CS_REN | SPI_CS_CLEAR_RX | SPI_CS_CLEAR_TX);
err = spi_register_master(master);
@@ -560,6 +562,7 @@ static int bcm2708_spi_probe(struct platform_device *pdev)
return 0;
out_free_irq:
+ clk_disable_unprepare(bs->clk);
free_irq(bs->irq, master);
out_workqueue:
destroy_workqueue(bs->workq);
@@ -585,7 +588,7 @@ static int bcm2708_spi_remove(struct platform_device *pdev)
flush_work_sync(&bs->work);
- clk_disable(bs->clk);
+ clk_disable_unprepare(bs->clk);
clk_put(bs->clk);
free_irq(bs->irq, master);
iounmap(bs->base);
@@ -595,10 +598,17 @@ static int bcm2708_spi_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id bcm2708_spi_match[] = {
+ { .compatible = "brcm,bcm2708-spi", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, bcm2708_spi_match);
+
static struct platform_driver bcm2708_spi_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
+ .of_match_table = bcm2708_spi_match,
},
.probe = bcm2708_spi_probe,
.remove = bcm2708_spi_remove,
diff --git a/drivers/thermal/bcm2835-thermal.c b/drivers/thermal/bcm2835-thermal.c
index 85fceb5..c214edc 100644
--- a/drivers/thermal/bcm2835-thermal.c
+++ b/drivers/thermal/bcm2835-thermal.c
@@ -167,6 +167,12 @@ static struct thermal_zone_device_ops ops = {
.get_mode = bcm2835_get_mode,
};
+static const struct of_device_id bcm2835_thermal_of_match[] = {
+ { .compatible = "brcm,bcm2835-thermal" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bcm2835_thermal_of_match);
+
/* Thermal Driver */
static struct platform_driver bcm2835_thermal_driver = {
.probe = bcm2835_thermal_probe,
@@ -174,6 +180,7 @@ static struct platform_driver bcm2835_thermal_driver = {
.driver = {
.name = "bcm2835_thermal",
.owner = THIS_MODULE,
+ .of_match_table = bcm2835_thermal_of_match,
},
};
diff --git a/drivers/video/bcm2708_fb.c b/drivers/video/bcm2708_fb.c
index c10c5ee..a4e22c6 100644
--- a/drivers/video/bcm2708_fb.c
+++ b/drivers/video/bcm2708_fb.c
@@ -27,6 +27,7 @@
#include <linux/clk.h>
#include <linux/printk.h>
#include <linux/console.h>
+#include <linux/of_device.h>
#include <mach/dma.h>
#include <mach/platform.h>
@@ -538,11 +539,29 @@ out:
return ret;
}
+static void bcm2708_fb_of_parse(struct platform_device *dev)
+{
+ u32 val;
+ struct device_node *np = dev->dev.of_node;
+
+ if (!np)
+ return;
+
+ if (!of_property_read_u32(np, "broadcom,width", &val))
+ fbwidth = val;
+ if (!of_property_read_u32(np, "broadcom,height", &val))
+ fbheight = val;
+ if (!of_property_read_u32(np, "broadcom,depth", &val))
+ fbdepth = val;
+}
+
static int bcm2708_fb_probe(struct platform_device *dev)
{
struct bcm2708_fb *fb;
int ret;
+ bcm2708_fb_of_parse(dev);
+
fb = kmalloc(sizeof(struct bcm2708_fb), GFP_KERNEL);
if (!fb) {
dev_err(&dev->dev,
@@ -612,12 +631,19 @@ static int bcm2708_fb_remove(struct platform_device *dev)
return 0;
}
+static const struct of_device_id bcm2708_fb_of_match[] = {
+ { .compatible = "brcm,bcm2708-fb" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bcm2708_fb_of_match);
+
static struct platform_driver bcm2708_fb_driver = {
.probe = bcm2708_fb_probe,
.remove = bcm2708_fb_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
+ .of_match_table = bcm2708_fb_of_match,
},
};
#define DEBUG
/*
* Driver for Broadcom BCM2708 GPIO unit (pinctrl only)
*
* Copyright (C) 2012 Chris Boot, Simon Arlott, Stephen Warren
* Copyright (C) 2013 Noralf Tronnes
*
* This driver is copied from pinctrl-bcm2835.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/bitmap.h>
#include <linux/bug.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/types.h>
#define MODULE_NAME "pinctrl-bcm2708"
#define BCM2708_NUM_GPIOS 54
#define BCM2708_PIN_BITMAP_SZ \
DIV_ROUND_UP(BCM2708_NUM_GPIOS, sizeof(unsigned long) * 8)
/* GPIO register offsets */
#define GPFSEL0 0x0 /* Function Select */
#define GPSET0 0x1c /* Pin Output Set */
#define GPCLR0 0x28 /* Pin Output Clear */
#define GPLEV0 0x34 /* Pin Level */
#define GPEDS0 0x40 /* Pin Event Detect Status */
#define GPREN0 0x4c /* Pin Rising Edge Detect Enable */
#define GPFEN0 0x58 /* Pin Falling Edge Detect Enable */
#define GPHEN0 0x64 /* Pin High Detect Enable */
#define GPLEN0 0x70 /* Pin Low Detect Enable */
#define GPAREN0 0x7c /* Pin Async Rising Edge Detect */
#define GPAFEN0 0x88 /* Pin Async Falling Edge Detect */
#define GPPUD 0x94 /* Pin Pull-up/down Enable */
#define GPPUDCLK0 0x98 /* Pin Pull-up/down Enable Clock */
#define FSEL_REG(p) (GPFSEL0 + (((p) / 10) * 4))
#define FSEL_SHIFT(p) (((p) % 10) * 3)
#define GPIO_REG_OFFSET(p) ((p) / 32)
#define GPIO_REG_SHIFT(p) ((p) % 32)
enum bcm2708_pinconf_param {
/* argument: bcm2708_pinconf_pull */
BCM2708_PINCONF_PARAM_PULL,
};
enum bcm2708_pinconf_pull {
BCM2708_PINCONFIG_PULL_NONE,
BCM2708_PINCONFIG_PULL_DOWN,
BCM2708_PINCONFIG_PULL_UP,
};
#define BCM2708_PINCONF_PACK(_param_, _arg_) ((_param_) << 16 | (_arg_))
#define BCM2708_PINCONF_UNPACK_PARAM(_conf_) ((_conf_) >> 16)
#define BCM2708_PINCONF_UNPACK_ARG(_conf_) ((_conf_) & 0xffff)
struct bcm2708_gpio_irqdata {
struct bcm2708_pinctrl *pc;
int bank;
};
struct bcm2708_pinctrl {
struct device *dev;
void __iomem *base;
struct pinctrl_dev *pctl_dev;
struct gpio_chip *gpio_chip;
struct pinctrl_gpio_range gpio_range;
};
/* pins are just named GPIO0..GPIO53 */
#define BCM2708_GPIO_PIN(a) PINCTRL_PIN(a, "gpio" #a)
struct pinctrl_pin_desc bcm2708_gpio_pins[] = {
BCM2708_GPIO_PIN(0),
BCM2708_GPIO_PIN(1),
BCM2708_GPIO_PIN(2),
BCM2708_GPIO_PIN(3),
BCM2708_GPIO_PIN(4),
BCM2708_GPIO_PIN(5),
BCM2708_GPIO_PIN(6),
BCM2708_GPIO_PIN(7),
BCM2708_GPIO_PIN(8),
BCM2708_GPIO_PIN(9),
BCM2708_GPIO_PIN(10),
BCM2708_GPIO_PIN(11),
BCM2708_GPIO_PIN(12),
BCM2708_GPIO_PIN(13),
BCM2708_GPIO_PIN(14),
BCM2708_GPIO_PIN(15),
BCM2708_GPIO_PIN(16),
BCM2708_GPIO_PIN(17),
BCM2708_GPIO_PIN(18),
BCM2708_GPIO_PIN(19),
BCM2708_GPIO_PIN(20),
BCM2708_GPIO_PIN(21),
BCM2708_GPIO_PIN(22),
BCM2708_GPIO_PIN(23),
BCM2708_GPIO_PIN(24),
BCM2708_GPIO_PIN(25),
BCM2708_GPIO_PIN(26),
BCM2708_GPIO_PIN(27),
BCM2708_GPIO_PIN(28),
BCM2708_GPIO_PIN(29),
BCM2708_GPIO_PIN(30),
BCM2708_GPIO_PIN(31),
BCM2708_GPIO_PIN(32),
BCM2708_GPIO_PIN(33),
BCM2708_GPIO_PIN(34),
BCM2708_GPIO_PIN(35),
BCM2708_GPIO_PIN(36),
BCM2708_GPIO_PIN(37),
BCM2708_GPIO_PIN(38),
BCM2708_GPIO_PIN(39),
BCM2708_GPIO_PIN(40),
BCM2708_GPIO_PIN(41),
BCM2708_GPIO_PIN(42),
BCM2708_GPIO_PIN(43),
BCM2708_GPIO_PIN(44),
BCM2708_GPIO_PIN(45),
BCM2708_GPIO_PIN(46),
BCM2708_GPIO_PIN(47),
BCM2708_GPIO_PIN(48),
BCM2708_GPIO_PIN(49),
BCM2708_GPIO_PIN(50),
BCM2708_GPIO_PIN(51),
BCM2708_GPIO_PIN(52),
BCM2708_GPIO_PIN(53),
};
/* one pin per group */
static const char * const bcm2708_gpio_groups[] = {
"gpio0",
"gpio1",
"gpio2",
"gpio3",
"gpio4",
"gpio5",
"gpio6",
"gpio7",
"gpio8",
"gpio9",
"gpio10",
"gpio11",
"gpio12",
"gpio13",
"gpio14",
"gpio15",
"gpio16",
"gpio17",
"gpio18",
"gpio19",
"gpio20",
"gpio21",
"gpio22",
"gpio23",
"gpio24",
"gpio25",
"gpio26",
"gpio27",
"gpio28",
"gpio29",
"gpio30",
"gpio31",
"gpio32",
"gpio33",
"gpio34",
"gpio35",
"gpio36",
"gpio37",
"gpio38",
"gpio39",
"gpio40",
"gpio41",
"gpio42",
"gpio43",
"gpio44",
"gpio45",
"gpio46",
"gpio47",
"gpio48",
"gpio49",
"gpio50",
"gpio51",
"gpio52",
"gpio53",
};
enum bcm2708_fsel {
BCM2708_FSEL_GPIO_IN = 0,
BCM2708_FSEL_GPIO_OUT = 1,
BCM2708_FSEL_ALT0 = 4,
BCM2708_FSEL_ALT1 = 5,
BCM2708_FSEL_ALT2 = 6,
BCM2708_FSEL_ALT3 = 7,
BCM2708_FSEL_ALT4 = 3,
BCM2708_FSEL_ALT5 = 2,
BCM2708_FSEL_COUNT = 8,
BCM2708_FSEL_MASK = 0x7,
};
static const char * const bcm2708_functions[BCM2708_FSEL_COUNT] = {
[BCM2708_FSEL_GPIO_IN] = "gpio_in",
[BCM2708_FSEL_GPIO_OUT] = "gpio_out",
[BCM2708_FSEL_ALT0] = "alt0",
[BCM2708_FSEL_ALT1] = "alt1",
[BCM2708_FSEL_ALT2] = "alt2",
[BCM2708_FSEL_ALT3] = "alt3",
[BCM2708_FSEL_ALT4] = "alt4",
[BCM2708_FSEL_ALT5] = "alt5",
};
static inline u32 bcm2708_gpio_rd(struct bcm2708_pinctrl *pc, unsigned reg)
{
return readl(pc->base + reg);
}
static inline void bcm2708_gpio_wr(struct bcm2708_pinctrl *pc, unsigned reg,
u32 val)
{
writel(val, pc->base + reg);
}
static inline int bcm2708_gpio_get_bit(struct bcm2708_pinctrl *pc, unsigned reg,
unsigned bit)
{
reg += GPIO_REG_OFFSET(bit) * 4;
return (bcm2708_gpio_rd(pc, reg) >> GPIO_REG_SHIFT(bit)) & 1;
}
/* note NOT a read/modify/write cycle */
static inline void bcm2708_gpio_set_bit(struct bcm2708_pinctrl *pc,
unsigned reg, unsigned bit)
{
reg += GPIO_REG_OFFSET(bit) * 4;
bcm2708_gpio_wr(pc, reg, BIT(GPIO_REG_SHIFT(bit)));
}
static inline enum bcm2708_fsel bcm2708_pinctrl_fsel_get(
struct bcm2708_pinctrl *pc, unsigned pin)
{
u32 val = bcm2708_gpio_rd(pc, FSEL_REG(pin));
enum bcm2708_fsel status = (val >> FSEL_SHIFT(pin)) & BCM2708_FSEL_MASK;
dev_dbg(pc->dev, "get %08x (%u => %s)\n", val, pin,
bcm2708_functions[status]);
return status;
}
static inline void bcm2708_pinctrl_fsel_set(
struct bcm2708_pinctrl *pc, unsigned pin,
enum bcm2708_fsel fsel)
{
u32 val = bcm2708_gpio_rd(pc, FSEL_REG(pin));
enum bcm2708_fsel cur = (val >> FSEL_SHIFT(pin)) & BCM2708_FSEL_MASK;
dev_dbg(pc->dev, "read %08x (%u => %s)\n", val, pin,
bcm2708_functions[cur]);
if (cur == fsel)
return;
if (cur != BCM2708_FSEL_GPIO_IN && fsel != BCM2708_FSEL_GPIO_IN) {
/* always transition through GPIO_IN */
val &= ~(BCM2708_FSEL_MASK << FSEL_SHIFT(pin));
val |= BCM2708_FSEL_GPIO_IN << FSEL_SHIFT(pin);
dev_dbg(pc->dev, "trans %08x (%u <= %s)\n", val, pin,
bcm2708_functions[BCM2708_FSEL_GPIO_IN]);
bcm2708_gpio_wr(pc, FSEL_REG(pin), val);
}
val &= ~(BCM2708_FSEL_MASK << FSEL_SHIFT(pin));
val |= fsel << FSEL_SHIFT(pin);
dev_dbg(pc->dev, "write %08x (%u <= %s)\n", val, pin,
bcm2708_functions[fsel]);
bcm2708_gpio_wr(pc, FSEL_REG(pin), val);
}
static int bcm2708_pctl_get_groups_count(struct pinctrl_dev *pctldev)
{
return ARRAY_SIZE(bcm2708_gpio_groups);
}
static const char *bcm2708_pctl_get_group_name(struct pinctrl_dev *pctldev,
unsigned selector)
{
return bcm2708_gpio_groups[selector];
}
static int bcm2708_pctl_get_group_pins(struct pinctrl_dev *pctldev,
unsigned selector,
const unsigned **pins,
unsigned *num_pins)
{
*pins = &bcm2708_gpio_pins[selector].number;
*num_pins = 1;
return 0;
}
static void bcm2708_pctl_pin_dbg_show(struct pinctrl_dev *pctldev,
struct seq_file *s,
unsigned offset)
{
struct bcm2708_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
enum bcm2708_fsel fsel = bcm2708_pinctrl_fsel_get(pc, offset);
const char *fname = bcm2708_functions[fsel];
int value = bcm2708_gpio_get_bit(pc, GPLEV0, offset);
seq_printf(s, "function '%s', value '%s'", fname, value ? "hi" : "lo");
}
static void bcm2708_pctl_dt_free_map(struct pinctrl_dev *pctldev,
struct pinctrl_map *maps, unsigned num_maps)
{
int i;
for (i = 0; i < num_maps; i++)
if (maps[i].type == PIN_MAP_TYPE_CONFIGS_PIN)
kfree(maps[i].data.configs.configs);
kfree(maps);
}
static int bcm2708_pctl_dt_node_to_map_func(struct bcm2708_pinctrl *pc,
struct device_node *np, u32 pin, u32 fnum,
struct pinctrl_map **maps)
{
struct pinctrl_map *map = *maps;
if (fnum >= ARRAY_SIZE(bcm2708_functions)) {
dev_err(pc->dev, "%s: invalid brcm,function %d\n",
of_node_full_name(np), fnum);
return -EINVAL;
}
map->type = PIN_MAP_TYPE_MUX_GROUP;
map->data.mux.group = bcm2708_gpio_groups[pin];
map->data.mux.function = bcm2708_functions[fnum];
(*maps)++;
return 0;
}
static int bcm2708_pctl_dt_node_to_map_pull(struct bcm2708_pinctrl *pc,
struct device_node *np, u32 pin, u32 pull,
struct pinctrl_map **maps)
{
struct pinctrl_map *map = *maps;
unsigned long *configs;
if (pull > 2) {
dev_err(pc->dev, "%s: invalid brcm,pull %d\n",
of_node_full_name(np), pull);
return -EINVAL;
}
configs = kzalloc(sizeof(*configs), GFP_KERNEL);
if (!configs)
return -ENOMEM;
configs[0] = BCM2708_PINCONF_PACK(BCM2708_PINCONF_PARAM_PULL, pull);
map->type = PIN_MAP_TYPE_CONFIGS_PIN;
map->data.configs.group_or_pin = bcm2708_gpio_pins[pin].name;
map->data.configs.configs = configs;
map->data.configs.num_configs = 1;
(*maps)++;
return 0;
}
static int bcm2708_pctl_dt_node_to_map(struct pinctrl_dev *pctldev,
struct device_node *np,
struct pinctrl_map **map, unsigned *num_maps)
{
struct bcm2708_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
struct property *pins, *funcs, *pulls;
int num_pins, num_funcs, num_pulls, maps_per_pin;
struct pinctrl_map *maps, *cur_map;
int i, err;
u32 pin, func, pull;
pins = of_find_property(np, "brcm,pins", NULL);
if (!pins) {
dev_err(pc->dev, "%s: missing brcm,pins property\n",
of_node_full_name(np));
return -EINVAL;
}
funcs = of_find_property(np, "brcm,function", NULL);
pulls = of_find_property(np, "brcm,pull", NULL);
if (!funcs && !pulls) {
dev_err(pc->dev,
"%s: neither brcm,function nor brcm,pull specified\n",
of_node_full_name(np));
return -EINVAL;
}
num_pins = pins->length / 4;
num_funcs = funcs ? (funcs->length / 4) : 0;
num_pulls = pulls ? (pulls->length / 4) : 0;
if (num_funcs > 1 && num_funcs != num_pins) {
dev_err(pc->dev,
"%s: brcm,function must have 1 or %d entries\n",
of_node_full_name(np), num_pins);
return -EINVAL;
}
if (num_pulls > 1 && num_pulls != num_pins) {
dev_err(pc->dev,
"%s: brcm,pull must have 1 or %d entries\n",
of_node_full_name(np), num_pins);
return -EINVAL;
}
maps_per_pin = 0;
if (num_funcs)
maps_per_pin++;
if (num_pulls)
maps_per_pin++;
cur_map = maps = kzalloc(num_pins * maps_per_pin * sizeof(*maps),
GFP_KERNEL);
if (!maps)
return -ENOMEM;
for (i = 0; i < num_pins; i++) {
err = of_property_read_u32_index(np, "brcm,pins", i, &pin);
if (err)
goto out;
if (pin >= ARRAY_SIZE(bcm2708_gpio_pins)) {
dev_err(pc->dev, "%s: invalid brcm,pins value %d\n",
of_node_full_name(np), pin);
err = -EINVAL;
goto out;
}
if (num_funcs) {
err = of_property_read_u32_index(np, "brcm,function",
(num_funcs > 1) ? i : 0, &func);
if (err)
goto out;
err = bcm2708_pctl_dt_node_to_map_func(pc, np, pin,
func, &cur_map);
if (err)
goto out;
}
if (num_pulls) {
err = of_property_read_u32_index(np, "brcm,pull",
(num_funcs > 1) ? i : 0, &pull);
if (err)
goto out;
err = bcm2708_pctl_dt_node_to_map_pull(pc, np, pin,
pull, &cur_map);
if (err)
goto out;
}
}
*map = maps;
*num_maps = num_pins * maps_per_pin;
return 0;
out:
kfree(maps);
return err;
}
static const struct pinctrl_ops bcm2708_pctl_ops = {
.get_groups_count = bcm2708_pctl_get_groups_count,
.get_group_name = bcm2708_pctl_get_group_name,
.get_group_pins = bcm2708_pctl_get_group_pins,
.pin_dbg_show = bcm2708_pctl_pin_dbg_show,
.dt_node_to_map = bcm2708_pctl_dt_node_to_map,
.dt_free_map = bcm2708_pctl_dt_free_map,
};
static int bcm2708_pmx_get_functions_count(struct pinctrl_dev *pctldev)
{
return BCM2708_FSEL_COUNT;
}
static const char *bcm2708_pmx_get_function_name(struct pinctrl_dev *pctldev,
unsigned selector)
{
return bcm2708_functions[selector];
}
static int bcm2708_pmx_get_function_groups(struct pinctrl_dev *pctldev,
unsigned selector,
const char * const **groups,
unsigned * const num_groups)
{
/* every pin can do every function */
*groups = bcm2708_gpio_groups;
*num_groups = ARRAY_SIZE(bcm2708_gpio_groups);
return 0;
}
static int bcm2708_pmx_enable(struct pinctrl_dev *pctldev,
unsigned func_selector,
unsigned group_selector)
{
struct bcm2708_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
bcm2708_pinctrl_fsel_set(pc, group_selector, func_selector);
return 0;
}
static void bcm2708_pmx_disable(struct pinctrl_dev *pctldev,
unsigned func_selector,
unsigned group_selector)
{
struct bcm2708_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
/* disable by setting to GPIO_IN */
bcm2708_pinctrl_fsel_set(pc, group_selector, BCM2708_FSEL_GPIO_IN);
}
static void bcm2708_pmx_gpio_disable_free(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned offset)
{
struct bcm2708_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
/* disable by setting to GPIO_IN */
bcm2708_pinctrl_fsel_set(pc, offset, BCM2708_FSEL_GPIO_IN);
}
static int bcm2708_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned offset,
bool input)
{
struct bcm2708_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
enum bcm2708_fsel fsel = input ?
BCM2708_FSEL_GPIO_IN : BCM2708_FSEL_GPIO_OUT;
bcm2708_pinctrl_fsel_set(pc, offset, fsel);
return 0;
}
static const struct pinmux_ops bcm2708_pmx_ops = {
.get_functions_count = bcm2708_pmx_get_functions_count,
.get_function_name = bcm2708_pmx_get_function_name,
.get_function_groups = bcm2708_pmx_get_function_groups,
.enable = bcm2708_pmx_enable,
.disable = bcm2708_pmx_disable,
.gpio_disable_free = bcm2708_pmx_gpio_disable_free,
.gpio_set_direction = bcm2708_pmx_gpio_set_direction,
};
static int bcm2708_pinconf_get(struct pinctrl_dev *pctldev,
unsigned pin, unsigned long *config)
{
/* No way to read back config in HW */
return -ENOTSUPP;
}
static int bcm2708_pinconf_set(struct pinctrl_dev *pctldev,
unsigned pin, unsigned long config)
{
struct bcm2708_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
enum bcm2708_pinconf_param param = BCM2708_PINCONF_UNPACK_PARAM(config);
u16 arg = BCM2708_PINCONF_UNPACK_ARG(config);
u32 off, bit;
if (param != BCM2708_PINCONF_PARAM_PULL)
return -EINVAL;
off = GPIO_REG_OFFSET(pin);
bit = GPIO_REG_SHIFT(pin);
bcm2708_gpio_wr(pc, GPPUD, arg & 3);
/*
* Docs say to wait 150 cycles, but not of what. We assume a
* 1 MHz clock here, which is pretty slow...
*/
udelay(150);
bcm2708_gpio_wr(pc, GPPUDCLK0 + (off * 4), BIT(bit));
udelay(150);
bcm2708_gpio_wr(pc, GPPUDCLK0 + (off * 4), 0);
return 0;
}
static const struct pinconf_ops bcm2708_pinconf_ops = {
.pin_config_get = bcm2708_pinconf_get,
.pin_config_set = bcm2708_pinconf_set,
};
static struct pinctrl_desc bcm2708_pinctrl_desc = {
.name = MODULE_NAME,
.pins = bcm2708_gpio_pins,
.npins = ARRAY_SIZE(bcm2708_gpio_pins),
.pctlops = &bcm2708_pctl_ops,
.pmxops = &bcm2708_pmx_ops,
.confops = &bcm2708_pinconf_ops,
.owner = THIS_MODULE,
};
static struct pinctrl_gpio_range bcm2708_pinctrl_gpio_range = {
.name = MODULE_NAME,
.npins = BCM2708_NUM_GPIOS,
};
static int bcm2708_pinctrl_gpiochip_find(struct gpio_chip *gc, void *data)
{
/* there is only one gpio chip */
return 1;
}
static int bcm2708_pinctrl_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct bcm2708_pinctrl *pc;
struct gpio_chip *gc;
struct resource iomem;
int err;
BUILD_BUG_ON(ARRAY_SIZE(bcm2708_gpio_pins) != BCM2708_NUM_GPIOS);
BUILD_BUG_ON(ARRAY_SIZE(bcm2708_gpio_groups) != BCM2708_NUM_GPIOS);
gc = gpiochip_find(NULL, bcm2708_pinctrl_gpiochip_find);
if (!gc)
return -EPROBE_DEFER;
gc->of_node = np;
gc->of_gpio_n_cells = 2;
gc->of_xlate = of_gpio_simple_xlate;
pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
if (!pc)
return -ENOMEM;
platform_set_drvdata(pdev, pc);
pc->dev = dev;
err = of_address_to_resource(np, 0, &iomem);
if (err) {
dev_err(dev, "could not get IO memory\n");
return err;
}
pc->base = devm_ioremap_resource(dev, &iomem);
if (IS_ERR(pc->base))
return PTR_ERR(pc->base);
pc->gpio_chip = gc;
pc->pctl_dev = pinctrl_register(&bcm2708_pinctrl_desc, dev, pc);
if (!pc->pctl_dev)
return -EINVAL;
pc->gpio_range = bcm2708_pinctrl_gpio_range;
pc->gpio_range.base = pc->gpio_chip->base;
pc->gpio_range.gc = pc->gpio_chip;
pinctrl_add_gpio_range(pc->pctl_dev, &pc->gpio_range);
return 0;
}
static int bcm2708_pinctrl_remove(struct platform_device *pdev)
{
struct bcm2708_pinctrl *pc = platform_get_drvdata(pdev);
pinctrl_unregister(pc->pctl_dev);
return 0;
}
static struct of_device_id bcm2708_pinctrl_match[] = {
{ .compatible = "brcm,bcm2708-pinctrl" },
{}
};
MODULE_DEVICE_TABLE(of, bcm2708_pinctrl_match);
static struct platform_driver bcm2708_pinctrl_driver = {
.probe = bcm2708_pinctrl_probe,
.remove = bcm2708_pinctrl_remove,
.driver = {
.name = MODULE_NAME,
.owner = THIS_MODULE,
.of_match_table = bcm2708_pinctrl_match,
},
};
module_platform_driver(bcm2708_pinctrl_driver);
MODULE_AUTHOR("Chris Boot, Simon Arlott, Stephen Warren, Noralf Tronnes");
MODULE_DESCRIPTION("BCM2708 Pin control driver");
MODULE_LICENSE("GPL");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment