diff --git a/Documentation/devicetree/bindings/arm/bcm2835.txt b/Documentation/devicetree/bindings/arm/bcm2835.txt index ac683480c48676..c78576bb772935 100644 --- a/Documentation/devicetree/bindings/arm/bcm2835.txt +++ b/Documentation/devicetree/bindings/arm/bcm2835.txt @@ -1,8 +1,35 @@ Broadcom BCM2835 device tree bindings ------------------------------------------- -Boards with the BCM2835 SoC shall have the following properties: +Raspberry Pi Model A +Required root node properties: +compatible = "raspberrypi,model-a", "brcm,bcm2835"; -Required root node property: +Raspberry Pi Model A+ +Required root node properties: +compatible = "raspberrypi,model-a-plus", "brcm,bcm2835"; +Raspberry Pi Model B +Required root node properties: +compatible = "raspberrypi,model-b", "brcm,bcm2835"; + +Raspberry Pi Model B (no P5) +early model B with I2C0 rather than I2C1 routed to the expansion header +Required root node properties: +compatible = "raspberrypi,model-b-i2c0", "brcm,bcm2835"; + +Raspberry Pi Model B rev2 +Required root node properties: +compatible = "raspberrypi,model-b-rev2", "brcm,bcm2835"; + +Raspberry Pi Model B+ +Required root node properties: +compatible = "raspberrypi,model-b-plus", "brcm,bcm2835"; + +Raspberry Pi Compute Module +Required root node properties: +compatible = "raspberrypi,compute-module", "brcm,bcm2835"; + +Generic BCM2835 board +Required root node properties: compatible = "brcm,bcm2835"; diff --git a/Documentation/devicetree/bindings/arm/bcm2836.txt b/Documentation/devicetree/bindings/arm/bcm2836.txt new file mode 100644 index 00000000000000..5e255ec52c5154 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/bcm2836.txt @@ -0,0 +1,10 @@ +Broadcom BCM2836 device tree bindings +------------------------------------------- + +Raspberry Pi 2 Model B +Required root node properties: +compatible = "raspberrypi,2-model-b", "brcm,bcm2836"; + +Generic BCM2836 board +Required root node properties: +compatible = "brcm,bcm2836"; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 389ca1347a771c..d3f4809eae95cb 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -147,6 +147,7 @@ radxa Radxa raidsonic RaidSonic Technology GmbH ralink Mediatek/Ralink Technology Corp. ramtron Ramtron International +raspberrypi Raspberry Pi Foundation realtek Realtek Semiconductor Corp. renesas Renesas Electronics Corporation ricoh Ricoh Co. Ltd. diff --git a/MAINTAINERS b/MAINTAINERS index ddc5a8cf9a8ac0..1e3f1f171bb538 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2128,6 +2128,13 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/rpi/linux-rpi.git S: Maintained N: bcm2835 +BROADCOM BCM2836 ARM ARCHITECTURE +M: Eric Anholt +L: linux-rpi-kernel@lists.infradead.org (moderated for non-subscribers) +T: git git://git.kernel.org/pub/scm/linux/kernel/git/rpi/linux-rpi.git +S: Maintained +N: bcm2836 + BROADCOM BCM33XX MIPS ARCHITECTURE M: Kevin Cernekee L: linux-mips@linux-mips.org diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug index 970de7518341d2..13870579f2e3e4 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -134,7 +134,12 @@ choice config DEBUG_BCM2835 bool "Kernel low-level debugging on BCM2835 PL011 UART" - depends on ARCH_BCM2835 + depends on ARCH_BCM2835 && ARCH_MULTI_V6 + select DEBUG_UART_PL01X + + config DEBUG_BCM2836 + bool "Kernel low-level debugging on BCM2836 PL011 UART" + depends on ARCH_BCM2835 && ARCH_MULTI_V7 select DEBUG_UART_PL01X config DEBUG_BCM_5301X @@ -1369,6 +1374,7 @@ config DEBUG_UART_PHYS default 0x20064000 if DEBUG_RK29_UART1 || DEBUG_RK3X_UART2 default 0x20068000 if DEBUG_RK29_UART2 || DEBUG_RK3X_UART3 default 0x20201000 if DEBUG_BCM2835 + default 0x3f201000 if DEBUG_BCM2836 default 0x3e000000 if DEBUG_BCM_KONA_UART default 0x4000e400 if DEBUG_LL_UART_EFM32 default 0x40090000 if ARCH_LPC32XX @@ -1450,7 +1456,7 @@ config DEBUG_UART_VIRT default 0xf0000be0 if ARCH_EBSA110 default 0xf0010000 if DEBUG_ASM9260_UART default 0xf01fb000 if DEBUG_NOMADIK_UART - default 0xf0201000 if DEBUG_BCM2835 + default 0xf0201000 if DEBUG_BCM2835 || DEBUG_BCM2836 default 0xf1000300 if DEBUG_BCM_5301X default 0xf1002000 if DEBUG_MT8127_UART0 default 0xf1006000 if DEBUG_MT6589_UART0 diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index a1c776b8dcec51..f4eda8fb19f470 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -51,7 +51,8 @@ dtb-$(CONFIG_ARCH_AXXIA) += \ axm5516-amarillo.dtb dtb-$(CONFIG_ARCH_BCM2835) += \ bcm2835-rpi-b.dtb \ - bcm2835-rpi-b-plus.dtb + bcm2835-rpi-b-plus.dtb \ + bcm2836-rpi-2-b.dtb dtb-$(CONFIG_ARCH_BCM_5301X) += \ bcm4708-buffalo-wzr-1750dhp.dtb \ bcm4708-luxul-xwc-1000.dtb \ diff --git a/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts b/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts index e479515099c335..668442b1bda581 100644 --- a/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts +++ b/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts @@ -1,5 +1,5 @@ /dts-v1/; -/include/ "bcm2835-rpi.dtsi" +#include "bcm2835-rpi.dtsi" / { compatible = "raspberrypi,model-b-plus", "brcm,bcm2835"; @@ -25,6 +25,6 @@ /* I2S interface */ i2s_alt0: i2s_alt0 { brcm,pins = <18 19 20 21>; - brcm,function = <4>; /* alt0 */ + brcm,function = ; }; }; diff --git a/arch/arm/boot/dts/bcm2835-rpi-b.dts b/arch/arm/boot/dts/bcm2835-rpi-b.dts index bafa46fc226a60..ee89b79426cf4d 100644 --- a/arch/arm/boot/dts/bcm2835-rpi-b.dts +++ b/arch/arm/boot/dts/bcm2835-rpi-b.dts @@ -1,5 +1,5 @@ /dts-v1/; -/include/ "bcm2835-rpi.dtsi" +#include "bcm2835-rpi.dtsi" / { compatible = "raspberrypi,model-b", "brcm,bcm2835"; @@ -18,6 +18,6 @@ /* I2S interface */ i2s_alt2: i2s_alt2 { brcm,pins = <28 29 30 31>; - brcm,function = <6>; /* alt2 */ + brcm,function = ; }; }; diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi index c7064487017d7e..46780bb48bbf9c 100644 --- a/arch/arm/boot/dts/bcm2835-rpi.dtsi +++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi @@ -1,4 +1,4 @@ -/include/ "bcm2835.dtsi" +#include "bcm2835.dtsi" / { memory { @@ -21,17 +21,17 @@ gpioout: gpioout { brcm,pins = <6>; - brcm,function = <1>; /* GPIO out */ + brcm,function = ; }; alt0: alt0 { brcm,pins = <0 1 2 3 4 5 7 8 9 10 11 14 15 40 45>; - brcm,function = <4>; /* alt0 */ + brcm,function = ; }; alt3: alt3 { brcm,pins = <48 49 50 51 52 53>; - brcm,function = <7>; /* alt3 */ + brcm,function = ; }; }; diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi index 3342cb1407bc92..bdf99934451adb 100644 --- a/arch/arm/boot/dts/bcm2835.dtsi +++ b/arch/arm/boot/dts/bcm2835.dtsi @@ -1,4 +1,6 @@ -/include/ "skeleton.dtsi" +#include +#include "skeleton.dtsi" +#include "bcm283x-common.dtsi" / { compatible = "brcm,bcm2835"; @@ -22,162 +24,8 @@ clock-frequency = <1000000>; }; - dma: dma@7e007000 { - compatible = "brcm,bcm2835-dma"; - reg = <0x7e007000 0xf00>; - interrupts = <1 16>, - <1 17>, - <1 18>, - <1 19>, - <1 20>, - <1 21>, - <1 22>, - <1 23>, - <1 24>, - <1 25>, - <1 26>, - <1 27>, - <1 28>; - - #dma-cells = <1>; - brcm,dma-channel-mask = <0x7f35>; - }; - - intc: interrupt-controller@7e00b200 { - compatible = "brcm,bcm2835-armctrl-ic"; - reg = <0x7e00b200 0x200>; - interrupt-controller; - #interrupt-cells = <2>; - }; - - watchdog@7e100000 { - compatible = "brcm,bcm2835-pm-wdt"; - reg = <0x7e100000 0x28>; - }; - - rng@7e104000 { - compatible = "brcm,bcm2835-rng"; - reg = <0x7e104000 0x10>; - }; - - gpio: gpio@7e200000 { - compatible = "brcm,bcm2835-gpio"; - reg = <0x7e200000 0xb4>; - /* - * The GPIO IP block is designed for 3 banks of GPIOs. - * Each bank has a GPIO interrupt for itself. - * There is an overall "any bank" interrupt. - * In order, these are GIC interrupts 17, 18, 19, 20. - * Since the BCM2835 only has 2 banks, the 2nd bank - * interrupt output appears to be mirrored onto the - * 3rd bank's interrupt signal. - * So, a bank0 interrupt shows up on 17, 20, and - * a bank1 interrupt shows up on 18, 19, 20! - */ - interrupts = <2 17>, <2 18>, <2 19>, <2 20>; - - gpio-controller; - #gpio-cells = <2>; - - interrupt-controller; - #interrupt-cells = <2>; - }; - - uart@7e201000 { - compatible = "brcm,bcm2835-pl011", "arm,pl011", "arm,primecell"; - reg = <0x7e201000 0x1000>; - interrupts = <2 25>; - clock-frequency = <3000000>; - arm,primecell-periphid = <0x00241011>; - }; - - i2s: i2s@7e203000 { - compatible = "brcm,bcm2835-i2s"; - reg = <0x7e203000 0x20>, - <0x7e101098 0x02>; - - dmas = <&dma 2>, - <&dma 3>; - dma-names = "tx", "rx"; - status = "disabled"; - }; - - spi: spi@7e204000 { - compatible = "brcm,bcm2835-spi"; - reg = <0x7e204000 0x1000>; - interrupts = <2 22>; - clocks = <&clk_spi>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - i2c0: i2c@20205000 { - compatible = "brcm,bcm2835-i2c"; - reg = <0x7e205000 0x1000>; - interrupts = <2 21>; - clocks = <&clk_i2c>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - sdhci: sdhci@7e300000 { - compatible = "brcm,bcm2835-sdhci"; - reg = <0x7e300000 0x100>; - interrupts = <2 30>; - clocks = <&clk_mmc>; - status = "disabled"; - }; - - i2c1: i2c@7e804000 { - compatible = "brcm,bcm2835-i2c"; - reg = <0x7e804000 0x1000>; - interrupts = <2 21>; - clocks = <&clk_i2c>; - #address-cells = <1>; - #size-cells = <0>; - status = "disabled"; - }; - - usb@7e980000 { - compatible = "brcm,bcm2835-usb"; - reg = <0x7e980000 0x10000>; - interrupts = <1 9>; - }; - arm-pmu { compatible = "arm,arm1176-pmu"; }; }; - - clocks { - compatible = "simple-bus"; - #address-cells = <1>; - #size-cells = <0>; - - clk_mmc: clock@0 { - compatible = "fixed-clock"; - reg = <0>; - #clock-cells = <0>; - clock-output-names = "mmc"; - clock-frequency = <100000000>; - }; - - clk_i2c: clock@1 { - compatible = "fixed-clock"; - reg = <1>; - #clock-cells = <0>; - clock-output-names = "i2c"; - clock-frequency = <250000000>; - }; - - clk_spi: clock@2 { - compatible = "fixed-clock"; - reg = <2>; - #clock-cells = <0>; - clock-output-names = "spi"; - clock-frequency = <250000000>; - }; - }; }; diff --git a/arch/arm/boot/dts/bcm2836-rpi-2-b.dts b/arch/arm/boot/dts/bcm2836-rpi-2-b.dts new file mode 100644 index 00000000000000..006d9c9c8c7914 --- /dev/null +++ b/arch/arm/boot/dts/bcm2836-rpi-2-b.dts @@ -0,0 +1,30 @@ +/dts-v1/; +#include "bcm2836-rpi.dtsi" + +/ { + compatible = "raspberrypi,2-model-b", "brcm,bcm2836"; + model = "Raspberry Pi 2 Model B"; + + leds { + act { + gpios = <&gpio 47 0>; + }; + + pwr { + label = "PWR"; + gpios = <&gpio 35 0>; + default-state = "keep"; + linux,default-trigger = "default-on"; + }; + }; +}; + +&gpio { + pinctrl-0 = <&gpioout &alt0 &i2s_alt0 &alt3>; + + /* I2S interface */ + i2s_alt0: i2s_alt0 { + brcm,pins = <18 19 20 21>; + brcm,function = ; + }; +}; diff --git a/arch/arm/boot/dts/bcm2836-rpi.dtsi b/arch/arm/boot/dts/bcm2836-rpi.dtsi new file mode 100644 index 00000000000000..b8669119023d86 --- /dev/null +++ b/arch/arm/boot/dts/bcm2836-rpi.dtsi @@ -0,0 +1,51 @@ +#include "bcm2836.dtsi" + +/ { + memory { + reg = <0 0x10000000>; + }; + + leds { + compatible = "gpio-leds"; + + act { + label = "ACT"; + default-state = "keep"; + linux,default-trigger = "heartbeat"; + }; + }; +}; + +&gpio { + pinctrl-names = "default"; + + gpioout: gpioout { + brcm,pins = <6>; + brcm,function = ; + }; + + alt0: alt0 { + brcm,pins = <0 1 2 3 4 5 7 8 9 10 11 14 15 40 45>; + brcm,function = ; + }; + + alt3: alt3 { + brcm,pins = <48 49 50 51 52 53>; + brcm,function = ; + }; +}; + +&i2c0 { + status = "okay"; + clock-frequency = <100000>; +}; + +&i2c1 { + status = "okay"; + clock-frequency = <100000>; +}; + +&sdhci { + status = "okay"; + bus-width = <4>; +}; diff --git a/arch/arm/boot/dts/bcm2836.dtsi b/arch/arm/boot/dts/bcm2836.dtsi new file mode 100644 index 00000000000000..748386986b726a --- /dev/null +++ b/arch/arm/boot/dts/bcm2836.dtsi @@ -0,0 +1,49 @@ +#include +#include "skeleton.dtsi" +#include "bcm283x-common.dtsi" + +/ { + compatible = "brcm,bcm2836"; + model = "BCM2836"; + interrupt-parent = <&intc>; + + chosen { + bootargs = "earlyprintk console=ttyAMA0"; + }; + + soc { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x7e000000 0x3f000000 0x01000000>, + <0x40000000 0x40000000 0x00001000>; + + timer@7e003000 { + compatible = "brcm,bcm2835-system-timer"; + reg = <0x7e003000 0x1000>; + interrupts = <1 0>, <1 1>, <1 2>, <1 3>; + clock-frequency = <1000000>; + }; + }; + + cpus: cpus { + #address-cells = <1>; + #size-cells = <0>; + + v7_cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0xf00>; + clock-frequency = <800000000>; + }; + }; + + __overrides__ { + arm_freq = <&v7_cpu0>, "clock-frequency:0"; + }; +}; + +&intc { + compatible = "brcm,bcm2836-armctrl-ic"; + reg = <0x7e00b200 0x200>, <0x40000000 0x100>; +}; diff --git a/arch/arm/boot/dts/bcm283x-common.dtsi b/arch/arm/boot/dts/bcm283x-common.dtsi new file mode 100644 index 00000000000000..377bd2528f3f0c --- /dev/null +++ b/arch/arm/boot/dts/bcm283x-common.dtsi @@ -0,0 +1,189 @@ +#include +/include/ "skeleton.dtsi" + +/ { + soc { + dma: dma@7e007000 { + compatible = "brcm,bcm2835-dma"; + reg = <0x7e007000 0xf00>; + interrupts = <1 16>, + <1 17>, + <1 18>, + <1 19>, + <1 20>, + <1 21>, + <1 22>, + <1 23>, + <1 24>, + <1 25>, + <1 26>, + <1 27>, + <1 28>; + + #dma-cells = <1>; + brcm,dma-channel-mask = <0x7f35>; + }; + + intc: interrupt-controller@7e00b200 { + compatible = "brcm,bcm2835-armctrl-ic"; + reg = <0x7e00b200 0x200>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + watchdog@7e100000 { + compatible = "brcm,bcm2835-pm-wdt"; + reg = <0x7e100000 0x28>; + }; + + rng@7e104000 { + compatible = "brcm,bcm2835-rng"; + reg = <0x7e104000 0x10>; + }; + + gpio: gpio@7e200000 { + compatible = "brcm,bcm2835-gpio"; + reg = <0x7e200000 0xb4>; + /* + * The GPIO IP block is designed for 3 banks of GPIOs. + * Each bank has a GPIO interrupt for itself. + * There is an overall "any bank" interrupt. + * In order, these are GIC interrupts 17, 18, 19, 20. + * Since the BCM2835 only has 2 banks, the 2nd bank + * interrupt output appears to be mirrored onto the + * 3rd bank's interrupt signal. + * So, a bank0 interrupt shows up on 17, 20, and + * a bank1 interrupt shows up on 18, 19, 20! + */ + interrupts = <2 17>, <2 18>, <2 19>, <2 20>; + + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + mailbox: mailbox@7e00b800 { + compatible = "brcm,bcm2835-mbox"; + reg = <0x7e00b880 0x40>; + interrupts = <0 1>; + #mbox-cells = <1>; + }; + + pm_domains: pm_domains0 { + compatible = "brcm,bcm2835-mbox-power"; + mboxes = <&mailbox 0>; + #power-domain-cells = <1>; + }; + + uart@7e201000 { + compatible = "brcm,bcm2835-pl011", "arm,pl011", "arm,primecell"; + reg = <0x7e201000 0x1000>; + interrupts = <2 25>; + arm,primecell-periphid = <0x00241011>; + clocks = <&uart0_clk>, <&apb_pclk>; + clock-names = "uartclk", "apb_pclk"; + }; + + i2s: i2s@7e203000 { + compatible = "brcm,bcm2835-i2s"; + reg = <0x7e203000 0x20>, + <0x7e101098 0x02>; + + dmas = <&dma 2>, + <&dma 3>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + + spi: spi@7e204000 { + compatible = "brcm,bcm2835-spi"; + reg = <0x7e204000 0x1000>; + interrupts = <2 22>; + clocks = <&clk_spi>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c0: i2c@7e205000 { + compatible = "brcm,bcm2835-i2c"; + reg = <0x7e205000 0x1000>; + interrupts = <2 21>; + clocks = <&clk_i2c>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + sdhci: sdhci@7e300000 { + compatible = "brcm,bcm2835-sdhci"; + reg = <0x7e300000 0x100>; + interrupts = <2 30>; + clocks = <&clk_mmc>; + status = "disabled"; + }; + + i2c1: i2c@7e804000 { + compatible = "brcm,bcm2835-i2c"; + reg = <0x7e804000 0x1000>; + interrupts = <2 21>; + clocks = <&clk_i2c>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + usb@7e980000 { + compatible = "brcm,bcm2835-usb"; + reg = <0x7e980000 0x10000>; + interrupts = <1 9>; + power-domains = <&pm_domains POWER_DOMAIN_USB>; + }; + }; + + clocks { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + clk_mmc: clock@0 { + compatible = "fixed-clock"; + reg = <0>; + #clock-cells = <0>; + clock-output-names = "mmc"; + clock-frequency = <100000000>; + }; + + clk_i2c: clock@1 { + compatible = "fixed-clock"; + reg = <1>; + #clock-cells = <0>; + clock-output-names = "i2c"; + clock-frequency = <250000000>; + }; + + clk_spi: clock@2 { + compatible = "fixed-clock"; + reg = <2>; + #clock-cells = <0>; + clock-output-names = "spi"; + clock-frequency = <250000000>; + }; + + apb_pclk: apb_pclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-output-names = "apb_pclk"; + clock-frequency = <126000000>; + }; + + uart0_clk: uart0_clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-output-names = "uart0_clk"; + clock-frequency = <3000000>; + }; + }; +}; diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig index 8b11f44bb36e5a..0fa89cfbc0e7f6 100644 --- a/arch/arm/mach-bcm/Kconfig +++ b/arch/arm/mach-bcm/Kconfig @@ -108,17 +108,19 @@ config ARCH_BCM_MOBILE_SMP comment "Other Architectures" config ARCH_BCM2835 - bool "Broadcom BCM2835 family" if ARCH_MULTI_V6 + bool "Broadcom BCM2835 family" if ARCH_MULTI_V6 || ARCH_MULTI_V7 select ARCH_REQUIRE_GPIOLIB select ARM_AMBA - select ARM_ERRATA_411920 + select ARM_ERRATA_411920 if ARCH_MULTI_V6 select ARM_TIMER_SP804 select CLKSRC_OF select PINCTRL select PINCTRL_BCM2835 + select PM_GENERIC_DOMAINS if PM + select PM_GENERIC_DOMAINS_OF if PM help - This enables support for the Broadcom BCM2835 SoC. This SoC is - used in the Raspberry Pi and Roku 2 devices. + This enables support for the Broadcom BCM2835 and BCM2836 SoCs. + This SoC is used in the Raspberry Pi and Roku 2 devices. config ARCH_BCM_63XX bool "Broadcom BCM63xx DSL SoC" if ARCH_MULTI_V7 diff --git a/arch/arm/mach-bcm/Makefile b/arch/arm/mach-bcm/Makefile index 4c38674c73ecb1..05cb2f0976c7b9 100644 --- a/arch/arm/mach-bcm/Makefile +++ b/arch/arm/mach-bcm/Makefile @@ -33,6 +33,7 @@ endif # BCM2835 obj-$(CONFIG_ARCH_BCM2835) += board_bcm2835.o +obj-$(CONFIG_BCM2835_MBOX) += bcm2835-mailbox-power.o # BCM5301X obj-$(CONFIG_ARCH_BCM_5301X) += bcm_5301x.o diff --git a/arch/arm/mach-bcm/bcm2835-mailbox-power.c b/arch/arm/mach-bcm/bcm2835-mailbox-power.c new file mode 100644 index 00000000000000..97a97b217112b4 --- /dev/null +++ b/arch/arm/mach-bcm/bcm2835-mailbox-power.c @@ -0,0 +1,195 @@ +/* + * Copyright © 2015 Broadcom + * + * 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. + */ + +/* + * Defines a power domain module for the power control available from + * the BCM2835 firmware. + */ + +#include +#include +#include +#include +#include +#include + +struct bcm_mbox_power { + struct genpd_onecell_data genpd_xlate; + struct mbox_client cl; + struct mbox_chan *chan; + struct completion c; + u32 enabled; +}; + +static DEFINE_MUTEX(bcm28356_mbox_power_lock); +static struct bcm_mbox_power *mbox_power; + +struct bcm_mbox_power_domain { + u32 bit; + struct generic_pm_domain base; +}; + +static void response_callback(struct mbox_client *cl, void *msg) +{ + complete(&mbox_power->c); +} + +/* + * Sends a message to the power channel to set which power domains are + * enabled. + * + * Note that this interface is also available through the property + * channel, but this is simpler to use. + */ +static int bcm_mbox_set_power(uint32_t power_enables) +{ + int ret; + + reinit_completion(&mbox_power->c); + ret = mbox_send_message(mbox_power->chan, &power_enables); + if (ret >= 0) { + wait_for_completion(&mbox_power->c); + ret = 0; + } + + return ret; +} + + +static int bcm2835_mbox_power_on(struct generic_pm_domain *domain) +{ + struct bcm_mbox_power_domain *bcm_domain = + container_of(domain, struct bcm_mbox_power_domain, base); + int ret; + + mutex_lock(&bcm28356_mbox_power_lock); + mbox_power->enabled |= bcm_domain->bit; + ret = bcm_mbox_set_power(mbox_power->enabled); + mutex_unlock(&bcm28356_mbox_power_lock); + + return ret; +} + +static int bcm2835_mbox_power_off(struct generic_pm_domain *domain) +{ + struct bcm_mbox_power_domain *bcm_domain = + container_of(domain, struct bcm_mbox_power_domain, base); + int ret; + + mutex_lock(&bcm28356_mbox_power_lock); + mbox_power->enabled &= ~bcm_domain->bit; + ret = bcm_mbox_set_power(mbox_power->enabled); + mutex_unlock(&bcm28356_mbox_power_lock); + + return ret; +} + +struct bcm_mbox_power_domain bcm2835_mbox_power_domain_sdcard = { + .bit = (1 << 4), + .base = { + .name = "SDCARD", + .power_off = bcm2835_mbox_power_off, + .power_on = bcm2835_mbox_power_on, + } +}; + +struct bcm_mbox_power_domain bcm2835_mbox_power_domain_usb = { + .bit = (1 << 7), + .base = { + .name = "USB", + .power_off = bcm2835_mbox_power_off, + .power_on = bcm2835_mbox_power_on, + } +}; + +struct bcm_mbox_power_domain bcm2835_mbox_power_domain_dsi = { + .bit = (1 << 13), + .base = { + .name = "DSI", + .power_off = bcm2835_mbox_power_off, + .power_on = bcm2835_mbox_power_on, + } +}; + +static struct generic_pm_domain *bcm2835_mbox_power_domains[] = { + [POWER_DOMAIN_SDCARD] = &bcm2835_mbox_power_domain_sdcard.base, + [POWER_DOMAIN_USB] = &bcm2835_mbox_power_domain_usb.base, + [POWER_DOMAIN_DSI] = &bcm2835_mbox_power_domain_dsi.base, +}; + +static int bcm2835_mbox_power_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int ret = 0; + int i; + + mbox_power = devm_kzalloc(dev, sizeof(*mbox_power), GFP_KERNEL); + if (!mbox_power) + return -ENOMEM; + + mbox_power->cl.dev = dev; + mbox_power->cl.rx_callback = response_callback; + mbox_power->cl.tx_block = true; + + mbox_power->chan = mbox_request_channel(&mbox_power->cl, 0); + if (IS_ERR(mbox_power->chan)) { + ret = PTR_ERR(mbox_power->chan); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get mbox channel: %d\n", ret); + goto fail; + } + + init_completion(&mbox_power->c); + + platform_set_drvdata(pdev, mbox_power); + + mbox_power->genpd_xlate.domains = + bcm2835_mbox_power_domains; + mbox_power->genpd_xlate.num_domains = + ARRAY_SIZE(bcm2835_mbox_power_domains); + + for (i = 0; i < ARRAY_SIZE(bcm2835_mbox_power_domains); i++) + pm_genpd_init(bcm2835_mbox_power_domains[i], NULL, true); + + of_genpd_add_provider_onecell(dev->of_node, &mbox_power->genpd_xlate); + + return ret; +fail: + mbox_power = NULL; + return ret; +} + +static int bcm2835_mbox_power_remove(struct platform_device *pdev) +{ + bcm_mbox_set_power(0); + + mbox_free_channel(mbox_power->chan); + + return 0; +} + +static const struct of_device_id bcm2835_mbox_power_of_match[] = { + { .compatible = "brcm,bcm2835-mbox-power", }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm2835_mbox_power_of_match); + +static struct platform_driver bcm2835_mbox_power_driver = { + .driver = { + .name = "bcm2835-mbox-power", + .owner = THIS_MODULE, + .of_match_table = bcm2835_mbox_power_of_match, + }, + .probe = bcm2835_mbox_power_probe, + .remove = bcm2835_mbox_power_remove, +}; +module_platform_driver(bcm2835_mbox_power_driver); + +MODULE_AUTHOR("Eric Anholt "); +MODULE_DESCRIPTION("BCM2835 mailbox power channel"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-bcm/board_bcm2835.c b/arch/arm/mach-bcm/board_bcm2835.c index 70f2f3925f0e8e..69f582152c4265 100644 --- a/arch/arm/mach-bcm/board_bcm2835.c +++ b/arch/arm/mach-bcm/board_bcm2835.c @@ -32,6 +32,7 @@ #define PM_RSTS_HADWRH_SET 0x00000040 #define BCM2835_PERIPH_PHYS 0x20000000 +#define BCM2836_PERIPH_PHYS 0x3f000000 #define BCM2835_PERIPH_VIRT 0xf0000000 #define BCM2835_PERIPH_SIZE SZ_16M @@ -93,16 +94,28 @@ static void bcm2835_power_off(void) bcm2835_restart(REBOOT_HARD, ""); } -static struct map_desc io_map __initdata = { +static struct map_desc bcm2835_io_map __initdata = { .virtual = BCM2835_PERIPH_VIRT, .pfn = __phys_to_pfn(BCM2835_PERIPH_PHYS), .length = BCM2835_PERIPH_SIZE, .type = MT_DEVICE }; +static struct map_desc bcm2836_io_map __initdata = { + .virtual = BCM2835_PERIPH_VIRT, + .pfn = __phys_to_pfn(BCM2836_PERIPH_PHYS), + .length = BCM2835_PERIPH_SIZE, + .type = MT_DEVICE +}; + static void __init bcm2835_map_io(void) { - iotable_init(&io_map, 1); + iotable_init(&bcm2835_io_map, 1); +} + +static void __init bcm2836_map_io(void) +{ + iotable_init(&bcm2836_io_map, 1); } static void __init bcm2835_init(void) @@ -128,6 +141,11 @@ static const char * const bcm2835_compat[] = { NULL }; +static const char * const bcm2836_compat[] = { + "brcm,bcm2836", + NULL +}; + DT_MACHINE_START(BCM2835, "BCM2835") .map_io = bcm2835_map_io, .init_irq = irqchip_init, @@ -135,3 +153,11 @@ DT_MACHINE_START(BCM2835, "BCM2835") .restart = bcm2835_restart, .dt_compat = bcm2835_compat MACHINE_END + +DT_MACHINE_START(BCM2836, "BCM2836") + .map_io = bcm2836_map_io, + .init_irq = irqchip_init, + .init_machine = bcm2835_init, + .restart = bcm2835_restart, + .dt_compat = bcm2836_compat +MACHINE_END diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index ba4abbe4693c3e..2b93c989d3b913 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -2064,7 +2064,7 @@ EXPORT_SYMBOL_GPL(of_genpd_del_provider); struct generic_pm_domain *of_genpd_get_from_provider( struct of_phandle_args *genpdspec) { - struct generic_pm_domain *genpd = ERR_PTR(-ENOENT); + struct generic_pm_domain *genpd = ERR_PTR(-EPROBE_DEFER); struct of_genpd_provider *provider; mutex_lock(&of_genpd_mutex); diff --git a/drivers/irqchip/irq-bcm2835.c b/drivers/irqchip/irq-bcm2835.c index 5916d6cdafa1c9..18f5c27c762786 100644 --- a/drivers/irqchip/irq-bcm2835.c +++ b/drivers/irqchip/irq-bcm2835.c @@ -56,7 +56,7 @@ #include "irqchip.h" /* Put the bank and irq (32 bits) into the hwirq */ -#define MAKE_HWIRQ(b, n) ((b << 5) | (n)) +#define MAKE_HWIRQ(b, n) (((b) << 5) | (n)) #define HWIRQ_BANK(i) (i >> 5) #define HWIRQ_BIT(i) BIT(i & 0x1f) @@ -76,6 +76,26 @@ #define NR_BANKS 3 #define IRQS_PER_BANK 32 +#define LOCAL_PM_ROUTING_SET 0x010 +#define LOCAL_PM_ROUTING_CLR 0x014 +#define LOCAL_TIMER_INT_CONTROL0 0x040 +#define LOCAL_MAILBOX_INT_CONTROL0 0x050 +#define LOCAL_IRQ_PENDING0 0x060 +#define LOCAL_MAILBOX0_SET0 0x080 +#define LOCAL_MAILBOX0_CLR0 0x0c0 + +#define LOCAL_IRQ_BASE (IRQS_PER_BANK * NR_BANKS) +#define LOCAL_IRQ_CNTPSIRQ (LOCAL_IRQ_BASE + 0) +#define LOCAL_IRQ_CNTPNSIRQ (LOCAL_IRQ_BASE + 1) +#define LOCAL_IRQ_CNTHPIRQ (LOCAL_IRQ_BASE + 2) +#define LOCAL_IRQ_CNTVIRQ (LOCAL_IRQ_BASE + 3) +#define LOCAL_IRQ_MAILBOX0 (LOCAL_IRQ_BASE + 4) +#define LOCAL_IRQ_MAILBOX1 (LOCAL_IRQ_BASE + 5) +#define LOCAL_IRQ_MAILBOX2 (LOCAL_IRQ_BASE + 6) +#define LOCAL_IRQ_MAILBOX3 (LOCAL_IRQ_BASE + 7) +#define LOCAL_IRQ_GPU_FAST (LOCAL_IRQ_BASE + 8) +#define LOCAL_IRQ_PMU_FAST (LOCAL_IRQ_BASE + 9) + static int reg_pending[] __initconst = { 0x00, 0x04, 0x08 }; static int reg_enable[] __initconst = { 0x18, 0x10, 0x14 }; static int reg_disable[] __initconst = { 0x24, 0x1c, 0x20 }; @@ -91,21 +111,60 @@ struct armctrl_ic { void __iomem *pending[NR_BANKS]; void __iomem *enable[NR_BANKS]; void __iomem *disable[NR_BANKS]; + + void __iomem *local_base; + struct irq_domain *domain; + bool is_2836; }; static struct armctrl_ic intc __read_mostly; static void __exception_irq_entry bcm2835_handle_irq( struct pt_regs *regs); +static void __exception_irq_entry bcm2836_handle_irq( + struct pt_regs *regs); + +static void +armctrl_mask_per_cpu_irq(unsigned int reg, unsigned int bit) +{ + void __iomem *reg_base = intc.local_base + reg; + unsigned int i; + for (i = 0; i < 4; i++) + writel(readl(reg_base + 4 * i) & ~bit, reg_base + 4 * i); +} + +static void +armctrl_unmask_per_cpu_irq(unsigned int reg, unsigned int bit) +{ + void __iomem *reg_base = intc.local_base + reg; + unsigned int i; + for (i = 0; i < 4; i++) + writel(readl(reg_base + 4 * i) | bit, reg_base + 4 * i); +} static void armctrl_mask_irq(struct irq_data *d) { - writel_relaxed(HWIRQ_BIT(d->hwirq), intc.disable[HWIRQ_BANK(d->hwirq)]); + if (d->hwirq == LOCAL_IRQ_PMU_FAST) { + writel(0xf, intc.local_base + LOCAL_PM_ROUTING_CLR); + } else if (d->hwirq >= LOCAL_IRQ_CNTPSIRQ) { + armctrl_mask_per_cpu_irq(LOCAL_TIMER_INT_CONTROL0, + BIT(d->hwirq - LOCAL_IRQ_CNTPSIRQ)); + } else { + writel_relaxed(HWIRQ_BIT(d->hwirq), + intc.disable[HWIRQ_BANK(d->hwirq)]); + } } static void armctrl_unmask_irq(struct irq_data *d) { - writel_relaxed(HWIRQ_BIT(d->hwirq), intc.enable[HWIRQ_BANK(d->hwirq)]); + if (d->hwirq == LOCAL_IRQ_PMU_FAST) { + writel(0xf, intc.local_base + LOCAL_PM_ROUTING_SET); + } else if (d->hwirq >= LOCAL_IRQ_CNTPSIRQ) { + armctrl_unmask_per_cpu_irq(LOCAL_TIMER_INT_CONTROL0, + BIT(d->hwirq - LOCAL_IRQ_CNTPSIRQ)); + } else { + writel_relaxed(HWIRQ_BIT(d->hwirq), intc.enable[HWIRQ_BANK(d->hwirq)]); + } } static struct irq_chip armctrl_chip = { @@ -114,6 +173,12 @@ static struct irq_chip armctrl_chip = { .irq_unmask = armctrl_unmask_irq }; +static bool +is_valid_bank3_irq(int irq) +{ + return intc.is_2836 && (irq < 4 || irq == 5 || irq == 9); +} + 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) @@ -121,7 +186,7 @@ static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr, if (WARN_ON(intsize != 2)) return -EINVAL; - if (WARN_ON(intspec[0] >= NR_BANKS)) + if (WARN_ON(intspec[0] >= NR_BANKS + 1)) return -EINVAL; if (WARN_ON(intspec[1] >= IRQS_PER_BANK)) @@ -130,6 +195,9 @@ static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr, if (WARN_ON(intspec[0] == 0 && intspec[1] >= NR_IRQS_BANK0)) return -EINVAL; + if (WARN_ON(intspec[0] == 3 && !is_valid_bank3_irq(intspec[1]))) + return -EINVAL; + *out_hwirq = MAKE_HWIRQ(intspec[0], intspec[1]); *out_type = IRQ_TYPE_NONE; return 0; @@ -139,21 +207,35 @@ static struct irq_domain_ops armctrl_ops = { .xlate = armctrl_xlate }; -static int __init armctrl_of_init(struct device_node *node, - struct device_node *parent) +#ifdef CONFIG_SMP +static void armctrl_send_ipi(const struct cpumask *mask, unsigned int irq) +{ + int cpu; + void __iomem *mailbox0_base = intc.local_base + LOCAL_MAILBOX0_SET0; + + /* + * Ensure that stores to Normal memory are visible to the + * other CPUs before issuing the IPI. + */ + dsb(); + + for_each_cpu(cpu, mask) { + writel(1 << irq, mailbox0_base + 16 * cpu); + } +} +#endif + +static void __init armctrl_of_init(struct device_node *node) { void __iomem *base; int irq, b, i; + printk("of init!\n"); base = of_iomap(node, 0); if (!base) panic("%s: unable to map IC registers\n", node->full_name); - - intc.domain = irq_domain_add_linear(node, MAKE_HWIRQ(NR_BANKS, 0), - &armctrl_ops, NULL); - if (!intc.domain) - panic("%s: unable to create IRQ domain\n", node->full_name); + printk("of mapped!\n"); for (b = 0; b < NR_BANKS; b++) { intc.pending[b] = base + reg_pending[b]; @@ -168,8 +250,67 @@ static int __init armctrl_of_init(struct device_node *node, set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } } + printk("of set chips!\n"); +} +static int __init armctrl_2835_of_init(struct device_node *node, + struct device_node *parent) +{ + printk("2835 init!\n"); + intc.domain = irq_domain_add_linear(node, MAKE_HWIRQ(NR_BANKS, 0), + &armctrl_ops, NULL); + if (!intc.domain) + panic("%s: unable to create IRQ domain\n", node->full_name); + printk("2835 domain added!\n"); + + armctrl_of_init(node); set_handle_irq(bcm2835_handle_irq); + printk("2835 irq set!\n"); + return 0; +} + +static int __init armctrl_2836_of_init(struct device_node *node, + struct device_node *parent) +{ + int irq, i; + + intc.is_2836 = true; + + printk("2836 init!\n"); + + intc.local_base = of_iomap(node, 1); + if (!intc.local_base) + panic("%s: unable to map local interrupt registers\n", + node->full_name); + + intc.domain = irq_domain_add_linear(node, MAKE_HWIRQ(NR_BANKS + 1, 0), + &armctrl_ops, NULL); + if (!intc.domain) + panic("%s: unable to create IRQ domain\n", node->full_name); + + armctrl_of_init(node); + printk("2836 of inited!\n"); + + /* Bank 3 local interrupts */ + for (i = 0; i < 9; i++) { + if (!is_valid_bank3_irq(i)) + continue; + + irq = irq_create_mapping(intc.domain, MAKE_HWIRQ(3, i)); + irq_set_percpu_devid(irq); + irq_set_chip_and_handler(irq, &armctrl_chip, + handle_percpu_devid_irq); + } + printk("2836 chips set!\n"); + +#ifdef CONFIG_SMP + /* unmask IPIs */ + armctrl_unmask_per_cpu_irq(LOCAL_MAILBOX_INT_CONTROL0, 1); + set_smp_cross_call(armctrl_send_ipi); +#endif + + set_handle_irq(bcm2836_handle_irq); + printk("2836 irq set!\n"); return 0; } @@ -196,8 +337,7 @@ static void armctrl_handle_shortcut(int bank, struct pt_regs *regs, handle_IRQ(irq_linear_revmap(intc.domain, irq), regs); } -static void __exception_irq_entry bcm2835_handle_irq( - struct pt_regs *regs) +static void bcm283x_handle_gpu_irq(struct pt_regs *regs) { u32 stat, irq; @@ -219,4 +359,34 @@ static void __exception_irq_entry bcm2835_handle_irq( } } -IRQCHIP_DECLARE(bcm2835_armctrl_ic, "brcm,bcm2835-armctrl-ic", armctrl_of_init); +static void __exception_irq_entry bcm2835_handle_irq( + struct pt_regs *regs) +{ + bcm283x_handle_gpu_irq(regs); +} + +static void __exception_irq_entry bcm2836_handle_irq( + struct pt_regs *regs) +{ + int cpu = smp_processor_id(); + u32 stat; + + stat = readl_relaxed(intc.local_base + LOCAL_IRQ_PENDING0 + 4 * cpu); + if (stat & 0x10) { + void __iomem *mailbox0 = (intc.local_base + + LOCAL_MAILBOX0_CLR0 + 16 * cpu); + u32 mbox_val = readl(mailbox0); + u32 ipi = ffs(mbox_val) - 1; + writel(1 << ipi, mailbox0); + do_IPI(ipi, regs); + } else if (stat & 0x100) { + bcm283x_handle_gpu_irq(regs); + } else { + /* Local (bank3) interrupts */ + u32 irq = MAKE_HWIRQ(3, ffs(stat) - 1); + handle_IRQ(irq_linear_revmap(intc.domain, irq), regs); + } +} + +IRQCHIP_DECLARE(bcm2835_armctrl_ic, "brcm,bcm2835-armctrl-ic", armctrl_2835_of_init); +IRQCHIP_DECLARE(bcm2836_armctrl_ic, "brcm,bcm2836-armctrl-ic", armctrl_2836_of_init); diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 84325f267acf02..2873a033f5f5a5 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -51,4 +51,12 @@ config ALTERA_MBOX An implementation of the Altera Mailbox soft core. It is used to send message between processors. Say Y here if you want to use the Altera mailbox support. + +config BCM2835_MBOX + tristate "BCM2835 Mailbox" + depends on ARCH_BCM2835 + help + An implementation of the BCM2385 Mailbox. It is used to invoke + the services of the Videocore. Say Y here if you want to use the + BCM2835 Mailbox. endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 2e79231154cf49..7feb8daf0408ba 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -9,3 +9,5 @@ obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o obj-$(CONFIG_PCC) += pcc.o obj-$(CONFIG_ALTERA_MBOX) += mailbox-altera.o + +obj-$(CONFIG_BCM2835_MBOX) += bcm2835-mailbox.o diff --git a/drivers/mailbox/bcm2835-mailbox.c b/drivers/mailbox/bcm2835-mailbox.c new file mode 100644 index 00000000000000..14d9f393b1983d --- /dev/null +++ b/drivers/mailbox/bcm2835-mailbox.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2010 Broadcom + * Copyright (C) 2013-2014 Lubomir Rintel + * Copyright (C) 2013 Craig McGeachie + * + * 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 device provides a mechanism for writing to the mailboxes, + * that are shared between the ARM and the VideoCore processor + * + * Parts of the driver are based on: + * - arch/arm/mach-bcm2708/vcio.c file written by Gray Girling that was + * obtained from branch "rpi-3.6.y" of git://github.com/raspberrypi/ + * linux.git + * - drivers/mailbox/bcm2835-ipc.c by Lubomir Rintel at + * https://github.com/hackerspace/rpi-linux/blob/lr-raspberry-pi/drivers/ + * mailbox/bcm2835-ipc.c + * - documentation available on the following web site: + * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Mailboxes */ +#define ARM_0_MAIL0 0x00 +#define ARM_0_MAIL1 0x20 + +/* + * Mailbox registers. We basically only support mailbox 0 & 1. We + * deliver to the VC in mailbox 1, it delivers to us in mailbox 0. See + * BCM2835-ARM-Peripherals.pdf section 1.3 for an explanation about + * the placement of memory barriers. + */ +#define MAIL0_RD (ARM_0_MAIL0 + 0x00) +#define MAIL0_POL (ARM_0_MAIL0 + 0x10) +#define MAIL0_STA (ARM_0_MAIL0 + 0x18) +#define MAIL0_CNF (ARM_0_MAIL0 + 0x1C) +#define MAIL1_WRT (ARM_0_MAIL1 + 0x00) + +#define MBOX_CHAN_COUNT 16 + +/* Status register: FIFO state. */ +#define ARM_MS_FULL 0x80000000 +#define ARM_MS_EMPTY 0x40000000 + +/* Configuration register: Enable interrupts. */ +#define ARM_MC_IHAVEDATAIRQEN 0x00000001 + +#define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf)) +#define MBOX_CHAN(msg) ((msg) & 0xf) +#define MBOX_DATA28(msg) ((msg) & ~0xf) + +struct bcm2835_mbox; + +struct bcm2835_channel { + struct bcm2835_mbox *mbox; + struct mbox_chan *link; + u32 chan_num; + bool started; +}; + +struct bcm2835_mbox { + struct platform_device *pdev; + struct device *dev; + void __iomem *regs; + spinlock_t lock; + struct bcm2835_channel channel[MBOX_CHAN_COUNT]; + struct mbox_controller controller; +}; + +#define to_channel(link) ((struct bcm2835_channel *)link->con_priv) + +static irqreturn_t bcm2835_mbox_irq(int irq, void *dev_id) +{ + struct bcm2835_mbox *mbox = (struct bcm2835_mbox *) dev_id; + struct device *dev = mbox->dev; + + while (!(readl(mbox->regs + MAIL0_STA) & ARM_MS_EMPTY)) { + u32 msg = readl(mbox->regs + MAIL0_RD); + unsigned int chan = MBOX_CHAN(msg); + u32 data = MBOX_DATA28(msg); + + if (!mbox->channel[chan].started) { + dev_err(dev, "Reply on stopped channel %d\n", chan); + continue; + } + dev_dbg(dev, "Reply 0x%08X\n", msg); + mbox_chan_received_data(mbox->channel[chan].link, &data); + } + return IRQ_HANDLED; +} + +static int bcm2835_send_data(struct mbox_chan *link, void *data) +{ + struct bcm2835_channel *chan = to_channel(link); + struct bcm2835_mbox *mbox = chan->mbox; + int ret = 0; + u32 msg = MBOX_MSG(chan->chan_num, *(u32 *)data); + + if (!chan->started) + return -ENODEV; + spin_lock(&mbox->lock); + if (readl(mbox->regs + MAIL0_STA) & ARM_MS_FULL) { + ret = -EBUSY; + goto end; + } + writel(msg, mbox->regs + MAIL1_WRT); + dev_dbg(mbox->dev, "Request 0x%08X\n", msg); +end: + spin_unlock(&mbox->lock); + return ret; +} + +static int bcm2835_startup(struct mbox_chan *link) +{ + struct bcm2835_channel *chan = to_channel(link); + + chan->started = true; + return 0; +} + +static void bcm2835_shutdown(struct mbox_chan *link) +{ + struct bcm2835_channel *chan = to_channel(link); + + chan->started = false; +} + +static bool bcm2835_last_tx_done(struct mbox_chan *link) +{ + struct bcm2835_channel *chan = to_channel(link); + struct bcm2835_mbox *mbox = chan->mbox; + bool ret; + + if (!chan->started) + return false; + spin_lock(&mbox->lock); + ret = !(readl(mbox->regs + MAIL0_STA) & ARM_MS_FULL); + spin_unlock(&mbox->lock); + return ret; +} + +static struct mbox_chan_ops bcm2835_mbox_chan_ops = { + .send_data = bcm2835_send_data, + .startup = bcm2835_startup, + .shutdown = bcm2835_shutdown, + .last_tx_done = bcm2835_last_tx_done +}; + +static int bcm2835_mbox_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bcm2835_mbox *mbox; + int i; + int ret = 0; + struct resource *iomem; + + mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); + if (mbox == NULL) + return -ENOMEM; + platform_set_drvdata(pdev, mbox); + mbox->pdev = pdev; + mbox->dev = dev; + spin_lock_init(&mbox->lock); + + ret = devm_request_irq(dev, irq_of_parse_and_map(dev->of_node, 0), + bcm2835_mbox_irq, 0, dev_name(dev), mbox); + if (ret) { + dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n", + ret); + return -ENODEV; + } + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mbox->regs = devm_ioremap_resource(&pdev->dev, iomem); + if (IS_ERR(mbox->regs)) { + ret = PTR_ERR(mbox->regs); + dev_err(&pdev->dev, "Failed to remap mailbox regs: %d\n", ret); + return ret; + } + + mbox->controller.txdone_poll = true; + mbox->controller.txpoll_period = 5; + mbox->controller.ops = &bcm2835_mbox_chan_ops; + mbox->controller.dev = dev; + mbox->controller.num_chans = MBOX_CHAN_COUNT; + mbox->controller.chans = devm_kzalloc(dev, + sizeof(*mbox->controller.chans) * MBOX_CHAN_COUNT, + GFP_KERNEL); + if (!mbox->controller.chans) + return -ENOMEM; + + for (i = 0; i != MBOX_CHAN_COUNT; ++i) { + mbox->channel[i].mbox = mbox; + mbox->channel[i].link = &mbox->controller.chans[i]; + mbox->channel[i].chan_num = i; + mbox->controller.chans[i].con_priv = + (void *)&mbox->channel[i]; + } + + ret = mbox_controller_register(&mbox->controller); + if (ret) + return ret; + + /* Enable the interrupt on data reception */ + writel(ARM_MC_IHAVEDATAIRQEN, mbox->regs + MAIL0_CNF); + dev_info(dev, "mailbox enabled\n"); + + return ret; +} + +static int bcm2835_mbox_remove(struct platform_device *pdev) +{ + struct bcm2835_mbox *mbox = platform_get_drvdata(pdev); + + writel(0, mbox->regs + MAIL0_CNF); + mbox_controller_unregister(&mbox->controller); + return 0; +} + +static const struct of_device_id bcm2835_mbox_of_match[] = { + { .compatible = "brcm,bcm2835-mbox", }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm2835_mbox_of_match); + +static struct platform_driver bcm2835_mbox_driver = { + .driver = { + .name = "bcm2835-mbox", + .owner = THIS_MODULE, + .of_match_table = bcm2835_mbox_of_match, + }, + .probe = bcm2835_mbox_probe, + .remove = bcm2835_mbox_remove, +}; +module_platform_driver(bcm2835_mbox_driver); + +MODULE_AUTHOR("Lubomir Rintel "); +MODULE_DESCRIPTION("BCM2835 mailbox IPC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/dt-bindings/arm/bcm2835_mbox_power.h b/include/dt-bindings/arm/bcm2835_mbox_power.h new file mode 100644 index 00000000000000..1e1ef3b076fa13 --- /dev/null +++ b/include/dt-bindings/arm/bcm2835_mbox_power.h @@ -0,0 +1,16 @@ +/* + * Copyright © 2015 Broadcom + * + * 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 _DT_BINDINGS_ARM_BCM2835_MBOX_POWER_H +#define _DT_BINDINGS_ARM_BCM2835_MBOX_POWER_H + +#define POWER_DOMAIN_SDCARD 0 +#define POWER_DOMAIN_USB 1 +#define POWER_DOMAIN_DSI 2 + +#endif diff --git a/include/dt-bindings/pinctrl/bcm2835.h b/include/dt-bindings/pinctrl/bcm2835.h new file mode 100644 index 00000000000000..6f0bc37af39ca6 --- /dev/null +++ b/include/dt-bindings/pinctrl/bcm2835.h @@ -0,0 +1,27 @@ +/* + * Header providing constants for bcm2835 pinctrl bindings. + * + * Copyright (C) 2015 Stefan Wahren + * + * 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 at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef __DT_BINDINGS_PINCTRL_BCM2835_H__ +#define __DT_BINDINGS_PINCTRL_BCM2835_H__ + +/* brcm,function property */ +#define BCM2835_FSEL_GPIO_IN 0 +#define BCM2835_FSEL_GPIO_OUT 1 +#define BCM2835_FSEL_ALT5 2 +#define BCM2835_FSEL_ALT4 3 +#define BCM2835_FSEL_ALT0 4 +#define BCM2835_FSEL_ALT1 5 +#define BCM2835_FSEL_ALT2 6 +#define BCM2835_FSEL_ALT3 7 + +#endif /* __DT_BINDINGS_PINCTRL_BCM2835_H__ */