Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 5/7] capebus: Beaglebone capebus DT update
From: Pantelis Antoniou @ 2012-10-31 16:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351702333-8456-1-git-send-email-panto@antoniou-consulting.com>

Update the common beaglebone's DTS with the required DT
entries for all known working capes as of now.

Signed-off-by: Pantelis Antoniou <panto@antoniou-consulting.com>
---
 arch/arm/boot/dts/am335x-bone-common.dtsi | 689 ++++++++++++++++++++++++++++--
 1 file changed, 659 insertions(+), 30 deletions(-)

diff --git a/arch/arm/boot/dts/am335x-bone-common.dtsi b/arch/arm/boot/dts/am335x-bone-common.dtsi
index 99240a5..46d5f27 100644
--- a/arch/arm/boot/dts/am335x-bone-common.dtsi
+++ b/arch/arm/boot/dts/am335x-bone-common.dtsi
@@ -51,6 +51,143 @@
 				0x60 0x17	/* gpmc_a8.gpio1_24, OUTPUT_PULLUP | MODE7 */
 			>;
 		};
+		i2c2_pins: pinmux_i2c2_pins {
+			pinctrl-single,pins = <
+				0x178 0x73 	/* uart1_ctsn.i2c2_sda, SLEWCTRL_SLOW | INPUT_PULLUP | MODE3 */
+				0x17c 0x73	/* uart1_rtsn.i2c2_scl, SLEWCTRL_SLOW | INPUT_PULLUP | MODE3 */
+			>;
+		};
+
+		bone_dvi_cape_led_pins: pinmux_bone_dvi_cape_led_pins {
+			pinctrl-single,pins = <
+				0x48 0x07	/* gpmc_a2.gpio1_18, OUTPUT | MODE7 */
+				0x4c 0x07	/* gpmc_a3.gpio1_19, OUTPUT | MODE7 */
+			>;
+		};
+
+		bone_dvi_cape_dvi_00A0_pins: pinmux_bone_dvi_cape_dvi_00A0_pins {
+			pinctrl-single,pins = <
+				0x1c 0x07	/* gpmc_ad7.gpio1_7, OUTPUT | MODE7 - DVIPDn */
+
+				0xa0 0x08	/* lcd_data0.lcd_data0, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xa4 0x08	/* lcd_data1.lcd_data1, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xa8 0x08	/* lcd_data2.lcd_data2, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xac 0x08	/* lcd_data3.lcd_data3, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xb0 0x08	/* lcd_data4.lcd_data4, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xb4 0x08	/* lcd_data5.lcd_data5, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xb8 0x08	/* lcd_data6.lcd_data6, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xbc 0x08	/* lcd_data7.lcd_data7, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xc0 0x08	/* lcd_data8.lcd_data8, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xc4 0x08	/* lcd_data9.lcd_data9, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xc8 0x08	/* lcd_data10.lcd_data10, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xcc 0x08	/* lcd_data11.lcd_data11, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xd0 0x08	/* lcd_data12.lcd_data12, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xd4 0x08	/* lcd_data13.lcd_data13, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xd8 0x08	/* lcd_data14.lcd_data14, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xdc 0x08	/* lcd_data15.lcd_data15, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xe0 0x00	/* lcd_vsync.lcd_vsync, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+				0xe4 0x00	/* lcd_hsync.lcd_hsync, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+				0xe8 0x00	/* lcd_pclk.lcd_pclk, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+				0xec 0x00	/* lcd_ac_bias_en.lcd_ac_bias_en, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+			>;
+		};
+
+		bone_dvi_cape_dvi_00A1_pins: pinmux_bone_dvi_cape_dvi_00A1_pins {
+			pinctrl-single,pins = <
+				0x84 0x07	/* gpmc_csn2.gpio1_31, OUTPUT | MODE7 - DVIPDn */
+
+				0xa0 0x08	/* lcd_data0.lcd_data0, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xa4 0x08	/* lcd_data1.lcd_data1, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xa8 0x08	/* lcd_data2.lcd_data2, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xac 0x08	/* lcd_data3.lcd_data3, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xb0 0x08	/* lcd_data4.lcd_data4, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xb4 0x08	/* lcd_data5.lcd_data5, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xb8 0x08	/* lcd_data6.lcd_data6, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xbc 0x08	/* lcd_data7.lcd_data7, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xc0 0x08	/* lcd_data8.lcd_data8, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xc4 0x08	/* lcd_data9.lcd_data9, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xc8 0x08	/* lcd_data10.lcd_data10, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xcc 0x08	/* lcd_data11.lcd_data11, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xd0 0x08	/* lcd_data12.lcd_data12, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xd4 0x08	/* lcd_data13.lcd_data13, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xd8 0x08	/* lcd_data14.lcd_data14, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xdc 0x08	/* lcd_data15.lcd_data15, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xe0 0x00	/* lcd_vsync.lcd_vsync, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+				0xe4 0x00	/* lcd_hsync.lcd_hsync, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+				0xe8 0x00	/* lcd_pclk.lcd_pclk, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+				0xec 0x00	/* lcd_ac_bias_en.lcd_ac_bias_en, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+			>;
+		};
+
+		bone_geiger_cape_led_pins: pinmux_bone_geiger_cape_led_pins {
+			pinctrl-single,pins = <
+				0xe4 0x07	/* lcd_hsync.gpio2_23, OUTPUT | MODE7 */
+				0xec 0x07	/* lcd_ac_bias_en.gpio2_25, OUTPUT | MODE7 */
+			>;
+		};
+
+		bone_geiger_cape_pins: pinmux_bone_geiger_cape_pins {
+			pinctrl-single,pins = <
+				0x48 0x06       /* gpmc_a2.ehrpwm1a, OMAP_MUX_MODE6 | AM33XX_PIN_OUTPUT */
+				/* 0x19c 0x34 */	/* mcasp0_ahclkr.eCAP2_in_PWM2_out, OMAP_MUX_MODE4 | INPUT_PULLUP */
+				0x19c 0x37 	/* mcasp0_ahclkr.gpio3_17, OMAP_MUX_MODE4 | INPUT_PULLUP */
+			>;
+		};
+
+		bone_lcd3_cape_led_00A0_pins: pinmux_bone_lcd3_cape_led_00A0_pins {
+			pinctrl-single,pins = <
+				0x48 0x07	/* gpmc_a2.gpio1_18, OUTPUT | MODE7 */
+				0x4c 0x07	/* gpmc_a3.gpio1_19, OUTPUT | MODE7 */
+			>;
+		};
+
+		bone_lcd3_cape_lcd_pins: pinmux_bone_lcd3_cape_lcd_pins {
+			pinctrl-single,pins = <
+				0xa0 0x08	/* lcd_data0.lcd_data0, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xa4 0x08	/* lcd_data1.lcd_data1, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xa8 0x08	/* lcd_data2.lcd_data2, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xac 0x08	/* lcd_data3.lcd_data3, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xb0 0x08	/* lcd_data4.lcd_data4, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xb4 0x08	/* lcd_data5.lcd_data5, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xb8 0x08	/* lcd_data6.lcd_data6, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xbc 0x08	/* lcd_data7.lcd_data7, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xc0 0x08	/* lcd_data8.lcd_data8, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xc4 0x08	/* lcd_data9.lcd_data9, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xc8 0x08	/* lcd_data10.lcd_data10, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xcc 0x08	/* lcd_data11.lcd_data11, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xd0 0x08	/* lcd_data12.lcd_data12, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xd4 0x08	/* lcd_data13.lcd_data13, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xd8 0x08	/* lcd_data14.lcd_data14, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xdc 0x08	/* lcd_data15.lcd_data15, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+				0xe0 0x00	/* lcd_vsync.lcd_vsync, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+				0xe4 0x00	/* lcd_hsync.lcd_hsync, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+				0xe8 0x00	/* lcd_pclk.lcd_pclk, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+				0xec 0x00	/* lcd_ac_bias_en.lcd_ac_bias_en, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+			>;
+		};
+
+		bone_lcd3_cape_keys_00A0_pins: pinmux_bone_lcd3_cape_keys_00A0_pins {
+			pinctrl-single,pins = <
+				0x040 0x2f	/* gpmc_a0.gpio1_16, INPUT | PULLDIS | MODE7 */
+				0x044 0x2f	/* gpmc_a1.gpio1_17, INPUT | PULLDIS | MODE7 */
+				0x1a4 0x2f	/* mcasp0_fsr.gpio3_19, INPUT | PULLDIS | MODE7 */
+				0x078 0x2f	/* gpmc_ben1.gpio1_28, INPUT | PULLDIS | MODE7 */
+				0x164 0x2f	/* ecap0_in_pwm0_out.gpio0_7, INPUT | PULLDIS | MODE7 */
+			>;
+		};
+
+		pwm_bl_pins: pinmux_pwm_bl_pins {
+			pinctrl-single,pins = <
+				0x4c 0x06	/* gpmc_a3.ehrpwm1b, OMAP_MUX_MODE6 | AM33XX_PIN_OUTPUT */
+				// 0x48 0x06       /* gpmc_a2.ehrpwm1a, OMAP_MUX_MODE6 | AM33XX_PIN_OUTPUT */
+			>;
+	       };
+
+		weather_cape_w1_pins: pinmux_weather_cape_w1_pins {
+			pinctrl-single,pins = <
+				0x0c 0x37       /* gpmc_ad3.gpio1_3, OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE7 - w1-gpio */
+			>;
+		};
 	};
 
 	ocp {
@@ -92,16 +229,6 @@
 			};
 		};
 
-		i2c1: i2c at 44e0b000 {
-			status = "okay";
-			clock-frequency = <400000>;
-
-			tps: tps at 24 {
-				reg = <0x24>;
-			};
-
-		};
-
 		gpevt {
 			compatible = "gpevt";
 			pinctrl-names = "default";
@@ -110,15 +237,117 @@
 			dma-names = "gpioevt";
 			gpio-evt = <&gpio3 2 0>;
 		};
+
+	};
+
+	capebus: capebus at 0 {
+			compatible = "bone-capebus";
+
+			bone_dvi_cape: cape at 0 {
+				compatible = "bone-generic-cape";
+			};
+
+			bone_geiger_cape: cape at 1 {
+				compatible = "bone-geiger-cape";
+			};
+
+			bone_lcd3_cape: cape at 2 {
+				compatible = "bone-generic-cape";
+			};
+
+			bone_lcd7_cape: cape at 3 {
+				compatible = "bone-lcd7-cape";
+			};
+
+			bone_weather_cape: cape at 4 {
+				compatible = "bone-generic-cape";
+			};
+
+			bone_adafruit_cape: cape at 5 {
+				compatible = "bone-generic-cape";
+			};
+
+			/* overrides; no EEPROM (prototyping) */
+//			override at 3 {
+//				compatible = "bone-capebus-slot-override";
+//				slot = <3>;
+//				board-name = "Geiger Cape";
+//				version = "00A0";
+//				manufacturer = "Geiger Inc";
+//				/* TODO: Add the rest */
+//			};
+
+//			override at 2 {
+//				compatible = "bone-capebus-slot-override";
+//				slot = <2>;
+//				board-name = "Weather Cape";
+//				version = "00A0";
+//				manufacturer = "CCO Inc";
+//				/* TODO: Add the rest */
+//			};
+
+//			override at 1 {
+//				compatible = "bone-capebus-slot-override";
+//				slot = <1>;
+//				board-name = "Adafruit 1.8 Cape";
+//				version = "00A0";
+//				manufacturer = "Adafruit";
+//				/* TODO: Add the rest */
+//			};
+
+//			override at 0 {
+//				compatible = "bone-capebus-slot-override";
+//				slot = <0>;
+//				board-name = "BeagleBone Weather CAPE";
+//				version = "00A0";
+//				manufacturer = "Beagleboardtoys";
+//				/* TODO: Add the rest */
+//			};
 	};
 
-	backlight {
-			compatible	= "pwm-backlight"; 
-			pwms = <&ehrpwm1 0 500000 0>;
-			pwm-names = "st7735fb";
-			brightness-levels = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100>;
-			default-brightness-level = <50>; /* index to the array above */ 
-		};
+};
+
+&i2c0 {
+	status = "okay";
+	clock-frequency = <400000>;
+
+	tps: tps at 24 {
+		reg = <0x24>;
+	};
+
+	baseboard_eeprom: baseboard_eeprom at 50 {
+		compatible = "at,24c256";
+		reg = <0x50>;
+	};
+};
+
+&i2c2 {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c2_pins>;
+
+	clock-frequency = <100000>;
+
+	/* OK, I know these are cape but for now it will do */
+	cape_eeprom_0: cape_eeprom_0 at 54 {
+		compatible = "at,24c256";
+		reg = <0x54>;
+	};
+
+	cape_eeprom_1: cape_eeprom_1 at 55 {
+		compatible = "at,24c256";
+		reg = <0x55>;
+	};
+
+	cape_eeprom_2: cape_eeprom_2 at 56 {
+		compatible = "at,24c256";
+		reg = <0x56>;
+	};
+
+	cape_eeprom_3: cape_eeprom_3 at 57 {
+		compatible = "at,24c256";
+		reg = <0x57>;
+	};
 };
 
 /include/ "tps65217.dtsi"
@@ -165,6 +394,7 @@
 			regulator-always-on;
 		};
 	};
+
 };
 
 &mmc1 {
@@ -172,21 +402,8 @@
 };
 
 &spi1 {
-	status = "okay";
 	pinctrl-names = "default";
 	pinctrl-0 = <&spi1_pins>;
-
-	lcd at 0 {
-		compatible = "adafruit,tft-lcd-1.8-green", "sitronix,st7735";
-		spi-max-frequency = <8000000>;
-		reg = <0>;
-		spi-cpol;
-		spi-cpha;
-		pinctrl-names = "default";
-		pinctrl-0 = <&lcd_pins>;
-		st7735-rst = <&gpio4 19 0>;
-		st7735-dc = <&gpio4 21 0>;
-	};
 };
 
 &edma {
@@ -200,3 +417,415 @@
 &cpsw_emac1 {
 	phy_id = "4a101000.mdio:01";
 };
+
+&ehrpwm1 {
+	status = "okay";
+};
+
+&capebus {
+	slots = <&cape_eeprom_0 &cape_eeprom_1 &cape_eeprom_2 &cape_eeprom_3>;
+};
+
+&bone_dvi_cape {
+	board-name = "BeagleBone DVI-D CAPE";
+
+	/* hacky, since this is not a proper DT platform device */
+	/* but until we have DT bindings... */
+	version at 00A0 {
+		version = "00A0";
+		dvi {
+			compatible = "da8xx-dt";
+			pinctrl-names = "default";
+			pinctrl-0 = <&bone_dvi_cape_dvi_00A0_pins>;
+			ti,hwmods = "lcdc";
+
+			disp-pll = <560000000>;
+			panel-type = "1024x768 at 60";
+			powerdn-gpio = <&gpio2 7 0>;
+		};
+	};
+
+	version at 00A1 {
+		version = "00A1", "01";
+		dvi {
+			compatible = "da8xx-dt";
+			pinctrl-names = "default";
+			pinctrl-0 = <&bone_dvi_cape_dvi_00A1_pins>;
+			ti,hwmods = "lcdc";
+
+			disp-pll = <560000000>;
+			panel-type = "1024x768 at 60";
+			powerdn-gpio = <&gpio2 31 0>;
+		};
+	};
+
+	gpio-leds {
+		compatible = "gpio-leds";
+		pinctrl-names = "default";
+		pinctrl-0 = <&bone_dvi_cape_led_pins>;
+
+		dvi-led0 {
+			label = "dvi:green:usr0";
+			gpios = <&gpio2 18 0>;
+			linux,default-trigger = "heartbeat";
+			default-state = "off";
+		};
+
+		dvi-led1 {
+			label = "dvi:green:usr1";
+			gpios = <&gpio2 19 0>;
+			linux,default-trigger = "mmc0";
+			default-state = "off";
+		};
+	};
+};
+
+&bone_geiger_cape {
+	board-name = "Geiger Cape";
+
+	/* note that these can't be versioned... */
+	pinctrl-names = "default";
+	pinctrl-0 = <&bone_geiger_cape_pins>;
+
+	pwms = <&ehrpwm1 0 500000 0>;
+	pwm-names = "bone-geiger-cape";
+
+	pwm-frequency  = <20000>;	/* 20KHz */
+	pwm-duty-cycle = <60>;		/* 60% */
+
+	event-blink-delay = <30>;	/* 30ms */
+
+	gpios = <&gpio4 17 0>;		/* pulse */
+
+	vsense-name = "AIN5";		/* analog vsense */
+	vsense-scale = <37325>;		/* scaling */
+
+	tscadc {
+		compatible = "ti-tscadc-dt";
+
+		ti,hwmods = "adc_tsc";
+
+		adc-channels = <8>;
+	};
+
+	gpio-leds {
+		compatible = "gpio-leds";
+		pinctrl-names = "default";
+		pinctrl-0 = <&bone_geiger_cape_led_pins>;
+
+		geiger-led0 {
+			label = "geiger:green:usr0";
+			gpios = <&gpio3 23 0>;
+			linux,default-trigger = "geiger-run";
+			default-state = "off";
+		};
+
+		geiger-led1 {
+			label = "geiger:red:usr1";
+			gpios = <&gpio3 25 0>;
+			linux,default-trigger = "geiger-event";
+			default-state = "off";
+		};
+	};
+};
+
+&bone_lcd3_cape {
+	board-name = "BeagleBone LCD3 CAPE";
+
+	/* hacky, since this is not a proper DT platform device */
+	/* but until we have DT bindings... */
+	lcd3 {
+		compatible = "da8xx-dt";
+		pinctrl-names = "default";
+		pinctrl-0 = <&bone_lcd3_cape_lcd_pins>;
+
+		ti,hwmods = "lcdc";
+
+		disp-pll = <16000000>;
+		panel-type = "CDTech_S035Q01";
+	};
+
+	/* same thing as above */
+	tscadc {
+		compatible = "ti-tscadc-dt";
+
+		ti,hwmods = "adc_tsc";
+
+		tsc-wires = <4>;
+		tsc-x-plate-resistance = <200>;
+		tsc-steps = <6>;
+
+		adc-channels = <4>;
+	};
+
+	version at 00A0 {
+		version = "00A0";
+
+		backlight {
+			compatible = "tps65217-backlight";
+			isel = <1>;
+			fdim = <200>;
+
+			tps = <&tps>;	/* link to the tps */
+			brightness = <100>;
+		};
+
+		gpio-leds {
+			compatible = "gpio-leds";
+			pinctrl-names = "default";
+			pinctrl-0 = <&bone_lcd3_cape_led_00A0_pins>;
+
+			lcd3-led0 {
+				label = "lcd3:green:usr0";
+				gpios = <&gpio2 18 0>;
+				linux,default-trigger = "heartbeat";
+				default-state = "off";
+			};
+
+			lcd3-led1 {
+				label = "lcd3:green:usr1";
+				gpios = <&gpio2 19 0>;
+				linux,default-trigger = "cpu0";
+				default-state = "off";
+			};
+		};
+
+		gpio_keys {
+			compatible = "gpio-keys";
+			pinctrl-names = "default";
+			pinctrl-0 = <&bone_lcd3_cape_keys_00A0_pins>;
+
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			button at 1 {
+				debounce_interval = <50>;
+				linux,code = <105>;
+				label = "left";
+				gpios = <&gpio2 16 0x0>;
+				gpio-key,wakeup;
+				autorepeat;
+			};
+			button at 2 {
+				debounce_interval = <50>;
+				linux,code = <106>;
+				label = "right";
+				gpios = <&gpio2 17 0x0>;
+				gpio-key,wakeup;
+				autorepeat;
+			};
+			button at 3 {
+				debounce_interval = <50>;
+				linux,code = <103>;
+				label = "up";
+				gpios = <&gpio4 19 0x0>;
+				gpio-key,wakeup;
+				autorepeat;
+			};
+			button at 4 {
+				debounce_interval = <50>;
+				linux,code = <108>;
+				label = "down";
+				gpios = <&gpio2 28 0x0>;
+				gpio-key,wakeup;
+				autorepeat;
+			};
+			button at 5 {
+				debounce_interval = <50>;
+				linux,code = <28>;
+				label = "enter";
+				gpios = <&gpio1 7 0x0>;
+				gpio-key,wakeup;
+			};
+		};
+	};
+};
+
+&bone_lcd7_cape {
+	board-name = "BeagleBone LCD7 CAPE";
+
+	/* hacky, since this is not a proper DT platform device */
+	/* but until we have DT bindings... */
+	lcd7 {
+		compatible = "da8xx-dt";
+		pinctrl-names = "default";
+		pinctrl-0 = <&bone_lcd3_cape_lcd_pins>;
+
+		ti,hwmods = "lcdc";
+
+		disp-pll = <60000000>;
+		panel-type = "TFC_S9700RTWV35TR_01B";
+	};
+
+	/* same thing as above */
+	tscadc {
+		compatible = "ti-tscadc-dt";
+
+		ti,hwmods = "adc_tsc";
+
+		tsc-wires = <4>;
+		tsc-x-plate-resistance = <200>;
+		tsc-steps = <6>;
+
+		adc-channels = <4>;
+	};
+
+	version at 00A0 {
+		version = "00A0";
+
+		backlight {
+			compatible = "tps65217-backlight";
+			isel = <1>;
+			fdim = <200>;
+
+			tps = <&tps>;	/* link to the tps */
+			brightness = <100>;
+		};
+
+		gpio-leds {
+			compatible = "gpio-leds";
+			pinctrl-names = "default";
+			pinctrl-0 = <&bone_lcd3_cape_led_00A0_pins>;
+
+			lcd3-led0 {
+				label = "lcd3:green:usr0";
+				gpios = <&gpio2 18 0>;
+				linux,default-trigger = "heartbeat";
+				default-state = "off";
+			};
+
+			lcd3-led1 {
+				label = "lcd3:green:usr1";
+				gpios = <&gpio2 19 0>;
+				linux,default-trigger = "cpu0";
+				default-state = "off";
+			};
+		};
+
+		gpio_keys {
+			compatible = "gpio-keys";
+			pinctrl-names = "default";
+			pinctrl-0 = <&bone_lcd3_cape_keys_00A0_pins>;
+
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			button at 1 {
+				debounce_interval = <50>;
+				linux,code = <105>;
+				label = "left";
+				gpios = <&gpio2 16 0x0>;
+				gpio-key,wakeup;
+				autorepeat;
+			};
+			button at 2 {
+				debounce_interval = <50>;
+				linux,code = <106>;
+				label = "right";
+				gpios = <&gpio2 17 0x0>;
+				gpio-key,wakeup;
+				autorepeat;
+			};
+			button at 3 {
+				debounce_interval = <50>;
+				linux,code = <103>;
+				label = "up";
+				gpios = <&gpio4 19 0x0>;
+				gpio-key,wakeup;
+				autorepeat;
+			};
+			button at 4 {
+				debounce_interval = <50>;
+				linux,code = <108>;
+				label = "down";
+				gpios = <&gpio2 28 0x0>;
+				gpio-key,wakeup;
+				autorepeat;
+			};
+			button at 5 {
+				debounce_interval = <50>;
+				linux,code = <28>;
+				label = "enter";
+				gpios = <&gpio1 7 0x0>;
+				gpio-key,wakeup;
+			};
+		};
+	};
+};
+
+&bone_weather_cape {
+	board-name = "BeagleBone Weather CAPE";
+
+	i2c2-devices {
+		compatible = "i2c-dt";
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		parent = <&i2c2>;
+
+		/* Ambient light sensor */
+		tsl2550 at 39 {
+			compatible = "tsl,tsl2550";
+			reg = <0x39>;
+		};
+
+		/* Humidity Sensor */
+		sht21 at 40 {
+			compatible = "sensiron,sht21";
+			reg = <0x40>;
+		};
+
+		/* Barometric pressure sensor */
+		bmp085 at 77 {
+			compatible = "bosch,bmp085";
+			reg = <0x77>;
+		};
+	};
+
+	onewire at 0 {
+		compatible	= "w1-gpio";
+		pinctrl-names	= "default";
+		pinctrl-0	= <&weather_cape_w1_pins>;
+		status		= "okay";
+
+		gpios = <&gpio2 3 0>;
+	};
+
+};
+
+&bone_adafruit_cape {
+	board-name = "Adafruit 1.8 Cape";
+
+	backlight {
+		compatible	= "pwm-backlight";
+		pinctrl-names	= "default";
+		pinctrl-0	= <&pwm_bl_pins>;
+
+		pwms = <&ehrpwm1 1 500000 0>;
+		pwm-names = "st7735fb";
+		brightness-levels = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100>;
+		default-brightness-level = <50>; /* index to the array above */
+	};
+
+	spi1-devices {
+		compatible = "spi-dt";
+
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		parent = <&spi1>;
+
+		lcd at 0 {
+			compatible = "adafruit,tft-lcd-1.8-red", "sitronix,st7735";
+			spi-max-frequency = <8000000>;
+			reg = <0>;
+			spi-cpol;
+			spi-cpha;
+			pinctrl-names = "default";
+			pinctrl-0 = <&lcd_pins>;
+			st7735-rst = <&gpio4 19 0>;
+			st7735-dc = <&gpio4 21 0>;
+		};
+
+	};
+};
-- 
1.7.12

^ permalink raw reply related

* [RFC 4/7] capebus: Beaglebone geiger cape support
From: Pantelis Antoniou @ 2012-10-31 16:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351702333-8456-1-git-send-email-panto@antoniou-consulting.com>

Support beaglebone's geiger cape.

The geiger cape allows you to measure the amount of
ionising radiation in your area, and as an example
of how to create a complex non-generic cape driver.

Signed-off-by: Pantelis Antoniou <panto@antoniou-consulting.com>
---
 drivers/capebus/capes/Kconfig            |   7 +
 drivers/capebus/capes/Makefile           |   1 +
 drivers/capebus/capes/bone-geiger-cape.c | 506 +++++++++++++++++++++++++++++++
 3 files changed, 514 insertions(+)
 create mode 100644 drivers/capebus/capes/bone-geiger-cape.c

diff --git a/drivers/capebus/capes/Kconfig b/drivers/capebus/capes/Kconfig
index bfe54a6..0418bef 100644
--- a/drivers/capebus/capes/Kconfig
+++ b/drivers/capebus/capes/Kconfig
@@ -4,3 +4,10 @@ config CAPEBUS_BONE_GENERIC
 	default n
 	help
 	  "Select this to enable a generic cape driver; LCD/DVI capes etc"
+
+config CAPEBUS_BONE_GEIGER
+	tristate "Beaglebone Geiger cape driver"
+	depends on CAPEBUS_BONE_CONTROLLER
+	default n
+	help
+	  "Select this to enable a driver for the geiger cape"
diff --git a/drivers/capebus/capes/Makefile b/drivers/capebus/capes/Makefile
index 83da381..d6f94ce 100644
--- a/drivers/capebus/capes/Makefile
+++ b/drivers/capebus/capes/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_CAPEBUS_BONE_GENERIC)	+= bone-generic-cape.o
+obj-$(CONFIG_CAPEBUS_BONE_GEIGER)	+= bone-geiger-cape.o
diff --git a/drivers/capebus/capes/bone-geiger-cape.c b/drivers/capebus/capes/bone-geiger-cape.c
new file mode 100644
index 0000000..880eaae
--- /dev/null
+++ b/drivers/capebus/capes/bone-geiger-cape.c
@@ -0,0 +1,506 @@
+/*
+ * Driver for beaglebone Geiger cape
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/atomic.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <asm/barrier.h>
+#include <plat/clock.h>
+#include <plat/omap_device.h>
+#include <linux/clkdev.h>
+#include <linux/pwm.h>
+#include <linux/math64.h>
+#include <linux/atomic.h>
+#include <linux/leds.h>
+#include <linux/input/ti_am335x_tsc.h>
+#include <linux/platform_data/ti_am335x_adc.h>
+#include <linux/mfd/ti_am335x_tscadc.h>
+#include <plat/omap_device.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/consumer.h>
+
+#include <linux/capebus/capebus-bone.h>
+
+/* fwd decl. */
+extern struct cape_driver bonegeiger_driver;
+
+struct bone_geiger_info {
+	struct cape_dev *dev;
+	struct bone_capebus_generic_info *geninfo;
+	struct pwm_device *pwm_dev;
+	int pwm_frequency;
+	int pwm_duty_cycle;
+	int run;
+	atomic64_t counter;
+	int event_gpio;
+	int event_irq;
+	struct led_trigger *event_led;		/* event detect */
+	struct led_trigger *run_led;		/* running      */
+	unsigned long event_blink_delay;
+	struct sysfs_dirent *counter_sd;	/* notifier */
+	const char *vsense_name;
+	unsigned int vsense_scale;
+	struct iio_channel *vsense_channel;
+};
+
+static const struct of_device_id bonegeiger_of_match[] = {
+	{
+		.compatible = "bone-geiger-cape",
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(of, bonegeiger_of_match);
+
+static int bonegeiger_start(struct cape_dev *dev)
+{
+	struct bone_geiger_info *info = dev->drv_priv;
+	int duty, period;
+
+	if (info->run != 0)
+		return 0;
+
+	/* checks */
+	if (info->pwm_frequency < 1000 || info->pwm_frequency > 50000) {
+		dev_err(&dev->dev, "Cowardly refusing to use a "
+				"frequency of %d\n",
+				info->pwm_frequency);
+		return -EINVAL;
+	}
+	if (info->pwm_duty_cycle > 80) {
+		dev_err(&dev->dev, "Cowardly refusing to use a "
+				"duty cycle of %d\n",
+				info->pwm_duty_cycle);
+		return -EINVAL;
+	}
+
+	period = div_u64(1000000000LLU, info->pwm_frequency);
+	duty = (period * info->pwm_duty_cycle) / 100;
+
+	dev_info(&dev->dev, "starting geiger tube with "
+			"duty=%duns period=%dus\n",
+			duty, period);
+
+	pwm_config(info->pwm_dev, duty, period);
+	pwm_enable(info->pwm_dev);
+
+	info->run = 1;
+	led_trigger_event(info->run_led, LED_FULL);
+
+	return 0;
+}
+
+static int bonegeiger_stop(struct cape_dev *dev)
+{
+	struct bone_geiger_info *info = dev->drv_priv;
+
+	if (info->run == 0)
+		return 0;
+
+	dev_info(&dev->dev, "disabling geiger tube\n");
+	pwm_config(info->pwm_dev, 0, 50000);	/* 0% duty cycle, 20KHz */
+	pwm_disable(info->pwm_dev);
+
+	info->run = 0;
+	led_trigger_event(info->run_led, LED_OFF);
+
+	return 0;
+}
+
+static ssize_t bonegeiger_show_run(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct cape_dev *cdev = to_cape_dev(dev);
+	struct bone_geiger_info *info = cdev->drv_priv;
+
+	return sprintf(buf, "%d\n", info->run);
+}
+
+static ssize_t bonegeiger_store_run(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct cape_dev *cdev = to_cape_dev(dev);
+	int run, err;
+
+	if (sscanf(buf, "%i", &run) != 1)
+		return -EINVAL;
+
+	if (run)
+		err = bonegeiger_start(cdev);
+	else
+		err = bonegeiger_stop(cdev);
+
+	return err ? err : count;
+}
+
+static ssize_t bonegeiger_show_counter(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct cape_dev *cdev = to_cape_dev(dev);
+	struct bone_geiger_info *info = cdev->drv_priv;
+
+	return sprintf(buf, "%llu\n", atomic64_read(&info->counter));
+}
+
+static ssize_t bonegeiger_store_counter(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct cape_dev *cdev = to_cape_dev(dev);
+	struct bone_geiger_info *info = cdev->drv_priv;
+
+	atomic64_set(&info->counter, 0);	/* just reset */
+	return count;
+}
+
+static ssize_t bonegeiger_show_vsense(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct cape_dev *cdev = to_cape_dev(dev);
+	struct bone_geiger_info *info = cdev->drv_priv;
+	int ret, val;
+	u32 mvolts;
+
+	ret = iio_read_channel_raw(info->vsense_channel, &val);
+	if (ret < 0)
+		return ret;
+
+	/* V = (1800 / 4096) * val * scale) = (1.8 * val * scale / 4096) */
+	mvolts = div_u64(1800 * info->vsense_scale * (u64)val, 4096 * 100);
+
+	return sprintf(buf, "%d\n", mvolts);
+}
+
+static DEVICE_ATTR(run, S_IRUGO | S_IWUSR,
+		bonegeiger_show_run, bonegeiger_store_run);
+static DEVICE_ATTR(counter, S_IRUGO | S_IWUSR,
+		bonegeiger_show_counter, bonegeiger_store_counter);
+static DEVICE_ATTR(vsense, S_IRUGO,
+		bonegeiger_show_vsense, NULL);
+
+static int bonegeiger_sysfs_register(struct cape_dev *cdev)
+{
+	int err;
+
+	err = device_create_file(&cdev->dev, &dev_attr_run);
+	if (err != 0)
+		goto err_no_run;
+
+	err = device_create_file(&cdev->dev, &dev_attr_counter);
+	if (err != 0)
+		goto err_no_counter;
+
+	err = device_create_file(&cdev->dev, &dev_attr_vsense);
+	if (err != 0)
+		goto err_no_vsense;
+
+	return 0;
+
+err_no_vsense:
+	device_remove_file(&cdev->dev, &dev_attr_counter);
+err_no_counter:
+	device_remove_file(&cdev->dev, &dev_attr_run);
+err_no_run:
+	return err;
+}
+
+static void bonegeiger_sysfs_unregister(struct cape_dev *cdev)
+{
+	device_remove_file(&cdev->dev, &dev_attr_vsense);
+	device_remove_file(&cdev->dev, &dev_attr_counter);
+	device_remove_file(&cdev->dev, &dev_attr_run);
+}
+
+static irqreturn_t bonegeiger_irq_handler(int irq, void *dev_id)
+{
+	struct cape_dev *dev = dev_id;
+	struct bone_geiger_info *info = dev->drv_priv;
+
+	atomic64_inc(&info->counter);
+
+	led_trigger_blink_oneshot(info->event_led,
+		  &info->event_blink_delay, &info->event_blink_delay, 0);
+
+	sysfs_notify_dirent(info->counter_sd);
+
+	return IRQ_HANDLED;
+}
+
+static int bonegeiger_probe(struct cape_dev *dev, const struct cape_device_id *id)
+{
+	char boardbuf[33];
+	char versionbuf[5];
+	const char *board_name;
+	const char *version;
+	struct bone_geiger_info *info;
+	struct pinctrl *pinctrl;
+	struct device_node *node, *pwm_node;
+	phandle phandle;
+	u32 val;
+	int err;
+
+	/* boiler plate probing */
+	err = bone_capebus_probe_prolog(dev, id);
+	if (err != 0)
+		return err;
+
+	/* get the board name (after check of cntrlboard match) */
+	board_name = bone_capebus_id_get_field(id, BONE_CAPEBUS_BOARD_NAME,
+			boardbuf, sizeof(boardbuf));
+	/* get the board version */
+	version = bone_capebus_id_get_field(id, BONE_CAPEBUS_VERSION,
+			versionbuf, sizeof(versionbuf));
+	/* should never happen; but check anyway */
+	if (board_name == NULL || version == NULL)
+		return -ENODEV;
+
+	dev->drv_priv = devm_kzalloc(&dev->dev, sizeof(*info), GFP_KERNEL);
+	if (dev->drv_priv == NULL) {
+		dev_err(&dev->dev, "Failed to allocate info\n");
+		err = -ENOMEM;
+		goto err_no_mem;
+	}
+	info = dev->drv_priv;
+
+	pinctrl = devm_pinctrl_get_select_default(&dev->dev);
+	if (IS_ERR(pinctrl))
+		dev_warn(&dev->dev,
+			"pins are not configured from the driver\n");
+
+	node = capebus_of_find_property_node(dev, "version", version, "pwms");
+	if (node == NULL) {
+		dev_err(&dev->dev, "unable to find pwms property\n");
+		err = -ENODEV;
+		goto err_no_pwm;
+	}
+
+	err = of_property_read_u32(node, "pwms", &val);
+	if (err != 0) {
+		dev_err(&dev->dev, "unable to read pwm handle\n");
+		goto err_no_pwm;
+	}
+	phandle = val;
+
+	pwm_node = of_find_node_by_phandle(phandle);
+	if (pwm_node == NULL) {
+		dev_err(&dev->dev, "Failed to pwm node\n");
+		err = -EINVAL;
+		goto err_no_pwm;
+	}
+
+	err = capebus_of_platform_device_enable(pwm_node);
+	of_node_put(pwm_node);
+	if (err != 0) {
+		dev_err(&dev->dev, "Failed to pwm node\n");
+		goto err_no_pwm;
+	}
+
+	info->pwm_dev = of_pwm_request(node, NULL);
+	of_node_put(node);
+	if (IS_ERR(info->pwm_dev)) {
+		dev_err(&dev->dev, "unable to request PWM\n");
+		err = PTR_ERR(info->pwm_dev);
+		goto err_no_pwm;
+	}
+
+	if (capebus_of_property_read_u32(dev,
+				"version", version,
+				"pwm-frequency", &val) != 0) {
+		val = 20000;
+		dev_warn(&dev->dev, "Could not read pwm-frequency property; "
+				"using default %u\n",
+				val);
+	}
+	info->pwm_frequency = val;
+
+	if (capebus_of_property_read_u32(dev,
+				"version", version,
+				"pwm-duty-cycle", &val) != 0) {
+		val = 60;
+		dev_warn(&dev->dev, "Could not read pwm-duty-cycle property; "
+				"using default %u\n",
+				val);
+	}
+	info->pwm_duty_cycle = val;
+
+	node = capebus_of_find_property_node(dev, "gpios", version, "pwms");
+	info->event_gpio = of_get_gpio_flags(node, 0, NULL);
+	of_node_put(node);
+	if (IS_ERR_VALUE(info->event_gpio)) {
+		dev_err(&dev->dev, "unable to get event GPIO\n");
+		err = info->event_gpio;
+		goto err_no_gpio;
+	}
+
+	err = gpio_request_one(info->event_gpio,
+			GPIOF_DIR_IN | GPIOF_EXPORT,
+			"bone-geiger-cape-event");
+	if (err != 0) {
+		dev_err(&dev->dev, "failed to request event GPIO\n");
+		goto err_no_gpio;
+	}
+
+	atomic64_set(&info->counter, 0);
+
+	info->event_irq = gpio_to_irq(info->event_gpio);
+	if (IS_ERR_VALUE(info->event_irq)) {
+		dev_err(&dev->dev, "unable to get event GPIO IRQ\n");
+		err = info->event_irq;
+		goto err_no_irq;
+	}
+
+	err = request_irq(info->event_irq, bonegeiger_irq_handler,
+			IRQF_TRIGGER_RISING | IRQF_SHARED,
+			"bone-geiger-irq", dev);
+	if (err != 0) {
+		dev_err(&dev->dev, "unable to request irq\n");
+		goto err_no_irq;
+	}
+
+	err = bonegeiger_sysfs_register(dev);
+	if (err != 0) {
+		dev_err(&dev->dev, "unable to register sysfs\n");
+		goto err_no_sysfs;
+	}
+
+	info->counter_sd = sysfs_get_dirent(dev->dev.kobj.sd, NULL, "counter");
+	if (info->counter_sd == NULL) {
+		dev_err(&dev->dev, "unable to get dirent of counter\n");
+		err = -ENODEV;
+		goto err_no_counter_dirent;
+	}
+
+	led_trigger_register_simple("geiger-event", &info->event_led);
+	led_trigger_register_simple("geiger-run", &info->run_led);
+
+	/* pick up the generics; tsc & leds */
+	info->geninfo = bone_capebus_probe_generic(dev, id);
+	if (info->geninfo == NULL) {
+		dev_err(&dev->dev, "Could not probe generic\n");
+		goto err_no_generic;
+	}
+
+	led_trigger_event(info->run_led, LED_OFF);
+
+	/* default */
+	if (capebus_of_property_read_u32(dev,
+				"version", version,
+				"event-blink-delay", &val) != 0) {
+		val = 30;
+		dev_warn(&dev->dev, "Could not read event-blink-delay "
+				"property; using default %u\n",
+					val);
+	}
+	info->event_blink_delay = val;
+
+	/* default */
+	if (capebus_of_property_read_string(dev,
+				"version", version,
+				"vsense-name", &info->vsense_name) != 0) {
+		info->vsense_name = "AIN5";
+		dev_warn(&dev->dev, "Could not read vsense-name property; "
+				"using default %u\n",
+					val);
+	}
+
+	if (capebus_of_property_read_u32(dev,
+				"version", version,
+				"vsense-scale", &info->vsense_scale) != 0) {
+		info->vsense_scale = 37325;	/* 373.25 */
+		dev_warn(&dev->dev, "Could not read vsense-scale property; "
+				"using default %u\n",
+					info->vsense_scale);
+	}
+
+	info->vsense_channel = iio_channel_get(NULL, info->vsense_name);
+	if (IS_ERR(info->vsense_channel)) {
+		dev_err(&dev->dev, "Could not get AIN5 analog input\n");
+		err = PTR_ERR(info->vsense_channel);
+		goto err_no_vsense;
+	}
+
+	dev_info(&dev->dev, "ready\n");
+
+	err = bonegeiger_start(dev);
+	if (err != 0) {
+		dev_err(&dev->dev, "Could not start geiger device\n");
+		goto err_no_start;
+	}
+
+	return 0;
+
+err_no_start:
+	iio_channel_release(info->vsense_channel);
+err_no_vsense:
+	bone_capebus_remove_generic(info->geninfo);
+err_no_generic:
+	led_trigger_unregister_simple(info->run_led);
+	led_trigger_unregister_simple(info->event_led);
+	sysfs_put(info->counter_sd);
+err_no_counter_dirent:
+	bonegeiger_sysfs_unregister(dev);
+err_no_sysfs:
+	free_irq(info->event_irq, dev);
+err_no_irq:
+	gpio_free(info->event_gpio);
+err_no_gpio:
+	pwm_put(info->pwm_dev);
+err_no_pwm:
+	devm_kfree(&dev->dev, info);
+err_no_mem:
+	return err;
+}
+
+static void bonegeiger_remove(struct cape_dev *dev)
+{
+	struct bone_geiger_info *info = dev->drv_priv;
+
+	dev_info(&dev->dev, "Removing geiger cape driver...\n");
+
+	bonegeiger_stop(dev);
+
+	iio_channel_release(info->vsense_channel);
+	bone_capebus_remove_generic(info->geninfo);
+	led_trigger_unregister_simple(info->run_led);
+	led_trigger_unregister_simple(info->event_led);
+	sysfs_put(info->counter_sd);
+	bonegeiger_sysfs_unregister(dev);
+	free_irq(info->event_irq, dev);
+	gpio_free(info->event_gpio);
+	pwm_put(info->pwm_dev);
+}
+
+struct cape_driver bonegeiger_driver = {
+	.driver = {
+		.name		= "bonegeiger",
+		.owner		= THIS_MODULE,
+		.of_match_table	= bonegeiger_of_match,
+	},
+	.probe		= bonegeiger_probe,
+	.remove		= bonegeiger_remove,
+};
+
+module_capebus_driver(bonegeiger_driver);
+
+MODULE_AUTHOR("Pantelis Antoniou");
+MODULE_DESCRIPTION("Beaglebone geiger cape");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bone-geiger-cape");
-- 
1.7.12

^ permalink raw reply related

* [RFC 3/7] capebus: Beaglebone generic cape support
From: Pantelis Antoniou @ 2012-10-31 16:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351702333-8456-1-git-send-email-panto@antoniou-consulting.com>

Introducing beaglebone generic cape support.

With this you can create almost any kind of cape driver
that doesn't require complex interconnection of the parts.

Most beaglebone capes can be created with this, including
all the display capes (DVI/VGA/LCD) with touchscreen or not,
capes that only use i2c or spi devices, gpio-keys, leds etc.

Signed-off-by: Pantelis Antoniou <panto@antoniou-consulting.com>
---
 drivers/capebus/capes/Kconfig             |  6 ++
 drivers/capebus/capes/Makefile            |  1 +
 drivers/capebus/capes/bone-generic-cape.c | 96 +++++++++++++++++++++++++++++++
 3 files changed, 103 insertions(+)
 create mode 100644 drivers/capebus/capes/Kconfig
 create mode 100644 drivers/capebus/capes/Makefile
 create mode 100644 drivers/capebus/capes/bone-generic-cape.c

diff --git a/drivers/capebus/capes/Kconfig b/drivers/capebus/capes/Kconfig
new file mode 100644
index 0000000..bfe54a6
--- /dev/null
+++ b/drivers/capebus/capes/Kconfig
@@ -0,0 +1,6 @@
+config CAPEBUS_BONE_GENERIC
+	tristate "Beaglebone Generic cape driver"
+	depends on CAPEBUS_BONE_CONTROLLER
+	default n
+	help
+	  "Select this to enable a generic cape driver; LCD/DVI capes etc"
diff --git a/drivers/capebus/capes/Makefile b/drivers/capebus/capes/Makefile
new file mode 100644
index 0000000..83da381
--- /dev/null
+++ b/drivers/capebus/capes/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_CAPEBUS_BONE_GENERIC)	+= bone-generic-cape.o
diff --git a/drivers/capebus/capes/bone-generic-cape.c b/drivers/capebus/capes/bone-generic-cape.c
new file mode 100644
index 0000000..70be50a
--- /dev/null
+++ b/drivers/capebus/capes/bone-generic-cape.c
@@ -0,0 +1,96 @@
+/*
+ * Generic cape support
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
+ * Copyright (C) 2012 Texas Instruments Inc.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/atomic.h>
+#include <linux/clk.h>
+#include <asm/barrier.h>
+#include <plat/clock.h>
+#include <plat/omap_device.h>
+#include <linux/clkdev.h>
+#include <linux/input/ti_am335x_tsc.h>
+#include <linux/platform_data/ti_am335x_adc.h>
+#include <linux/mfd/ti_am335x_tscadc.h>
+
+#include <linux/capebus/capebus-bone.h>
+
+/* fwd decl. */
+extern struct cape_driver bonegeneric_driver;
+
+static const struct of_device_id bonegeneric_of_match[] = {
+	{
+		.compatible = "bone-generic-cape",
+	},	{ },
+};
+MODULE_DEVICE_TABLE(of, bonegeneric_of_match);
+
+static int bonegeneric_probe(struct cape_dev *dev,
+		const struct cape_device_id *id)
+{
+	struct bone_capebus_generic_info *ginfo;
+	int err;
+
+	err = bone_capebus_probe_prolog(dev, id);
+	if (err != 0)
+		return err;
+
+	ginfo = bone_capebus_probe_generic(dev, id);
+	if (IS_ERR_OR_NULL(ginfo))
+		return IS_ERR(ginfo) ? PTR_ERR(ginfo) : -ENODEV;
+	dev->drv_priv = ginfo;
+	return 0;
+}
+
+static void bonegeneric_remove(struct cape_dev *dev)
+{
+	bone_capebus_remove_generic(dev->drv_priv);
+}
+
+struct cape_driver bonegeneric_driver = {
+	.driver = {
+		.name		= "bonegeneric",
+		.owner		= THIS_MODULE,
+		.of_match_table	= bonegeneric_of_match,
+	},
+	.probe		= bonegeneric_probe,
+	.remove		= bonegeneric_remove,
+};
+
+module_capebus_driver(bonegeneric_driver);
+
+MODULE_AUTHOR("Pantelis Antoniou");
+MODULE_DESCRIPTION("Beaglebone generic cape");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bone-generic-cape");
-- 
1.7.12

^ permalink raw reply related

* [RFC 2/7] capebus: Add beaglebone board support
From: Pantelis Antoniou @ 2012-10-31 16:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351702333-8456-1-git-send-email-panto@antoniou-consulting.com>

Introduce beaglebone capebus board support.

This patch creates the beaglebone's board cape bus controller.

The board controller is responsible for the probing of capes
at the well defined I2C address for capes, parsing the EEPROM
info and matching them to specific cape drivers.

On top of that, adapter DT enabled devices are created for
am33xx devices that have no DT bindings yet, as well as generic
devices that can be used as building blocks for the cape drivers.

Signed-off-by: Pantelis Antoniou <panto@antoniou-consulting.com>
---
 drivers/capebus/boards/Kconfig                |   6 +
 drivers/capebus/boards/Makefile               |   3 +
 drivers/capebus/boards/capebus-bone-generic.c | 237 +++++++
 drivers/capebus/boards/capebus-bone-pdevs.c   | 602 +++++++++++++++++
 drivers/capebus/boards/capebus-bone.c         | 931 ++++++++++++++++++++++++++
 include/linux/capebus/capebus-bone.h          | 120 ++++
 6 files changed, 1899 insertions(+)
 create mode 100644 drivers/capebus/boards/Kconfig
 create mode 100644 drivers/capebus/boards/Makefile
 create mode 100644 drivers/capebus/boards/capebus-bone-generic.c
 create mode 100644 drivers/capebus/boards/capebus-bone-pdevs.c
 create mode 100644 drivers/capebus/boards/capebus-bone.c
 create mode 100644 include/linux/capebus/capebus-bone.h

diff --git a/drivers/capebus/boards/Kconfig b/drivers/capebus/boards/Kconfig
new file mode 100644
index 0000000..76b0f94
--- /dev/null
+++ b/drivers/capebus/boards/Kconfig
@@ -0,0 +1,6 @@
+config CAPEBUS_BONE_CONTROLLER
+	bool "Beaglebone capebus board controller"
+	depends on CAPEBUS && ARCH_OMAP2PLUS && OF && I2C
+	default n
+	help
+	  "Select this to enable the beaglebone capebus board controller"
diff --git a/drivers/capebus/boards/Makefile b/drivers/capebus/boards/Makefile
new file mode 100644
index 0000000..9048231
--- /dev/null
+++ b/drivers/capebus/boards/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_CAPEBUS_BONE_CONTROLLER)	+= capebus-bone.o \
+					   capebus-bone-pdevs.o \
+					   capebus-bone-generic.o \
diff --git a/drivers/capebus/boards/capebus-bone-generic.c b/drivers/capebus/boards/capebus-bone-generic.c
new file mode 100644
index 0000000..b1b79eb
--- /dev/null
+++ b/drivers/capebus/boards/capebus-bone-generic.c
@@ -0,0 +1,237 @@
+/*
+ * TI Beaglebone capebus controller - Generic devices
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
+ * Copyright (C) 2012 Texas Instruments Inc.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <video/da8xx-fb.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/atomic.h>
+#include <linux/clk.h>
+#include <asm/barrier.h>
+#include <plat/clock.h>
+#include <plat/omap_device.h>
+#include <linux/clkdev.h>
+#include <linux/input/ti_am335x_tsc.h>
+#include <linux/platform_data/ti_am335x_adc.h>
+#include <linux/mfd/ti_am335x_tscadc.h>
+
+#include <linux/capebus/capebus-bone.h>
+
+int bone_capebus_probe_prolog(struct cape_dev *dev,
+		const struct cape_device_id *id)
+{
+	char boardbuf[33];
+	char versionbuf[5];
+	const char *board_name;
+	const char *version;
+	const struct of_device_id *match;
+	struct pinctrl *pinctrl;
+
+	/* get the board name (also matches the cntrlboard before checking) */
+	board_name = bone_capebus_id_get_field(id, BONE_CAPEBUS_BOARD_NAME,
+			boardbuf, sizeof(boardbuf));
+	if (board_name == NULL)
+		return -ENODEV;
+
+	/* match compatible? */
+	match = capebus_of_match_device(dev, "board-name", board_name);
+	if (match == NULL)
+		return -ENODEV;
+
+	/* get the board version */
+	version = bone_capebus_id_get_field(id, BONE_CAPEBUS_VERSION,
+			versionbuf, sizeof(versionbuf));
+	if (version == NULL)
+		return -ENODEV;
+
+	pinctrl = devm_pinctrl_get_select_default(&dev->dev);
+	if (IS_ERR(pinctrl))
+		dev_warn(&dev->dev,
+			"pins are not configured from the driver\n");
+
+	dev_info(&dev->dev, "%s: V=%s '%s'\n", board_name,
+			version, match->compatible);
+
+	return 0;
+}
+EXPORT_SYMBOL(bone_capebus_probe_prolog);
+
+static const struct bone_capebus_generic_device_data gendevs[] = {
+	{
+		.name	= "leds",
+		.of_match = (const struct of_device_id []) {
+				{ .compatible = "gpio-leds", }, { },
+			},
+		.units	 = 0,	/* no limit */
+	}, {
+		.name	= "tps-bl",
+		.of_match = (const struct of_device_id []) {
+				{ .compatible = "tps65217-backlight", }, { },
+			},
+		.units	 = 0,	/* no limit */
+	}, {
+		.name	= "keys",
+		.of_match = (const struct of_device_id []) {
+				{ .compatible = "gpio-keys", }, { },
+			},
+		.units	 = 0,	/* no limit */
+	}, {
+		.name	= "tscadc",
+		.of_match = (const struct of_device_id []) {
+				{ .compatible = "ti-tscadc-dt", }, { },
+			},
+		.units	 = 1,
+	}, {
+		.name	= "lcdc",
+		.of_match = (const struct of_device_id []) {
+				{ .compatible = "da8xx-dt", }, { },
+			},
+		.units	 = 1,
+	},{
+		.name	= "i2c-dt",
+		.of_match = (const struct of_device_id []) {
+				{ .compatible = "i2c-dt", }, { },
+			},
+		.units	 = 0,
+	}, {
+		.name	= "w1-gpio",
+		.of_match = (const struct of_device_id []) {
+				{ .compatible = "w1-gpio", }, { },
+			},
+		.units	 = 0,
+	}, {
+		.name	= "pwm-backlight",
+		.of_match = (const struct of_device_id []) {
+				{ .compatible = "pwm-backlight", }, { },
+			},
+		.units	 = 0,	/* no limit */
+	}, {
+		.name	= "spi-dt",
+		.of_match = (const struct of_device_id []) {
+				{ .compatible = "spi-dt", }, { },
+			},
+		.units	 = 0,	/* no limit */
+	}
+};
+
+struct bone_capebus_generic_info *
+bone_capebus_probe_generic(struct cape_dev *dev,
+		const struct cape_device_id *id)
+{
+	struct bone_capebus_generic_info *info;
+	char boardbuf[33];
+	char versionbuf[5];
+	const char *board_name;
+	const char *version;
+	struct platform_device *pdev;
+	const struct bone_capebus_generic_device_data *dd;
+	struct bone_capebus_generic_device_entry *de;
+	int i;
+
+	/* get the board name (also matches the cntrlboard before checking) */
+	board_name = bone_capebus_id_get_field(id, BONE_CAPEBUS_BOARD_NAME,
+			boardbuf, sizeof(boardbuf));
+	/* get the board version */
+	version = bone_capebus_id_get_field(id, BONE_CAPEBUS_VERSION,
+			versionbuf, sizeof(versionbuf));
+
+	/* should never happen, but it doesn't hurt to play it safe */
+	if (board_name == NULL || version == NULL)
+		return ERR_PTR(-ENODEV);
+
+	info = devm_kzalloc(&dev->dev, sizeof(*info), GFP_KERNEL);
+	if (info == NULL) {
+		dev_err(&dev->dev, "Failed to allocate info\n");
+		return ERR_PTR(-ENOMEM);
+	}
+	info->dev = dev;
+	INIT_LIST_HEAD(&info->pdev_list);
+
+	/* iterate over the supported devices */
+	for (i = 0, dd = gendevs; i < ARRAY_SIZE(gendevs); i++, dd++) {
+
+		pdev = capebus_of_platform_compatible_device_create(dev,
+			dd->of_match, dd->name, "version", version);
+
+		/* node not found (mostly harmless) */
+		if (IS_ERR(pdev) && PTR_ERR(pdev) == -ENXIO) {
+			/* TODO: deal with required nodes */
+			continue;
+		}
+
+		/* failed to create due to an error; fatal */
+		if (IS_ERR_OR_NULL(pdev)) {
+			dev_err(&dev->dev, "failed to create device %s\n",
+					dd->name);
+			goto err_fail;
+		}
+
+		de = devm_kzalloc(&dev->dev, sizeof(*de), GFP_KERNEL);
+		if (de == NULL) {
+			dev_err(&dev->dev, "failed to allocate entry for %s\n",
+					dd->name);
+			goto err_fail;
+		}
+
+		/* add it to the list */
+		de->data = dd;
+		de->pdev = pdev;
+		list_add_tail(&de->node, &info->pdev_list);
+	}
+
+	return info;
+
+err_fail:
+	bone_capebus_remove_generic(info);
+	return NULL;
+}
+EXPORT_SYMBOL(bone_capebus_probe_generic);
+
+void bone_capebus_remove_generic(struct bone_capebus_generic_info *info)
+{
+	struct list_head *lh, *lhn;
+	struct bone_capebus_generic_device_entry *de;
+
+	if (info == NULL || info->dev == NULL)
+		return;
+
+	list_for_each_safe(lh, lhn, &info->pdev_list) {
+		de = list_entry(lh, struct bone_capebus_generic_device_entry,
+				node);
+		list_del(lh);
+		platform_device_unregister(de->pdev);
+		devm_kfree(&info->dev->dev, de);
+	}
+	devm_kfree(&info->dev->dev, info);
+}
+EXPORT_SYMBOL(bone_capebus_remove_generic);
diff --git a/drivers/capebus/boards/capebus-bone-pdevs.c b/drivers/capebus/boards/capebus-bone-pdevs.c
new file mode 100644
index 0000000..a55aad6
--- /dev/null
+++ b/drivers/capebus/boards/capebus-bone-pdevs.c
@@ -0,0 +1,602 @@
+/*
+ * TI Beaglebone capebus controller - Platform adapters
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
+ * Copyright (C) 2012 Texas Instruments Inc.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <video/da8xx-fb.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/atomic.h>
+#include <linux/clk.h>
+#include <asm/barrier.h>
+#include <plat/clock.h>
+#include <plat/omap_device.h>
+#include <linux/clkdev.h>
+#include <linux/input/ti_am335x_tsc.h>
+#include <linux/platform_data/ti_am335x_adc.h>
+#include <linux/mfd/ti_am335x_tscadc.h>
+#include <linux/i2c.h>
+#include <linux/of_i2c.h>
+#include <linux/spi/spi.h>
+
+#include <linux/capebus/capebus-bone.h>
+
+#if defined(CONFIG_FB_DA8XX) || defined(CONFIG_FB_DA8XX_MODULE)
+
+struct da8xx_priv {
+	struct da8xx_lcdc_platform_data lcd_pdata;
+	struct lcd_ctrl_config lcd_cfg;
+	struct display_panel lcd_panel;
+	struct platform_device *lcdc_pdev;
+	struct omap_hwmod *lcdc_oh;
+	struct resource lcdc_res[1];
+	int power_dn_gpio;
+};
+
+static const struct of_device_id of_da8xx_dt_match[] = {
+	{ .compatible = "da8xx-dt", },
+	{},
+};
+
+static int __devinit da8xx_dt_probe(struct platform_device *pdev)
+{
+	struct da8xx_priv *priv;
+	struct clk *disp_pll;
+	struct pinctrl *pinctrl;
+	u32 disp_pll_val;
+	const char *panel_type;
+	int ret = -EINVAL;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (priv == NULL) {
+		dev_err(&pdev->dev, "Failed to allocate priv\n");
+		return -ENOMEM;
+	}
+	priv->power_dn_gpio = -1;
+
+	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+	if (IS_ERR(pinctrl))
+		dev_warn(&pdev->dev,
+			"pins are not configured from the driver\n");
+
+	ret = of_property_read_u32(pdev->dev.of_node, "disp-pll", &disp_pll_val);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to read disp-pll property\n");
+		return ret;
+	}
+
+	ret = of_property_read_string(pdev->dev.of_node, "panel-type", &panel_type);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to read panel-type property\n");
+		return ret;
+	}
+
+	/* conf_disp_pll(disp_pll); */
+	disp_pll = clk_get(NULL, "dpll_disp_ck");
+	if (IS_ERR(disp_pll)) {
+		dev_err(&pdev->dev, "Cannot clk_get disp_pll\n");
+		return PTR_ERR(disp_pll);
+	}
+	ret = clk_set_rate(disp_pll, disp_pll_val);
+	clk_put(disp_pll);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to set disp_pll\n");
+		return ret;
+	}
+
+	ret = of_get_named_gpio_flags(pdev->dev.of_node, "powerdn-gpio",
+			0, NULL);
+	if (IS_ERR_VALUE(ret)) {
+		dev_info(&pdev->dev, "No power down GPIO\n");
+	} else {
+		priv->power_dn_gpio = ret;
+
+		ret = devm_gpio_request(&pdev->dev, priv->power_dn_gpio, "bone-dvi-cape:DVI_PDN");
+		if (ret != 0) {
+			dev_err(&pdev->dev, "Failed to gpio_request\n");
+			return ret;
+		}
+
+		ret = gpio_direction_output(priv->power_dn_gpio, 1);
+		if (ret != 0) {
+			dev_err(&pdev->dev, "Failed to set powerdn to 1\n");
+			return ret;
+		}
+	}
+
+	/* display_panel */
+	priv->lcd_panel.panel_type	= QVGA;
+	priv->lcd_panel.max_bpp		= 16;
+	priv->lcd_panel.min_bpp		= 16;
+	priv->lcd_panel.panel_shade	= COLOR_ACTIVE;
+
+	/* lcd_ctrl_config */
+	priv->lcd_cfg.p_disp_panel	= &priv->lcd_panel;
+	priv->lcd_cfg.ac_bias		= 255;
+	priv->lcd_cfg.ac_bias_intrpt	= 0;
+	priv->lcd_cfg.dma_burst_sz	= 16;
+	priv->lcd_cfg.bpp		= 16;
+	priv->lcd_cfg.fdd		= 0x80;
+	priv->lcd_cfg.tft_alt_mode	= 0;
+	priv->lcd_cfg.stn_565_mode	= 0;
+	priv->lcd_cfg.mono_8bit_mode	= 0;
+	priv->lcd_cfg.invert_line_clock	= 1;
+	priv->lcd_cfg.invert_frm_clock	= 1;
+	priv->lcd_cfg.sync_edge		= 0;
+	priv->lcd_cfg.sync_ctrl		= 1;
+	priv->lcd_cfg.raster_order	= 0;
+
+	/* da8xx_lcdc_platform_data */
+	strcpy(priv->lcd_pdata.manu_name, "BBToys");
+	priv->lcd_pdata.controller_data = &priv->lcd_cfg;
+	strcpy(priv->lcd_pdata.type, panel_type);
+
+	priv->lcdc_oh = omap_hwmod_lookup("lcdc");
+	if (priv->lcdc_oh == NULL) {
+		dev_err(&pdev->dev, "Failed to lookup omap_hwmod lcdc\n");
+		return -ENODEV;
+	}
+
+	priv->lcdc_pdev = omap_device_build("da8xx_lcdc", 0, priv->lcdc_oh,
+			&priv->lcd_pdata,
+			sizeof(struct da8xx_lcdc_platform_data),
+			NULL, 0, 0);
+	if (priv->lcdc_pdev == NULL) {
+		dev_err(&pdev->dev, "Failed to build LCDC device\n");
+		return -ENODEV;
+	}
+
+	dev_info(&pdev->dev, "Registered bone LCDC OK.\n");
+
+	platform_set_drvdata(pdev, priv);
+
+	return 0;
+}
+
+static int __devexit da8xx_dt_remove(struct platform_device *pdev)
+{
+	return -EINVAL;	/* not supporting removal yet */
+}
+
+static struct platform_driver da8xx_dt_driver = {
+	.probe		= da8xx_dt_probe,
+	.remove		= __devexit_p(da8xx_dt_remove),
+	.driver		= {
+		.name	= "da8xx-dt",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_da8xx_dt_match,
+	},
+};
+
+#endif
+
+#if defined(CONFIG_MFD_TI_AM335X_TSCADC) || defined(CONFIG_MFD_TI_AM335X_TSCADC_MODULE)
+
+struct ti_tscadc_priv {
+	struct omap_hwmod *tsc_oh;
+	struct tsc_data tsc_data;
+	struct adc_data adc_data;
+	struct mfd_tscadc_board tscadc_data;
+	struct platform_device *tscadc_pdev;
+};
+
+static const struct of_device_id of_ti_tscadc_dt_match[] = {
+	{ .compatible = "ti-tscadc-dt", },
+	{},
+};
+
+static int __devinit ti_tscadc_dt_probe(struct platform_device *pdev)
+{
+	struct ti_tscadc_priv *priv;
+	struct pinctrl *pinctrl;
+	u32 val;
+	int ret;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (priv == NULL) {
+		dev_err(&pdev->dev, "Failed to allocate priv\n");
+		return -ENOMEM;
+	}
+
+	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+	if (IS_ERR(pinctrl))
+		dev_warn(&pdev->dev,
+			"pins are not configured from the driver\n");
+
+	ret = of_property_read_u32(pdev->dev.of_node, "tsc-wires", &val);
+	if (ret != 0) {
+		dev_info(&pdev->dev, "no tsc-wires property; disabling TSC\n");
+		val = 0;
+	}
+	priv->tsc_data.wires = val;
+
+	if (priv->tsc_data.wires > 0) {
+		ret = of_property_read_u32(pdev->dev.of_node,
+				"tsc-x-plate-resistance", &val);
+		if (ret != 0) {
+			dev_err(&pdev->dev, "Failed to read "
+					"tsc-x-plate-resistance property\n");
+			return ret;
+		}
+		priv->tsc_data.x_plate_resistance = val;
+
+		ret = of_property_read_u32(pdev->dev.of_node,
+				"tsc-steps", &val);
+		if (ret != 0) {
+			dev_err(&pdev->dev, "Failed to read "
+					"tsc-steps property\n");
+			return ret;
+		}
+		priv->tsc_data.steps_to_configure = val;
+	}
+
+	ret = of_property_read_u32(pdev->dev.of_node, "adc-channels", &val);
+	if (ret != 0) {
+		dev_info(&pdev->dev, "No adc-channels property; disabling adc\n");
+		val = 0;
+	}
+	priv->adc_data.adc_channels = val;
+
+	priv->tscadc_data.tsc_init = &priv->tsc_data;
+	priv->tscadc_data.adc_init = &priv->adc_data;
+
+	priv->tsc_oh = omap_hwmod_lookup("adc_tsc");
+	if (priv->tsc_oh == NULL) {
+		dev_err(&pdev->dev, "Could not lookup HWMOD %s\n", "adc_tsc");
+		return -ENODEV;
+	}
+
+	priv->tscadc_pdev = omap_device_build("ti_tscadc", -1, priv->tsc_oh,
+			&priv->tscadc_data, sizeof(priv->tscadc_data),
+			NULL, 0, 0);
+	if (priv->tscadc_pdev == NULL) {
+		dev_err(&pdev->dev, "Could not create tsc_adc device\n");
+		return -ENODEV;
+	}
+
+	dev_info(&pdev->dev, "TI tscadc pdev created OK\n");
+
+	platform_set_drvdata(pdev, priv);
+
+	return 0;
+}
+
+static int __devexit ti_tscadc_dt_remove(struct platform_device *pdev)
+{
+	return -EINVAL;	/* not supporting removal yet */
+}
+
+static struct platform_driver ti_tscadc_dt_driver = {
+	.probe		= ti_tscadc_dt_probe,
+	.remove		= __devexit_p(ti_tscadc_dt_remove),
+	.driver		= {
+		.name	= "ti_tscadc-dt",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_ti_tscadc_dt_match,
+	},
+};
+
+#endif
+
+struct i2c_priv {
+	struct i2c_adapter *i2c_adapter;
+	phandle parent_handle;
+};
+
+static const struct of_device_id of_i2c_dt_match[] = {
+	{ .compatible = "i2c-dt", },
+	{},
+};
+
+static int __devinit i2c_dt_probe(struct platform_device *pdev)
+{
+	struct i2c_priv *priv = NULL;
+	int ret = -EINVAL;
+	struct device_node *adap_node;
+	u32 val;
+
+	if (pdev->dev.of_node == NULL) {
+		dev_err(&pdev->dev, "Only support OF case\n");
+		return -ENOMEM;
+	}
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (priv == NULL) {
+		dev_err(&pdev->dev, "Failed to allocate priv\n");
+		return -ENOMEM;
+	}
+
+	ret = of_property_read_u32(pdev->dev.of_node, "parent", &val);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to find parent property\n");
+		goto err_prop_fail;
+	}
+	priv->parent_handle = val;
+
+	adap_node = of_find_node_by_phandle(priv->parent_handle);
+	if (adap_node == NULL) {
+		dev_err(&pdev->dev, "Failed to find i2c adapter node\n");
+		ret = -EINVAL;
+		goto err_node_fail;
+	}
+
+	ret = capebus_of_platform_device_enable(adap_node);
+	if (ret != 0) {
+		dev_info(&pdev->dev, "I2C adapter platform device failed "
+				"to enable\n");
+		goto err_enable_fail;
+	}
+
+	priv->i2c_adapter = of_find_i2c_adapter_by_node(adap_node);
+	if (priv->i2c_adapter == NULL) {
+		dev_err(&pdev->dev, "Failed to find i2c adapter node\n");
+		ret = -EINVAL;
+		goto err_adap_fail;
+	}
+
+	of_i2c_register_node_devices(priv->i2c_adapter, pdev->dev.of_node);
+
+	of_node_put(adap_node);
+
+	dev_info(&pdev->dev, "Registered bone I2C OK.\n");
+
+	platform_set_drvdata(pdev, priv);
+
+	return 0;
+err_adap_fail:
+	of_node_put(adap_node);
+err_enable_fail:
+	/* nothing */
+err_node_fail:
+	/* nothing */
+err_prop_fail:
+	devm_kfree(&pdev->dev, priv);
+	return ret;
+}
+
+static int __devexit i2c_dt_remove(struct platform_device *pdev)
+{
+	return -EINVAL;	/* not supporting removal yet */
+}
+
+static struct platform_driver i2c_dt_driver = {
+	.probe		= i2c_dt_probe,
+	.remove		= __devexit_p(i2c_dt_remove),
+	.driver		= {
+		.name	= "i2c-dt",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_i2c_dt_match,
+	},
+};
+
+struct spi_priv {
+	struct spi_master *master;
+	phandle parent_handle;
+};
+
+static const struct of_device_id of_spi_dt_match[] = {
+	{ .compatible = "spi-dt", },
+	{},
+};
+
+static int of_dev_node_match(struct device *dev, void *data)
+{
+        return dev->of_node == data;
+}
+
+/* must call put_device() when done with returned i2c_adapter device */
+static struct spi_master *of_find_spi_master_by_node(struct device_node *node)
+{
+	struct device *dev;
+	struct spi_master *master;
+
+	dev = class_find_device(&spi_master_class, NULL, node,
+					 of_dev_node_match);
+	if (!dev)
+		return NULL;
+
+	master = container_of(dev, struct spi_master, dev);
+
+	/* TODO: No checks what-so-ever... be careful. */
+	return master;
+}
+
+static int __devinit spi_dt_probe(struct platform_device *pdev)
+{
+	struct spi_priv *priv = NULL;
+	int ret = -EINVAL;
+	struct device_node *master_node;
+	u32 val;
+
+	if (pdev->dev.of_node == NULL) {
+		dev_err(&pdev->dev, "Only support OF case\n");
+		return -ENOMEM;
+	}
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (priv == NULL) {
+		dev_err(&pdev->dev, "Failed to allocate priv\n");
+		return -ENOMEM;
+	}
+
+	ret = of_property_read_u32(pdev->dev.of_node, "parent", &val);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to find parent property\n");
+		goto err_prop_fail;
+	}
+	priv->parent_handle = val;
+
+	master_node = of_find_node_by_phandle(priv->parent_handle);
+	if (master_node == NULL) {
+		dev_err(&pdev->dev, "Failed to find spi bus master node\n");
+		ret = -EINVAL;
+		goto err_node_fail;
+	}
+
+	ret = capebus_of_platform_device_enable(master_node);
+	if (ret != 0) {
+		dev_info(&pdev->dev, "SPI platform device failed to enable\n");
+		goto err_enable_fail;
+	}
+
+	priv->master = of_find_spi_master_by_node(master_node);
+	if (priv->master == NULL) {
+		dev_err(&pdev->dev, "Failed to find bus master node\n");
+		ret = -EINVAL;
+		goto err_master_fail;
+	}
+
+	of_register_node_spi_devices(priv->master, pdev->dev.of_node);
+
+	of_node_put(master_node);
+
+	dev_info(&pdev->dev, "Registered bone SPI OK.\n");
+
+	platform_set_drvdata(pdev, priv);
+
+	return 0;
+err_master_fail:
+	of_node_put(master_node);
+err_enable_fail:
+	/* nothing */
+err_node_fail:
+	/* nothing */
+err_prop_fail:
+	devm_kfree(&pdev->dev, priv);
+	return ret;
+}
+
+static int __devexit spi_dt_remove(struct platform_device *pdev)
+{
+	return -EINVAL;	/* not supporting removal yet */
+}
+
+static struct platform_driver spi_dt_driver = {
+	.probe		= spi_dt_probe,
+	.remove		= __devexit_p(spi_dt_remove),
+	.driver		= {
+		.name	= "spi-dt",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_spi_dt_match,
+	},
+};
+
+/*
+ *
+ */
+struct bone_capebus_pdev_driver {
+	struct platform_driver *driver;
+	unsigned int registered : 1;
+	/* more? */
+};
+
+static struct bone_capebus_pdev_driver pdev_drivers[] = {
+#if defined(CONFIG_FB_DA8XX) || defined(CONFIG_FB_DA8XX_MODULE)
+	{
+		.driver		= &da8xx_dt_driver,
+	},
+#endif
+#if defined(CONFIG_MFD_TI_AM335X_TSCADC) || defined(CONFIG_MFD_TI_AM335X_TSCADC_MODULE)
+	{
+		.driver		= &ti_tscadc_dt_driver,
+	},
+#endif
+	{
+		.driver		= &i2c_dt_driver,
+	},
+	{
+		.driver		= &spi_dt_driver,
+	},
+	{
+		.driver		= NULL,
+	}
+};
+
+int bone_capebus_register_pdev_adapters(struct bone_capebus_bus *bus)
+{
+	struct bone_capebus_pdev_driver *drvp;
+	int err;
+
+	/* first check if we do it twice */
+	for (drvp = pdev_drivers; drvp->driver != NULL; drvp++)
+		if (drvp->registered)
+			return -EBUSY;
+
+	for (drvp = pdev_drivers; drvp->driver != NULL; drvp++) {
+
+		err = platform_driver_register(drvp->driver);
+		if (err != 0)
+			goto err_out;
+
+		drvp->registered = 1;
+
+		dev_info(bus->dev, "Registered %s "
+				"platform driver\n", drvp->driver->driver.name);
+	}
+
+	return 0;
+
+err_out:
+	dev_err(bus->dev, "Failed to register %s "
+			"platform driver\n", drvp->driver->driver.name);
+
+	/* unregister */
+	while (--drvp >= pdev_drivers) {
+
+		if (!drvp->registered)
+			continue;
+
+		platform_driver_unregister(drvp->driver);
+	}
+
+	return err;
+}
+
+void bone_capebus_unregister_pdev_adapters(struct bone_capebus_bus *bus)
+{
+	struct bone_capebus_pdev_driver *drvp;
+
+	/* unregister */
+	drvp = &pdev_drivers[ARRAY_SIZE(pdev_drivers)];
+	while (--drvp >= pdev_drivers) {
+
+		if (drvp->driver == NULL)	/* skip terminator */
+			continue;
+
+		if (!drvp->registered)
+			continue;
+
+		platform_driver_unregister(drvp->driver);
+
+		drvp->registered = 0;
+	}
+}
diff --git a/drivers/capebus/boards/capebus-bone.c b/drivers/capebus/boards/capebus-bone.c
new file mode 100644
index 0000000..33a6bde
--- /dev/null
+++ b/drivers/capebus/boards/capebus-bone.c
@@ -0,0 +1,931 @@
+/*
+ * TI Beaglebone capebus controller
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
+ * Copyright (C) 2012 Texas Instruments Inc.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_i2c.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/err.h>
+#include <linux/ctype.h>
+
+#include <linux/capebus.h>
+#include <linux/capebus/capebus-bone.h>
+
+/* what to fill in to the cntrlboard field of the id */
+#define BONE_CAPEBUS_CNTRLBOARD		"beaglebone"
+
+/* various EEPROM definition for the bone */
+struct bone_capebus_eeprom_field {
+	const char 	*name;
+	int 		start;
+	int		size;
+	unsigned int	ascii : 1;
+	unsigned int	strip_trailing_dots : 1;
+	const char	*override;
+};
+
+static const struct bone_capebus_eeprom_field eeprom_fields[] = {
+	[BONE_CAPEBUS_HEADER] = {
+		.name		= "header",
+		.start		= 0,
+		.size		= 4,
+		.ascii		= 0,
+		.override	= "\xaa\x55\x33\xee",	/* AA 55 33 EE */
+	},
+	[BONE_CAPEBUS_EEPROM_REV] = {
+		.name		= "eeprom-format-revision",
+		.start		= 4,
+		.size		= 2,
+		.ascii		= 1,
+		.override	= "A0",
+	},
+	[BONE_CAPEBUS_BOARD_NAME] = {
+		.name		= "board-name",
+		.start		= 6,
+		.size		= 32,
+		.ascii		= 1,
+		.strip_trailing_dots = 1,
+		.override	= "Override Board Name",
+	},
+	[BONE_CAPEBUS_VERSION] = {
+		.name		= "version",
+		.start		= 38,
+		.size		= 4,
+		.ascii		= 1,
+		.override	= "00A0",
+	},
+	[BONE_CAPEBUS_MANUFACTURER] = {
+		.name		= "manufacturer",
+		.start		= 42,
+		.size		= 16,
+		.ascii		= 1,
+		.strip_trailing_dots = 1,
+		.override	= "Override Manuf",
+	},
+	[BONE_CAPEBUS_PART_NUMBER] = {
+		.name		= "part-number",
+		.start		= 58,
+		.size		= 16,
+		.ascii		= 1,
+		.override	= "Override Part#",
+	},
+	[BONE_CAPEBUS_NUMBER_OF_PINS] = {
+		.name		= "number-of-pins",
+		.start		= 74,
+		.size		= 2,
+		.ascii		= 0,
+		.override	= NULL,
+	},
+	[BONE_CAPEBUS_SERIAL_NUMBER] = {
+		.name		= "serial-number",
+		.start		= 76,
+		.size		= 12,
+		.ascii		= 1,
+		.override	= "0000000000",
+	},
+	[BONE_CAPEBUS_PIN_USAGE] = {
+		.name		= "pin-usage",
+		.start		= 88,
+		.size		= 140,
+		.ascii		= 0,
+		.override	= NULL,
+	},
+	[BONE_CAPEBUS_VDD_3V3EXP] = {
+		.name		= "vdd-3v3exp",
+		.start		= 228,
+		.size		= 2,
+		.ascii		= 0,
+		.override	= NULL,
+	},
+	[BONE_CAPEBUS_VDD_5V] = {
+		.name		= "vdd-5v",
+		.start		= 230,
+		.size		= 2,
+		.ascii		= 0,
+		.override	= NULL,
+	},
+	[BONE_CAPEBUS_SYS_5V] = {
+		.name		= "sys-5v",
+		.start		= 232,
+		.size		= 2,
+		.ascii		= 0,
+		.override	= NULL,
+	},
+	[BONE_CAPEBUS_DC_SUPPLIED] = {
+		.name		= "dc-supplied",
+		.start		= 234,
+		.size		= 2,
+		.ascii		= 0,
+		.override	= NULL,
+	},
+};
+
+char *bone_capebus_id_get_field(const struct cape_device_id *id,
+		int field, char *buf, int bufsz)
+{
+	const struct bone_capebus_eeprom_field *ee_field;
+	int len;
+
+	/* make sure the ID is valid for the bone */
+	if (bone_capebus_match_cntrlboard(id) != 0)
+		return NULL;
+
+	if ((unsigned int)field >= ARRAY_SIZE(eeprom_fields))
+		return NULL;
+
+	ee_field = &eeprom_fields[field];
+
+	/* enough space? */
+	if (bufsz < ee_field->size + ee_field->ascii)
+		return NULL;
+
+	memcpy(buf, (char *)id->data + ee_field->start, ee_field->size);
+
+	/* terminate ascii field */
+	if (ee_field->ascii)
+		buf[ee_field->size] = '\0';;
+
+	if (ee_field->strip_trailing_dots) {
+		len = strlen(buf);
+		while (len > 1 && buf[len - 1] == '.')
+			buf[--len] = '\0';
+	}
+
+	return buf;
+}
+EXPORT_SYMBOL(bone_capebus_id_get_field);
+
+int bone_capebus_match_cntrlboard(const struct cape_device_id *id)
+{
+	if (strcmp(id->cntrlboard, BONE_CAPEBUS_CNTRLBOARD) != 0)
+		return -ENODEV;
+	return 0;
+}
+EXPORT_SYMBOL(bone_capebus_match_cntrlboard);
+
+int bone_capebus_match_board(const struct cape_device_id *id,
+		const char **board_names)
+{
+	char rname[33];
+	const char *s;
+	int ret;
+	int i;
+
+	/* be safe; check for matching cntrlboard */
+	ret = bone_capebus_match_cntrlboard(id);
+	if (ret != 0)
+		return ret;
+
+	s = bone_capebus_id_get_field(id, BONE_CAPEBUS_BOARD_NAME,
+			rname, sizeof(rname));
+	if (s == NULL)
+		return -EINVAL;
+
+	i = 0;
+	while (*board_names) {
+		if (strcmp(rname, *board_names) == 0)
+			return i;
+		board_names++;
+	}
+
+	return -1;
+}
+EXPORT_SYMBOL(bone_capebus_match_board);
+
+#ifdef CONFIG_OF
+static const struct of_device_id bone_capebus_of_match[] = {
+	{
+		.compatible = "bone-capebus",
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(of, bone_capebus_of_match);
+
+static const struct of_device_id slot_override_of_match[] = {
+	{
+		.compatible = "bone-capebus-slot-override",
+	},
+	{ },
+};
+
+#endif
+
+const struct cape_device_id *bone_capebus_get_dev_id(struct cape_slot *slot)
+{
+	struct cape_bus *bus = slot->bus;
+	struct bone_capebus_slot *bone_slot = to_bone_capebus_slot(slot);
+	struct i2c_client *client = bone_slot->client;
+	struct cape_device_id *id;
+	const u8 *p;
+	int r;
+	char board_name[32+1];
+	char version[4+1];
+	char manufacturer[16+1];
+	char part_number[16+1];
+
+	id = &bone_slot->id;
+
+	/* need to read EEPROM? */
+	if (!bone_slot->eeprom_probed) {
+
+		bone_slot->eeprom_probed = 1;
+
+		if (!bone_slot->eeprom_override) {
+			r = i2c_memory_read(bone_slot->client,
+				bone_slot->eeprom_signature, 0,
+				sizeof(bone_slot->eeprom_signature));
+			if (r != sizeof(bone_slot->eeprom_signature)) {
+				dev_err(&bus->dev,
+					"bone: Failed to read EEPROM at "
+					"slot %d (addr 0x%02x)\n",
+					slot->slotno, client->addr & 0x7f);
+				bone_slot->eeprom_failed = 1;
+				return NULL;
+			}
+		} else
+			dev_info(&bus->dev,
+				"bone: Using override eeprom data at slot %d\n",
+				slot->slotno);
+
+		p = bone_slot->eeprom_signature;
+		if (BONE_CAPEBUS_MAKE_HEADER(p) != BONE_CAPEBUS_HEADER_VALID) {
+			dev_err(&bus->dev, "bone: Invalid EEPROM signature "
+				"'%08x' at slot %d (addr 0x%02x)\n",
+				BONE_CAPEBUS_MAKE_HEADER(p),
+				slot->slotno, client->addr & 0x7f);
+			bone_slot->eeprom_failed = 1;
+			return NULL;
+		}
+
+		bone_slot->id.cntrlboard = BONE_CAPEBUS_CNTRLBOARD;
+		bone_slot->id.len = sizeof(bone_slot->eeprom_signature);
+		bone_slot->id.data = bone_slot->eeprom_signature;
+
+		bone_capebus_id_get_field(id, BONE_CAPEBUS_BOARD_NAME,
+					board_name, sizeof(board_name));
+		bone_capebus_id_get_field(id, BONE_CAPEBUS_VERSION,
+					version, sizeof(version));
+		bone_capebus_id_get_field(id, BONE_CAPEBUS_MANUFACTURER,
+					manufacturer, sizeof(manufacturer));
+		bone_capebus_id_get_field(id, BONE_CAPEBUS_PART_NUMBER,
+					part_number, sizeof(part_number));
+
+		/* board_name,version,manufacturer,part_number */
+		snprintf(bone_slot->text_id, sizeof(bone_slot->text_id) - 1,
+				"%s,%s,%s,%s", board_name, version,
+				manufacturer, part_number);
+
+		/* terminate always */
+		bone_slot->text_id[sizeof(bone_slot->text_id) - 1] = '\0';
+
+	}
+
+	/* slot has failed and we don't support hotpluging */
+	if (bone_slot->eeprom_failed)
+		return NULL;
+
+	return id;
+}
+
+const char *bone_capebus_get_text_dev_id(struct cape_slot *slot)
+{
+	struct bone_capebus_slot *bone_slot = to_bone_capebus_slot(slot);
+
+	if (bone_slot->eeprom_failed || !bone_slot->eeprom_probed)
+		return NULL;
+
+	return bone_slot->text_id;
+}
+
+struct bonedev_ee_attribute {
+	struct device_attribute devattr;
+	unsigned int field;
+};
+#define to_bonedev_ee_attribute(x) \
+	container_of((x), struct bonedev_ee_attribute, devattr)
+
+static ssize_t bonedev_ee_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct bonedev_ee_attribute *ee_attr = to_bonedev_ee_attribute(attr);
+	struct cape_dev *cdev = to_cape_dev(dev);
+	const struct cape_device_id *id = cdev->id;
+	const struct bone_capebus_eeprom_field *ee_field;
+	int i, len;
+	char *p, *s;
+	u16 val;
+
+	if (id == NULL)
+		return -EINVAL;
+
+	/* add newline for ascii fields */
+	ee_field = &eeprom_fields[ee_attr->field];
+
+	len = ee_field->size + ee_field->ascii;
+	p = kmalloc(len, GFP_KERNEL);
+	if (p == NULL)
+		return -ENOMEM;
+
+	s = bone_capebus_id_get_field(id, ee_attr->field, p, len);
+	if (s == NULL)
+		return -EINVAL;
+
+	/* add newline for ascii fields and return */
+	if (ee_field->ascii) {
+		len = sprintf(buf, "%s\n", s);
+		goto out;
+	}
+
+	/* case by case handling */
+	switch (ee_attr->field) {
+		case BONE_CAPEBUS_HEADER:
+			len = sprintf(buf, "%02x %02x %02x %02x\n",
+					s[0], s[1], s[2], s[3]);
+			break;
+
+			/* 2 bytes */
+		case BONE_CAPEBUS_NUMBER_OF_PINS:
+		case BONE_CAPEBUS_VDD_3V3EXP:
+		case BONE_CAPEBUS_VDD_5V:
+		case BONE_CAPEBUS_SYS_5V:
+		case BONE_CAPEBUS_DC_SUPPLIED:
+			/* the bone is LE */
+			val = s[0] & (s[1] << 8);
+			len = sprintf(buf, "%u\n", (unsigned int)val & 0xffff);
+			break;
+
+		case BONE_CAPEBUS_PIN_USAGE:
+
+			len = 0;
+			for (i = 0; i < ee_field->size / 2; i++) {
+				/* the bone is LE */
+				val = s[0] & (s[1] << 8);
+				sprintf(buf, "%04x\n", val);
+				buf += 5;
+				len += 5;
+				s += 2;
+			}
+
+			break;
+
+		default:
+			*buf = '\0';
+			len = 0;
+			break;
+	}
+
+out:
+	kfree(p);
+
+	return len;
+}
+
+#define BONEDEV_EE_ATTR(_name, _field) \
+	{ \
+		.devattr = __ATTR(_name, 0440, bonedev_ee_show, NULL), \
+		.field = BONE_CAPEBUS_##_field , \
+	}
+
+struct bonedev_ee_attribute ee_attrs[] = {
+	BONEDEV_EE_ATTR(header, HEADER),
+	BONEDEV_EE_ATTR(eeprom-format-revision, EEPROM_REV),
+	BONEDEV_EE_ATTR(board-name, BOARD_NAME),
+	BONEDEV_EE_ATTR(version, VERSION),
+	BONEDEV_EE_ATTR(manufacturer, MANUFACTURER),
+	BONEDEV_EE_ATTR(part-number, PART_NUMBER),
+	BONEDEV_EE_ATTR(number-of-pins, NUMBER_OF_PINS),
+	BONEDEV_EE_ATTR(serial-number, SERIAL_NUMBER),
+	BONEDEV_EE_ATTR(pin-usage, PIN_USAGE),
+	BONEDEV_EE_ATTR(vdd-3v3exp, VDD_3V3EXP),
+	BONEDEV_EE_ATTR(vdd-5v, VDD_5V),
+	BONEDEV_EE_ATTR(sys-5v, SYS_5V),
+	BONEDEV_EE_ATTR(dc-supplied, DC_SUPPLIED),
+};
+
+static struct attribute *ee_attrs_flat[] = {
+	&ee_attrs[BONE_CAPEBUS_HEADER		].devattr.attr,
+	&ee_attrs[BONE_CAPEBUS_EEPROM_REV	].devattr.attr,
+	&ee_attrs[BONE_CAPEBUS_BOARD_NAME	].devattr.attr,
+	&ee_attrs[BONE_CAPEBUS_VERSION		].devattr.attr,
+	&ee_attrs[BONE_CAPEBUS_MANUFACTURER	].devattr.attr,
+	&ee_attrs[BONE_CAPEBUS_PART_NUMBER	].devattr.attr,
+	&ee_attrs[BONE_CAPEBUS_NUMBER_OF_PINS	].devattr.attr,
+	&ee_attrs[BONE_CAPEBUS_SERIAL_NUMBER	].devattr.attr,
+	&ee_attrs[BONE_CAPEBUS_PIN_USAGE	].devattr.attr,
+	&ee_attrs[BONE_CAPEBUS_VDD_3V3EXP	].devattr.attr,
+	&ee_attrs[BONE_CAPEBUS_VDD_5V		].devattr.attr,
+	&ee_attrs[BONE_CAPEBUS_SYS_5V		].devattr.attr,
+	&ee_attrs[BONE_CAPEBUS_DC_SUPPLIED	].devattr.attr,
+	NULL,
+};
+
+static const struct attribute_group bone_ee_attrgroup = {
+	.name		= "ee-fields",
+	.is_visible 	= NULL,
+	.attrs 		= ee_attrs_flat,
+};
+
+static int bone_capebus_sysfs_register(struct cape_dev *dev)
+{
+	return sysfs_create_group(&dev->dev.kobj, &bone_ee_attrgroup);
+}
+
+static void bone_capebus_sysfs_unregister(struct cape_dev *dev)
+{
+	sysfs_remove_group(&dev->dev.kobj, &bone_ee_attrgroup);
+}
+
+static int bone_capebus_dev_probed(struct cape_dev *dev)
+{
+	return 0;
+}
+
+static void bone_capebus_dev_removed(struct cape_dev *dev)
+{
+	bone_capebus_sysfs_unregister(dev);
+}
+
+static int bone_capebus_dev_registered(struct cape_dev *dev)
+{
+	int ret;
+
+	ret = bone_capebus_sysfs_register(dev);
+	if (ret != 0) {
+		dev_err(&dev->dev, "bone_capebus sysfs registration failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct cape_bus_ops bone_capebus_ops = {
+	.get_dev_id 		= bone_capebus_get_dev_id,
+	.get_text_dev_id	= bone_capebus_get_text_dev_id,
+	.dev_probed		= bone_capebus_dev_probed,
+	.dev_removed		= bone_capebus_dev_removed,
+	.dev_registered		= bone_capebus_dev_registered,
+};
+
+static ssize_t slots_show(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct bone_capebus_bus	*bus = platform_get_drvdata(pdev);
+	struct bone_capebus_slot *slot;
+	ssize_t len, sz;
+	int i;
+
+	sz = 0;
+
+	for (i = 0; i < bus->slots_nr; i++) {
+		slot = &bus->slots[i];
+
+		len = sprintf(buf, "%02x:%c%c%c%c %s\n",
+				(int)slot->eeprom_addr & 0x7f,
+				slot->eeprom_probed     ? 'P' : '-',
+				slot->eeprom_failed     ? 'F' : '-',
+				slot->eeprom_override   ? 'O' : '-',
+				(slot->cape_slot.dev && slot->cape_slot.dev->added) ? 'A' : '-',
+				slot->text_id);
+
+		buf += len;
+		sz += len;
+	}
+	return sz;
+}
+
+static ssize_t slots_store(struct device *dev, struct device_attribute *attr,
+		 const char *buf, size_t count)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct bone_capebus_bus	*bus = platform_get_drvdata(pdev);
+	int slotno, err, i, len;
+	char *s, *board_name, *version, *p;
+	const struct bone_capebus_eeprom_field *ee_field, *eebrd, *eevrs;
+	struct bone_capebus_slot *slot;
+
+	eebrd = &eeprom_fields[BONE_CAPEBUS_BOARD_NAME];
+	eevrs = &eeprom_fields[BONE_CAPEBUS_VERSION];
+
+	slotno = simple_strtoul(buf, &s, 10);
+	if (slotno < 0 || slotno >= bus->slots_nr)
+		return -EINVAL;
+	slot = &bus->slots[slotno];
+	if (slot->eeprom_override || (slot->cape_slot.dev && slot->cape_slot.dev->added))
+		return -EINVAL;
+
+	board_name = kzalloc(eebrd->size + 1 + eevrs->size + 1, GFP_KERNEL);
+	if (board_name == NULL)
+		return -ENOMEM;
+	version = board_name + eebrd->size + 1;
+
+	s = strchr(s, ':');
+	if (s == NULL) {
+		kfree(board_name);
+		return -EINVAL;
+	}
+	s++;
+	p = strchr(s, ':');
+	if (p == NULL) {
+		len = strlen(s);
+		strncpy(board_name, s, eebrd->size);
+		strcpy(version, "00A0");
+	} else {
+		len = p - s;
+		if (len > eebrd->size)
+			len = p - s;
+		memcpy(board_name, s, len);
+		board_name[len] = '\0';
+		strncpy(version, p + 1, eevrs->size);
+	}
+	board_name[eebrd->size] = '\0';
+	version[eevrs->size]  = '\0';
+
+	/* strip trailing spaces, dots & newlines */
+	s = board_name + strlen(board_name);
+	while (s > board_name &&
+			(isspace(s[-1]) || s[-1] == '\n' || s[-1] == '.'))
+		*--s = '\0';
+
+	printk(KERN_INFO "Override for slot #%d, board-name '%s', version '%s'\n",
+			slotno, board_name, version);
+
+	slot->eeprom_override = 1;
+	slot->eeprom_failed = 0;
+	slot->eeprom_probed = 0;
+
+	/* zero out signature */
+	memset(slot->eeprom_signature, 0,
+			sizeof(slot->eeprom_signature));
+
+	/* create an eeprom field */
+	for (i = 0; i < ARRAY_SIZE(eeprom_fields); i++) {
+
+		ee_field = &eeprom_fields[i];
+
+		/* point to the entry */
+		p = slot->eeprom_signature + ee_field->start;
+
+		/* if no such property, assign default */
+		if (i != BONE_CAPEBUS_BOARD_NAME) {
+
+			if (ee_field->override)
+				memcpy(p, ee_field->override,
+						ee_field->size);
+			else
+				memset(p, 0, ee_field->size);
+
+			continue;
+		}
+
+		/* copy it to the eeprom signature buf */
+		len = strlen(board_name);
+		if (len > ee_field->size)
+			len = ee_field->size;
+
+		/* copy and zero out rest */
+		memcpy(p, board_name, len);
+		if (len < ee_field->size)
+			memset(p + len, 0, ee_field->size - len);
+	}
+
+	printk(KERN_INFO "calling cape_bus_scan_one_slot\n");
+	err = cape_bus_scan_one_slot(&bus->cape_bus, &slot->cape_slot);
+
+	printk(KERN_INFO "cape_bus_scan_one_slot returned %d\n", err);
+
+	/* failed to scan... */
+	if (err != 0)
+		slot->eeprom_override = 0;
+
+	kfree(board_name);
+
+	return strlen(buf);
+}
+
+static DEVICE_ATTR(slots, 0644, slots_show, slots_store);
+
+static int bone_capebus_bus_sysfs_register(struct bone_capebus_bus *bus)
+{
+	return device_create_file(bus->dev, &dev_attr_slots);
+}
+
+static void bone_capebus_bus_sysfs_unregister(struct bone_capebus_bus *bus)
+{
+	device_remove_file(bus->dev, &dev_attr_slots);
+}
+
+static int __devinit
+bone_capebus_probe(struct platform_device *pdev)
+{
+	struct bone_capebus_bus	*bus;
+	struct device_node	*pnode = pdev->dev.of_node;
+	const struct of_device_id *cntrlboard_match;
+	const struct of_device_id *dev_match;
+	struct bone_capebus_slot *slot;
+	const struct bone_capebus_eeprom_field *ee_field;
+	struct property *prop;
+	int length;
+	int r;
+	struct device_node *node;
+	struct i2c_client *client;
+	phandle handle;
+	u32 *slot_handles = NULL;
+	u32 val;
+	const char *str;
+	u8 *p;
+	int i, len;
+
+	/* we don't use platform_data */
+
+	bus = devm_kzalloc(&pdev->dev,
+			sizeof(struct bone_capebus_bus), GFP_KERNEL);
+	if (!bus) {
+		dev_err(&pdev->dev, "Failed to allocate device structure\n");
+		return -ENOMEM;
+	}
+
+	/* register the cape bus */
+	r = cape_bus_register(&bus->cape_bus, "bone", 0, &pdev->dev,
+			&bone_capebus_ops);
+	if (r != 0) {
+		dev_err(&pdev->dev, "Failed to register the cape device\n");
+		return r;
+	}
+
+	cntrlboard_match = of_match_device(of_match_ptr(bone_capebus_of_match),
+			&pdev->dev);
+	if (!cntrlboard_match) {
+		dev_err(&pdev->dev, "Failed to configure bone capebus\n");
+		return -ENODEV;
+	}
+	bus->dev = &pdev->dev;
+
+	prop = of_find_property(pnode, "slots", &length);
+	if (prop == NULL) {
+		dev_err(&pdev->dev, "Unable to find required "
+				"property 'slots'\n");
+		return -EINVAL;
+	}
+	bus->slots_nr = length / sizeof(u32);
+	bus->slots = devm_kzalloc(&pdev->dev,
+			sizeof(bus->slots[0]) * bus->slots_nr, GFP_KERNEL);
+	if (!bus->slots) {
+		dev_err(&pdev->dev, "Failed to allocate %d slot areas\n",
+				bus->slots_nr);
+		return -ENOMEM;
+	}
+	slot_handles = devm_kzalloc(&pdev->dev, length, GFP_KERNEL);
+	if (!slot_handles) {
+		dev_err(&pdev->dev, "Failed to allocate %d slot areas\n",
+				bus->slots_nr);
+		return -ENOMEM;
+	}
+	r = of_property_read_u32_array(pnode, "slots",
+			slot_handles, bus->slots_nr);
+	if (r < 0) {
+		dev_err(&pdev->dev, "Failed to read %d slot handles\n",
+				bus->slots_nr);
+		return r;
+	}
+
+	/* now we iterate over any overrides */
+	for_each_child_of_node(pnode, node) {
+
+		dev_match = of_match_node(slot_override_of_match, node);
+		if (!dev_match)
+			continue;
+
+		/* no reg property */
+		if (of_property_read_u32(node, "slot", &val) != 0) {
+			dev_warn(&pdev->dev, "override: Failed to read "
+					"slot property\n");
+			continue;
+		}
+
+		if (val >= bus->slots_nr) {
+			dev_warn(&pdev->dev, "override: invalid slot #%u\n",
+					val);
+			continue;
+		}
+
+		slot = &bus->slots[val];
+
+		if (slot->eeprom_override) {
+			dev_warn(&pdev->dev, "override: slot #%u is already "
+					"overriden\n", val);
+			continue;
+		}
+
+		slot->eeprom_override = 1;
+
+		/* zero out signature */
+		memset(slot->eeprom_signature, 0,
+				sizeof(slot->eeprom_signature));
+
+		/* for any matching field assign them */
+		for (i = 0; i < ARRAY_SIZE(eeprom_fields); i++) {
+
+			ee_field = &eeprom_fields[i];
+
+			/* point to the entry */
+			p = slot->eeprom_signature + ee_field->start;
+
+			/* if no such property, assign default */
+			if (of_property_read_string(node, ee_field->name,
+						&str) != 0) {
+
+				if (ee_field->override)
+					memcpy(p, ee_field->override,
+							ee_field->size);
+				else
+					memset(p, 0, ee_field->size);
+
+				continue;
+			}
+
+			/* copy it to the eeprom signature buf */
+			len = strlen(str);
+			if (len > ee_field->size)
+				len = ee_field->size;
+
+			/* copy and zero out rest */
+			memcpy(p, str, len);
+			if (len < ee_field->size)
+				memset(p + len, 0, ee_field->size - len);
+		}
+	}
+
+	platform_set_drvdata(pdev, bus);
+
+	/* now find the i2c clients */
+	for (i = 0; i < bus->slots_nr; i++) {
+
+		slot = &bus->slots[i];
+
+		handle = slot_handles[i];
+		node = of_find_node_by_phandle(handle);
+		if (node == NULL) {
+			dev_warn(&pdev->dev, "Failed to find node with phandle "
+					"0x%x (#%d)\n", handle, i);
+			continue;
+		}
+		dev_dbg(&pdev->dev, "Found device node for phandle "
+				"0x%x (#%d)\n", handle, i);
+
+		client = of_find_i2c_device_by_node(node);
+		if (client == NULL) {
+			dev_warn(&pdev->dev, "Invalid I2C client node with "
+					"phandle 0x%x (#%d)\n", handle, i);
+			continue;
+		}
+
+		slot->client = i2c_use_client(client);
+		/* no use for this anymore */
+		of_node_put(node);
+
+		/* save handle */
+		client = slot->client;	/* get again */
+		slot->eeprom_addr = client->addr;
+		dev_dbg(&pdev->dev, "Found i2c_client at #%d "
+				"(address = 0x%02x)\n",
+				i, slot->eeprom_addr);
+
+		r = cape_bus_register_slot(&bus->cape_bus, &slot->cape_slot, i);
+		if (r != 0) {
+			dev_err(&pdev->dev, "Failed to register slot #%d\n", i);
+			continue;
+		}
+
+		dev_info(&pdev->dev, "Registered slot #%d OK\n", i);
+	}
+
+	/* we don't need the handles anymore */
+	devm_kfree(&pdev->dev, slot_handles);
+	slot_handles = NULL;
+
+	r = bone_capebus_register_pdev_adapters(bus);
+	if (r != 0) {
+		dev_err(&pdev->dev, "Failed to register the pdev adapters\n");
+		goto err_no_pdevs;
+	}
+
+	pm_runtime_enable(bus->dev);
+	r = pm_runtime_get_sync(bus->dev);
+	if (IS_ERR_VALUE(r)) {
+		dev_err(&pdev->dev, "Failed to pm_runtime_get_sync()\n");
+		goto err_exit;
+	}
+
+	pm_runtime_put(bus->dev);
+
+	bone_capebus_bus_sysfs_register(bus);
+
+	dev_info(&pdev->dev, "initialized OK.\n");
+
+	return 0;
+
+err_exit:
+	bone_capebus_unregister_pdev_adapters(bus);
+err_no_pdevs:
+	platform_set_drvdata(pdev, NULL);
+
+	return r;
+}
+
+static int __devexit bone_capebus_remove(struct platform_device *pdev)
+{
+	struct bone_capebus_bus	*bus = platform_get_drvdata(pdev);
+	int ret;
+
+	bone_capebus_bus_sysfs_unregister(bus);
+	bone_capebus_unregister_pdev_adapters(bus);
+
+	platform_set_drvdata(pdev, NULL);
+
+	ret = pm_runtime_get_sync(&pdev->dev);
+	if (IS_ERR_VALUE(ret))
+		return ret;
+
+	pm_runtime_put(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+#ifdef CONFIG_PM_RUNTIME
+static int bone_capebus_runtime_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct bone_capebus_bus *_dev = platform_get_drvdata(pdev);
+
+	(void)_dev;
+	return 0;
+}
+
+static int bone_capebus_runtime_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct bone_capebus_bus *_dev = platform_get_drvdata(pdev);
+
+	(void)_dev;
+	return 0;
+}
+#endif /* CONFIG_PM_RUNTIME */
+
+static struct dev_pm_ops bone_capebus_pm_ops = {
+	SET_RUNTIME_PM_OPS(bone_capebus_runtime_suspend,
+			   bone_capebus_runtime_resume, NULL)
+};
+#define BONE_CAPEBUS_PM_OPS (&bone_capebus_pm_ops)
+#else
+#define BONE_CAPEBUS_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver bone_capebus_driver = {
+	.probe		= bone_capebus_probe,
+	.remove		= __devexit_p(bone_capebus_remove),
+	.driver		= {
+		.name	= "bone-capebus",
+		.owner	= THIS_MODULE,
+		.pm	= BONE_CAPEBUS_PM_OPS,
+		.of_match_table = of_match_ptr(bone_capebus_of_match),
+	},
+};
+
+module_platform_driver(bone_capebus_driver);
+
+MODULE_AUTHOR("Pantelis Antoniou");
+MODULE_DESCRIPTION("Beaglebone cape bus controller");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:capebus_bone");
diff --git a/include/linux/capebus/capebus-bone.h b/include/linux/capebus/capebus-bone.h
new file mode 100644
index 0000000..e394304
--- /dev/null
+++ b/include/linux/capebus/capebus-bone.h
@@ -0,0 +1,120 @@
+/*
+ * capebus-bone.h
+ *
+ * Cape bus defines and function prototypes for the beaglebone
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
+ * Copyright (C) 2012 Texas Instruments Inc.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef LINUX_CAPEBUS_BONE_H
+#define LINUX_CAPEBUS_BONE_H
+
+#include <linux/list.h>
+#include <linux/capebus.h>
+
+struct bone_capebus_slot {
+	struct cape_slot	cape_slot;
+	u32			slot_handle;
+	int			eeprom_addr;
+	struct i2c_client	*client;
+	unsigned int		eeprom_probed : 1;
+	unsigned int		eeprom_failed : 1;
+	unsigned int		eeprom_override : 1;
+	struct cape_device_id	id;
+	char			text_id[256];
+	char			eeprom_signature[256];
+};
+
+#define to_bone_capebus_slot(n)	\
+	container_of(n, struct bone_capebus_slot, cape_slot)
+
+struct bone_capebus_bus {
+	struct cape_bus			cape_bus;
+	struct device			*dev;		/* pdev->dev */
+	int 				slots_nr;
+	struct bone_capebus_slot	*slots;
+};
+
+#define to_bone_capebus_bus(n)	\
+	container_of(n, struct bone_capebus_bus, cape_bus)
+
+#define BONE_CAPEBUS_HEADER		0
+#define BONE_CAPEBUS_EEPROM_REV		1
+#define BONE_CAPEBUS_BOARD_NAME		2
+#define BONE_CAPEBUS_VERSION		3
+#define BONE_CAPEBUS_MANUFACTURER	4
+#define BONE_CAPEBUS_PART_NUMBER	5
+#define BONE_CAPEBUS_NUMBER_OF_PINS	6
+#define BONE_CAPEBUS_SERIAL_NUMBER	7
+#define BONE_CAPEBUS_PIN_USAGE		8
+#define BONE_CAPEBUS_VDD_3V3EXP		9
+#define BONE_CAPEBUS_VDD_5V		10
+#define BONE_CAPEBUS_SYS_5V		11
+#define BONE_CAPEBUS_DC_SUPPLIED	12
+#define BONE_CAPEBUS_FIELDS_NR		13
+
+#define BONE_CAPEBUS_MAKE_HEADER(p)	\
+	({ \
+		const u8 *_p = (p); \
+		(((u32)_p[0] << 24) | ((u32)_p[1] << 16) | \
+		((u32)_p[2] <<  8) |  (u32)_p[3]       ); \
+	})
+
+#define BONE_CAPEBUS_HEADER_VALID	0xaa5533ee
+
+char *bone_capebus_id_get_field(const struct cape_device_id *id,
+		int field, char *buf, int bufsz);
+
+int bone_capebus_match_cntrlboard(const struct cape_device_id *id);
+
+int bone_capebus_match_board(const struct cape_device_id *id,
+		const char **board_names);
+
+/* in pdevs */
+int bone_capebus_register_pdev_adapters(struct bone_capebus_bus *bus);
+void bone_capebus_unregister_pdev_adapters(struct bone_capebus_bus *bus);
+
+/* generic cape support */
+
+struct bone_capebus_generic_device_data {
+	const char *name;
+	const struct of_device_id *of_match;
+	unsigned int units;
+};
+
+struct bone_capebus_generic_device_entry {
+	struct list_head node;
+	const struct bone_capebus_generic_device_data *data;
+	struct platform_device *pdev;
+};
+
+struct bone_capebus_generic_info {
+	struct cape_dev *dev;
+	struct list_head pdev_list;
+};
+
+int bone_capebus_probe_prolog(struct cape_dev *dev,
+		const struct cape_device_id *id);
+
+struct bone_capebus_generic_info *
+bone_capebus_probe_generic(struct cape_dev *dev,
+		const struct cape_device_id *id);
+
+void bone_capebus_remove_generic(
+		struct bone_capebus_generic_info *info);
+
+#endif
-- 
1.7.12

^ permalink raw reply related

* [RFC 1/7] capebus: Core capebus support
From: Pantelis Antoniou @ 2012-10-31 16:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351702333-8456-1-git-send-email-panto@antoniou-consulting.com>

Introducing capebus; a bus that allows small boards (capes) to connect
to a complex SoC using simple expansion connectors.

Up to now to support these kind of boards, one had to hack the board files,
and do all sort of gymnastics to handle all the different cases of
conflict resolution.

Capebus provides abstractions that keep the pain to a minimum.

This part of the series is introducing the core capebus functionality
dealing with the basic bus & driver probe functions.

Signed-off-by: Pantelis Antoniou <panto@antoniou-consulting.com>
---
 drivers/Kconfig                  |   2 +
 drivers/Makefile                 |   3 +
 drivers/capebus/Kconfig          |  17 ++
 drivers/capebus/Makefile         |   8 +
 drivers/capebus/capebus-driver.c | 608 +++++++++++++++++++++++++++++++++++++++
 drivers/capebus/capebus-probe.c  | 320 +++++++++++++++++++++
 drivers/capebus/capebus-sysfs.c  |  52 ++++
 include/linux/capebus.h          | 298 +++++++++++++++++++
 8 files changed, 1308 insertions(+)
 create mode 100644 drivers/capebus/Kconfig
 create mode 100644 drivers/capebus/Makefile
 create mode 100644 drivers/capebus/capebus-driver.c
 create mode 100644 drivers/capebus/capebus-probe.c
 create mode 100644 drivers/capebus/capebus-sysfs.c
 create mode 100644 include/linux/capebus.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index dbdefa3..bfbe1d1 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -156,4 +156,6 @@ source "drivers/pwm/Kconfig"
 
 source "drivers/irqchip/Kconfig"
 
+source "drivers/capebus/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index a16a8d0..d7a103b 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -145,3 +145,6 @@ obj-$(CONFIG_EXTCON)		+= extcon/
 obj-$(CONFIG_MEMORY)		+= memory/
 obj-$(CONFIG_IIO)		+= iio/
 obj-$(CONFIG_VME_BUS)		+= vme/
+
+# Capebus
+obj-$(CONFIG_CAPEBUS)		+= capebus/
diff --git a/drivers/capebus/Kconfig b/drivers/capebus/Kconfig
new file mode 100644
index 0000000..cea1b68
--- /dev/null
+++ b/drivers/capebus/Kconfig
@@ -0,0 +1,17 @@
+#
+# Capebus core support
+#
+
+menu "CAPEBUS support"
+
+config CAPEBUS
+	bool "Capebus support"
+	default n
+	help
+	  Enable to support capebus devices.
+
+source "drivers/capebus/boards/Kconfig"
+
+source "drivers/capebus/capes/Kconfig"
+
+endmenu
diff --git a/drivers/capebus/Makefile b/drivers/capebus/Makefile
new file mode 100644
index 0000000..45aa303
--- /dev/null
+++ b/drivers/capebus/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for CAPEBUS devices
+#
+
+obj-$(CONFIG_CAPEBUS)		+= capebus-probe.o \
+					   capebus-driver.o capebus-sysfs.o
+obj-$(CONFIG_CAPEBUS)		+= boards/
+obj-$(CONFIG_CAPEBUS)		+= capes/
diff --git a/drivers/capebus/capebus-driver.c b/drivers/capebus/capebus-driver.c
new file mode 100644
index 0000000..82b1d1b
--- /dev/null
+++ b/drivers/capebus/capebus-driver.c
@@ -0,0 +1,608 @@
+/*
+ * Capebus driver infrastructure
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
+ * Copyright (C) 2012 Texas Instruments Inc.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/mempolicy.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/cpu.h>
+#include <linux/pm_runtime.h>
+#include <linux/suspend.h>
+
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+
+#include <linux/capebus.h>
+
+/**
+ * capebus_match_device - Tell if a cape device structure has a
+ *                        matching cape device id structure
+ * @drv: the cape driver to match against
+ * @dev: the cape device structure to match against
+ *
+ * Used by a driver to check whether a cape device present in the
+ * system is in its list of supported devices.  Returns the matching
+ * cape_device_id structure or %NULL if there is no match.
+ */
+static const struct cape_device_id *capebus_match_device(
+		struct cape_driver *drv, struct cape_dev *dev)
+{
+	struct cape_bus *bus = dev->bus;
+	struct cape_slot *slot = dev->slot;
+
+	BUG_ON(bus == NULL);
+	BUG_ON(slot == NULL);
+	BUG_ON(bus->ops == NULL);
+	BUG_ON(bus->ops->get_dev_id == NULL);
+
+	return bus->ops->get_dev_id(slot);
+}
+
+/**
+ * capebus_device_probe - check if a driver wants to claim a
+ *                          specific cape device
+ * @dev: cape device being probed
+ *
+ * returns 0 on success, else error.
+ * side-effect: cape_dev->driver is set to drv when drv claims cape_dev.
+ */
+static int capebus_device_probe(struct device *dev)
+{
+	const struct cape_device_id *id;
+	int error = 0;
+	struct cape_driver *drv;
+	struct cape_dev *cape_dev;
+	struct device *parent;
+
+	drv = to_cape_driver(dev->driver);
+	cape_dev = to_cape_dev(dev);
+	cape_dev = capebus_dev_get(cape_dev);
+
+	/* sanity checks */
+	if (cape_dev == NULL ||
+		cape_dev->bus == NULL || cape_dev->bus->ops == NULL ||
+		cape_dev->driver != NULL || drv->probe == NULL) {
+		error = -EINVAL;
+		goto err_no_sanity;
+	}
+
+	id = capebus_match_device(drv, cape_dev);
+	if (!id) {
+		error = -ENODEV;
+		goto err_no_match;
+	}
+
+	/* The parent device must be in active state when probing */
+	parent = cape_dev->dev.parent;
+	if (parent)
+		pm_runtime_get_sync(parent);
+
+	/* Unbound cape devices are always set to disabled and suspended.
+	 * During probe, the device is set to enabled and active and the
+	 * usage count is incremented.  If the driver supports runtime PM,
+	 * it should call pm_runtime_put_noidle() in its probe routine and
+	 * pm_runtime_get_noresume() in its remove routine.
+	 */
+	pm_runtime_get_noresume(&cape_dev->dev);
+	pm_runtime_set_active(&cape_dev->dev);
+	pm_runtime_enable(&cape_dev->dev);
+
+	/* call the driver's probe method */
+	error = drv->probe(cape_dev, id);
+
+	/* release the parent no matter what */
+	if (parent)
+		pm_runtime_put(parent);
+
+	if (error != 0)
+		goto err_probe_fail;
+
+	/* call the probed bus method */
+	if (cape_dev->bus->ops->dev_probed != NULL) {
+		error = cape_dev->bus->ops->dev_probed(cape_dev);
+		if (error != 0)
+			goto err_dev_probed_fail;
+	}
+
+	/* all is fine... */
+	cape_dev->driver = drv;
+	cape_dev->added = 1;
+
+	return 0;
+
+err_dev_probed_fail:
+	if (drv->remove) {
+		pm_runtime_get_sync(&cape_dev->dev);
+		drv->remove(cape_dev);
+		pm_runtime_put_noidle(&cape_dev->dev);
+	}
+err_probe_fail:
+	pm_runtime_disable(&cape_dev->dev);
+	pm_runtime_set_suspended(&cape_dev->dev);
+	pm_runtime_put_noidle(&cape_dev->dev);
+err_no_match:
+	/* nothing */
+err_no_sanity:
+	capebus_dev_put(cape_dev);
+	return error;
+}
+
+static int capebus_device_remove(struct device *dev)
+{
+	struct cape_dev *cape_dev = to_cape_dev(dev);
+	struct cape_driver *drv = cape_dev->driver;
+
+	if (drv) {
+		/* call the removed bus method (if added prev.) */
+		if (cape_dev->added) {
+			BUG_ON(cape_dev->bus == NULL);
+			BUG_ON(cape_dev->bus->ops == NULL);
+			if (cape_dev->bus->ops->dev_removed)
+				cape_dev->bus->ops->dev_removed(cape_dev);
+			cape_dev->added = 0;
+		}
+		if (drv->remove) {
+			pm_runtime_get_sync(dev);
+			drv->remove(cape_dev);
+			pm_runtime_put_noidle(dev);
+		}
+		cape_dev->driver = NULL;
+	}
+
+	/* Undo the runtime PM settings in local_capebus_probe() */
+	pm_runtime_disable(dev);
+	pm_runtime_set_suspended(dev);
+	pm_runtime_put_noidle(dev);
+
+	capebus_dev_put(cape_dev);
+	return 0;
+}
+
+static void capebus_device_shutdown(struct device *dev)
+{
+	struct cape_dev *cape_dev = to_cape_dev(dev);
+	struct cape_driver *drv = cape_dev->driver;
+
+	if (drv && drv->shutdown)
+		drv->shutdown(cape_dev);
+
+	capebus_disable_device(cape_dev);
+
+	if (!device_may_wakeup(dev))
+		capebus_enable_wake(cape_dev, false);
+}
+
+static int capebus_bus_match(struct device *dev, struct device_driver *drv);
+static int capebus_device_probe(struct device *dev);
+static int capebus_device_remove(struct device *dev);
+static void capebus_device_shutdown(struct device *dev);
+
+struct bus_type capebus_bus_type = {
+	.name		= "capebus",
+	.match		= capebus_bus_match,
+	.probe		= capebus_device_probe,
+	.remove		= capebus_device_remove,
+	.shutdown	= capebus_device_shutdown,
+	.dev_attrs	= capebus_dev_attrs,
+	.bus_attrs	= capebus_bus_attrs,
+	.pm		= NULL,	/* No PM for now */
+};
+EXPORT_SYMBOL(capebus_bus_type);
+
+/**
+ * __capebus_register_driver - register a new capebus driver
+ * @drv: the driver structure to register
+ * @owner: owner module of drv
+ * @mod_name: module name string
+ *
+ * Adds the driver structure to the list of registered drivers.
+ * Returns a negative value on error, otherwise 0.
+ * If no error occurred, the driver remains registered even if
+ * no device was claimed during registration.
+ */
+int __capebus_register_driver(struct cape_driver *drv, struct module *owner,
+			  const char *mod_name)
+{
+	/* initialize common driver fields */
+	drv->driver.bus = &capebus_bus_type;
+	drv->driver.owner = owner;
+	drv->driver.mod_name = mod_name;
+
+	/* register with core */
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL(__capebus_register_driver);
+
+/**
+ * capebus_unregister_driver - unregister a capebus driver
+ * @drv: the driver structure to unregister
+ *
+ * Deletes the driver structure from the list of registered cape drivers,
+ * gives it a chance to clean up by calling its remove() function for
+ * each device it was responsible for, and marks those devices as
+ * driverless.
+ */
+
+void
+capebus_unregister_driver(struct cape_driver *drv)
+{
+	/* TODO: not really working properly */
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(capebus_unregister_driver);
+
+/**
+ * capebus_bus_match - Tell if a cape device structure has a matching
+ *                     cape device id structure
+ * @dev: the cape device structure to match against
+ * @drv: the device driver to search for matching cape device id structures
+ *
+ * Used by a driver to check whether a cape device present in the
+ * system is in its list of supported devices. Returns the matching
+ * cape_device_id structure or %NULL if there is no match.
+ */
+static int capebus_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct cape_dev *cape_dev = to_cape_dev(dev);
+	struct cape_driver *cape_drv = to_cape_driver(drv);
+	const struct cape_device_id *found_id;
+
+	found_id = capebus_match_device(cape_drv, cape_dev);
+	if (found_id)
+		return 1;
+
+	return 0;
+}
+
+/**
+ * capebus_dev_get - increments the reference count of the capebus
+ *                   device structure
+ * @dev: the device being referenced
+ *
+ * Each live reference to a device should be refcounted.
+ *
+ * Drivers for cape devices should normally record such references in
+ * their probe() methods, when they bind to a device, and release
+ * them by calling capebus_dev_put(), in their disconnect() methods.
+ *
+ * A pointer to the device with the incremented reference counter is returned.
+ */
+struct cape_dev *capebus_dev_get(struct cape_dev *dev)
+{
+	if (dev)
+		get_device(&dev->dev);
+	return dev;
+}
+EXPORT_SYMBOL(capebus_dev_get);
+
+/**
+ * capebus_dev_put - release a use of the capebus device structure
+ * @dev: device that's been disconnected
+ *
+ * Must be called when a user of a device is finished with it.  When the last
+ * user of the device calls this function, the memory of the device is freed.
+ */
+void capebus_dev_put(struct cape_dev *dev)
+{
+	if (dev)
+		put_device(&dev->dev);
+}
+EXPORT_SYMBOL(capebus_dev_put);
+
+static int __init capebus_driver_init(void)
+{
+	return bus_register(&capebus_bus_type);
+}
+
+postcore_initcall(capebus_driver_init);
+
+const struct of_device_id *
+capebus_of_match_device(struct cape_dev *cdev,
+		const char *property, const char *value)
+{
+	struct cape_bus *bus = cdev->bus;
+	struct device *dev = &cdev->dev;
+	struct device_node *pnode = cape_bus_to_parent_of_node(bus);
+	struct device_node *node;
+	const struct of_device_id *match;
+	const char* cp;
+	int cplen, l;
+
+	dev_dbg(dev, "Iterating on parent of node "
+			"name='%s' type='%s' full_name='%s'\n",
+			pnode->name, pnode->type, pnode->full_name);
+
+	match = NULL;
+	for_each_child_of_node(pnode, node) {
+
+		dev->of_node = node;
+		match = of_match_device(dev->driver->of_match_table, dev);
+		if (!match)
+			goto next_node;
+
+		cp = of_get_property(node, property, &cplen);
+		if (cp == NULL)
+			goto next_node;
+
+		while (cplen > 0) {
+			if (of_compat_cmp(cp, value, strlen(value)) == 0)
+				break;
+			l = strlen(cp) + 1;
+			cp += l;
+			cplen -= l;
+		}
+
+		/* matched */
+		if (cplen > 0)
+			break;
+next_node:
+		match = NULL;
+		dev->of_node = NULL;
+	}
+
+	if (match == NULL) {
+		dev_dbg(dev, "Failed to find matching child-node\n");
+		return NULL;
+	}
+
+	dev_dbg(dev, "Found matching child node "
+			"name='%s' type='%s' "
+			"full_name='%s' (compatible='%s')\n",
+		node->name, node->type, node->full_name,
+		match->compatible);
+
+	return match;
+}
+EXPORT_SYMBOL(capebus_of_match_device);
+
+struct device_node *
+capebus_of_compatible_device_property_match(struct cape_dev *dev,
+		const struct of_device_id *matches,
+		const char *prop, const char *prop_value)
+{
+	const struct of_device_id *match;
+	struct device_node *node, *cnode;
+	const char* cp;
+	int cplen, l;
+
+	if (prop == NULL || prop_value == NULL)
+		goto try_non_property;
+
+	/* at first try secondary match */
+	for_each_child_of_node(dev->dev.of_node, node) {
+
+		cp = of_get_property(node, prop, &cplen);
+		if (cp == NULL)
+			continue;
+
+		while (cplen > 0) {
+			if (of_compat_cmp(cp, prop_value,
+						strlen(prop_value)) == 0)
+				break;
+			l = strlen(cp) + 1;
+			cp += l;
+			cplen -= l;
+		}
+
+		/* not matched */
+		if (cplen <= 0)
+			continue;
+
+		/* now iterate in the children nodes */
+		for_each_child_of_node(node, cnode) {
+
+			match = of_match_node(matches, cnode);
+			if (match) {
+				/* release reference to parent, keep this one */
+				of_node_put(node);
+				return cnode;
+			}
+		}
+	}
+
+try_non_property:
+	for_each_child_of_node(dev->dev.of_node, node) {
+
+		match = of_match_node(matches, node);
+		if (match)
+			return node;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL(capebus_of_compatible_device_property_match);
+
+struct platform_device *
+capebus_of_platform_compatible_device_create(struct cape_dev *dev,
+		const struct of_device_id *matches,
+		const char *pdev_name,
+		const char *prop, const char *prop_value)
+{
+	struct device_node *node;
+	struct platform_device *pdev;
+
+	node = capebus_of_compatible_device_property_match(dev, matches, prop,
+			prop_value);
+	if (node == NULL)
+		return ERR_PTR(-ENXIO);
+
+	pdev = of_platform_device_create(node, pdev_name, dev->bus->dev.parent);
+
+	/* release the reference to the node */
+	of_node_put(node);
+	node = NULL;
+
+	if (pdev == NULL) {
+		dev_err(&dev->dev, "Failed to create platform device '%s'\n",
+				pdev_name);
+		return ERR_PTR(-ENODEV);
+	}
+
+	return pdev;
+}
+EXPORT_SYMBOL(capebus_of_platform_compatible_device_create);
+
+struct device_node *
+capebus_of_find_property_node(struct cape_dev *dev,
+		const char *prop, const char *prop_value,
+		const char *name)
+{
+	struct device_node *node;
+	const char* cp;
+	int cplen, l;
+	struct property *pp;
+
+	node = NULL;
+	if (prop == NULL || prop_value == NULL)
+		goto find_direct;
+
+	/* at first try secondary match */
+	for_each_child_of_node(dev->dev.of_node, node) {
+
+		cp = of_get_property(node, prop, &cplen);
+		if (cp == NULL)
+			continue;
+
+		while (cplen > 0) {
+			if (of_compat_cmp(cp, prop_value,
+						strlen(prop_value)) == 0)
+				break;
+			l = strlen(cp) + 1;
+			cp += l;
+			cplen -= l;
+		}
+
+		/* not matched */
+		if (cplen <= 0)
+			continue;
+
+		/* found ? */
+		pp = of_find_property(node, name, NULL);
+		if (pp != NULL)
+			return node;
+	}
+find_direct:
+	pp = of_find_property(dev->dev.of_node, name, NULL);
+	if (pp == NULL)
+		return NULL;
+
+	return of_node_get(dev->dev.of_node);
+}
+EXPORT_SYMBOL_GPL(capebus_of_find_property_node);
+
+struct property *
+capebus_of_find_property(struct cape_dev *dev,
+		const char *prop, const char *prop_value,
+		const char *name, int *lenp)
+{
+	struct device_node *node;
+	struct property *pp;
+
+	node = capebus_of_find_property_node(dev, prop, prop_value, name);
+	if (node == NULL)
+		return NULL;
+
+	pp = of_find_property(node, name, lenp);
+
+	of_node_put(node);
+
+	return pp;
+}
+EXPORT_SYMBOL_GPL(capebus_of_find_property);
+
+const void *capebus_of_get_property(struct cape_dev *dev,
+		const char *prop, const char *prop_value,
+		const char *name, int *lenp)
+{
+	struct property *pp;
+
+	pp = capebus_of_find_property(dev, prop, prop_value, name, lenp);
+	return pp ? pp->value : NULL;
+}
+EXPORT_SYMBOL_GPL(capebus_of_get_property);
+
+/* node exists, but it's not available? make it so */
+int capebus_of_device_node_enable(struct device_node *node)
+{
+	struct property *prop;
+	int ret;
+
+	prop = kzalloc(sizeof(*prop), GFP_KERNEL);
+	if (prop == NULL)
+		goto err_no_prop_mem;
+
+	prop->name = kstrdup("status", GFP_KERNEL);
+	if (prop->name == NULL)
+		goto err_no_name_mem;
+
+	prop->value = kstrdup("okay", GFP_KERNEL);
+	if (prop->value == NULL)
+		goto err_no_value_mem;
+
+	prop->length = strlen(prop->value) + 1;
+	set_bit(OF_DYNAMIC, &prop->_flags);
+
+	ret = prom_update_property(node, prop);
+	if (ret != 0)
+		goto err_update_failed;
+
+	return 0;
+
+err_update_failed:
+	kfree(prop->value);
+err_no_value_mem:
+	kfree(prop->name);
+err_no_name_mem:
+	kfree(prop);
+err_no_prop_mem:
+	return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(capebus_of_device_node_enable);
+
+/* Make sure this node is activated (even if it was disabled) */
+int capebus_of_platform_device_enable(struct device_node *node)
+{
+	struct platform_device *pdev, *ppdev;
+	int ret;
+
+	if (of_device_is_available(node))
+		return 0;
+
+	ret = capebus_of_device_node_enable(node);
+	if (ret != 0)
+		return ret;
+
+	/* now we need to find the parent of the node */
+	ppdev = of_find_device_by_node(node->parent);
+
+	pdev = of_platform_device_create(node, NULL,
+			ppdev ? &ppdev->dev : NULL);
+	if (IS_ERR_OR_NULL(pdev)) {
+		ret = pdev ? PTR_ERR(pdev) : -ENODEV;
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(capebus_of_platform_device_enable);
diff --git a/drivers/capebus/capebus-probe.c b/drivers/capebus/capebus-probe.c
new file mode 100644
index 0000000..b46e915
--- /dev/null
+++ b/drivers/capebus/capebus-probe.c
@@ -0,0 +1,320 @@
+/*
+ * Capebus bus infrastructure
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
+ * Copyright (C) 2012 Texas Instruments Inc.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_i2c.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include <linux/capebus.h>
+
+LIST_HEAD(cape_buses);
+EXPORT_SYMBOL(cape_buses);
+
+DEFINE_MUTEX(cape_buses_mutex);
+EXPORT_SYMBOL(cape_buses_mutex);
+
+/*
+ * Cape Bus Class
+ */
+static void release_capebus_dev(struct device *dev)
+{
+	struct cape_dev *cape_dev = to_cape_dev(dev);
+
+	kfree(cape_dev);
+}
+
+static struct class capebus_class = {
+	.name		= "capebus",
+	.dev_release	= &release_capebus_dev,
+};
+
+static int __init capebus_class_init(void)
+{
+	return class_register(&capebus_class);
+}
+postcore_initcall(capebus_class_init);
+
+static struct cape_bus *cape_bus_find(const char *name, int busno)
+{
+	struct cape_bus *bus;
+	int found;
+
+	if (busno < 0)
+		return NULL;
+
+	found = 0;
+	cape_bus_for_each(bus) {
+		if (strcmp(name, bus->name) == 0 && bus->busno == busno) {
+			found = 1;
+			break;
+		}
+	}
+	return found ? bus : NULL;
+}
+
+static int cape_bus_pick_busno(const char *name, int busno)
+{
+	struct cape_bus *bus;
+
+	BUG_ON(name == NULL);
+
+	/* fixed id */
+	if (busno >= 0)
+		return busno;
+
+	/* dynamic id */
+	busno = -1;
+	cape_bus_for_each(bus) {
+		/* name must match */
+		if (strcmp(name, bus->name) != 0)
+			continue;
+		busno = max(busno, bus->busno);
+	}
+	return busno + 1;
+}
+
+int cape_bus_register(struct cape_bus *bus, const char *name, int busno,
+		struct device *parent, struct cape_bus_ops *ops)
+{
+	struct cape_bus *b2;
+	int r;
+
+	if (name == NULL)
+		return -EINVAL;
+
+	INIT_LIST_HEAD(&bus->node);
+	INIT_LIST_HEAD(&bus->devices);
+	INIT_LIST_HEAD(&bus->slots);
+
+	/* do everything under lock */
+	mutex_lock(&cape_buses_mutex);
+
+	b2 = cape_bus_find(name, busno);
+	if (b2 != NULL) {
+		if (parent != NULL)
+			dev_err(parent, "capebus %s:%d in use\n", name, busno);
+		else
+			pr_err("capebus %s:%d in use\n", name, busno);
+		r = -EBUSY;
+		goto err_unlock;
+	}
+	bus->name = name;
+	bus->busno = cape_bus_pick_busno(name, busno);
+	bus->ops = ops;
+
+	bus->dev.class = &capebus_class;
+	bus->dev.parent = parent;
+	dev_set_name(&bus->dev, "%s:%d", bus->name, bus->busno);
+	r = device_register(&bus->dev);
+	if (r != 0) {
+		if (parent != NULL)
+			dev_err(parent, "capebus #%d failed to register dev\n",
+					bus->busno);
+		else
+			pr_err("capebus #%d failed to register dev\n",
+					bus->busno);
+		goto err_unlock;
+	}
+
+	list_add_tail(&bus->node, &cape_buses);
+	mutex_unlock(&cape_buses_mutex);
+
+	dev_info(&bus->dev, "Registered\n");
+
+	return 0;
+err_unlock:
+	mutex_unlock(&cape_buses_mutex);
+	return r;
+}
+
+int cape_bus_deregister(struct cape_bus *bus)
+{
+	return -EINVAL;	/* not yet supported */
+}
+
+/* must have cape_buses_mutex */
+struct cape_slot *cape_slot_find(struct cape_bus *bus, int slotno)
+{
+	struct cape_slot *slot;
+	int found;
+
+	found = 0;
+	cape_slot_for_each(bus, slot) {
+		if (slot->slotno == slotno) {
+			found = 1;
+			break;
+		}
+	}
+	return found ? slot : NULL;
+}
+
+/**
+ * cape_bus_release_dev - free a cape device structure when all users
+ *                        of it are finished.
+ * @dev: device that's been disconnected
+ *
+ * Will be called only by the device core when all users of this cape device are
+ * done.
+ */
+static void cape_bus_release_dev(struct device *dev)
+{
+	struct cape_dev *cdev;
+
+	cdev = to_cape_dev(dev);
+	/* cape_release_capabilities(cdev); TODO */
+	/* cape_release_of_node(cdev); TODO */
+	kfree(cdev);
+}
+
+/* mutex lock must be held */
+static struct cape_dev *cape_bus_scan_slot(struct cape_slot *slot)
+{
+	struct cape_bus *bus = slot->bus;
+	struct cape_dev *dev;
+	const struct cape_device_id *id;
+
+	/* get the ID (if a device exists) */
+	id = bus->ops->get_dev_id(slot);
+	if (id == NULL)
+		return ERR_PTR(-ENODEV);
+
+	/* slot must not have a device yet */
+	dev = slot->dev;
+	if (dev == NULL) {
+		dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+		if (dev == NULL) {
+			dev_info(&bus->dev, "Failed to allocate cape device "
+					"for slot #%d\n", slot->slotno);
+			return ERR_PTR(-ENOMEM);
+		}
+
+		INIT_LIST_HEAD(&dev->bus_list);
+		dev->bus = bus;
+		dev->slot = slot;
+	}
+
+	dev->id = id;
+	dev->text_id = bus->ops->get_text_dev_id(slot);
+
+	/* capebus_set_of_node(dev); TODO */
+
+	return dev;
+}
+
+int cape_bus_scan_one_slot(struct cape_bus *bus, struct cape_slot *slot)
+{
+	struct cape_dev *dev;
+	int r;
+
+	mutex_lock(&cape_buses_mutex);
+
+	dev = slot->dev;
+	if (dev == NULL) {
+
+		dev = cape_bus_scan_slot(slot);
+		if (IS_ERR(dev)) {
+			r = PTR_ERR(dev);
+			goto err_out;
+		}
+
+		dev_info(&bus->dev, "Slot #%d id='%s'\n", slot->slotno,
+				dev->text_id ? dev->text_id : "");
+
+		slot->dev = dev;
+
+		dev->dev.release = cape_bus_release_dev;
+		dev->dev.parent = &dev->bus->dev;
+		dev->dev.bus = &capebus_bus_type;
+		dev_set_name(&dev->dev, "%s-%d:%d",
+			     dev->bus->name, dev->bus->busno,
+			     dev->slot->slotno);
+
+		list_add_tail(&dev->bus_list, &bus->devices);
+
+	} else {
+		dev_info(&bus->dev, "Slot #%d id='%s' - rescan\n", slot->slotno,
+				dev->text_id ? dev->text_id : "");
+
+		if (dev->added) {
+			r = -EEXIST;
+			goto err_out;
+		}
+	}
+
+	r = device_register(&dev->dev);
+	if (r != 0) {
+		dev_info(&bus->dev, "Slot #%d id='%s' - "
+				"Failed to register\n",
+				slot->slotno,
+				dev->text_id ? dev->text_id : "");
+		r = 0;
+	} else {
+		if (dev->bus->ops->dev_registered)
+			dev->bus->ops->dev_registered(dev);
+	}
+
+err_out:
+	mutex_unlock(&cape_buses_mutex);
+
+	return r;
+}
+
+int cape_bus_register_slot(struct cape_bus *bus, struct cape_slot *slot,
+		int slotno)
+{
+	struct cape_slot *s2;
+	int r;
+
+	r = 0;
+
+	/* invalid (slot must always be numbered - no hotplug) */
+	if (slotno < 0) {
+		dev_err(&bus->dev, "Slot registration #%d failed\n", slotno);
+		return -EINVAL;
+	}
+
+	mutex_lock(&cape_buses_mutex);
+	s2 = cape_slot_find(bus, slotno);
+	if (s2 != NULL) {
+		dev_err(&bus->dev, "Slot #%d already exists\n", slotno);
+		mutex_unlock(&cape_buses_mutex);
+		return -EINVAL;
+	}
+
+	INIT_LIST_HEAD(&slot->node);
+	slot->bus = bus;
+	list_add(&slot->node, &bus->slots);
+	slot->slotno = slotno;
+	slot->dev = NULL;
+	mutex_unlock(&cape_buses_mutex);
+
+	dev_info(&bus->dev, "Slot #%d registered\n", slot->slotno);
+
+	return cape_bus_scan_one_slot(bus, slot);
+}
diff --git a/drivers/capebus/capebus-sysfs.c b/drivers/capebus/capebus-sysfs.c
new file mode 100644
index 0000000..81c21fe
--- /dev/null
+++ b/drivers/capebus/capebus-sysfs.c
@@ -0,0 +1,52 @@
+/*
+ * drivers/capebus/capebus-sysfs.c
+ *
+ * sysfs for capebus devices
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
+ * Copyright (C) 2012 Texas Instruments Inc.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Modeled after PCI's pci-sysfs.c
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/stat.h>
+#include <linux/export.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+
+#include <linux/capebus.h>
+
+static ssize_t id_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct cape_dev *cdev;
+
+	cdev = to_cape_dev(dev);
+	return sprintf(buf, "%s\n", cdev->text_id);
+}
+
+struct device_attribute capebus_dev_attrs[] = {
+	__ATTR_RO(id),
+	__ATTR_NULL,
+};
+
+struct bus_attribute capebus_bus_attrs[] = {
+	__ATTR_NULL
+};
diff --git a/include/linux/capebus.h b/include/linux/capebus.h
new file mode 100644
index 0000000..7524401
--- /dev/null
+++ b/include/linux/capebus.h
@@ -0,0 +1,298 @@
+/*
+ * capebus.h
+ *
+ * Cape bus defines and function prototypes
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
+ * Copyright (C) 2012 Texas Instruments Inc.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef LINUX_CAPEBUS_H
+#define LINUX_CAPEBUS_H
+
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/atomic.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+struct cape_device_id {
+	const char *cntrlboard;	/* controlling board; i.e. "beaglebone" */
+	int len;		/* opaque addressing data */
+	const void *data;
+};
+
+struct cape_dev;
+struct cape_bus;
+struct cape_slot;
+
+struct cape_slot {
+	struct list_head	node;
+	struct cape_bus 	*bus;	/* the bus this slot is on */
+	int 			slotno;	/* index of this slot */
+	struct cape_dev		*dev;	/* the device (if found) */
+};
+
+struct cape_driver {
+	struct list_head node;
+	int (*probe)(struct cape_dev *dev, const struct cape_device_id *id);
+	void (*remove)(struct cape_dev *dev);
+	int  (*suspend) (struct cape_dev *dev, pm_message_t state);
+	int  (*suspend_late) (struct cape_dev *dev, pm_message_t state);
+	int  (*resume_early) (struct cape_dev *dev);
+	int  (*resume) (struct cape_dev *dev);
+	void (*shutdown) (struct cape_dev *dev);
+	struct device_driver driver;
+};
+
+/*
+ * capebus_register_driver must be a macro so that
+ * KBUILD_MODNAME can be expanded
+ */
+#define capebus_register_driver(driver)		\
+	__capebus_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
+
+int __capebus_register_driver(struct cape_driver *drv, struct module *owner,
+			  const char *mod_name);
+
+void capebus_unregister_driver(struct cape_driver *dev);
+
+/**
+ * module_capebus_driver() - Helper macro for registering a capebus driver
+ * @__capebus_driver: capebus_driver struct
+ *
+ * Helper macro for capebus drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only
+ * use this macro once, and calling it replaces module_init() and module_exit()
+ */
+#define module_capebus_driver(__capebus_driver) \
+	module_driver(__capebus_driver, capebus_register_driver, \
+		       capebus_unregister_driver)
+
+#define	to_cape_driver(n) container_of(n, struct cape_driver, driver)
+
+struct cape_bus_ops {
+	const struct cape_device_id *(*get_dev_id)(struct cape_slot *slot);
+	const char *(*get_text_dev_id)(struct cape_slot *slot);
+	int (*dev_probed)(struct cape_dev *dev);	/* probed succesfully */
+	void (*dev_removed)(struct cape_dev *dev);	/* removed */
+	int (*dev_registered)(struct cape_dev *dev);	/* registered OK */
+};
+
+struct cape_bus {
+	struct list_head	node;
+	const char		*name;
+	struct list_head	devices;
+	struct cape_dev		*self;
+	struct list_head	slots;
+	struct cape_bus_ops	*ops;
+	int			busno;
+	struct device		dev;
+	/* TODO: resources.... */
+};
+
+#define	to_cape_bus(n) container_of(n, struct cape_bus, dev)
+
+#define cape_bus_to_parent_of_node(n) ((n)->dev.parent->of_node)
+
+struct cape_dev {
+	struct list_head	bus_list;	/* node in per-bus list     */
+	struct cape_bus		*bus;		/* bus this device is on    */
+	struct cape_slot	*slot;		/* cape slot of this device */
+	struct cape_driver	*driver;	/* driver of this device    */
+	struct device		dev;
+	atomic_t		enable_cnt;	/* capebus_enable_device    */
+						/* has been called          */
+	const struct cape_device_id *id;
+	const char 		*text_id;
+	unsigned int		added : 1;	/* device has been added    */
+	void			*drv_priv;	/* driver private data      */
+};
+
+#define	to_cape_dev(n) container_of(n, struct cape_dev, dev)
+
+struct cape_dev *capebus_dev_get(struct cape_dev *dev);
+void capebus_dev_put(struct cape_dev *dev);
+
+/* must have cape_buses_mutex */
+#define cape_bus_for_each(_bus) \
+	list_for_each_entry(_bus, &cape_buses, node)
+
+#define cape_bus_for_each_safe(_bus, _busn) \
+	list_for_each_entry_safe(_bus, _busn, &cape_buses, node)
+
+int cape_bus_register(struct cape_bus *bus, const char *name, int busno,
+		struct device *parent, struct cape_bus_ops *ops);
+
+/* must have cape_buses_mutex */
+#define cape_slot_for_each(_bus, _slot) \
+	list_for_each_entry(_slot, &(_bus)->slots, node)
+
+#define cape_slot_for_each_safe(_bus, _slot, _slotn) \
+	list_for_each_entry_safe(_slot, _slotn, &(_bus)->slots, node)
+
+int cape_bus_register_slot(struct cape_bus *bus,
+		struct cape_slot *slot, int slotno);
+
+int cape_bus_scan_one_slot(struct cape_bus *bus, struct cape_slot *slot);
+int cape_bus_scan(struct cape_bus *bus);
+
+extern struct list_head cape_buses;
+extern struct mutex cape_buses_mutex;
+
+static inline int capebus_is_enabled(struct cape_dev *cdev)
+{
+	return atomic_read(&cdev->enable_cnt) > 0;
+}
+
+static inline int capebus_enable_device(struct cape_dev *cdev)
+{
+	if (atomic_add_return(1, &cdev->enable_cnt) > 1)
+		return 0;		/* already enabled */
+
+	/* XXX do enable */
+
+	return 0;
+}
+
+static inline void capebus_disable_device(struct cape_dev *cdev)
+{
+	if (atomic_sub_return(1, &cdev->enable_cnt) != 0)
+		return;
+
+	/* callback to disable device? */
+}
+
+static inline int capebus_enable_wake(struct cape_dev *dev, int what)
+{
+	return 0;
+}
+
+extern struct device_attribute capebus_dev_attrs[];
+extern struct bus_attribute capebus_bus_attrs[];
+
+extern struct bus_type capebus_bus_type;
+
+const struct of_device_id *
+capebus_of_match_device(struct cape_dev *cdev,
+		const char *property, const char *value);
+
+struct device_node *
+capebus_of_compatible_device_property_match(struct cape_dev *dev,
+		const struct of_device_id *matches,
+		const char *prop, const char *prop_value);
+
+struct platform_device *
+capebus_of_platform_compatible_device_create(struct cape_dev *dev,
+		const struct of_device_id *matches,
+		const char *pdev_name,
+		const char *prop, const char *prop_value);
+
+/* of tree support */
+
+struct device_node *
+capebus_of_find_property_node(struct cape_dev *dev,
+		const char *prop, const char *prop_value,
+		const char *name);
+
+struct property *
+capebus_of_find_property(struct cape_dev *dev,
+		const char *prop, const char *prop_value,
+		const char *name, int *lenp);
+
+const void *capebus_of_get_property(struct cape_dev *dev,
+		const char *prop, const char *prop_value,
+		const char *name, int *lenp);
+
+static inline int capebus_of_property_read_u32_array(struct cape_dev *dev,
+		const char *prop, const char *prop_value,
+		const char *name, u32 *out_values, size_t sz)
+{
+	struct device_node *node;
+	int ret;
+
+	node = capebus_of_find_property_node(dev, prop, prop_value, name);
+	ret = of_property_read_u32_array(node, name, out_values, sz);
+	of_node_put(node);
+	return ret;
+}
+
+static inline int capebus_of_property_read_u32(struct cape_dev *dev,
+		const char *prop, const char *prop_value,
+	       const char *name, u32 *out_value)
+{
+	return capebus_of_property_read_u32_array(dev, prop,
+			prop_value, name, out_value, 1);
+}
+
+static inline bool capebus_of_property_read_bool(struct cape_dev *dev,
+		const char *prop, const char *prop_value,
+		const char *name)
+{
+	struct device_node *node;
+	bool ret;
+
+	node = capebus_of_find_property_node(dev, prop, prop_value, name);
+	ret = of_property_read_bool(node, name);
+	of_node_put(node);
+	return ret;
+}
+
+static inline int capebus_of_property_read_string(struct cape_dev *dev,
+		const char *prop, const char *prop_value,
+		const char *name, const char **out_string)
+{
+	struct device_node *node;
+	int ret;
+
+	node = capebus_of_find_property_node(dev, prop, prop_value, name);
+	ret = of_property_read_string(node, name, out_string);
+	of_node_put(node);
+	return ret;
+}
+
+static inline int capebus_of_property_read_string_index(struct cape_dev *dev,
+		const char *prop, const char *prop_value,
+		const char *name, int index, const char **out_string)
+{
+	struct device_node *node;
+	int ret;
+
+	node = capebus_of_find_property_node(dev, prop, prop_value, name);
+	ret = of_property_read_string_index(node, name, index, out_string);
+	of_node_put(node);
+	return ret;
+}
+
+static inline int capebus_of_property_read_u64(struct cape_dev *dev,
+		const char *prop, const char *prop_value,
+		const char *name, u64 *out_value)
+{
+	struct device_node *node;
+	int ret;
+
+	node = capebus_of_find_property_node(dev, prop, prop_value, name);
+	ret = of_property_read_u64(node, name, out_value);
+	of_node_put(node);
+	return ret;
+}
+
+int capebus_of_device_node_enable(struct device_node *node);
+int capebus_of_platform_device_enable(struct device_node *node);
+
+#endif
-- 
1.7.12

^ permalink raw reply related

* [RFC 0/7] Capebus; a bus for SoCs using simple expansion connectors
From: Pantelis Antoniou @ 2012-10-31 16:52 UTC (permalink / raw)
  To: linux-arm-kernel

Capebus is created to address the problem of many SoCs that can provide a
multitude of hardware interfaces but in order to keep costs down the main
boards only support a limited number of them. The rest are typically brought
out to pin connectors on to which other boards, named capes are connected and
allow those peripherals to be used.

These capes connect to the SoC interfaces but might also contain various other
parts that may need some kind of driver to work.

Since SoCs have limited pins and pin muxing options, not all capes can work
together so some kind of resource tracking (at least for the pins in use) is
required.

Before capebus all of this took place in the board support file, and frankly
for boards with too many capes it was becoming unmanageable.

Capebus provides a virtual bus, which along with a board specific controller,
cape drivers can be written using the standard Linux device model.

The core capebus infrastructure is not depended on any specific board.
However capebus needs a board controller to provide services to the cape devices
it controls. Services like addressing and resource reservation are provided
by the board controller.

Capebus at the moment only support TI's Beaglebone platform.

This RFC introduces the core concept; most supporting patches
have been posted to the relevant places.

If you have a beaglebone and want to check it out, you can do
so at:

git://github.com/pantoniou/linux-bbxm.git branch capebus-v3

Pantelis Antoniou (7):
  capebus: Core capebus support
  capebus: Add beaglebone board support
  capebus: Beaglebone generic cape support
  capebus: Beaglebone geiger cape support
  capebus: Beaglebone capebus DT update
  capebus: Document DT bindings
  capebus: Documentation; capebus-summary

 Documentation/capebus/capebus-summary              |  40 +
 .../capebus/bone-capebus-slot-override.txt         |  28 +
 .../devicetree/bindings/capebus/bone-capebus.txt   |  50 ++
 .../bindings/capebus/bone-geiger-cape.txt          |  78 ++
 .../bindings/capebus/bone-generic-cape.txt         |  97 +++
 .../devicetree/bindings/capebus/da8xx-dt.txt       |  31 +
 .../devicetree/bindings/capebus/i2c-dt.txt         |  42 +
 .../devicetree/bindings/capebus/spi-dt.txt         |  37 +
 .../devicetree/bindings/capebus/ti-tscadc-dt.txt   |  34 +
 arch/arm/boot/dts/am335x-bone-common.dtsi          | 689 ++++++++++++++-
 drivers/Kconfig                                    |   2 +
 drivers/Makefile                                   |   3 +
 drivers/capebus/Kconfig                            |  17 +
 drivers/capebus/Makefile                           |   8 +
 drivers/capebus/boards/Kconfig                     |   6 +
 drivers/capebus/boards/Makefile                    |   3 +
 drivers/capebus/boards/capebus-bone-generic.c      | 237 ++++++
 drivers/capebus/boards/capebus-bone-pdevs.c        | 602 +++++++++++++
 drivers/capebus/boards/capebus-bone.c              | 931 +++++++++++++++++++++
 drivers/capebus/capebus-driver.c                   | 608 ++++++++++++++
 drivers/capebus/capebus-probe.c                    | 320 +++++++
 drivers/capebus/capebus-sysfs.c                    |  52 ++
 drivers/capebus/capes/Kconfig                      |  13 +
 drivers/capebus/capes/Makefile                     |   2 +
 drivers/capebus/capes/bone-geiger-cape.c           | 506 +++++++++++
 drivers/capebus/capes/bone-generic-cape.c          |  96 +++
 include/linux/capebus.h                            | 298 +++++++
 include/linux/capebus/capebus-bone.h               | 120 +++
 28 files changed, 4920 insertions(+), 30 deletions(-)
 create mode 100644 Documentation/capebus/capebus-summary
 create mode 100644 Documentation/devicetree/bindings/capebus/bone-capebus-slot-override.txt
 create mode 100644 Documentation/devicetree/bindings/capebus/bone-capebus.txt
 create mode 100644 Documentation/devicetree/bindings/capebus/bone-geiger-cape.txt
 create mode 100644 Documentation/devicetree/bindings/capebus/bone-generic-cape.txt
 create mode 100644 Documentation/devicetree/bindings/capebus/da8xx-dt.txt
 create mode 100644 Documentation/devicetree/bindings/capebus/i2c-dt.txt
 create mode 100644 Documentation/devicetree/bindings/capebus/spi-dt.txt
 create mode 100644 Documentation/devicetree/bindings/capebus/ti-tscadc-dt.txt
 create mode 100644 drivers/capebus/Kconfig
 create mode 100644 drivers/capebus/Makefile
 create mode 100644 drivers/capebus/boards/Kconfig
 create mode 100644 drivers/capebus/boards/Makefile
 create mode 100644 drivers/capebus/boards/capebus-bone-generic.c
 create mode 100644 drivers/capebus/boards/capebus-bone-pdevs.c
 create mode 100644 drivers/capebus/boards/capebus-bone.c
 create mode 100644 drivers/capebus/capebus-driver.c
 create mode 100644 drivers/capebus/capebus-probe.c
 create mode 100644 drivers/capebus/capebus-sysfs.c
 create mode 100644 drivers/capebus/capes/Kconfig
 create mode 100644 drivers/capebus/capes/Makefile
 create mode 100644 drivers/capebus/capes/bone-geiger-cape.c
 create mode 100644 drivers/capebus/capes/bone-generic-cape.c
 create mode 100644 include/linux/capebus.h
 create mode 100644 include/linux/capebus/capebus-bone.h

-- 
1.7.12

^ permalink raw reply

* [PATCH V4 3/4] ARM: tegra: dts: cardhu: enable SLINK4
From: Stephen Warren @ 2012-10-31 16:44 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351674176-20603-3-git-send-email-ldewangan@nvidia.com>

On 10/31/2012 03:02 AM, Laxman Dewangan wrote:
> 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.

> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt

> +winbond Winbond Electronics corp.

That should really be a separate patch that goes through the devicetree
branch. But I guess it's fairly trivial, so it's not a big deal.

> diff --git a/arch/arm/boot/dts/tegra30-cardhu.dtsi b/arch/arm/boot/dts/tegra30-cardhu.dtsi

> +		spi-flash at 1 {
> +			compatible = "winbond,w25q32";
> +			spi-max-frequency = <20000000>;
> +			reg = <1>;
> +		};

Since I have an Atmel flash on my board, I see:

[    1.282958] m25p80 spi32766.1: found at25df321a, expected w25q32
[    1.289103] m25p80 spi32766.1: at25df321a (4096 Kbytes)

Is there any way around that (the first log line above)?

I suppose the answer is that the DT should reflect the exact flash
device that's on the board, and perhaps the bootloader should be
adjusting the DT to reflect the correct value. That's probably complex.

Do you have any idea whether Atmel or Winbond flash is more common, so
we can at least minimize the number of times this message appears?

^ permalink raw reply

* [PATCH] fix DEBUG_LL DCC race condition
From: Russell King - ARM Linux @ 2012-10-31 16:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20121029151832.GA2162@sig21.net>

On Mon, Oct 29, 2012 at 04:18:32PM +0100, Johannes Stezenbach wrote:
> Trying to boot a kernel with I- and D-caches disabled
> sometimes hangs when DEBUG_LL output to DCC is enabled.
> Apparently the JTAG debugger sometimes reads the
> DCC register before busyuart could see the wDTRfull flag,
> thus busyuart spins in an endless loop.

This makes no sense.  Why is busyuart spinning if it does _not_ see a
full flag?  busyuart is supposed to spin _if_ the UART has data to be
sent.  It's not supposed to spin if the data has been sent.

> The reason seems to be a misunderstanding of the purpose
> of the busyuart macro.  For UART, waituart waits until
> there is space in the FIFO, and busyuart waits until
> the FIFO is empty (all data is sent).
> For DCC, busyuart should be identical to waituart since
> there is no FIFO.

Not quite.  waituart is supposed to check that the flow control allows
the character to be sent _or_ it's supposed to do nothing.  Look at
the 8250 implementation debug-8250.S - this is the first implementation,
authored by me, and therefore is the definitive implementation for this
stuff.

^ permalink raw reply

* [PATCH v2 2/5] ARM: gic: remove direct use of gic_raise_softirq
From: Tony Lindgren @ 2012-10-31 16:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351695517-5636-3-git-send-email-robherring2@gmail.com>

* Rob Herring <robherring2@gmail.com> [121031 08:00]:
> From: Rob Herring <rob.herring@calxeda.com>
> 
> In preparation of moving gic code to drivers/irqchip, remove the direct
> platform dependencies on gic_raise_softirq. Move the setup of
> smp_cross_call into the gic code. Now that all platforms are using IPI#0
> for core wakeup, create a common wakeup ipi function.
> 
> Signed-off-by: Rob Herring <rob.herring@calxeda.com>
> Cc: Russell King <linux@arm.linux.org.uk>
> Cc: Kukjin Kim <kgene.kim@samsung.com>
> Cc: Rob Herring <rob.herring@calxeda.com>
> Cc: Sascha Hauer <kernel@pengutronix.de>
> Cc: David Brown <davidb@codeaurora.org>
> Cc: Daniel Walker <dwalker@fifo99.com>
> Cc: Bryan Huntsman <bryanh@codeaurora.org>
> Cc: Tony Lindgren <tony@atomide.com>
> Cc: Paul Mundt <lethal@linux-sh.org>
> Cc: Magnus Damm <magnus.damm@gmail.com>
> Cc: Viresh Kumar <viresh.linux@gmail.com>
> Cc: Shiraz Hashim <shiraz.hashim@st.com>
> Cc: Stephen Warren <swarren@wwwdotorg.org>
> Cc: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
> Cc: Linus Walleij <linus.walleij@linaro.org>

The omap changes look OK to me:

Acked-by: Tony Lindgren <tony@atomide.com>

^ permalink raw reply

* [PATCH V4 0/4] ARM: tegra: Enable SLINK controller driver
From: Stephen Warren @ 2012-10-31 16:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351674176-20603-1-git-send-email-ldewangan@nvidia.com>

On 10/31/2012 03:02 AM, Laxman Dewangan wrote:
> This series modify the dts file to add the slink addresses,
> make AUXDATA in board dt files, enable slink4 for tegra30-cardhu and 
> enable slink controller defconfig.

I don't appear to have received patch 1/4 this time around. I'll assume
it's identical to V3, since I don't think anything needed to change there...

^ permalink raw reply

* [PATCH] fix DEBUG_LL DCC race condition
From: Tony Lindgren @ 2012-10-31 16:27 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20121029151832.GA2162@sig21.net>

* Johannes Stezenbach <js@sig21.net> [121029 08:20]:
> Trying to boot a kernel with I- and D-caches disabled
> sometimes hangs when DEBUG_LL output to DCC is enabled.
> Apparently the JTAG debugger sometimes reads the
> DCC register before busyuart could see the wDTRfull flag,
> thus busyuart spins in an endless loop.
> 
> The reason seems to be a misunderstanding of the purpose
> of the busyuart macro.  For UART, waituart waits until
> there is space in the FIFO, and busyuart waits until
> the FIFO is empty (all data is sent).
> For DCC, busyuart should be identical to waituart since
> there is no FIFO.
> 
> Signed-off-by: Johannes Stezenbach <js@sig21.net>
> ---
> Only tested on ARMv6.
> e.g. arch/arm/mach-at91/include/mach/debug-macro.S has some
> comments which clarify what busyuart is supposed to be doing.

Your explanation makes sense to me:

Acked-by: Tony Lindgren <tony@atomide.com>

^ permalink raw reply

* linux-next: manual merge of the arm-soc tree with the staging tree
From: Tony Lindgren @ 2012-10-31 16:23 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20121031161614.GF31804@kroah.com>

* Greg KH <greg@kroah.com> [121031 09:17]:
> On Wed, Oct 31, 2012 at 04:19:21PM +1100, Stephen Rothwell wrote:
> > Hi all,
> > 
> > Today's linux-next merge of the arm-soc tree got a conflict in
> > arch/arm/mach-omap2/drm.c between commit 5e3b08749951 ("staging:
> > drm/omap: add support for ARCH_MULTIPLATFORM") from the staging tree and
> > commit 2a296c8f89bc ("ARM: OMAP: Make plat/omap_hwmod.h local to
> > mach-omap2") from the arm-soc tree.
> > 
> > I fixed it up (see below) and can carry the fix as necessary (no action
> > is required).
> 
> Looks good to me, thanks for this.

Yes thanks.

Tony

^ permalink raw reply

* linux-next: manual merge of the arm-soc tree with the staging tree
From: Greg KH @ 2012-10-31 16:16 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20121031161921.3c9a46bd03f5864eed88fc87@canb.auug.org.au>

On Wed, Oct 31, 2012 at 04:19:21PM +1100, Stephen Rothwell wrote:
> Hi all,
> 
> Today's linux-next merge of the arm-soc tree got a conflict in
> arch/arm/mach-omap2/drm.c between commit 5e3b08749951 ("staging:
> drm/omap: add support for ARCH_MULTIPLATFORM") from the staging tree and
> commit 2a296c8f89bc ("ARM: OMAP: Make plat/omap_hwmod.h local to
> mach-omap2") from the arm-soc tree.
> 
> I fixed it up (see below) and can carry the fix as necessary (no action
> is required).

Looks good to me, thanks for this.

greg k-h

^ permalink raw reply

* [PATCH V3 0/4] ARM: tegra: Enable SLINK controller driver
From: Stephen Warren @ 2012-10-31 16:06 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <5090E69C.4080108@nvidia.com>

On 10/31/2012 02:51 AM, Laxman Dewangan wrote:
> On Wednesday 31 October 2012 01:59 AM, Stephen Warren wrote:
>> On 10/30/2012 01:05 AM, Laxman Dewangan wrote:
>>> This series modify the dts file to add the slink addresses,
>>> make AUXDATA in board dt files, enable slink4 for tegra30-cardhu and
>>> enable slink controller defconfig.
>> This series only instantiates the SPI controller, and not any SPI
>> devices. I tried to solve this:
>>
>>> diff --git a/arch/arm/boot/dts/tegra30-cardhu.dtsi
>>> b/arch/arm/boot/dts/tegra30-cardhu.dtsi
>>> index 700f0a3..3689853 100644
>>> --- a/arch/arm/boot/dts/tegra30-cardhu.dtsi
>>> +++ b/arch/arm/boot/dts/tegra30-cardhu.dtsi
>>> @@ -278,6 +278,11 @@
>>>          spi at 7000da00 {
>>>                  status = "okay";
>>>                  spi-max-frequency =<25000000>;
>>> +               flash at 0 {
>>> +                       reg =<0>;
>>> +                       compatible = "atmel,at25df321a";
>>> +                       spi-max-frequency =<25000000>;
>>> +               };
>>>          };
>>>
>>>          ahub {
>> However, this didn't work. Has the driver been tested? I'm seeing the
>> exact same problems I saw with the old driver, namely that the SPI flash
>> chip can't be identified; I think the ID bytes are read back as 0, and
>> hence drivers/mtd/devices/m25p80.c falls back to identifying the device
>> as mr25h256. Attempting to hexdump -C /dev/mtd0 gives me 32K of zeros,
>> whereas the device is 4M and full of non-zero data according to U-Boot.
>>
>> Do you know what the problem is here? Can you please update the patch
>> series so that the SPI flash is instantiated and works correctly?
> 
> I will send the new patch series for taking care of the issue.
> 
> In Cardhu, there is 2 different type of flash, Winbond and Atmel.
> There is hw rework require in Cardhu A02 to enable the serial flash and
> it is already taken care in Cardhu A04. I will forward the hw rework
> details to you in internal mail communication.

U-Boot appears able to identify the flash chip on my board:

SF: Detected AT25DF321A with page size 256, total 4 MiB

So I guess I have the rework.

> Also Serial flash is in CS1, not in CS0. So even if you have hw rework,
> above change will not work.

Presumably that simply requires changing from:

              flash at 0 {
                      reg = <0>;

to:

              flash at 1 {
                      reg = <1>;

Aha, indeed that does appear to solve the issue; I can see the U-Boot
environment block when I run "hexdump -C /dev/mtd0" now.

I had forgotten about this from when I tested the older SPI driver that
we ripped out; I had at least compared the registers between U-Boot and
the kernel with that driver, so I'm pretty sure I didn't make that
mistake with the old driver.

So, I'll apply your patch series, and send one to add the flash device
to the Cardhu .dts.

^ permalink raw reply

* [PATCH v4 10/10] net/macb: add pinctrl consumer support
From: Nicolas Ferre @ 2012-10-31 16:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1351690694.git.nicolas.ferre@atmel.com>

From: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>

If no pinctrl available just report a warning as some architecture may not
need to do anything.

Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
[nicolas.ferre at atmel.com: adapt the error path, remove unneeded headers]
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Tested-by: Joachim Eastwood <manabian@gmail.com>
---
 drivers/net/ethernet/cadence/macb.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index c1f20b0..c374875 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -26,6 +26,7 @@
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/of_net.h>
+#include <linux/pinctrl/consumer.h>
 
 #include "macb.h"
 
@@ -1472,6 +1473,7 @@ static int __init macb_probe(struct platform_device *pdev)
 	struct phy_device *phydev;
 	u32 config;
 	int err = -ENXIO;
+	struct pinctrl *pinctrl;
 
 	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!regs) {
@@ -1479,6 +1481,15 @@ static int __init macb_probe(struct platform_device *pdev)
 		goto err_out;
 	}
 
+	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+	if (IS_ERR(pinctrl)) {
+		err = PTR_ERR(pinctrl);
+		if (err == -EPROBE_DEFER)
+			goto err_out;
+
+		dev_warn(&pdev->dev, "No pinctrl provided\n");
+	}
+
 	err = -ENOMEM;
 	dev = alloc_etherdev(sizeof(*bp));
 	if (!dev)
-- 
1.8.0

^ permalink raw reply related

* [PATCH v4 09/10] net/macb: Offset first RX buffer by two bytes
From: Nicolas Ferre @ 2012-10-31 16:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1351690694.git.nicolas.ferre@atmel.com>

From: Havard Skinnemoen <havard@skinnemoen.net>

Make the ethernet frame payload word-aligned, possibly making the
memcpy into the skb a bit faster. This will be even more important
after we eliminate the copy altogether.

Also eliminate the redundant RX_OFFSET constant -- it has the same
definition and purpose as NET_IP_ALIGN.

Signed-off-by: Havard Skinnemoen <havard@skinnemoen.net>
[nicolas.ferre at atmel.com: adapt to newer kernel]
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Tested-by: Joachim Eastwood <manabian@gmail.com>
---
 drivers/net/ethernet/cadence/macb.c | 23 ++++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index d5b52ff..c1f20b0 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -33,9 +33,6 @@
 #define RX_RING_SIZE		512 /* must be power of 2 */
 #define RX_RING_BYTES		(sizeof(struct macb_dma_desc) * RX_RING_SIZE)
 
-/* Make the IP header word-aligned (the ethernet header is 14 bytes) */
-#define RX_OFFSET		2
-
 #define TX_RING_SIZE		128 /* must be power of 2 */
 #define TX_RING_BYTES		(sizeof(struct macb_dma_desc) * TX_RING_SIZE)
 
@@ -498,7 +495,7 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
 {
 	unsigned int len;
 	unsigned int frag;
-	unsigned int offset = 0;
+	unsigned int offset;
 	struct sk_buff *skb;
 	struct macb_dma_desc *desc;
 
@@ -509,7 +506,16 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
 		macb_rx_ring_wrap(first_frag),
 		macb_rx_ring_wrap(last_frag), len);
 
-	skb = netdev_alloc_skb(bp->dev, len + RX_OFFSET);
+	/*
+	 * The ethernet header starts NET_IP_ALIGN bytes into the
+	 * first buffer. Since the header is 14 bytes, this makes the
+	 * payload word-aligned.
+	 *
+	 * Instead of calling skb_reserve(NET_IP_ALIGN), we just copy
+	 * the two padding bytes into the skb so that we avoid hitting
+	 * the slowpath in memcpy(), and pull them off afterwards.
+	 */
+	skb = netdev_alloc_skb(bp->dev, len + NET_IP_ALIGN);
 	if (!skb) {
 		bp->stats.rx_dropped++;
 		for (frag = first_frag; ; frag++) {
@@ -525,7 +531,8 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
 		return 1;
 	}
 
-	skb_reserve(skb, RX_OFFSET);
+	offset = 0;
+	len += NET_IP_ALIGN;
 	skb_checksum_none_assert(skb);
 	skb_put(skb, len);
 
@@ -549,10 +556,11 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
 	/* Make descriptor updates visible to hardware */
 	wmb();
 
+	__skb_pull(skb, NET_IP_ALIGN);
 	skb->protocol = eth_type_trans(skb, bp->dev);
 
 	bp->stats.rx_packets++;
-	bp->stats.rx_bytes += len;
+	bp->stats.rx_bytes += skb->len;
 	netdev_vdbg(bp->dev, "received skb of length %u, csum: %08x\n",
 		   skb->len, skb->csum);
 	netif_receive_skb(skb);
@@ -1012,6 +1020,7 @@ static void macb_init_hw(struct macb *bp)
 	__macb_set_hwaddr(bp);
 
 	config = macb_mdc_clk_div(bp);
+	config |= MACB_BF(RBOF, NET_IP_ALIGN);	/* Make eth data aligned */
 	config |= MACB_BIT(PAE);		/* PAuse Enable */
 	config |= MACB_BIT(DRFCS);		/* Discard Rx FCS */
 	config |= MACB_BIT(BIG);		/* Receive oversized frames */
-- 
1.8.0

^ permalink raw reply related

* [PATCH v4 08/10] net/macb: better manage tx errors
From: Nicolas Ferre @ 2012-10-31 16:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1351690694.git.nicolas.ferre@atmel.com>

Handle all TX errors, not only underruns. TX error management is
deferred to a dedicated workqueue.
Reinitialize the TX ring after treating all remaining frames, and
restart the controller when everything has been cleaned up properly.
Napi is not stopped during this task as the driver only handles
napi for RX for now.
With this sequence, we do not need a special check during the xmit
method as the packets will be caught by TX disable during workqueue
execution.

Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Tested-by: Joachim Eastwood <manabian@gmail.com>
---
 drivers/net/ethernet/cadence/macb.c | 166 ++++++++++++++++++++++++------------
 drivers/net/ethernet/cadence/macb.h |   1 +
 2 files changed, 113 insertions(+), 54 deletions(-)

diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index 78488b4..d5b52ff 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -44,6 +44,16 @@
 
 #define MACB_RX_INT_FLAGS	(MACB_BIT(RCOMP) | MACB_BIT(RXUBR)	\
 				 | MACB_BIT(ISR_ROVR))
+#define MACB_TX_ERR_FLAGS	(MACB_BIT(ISR_TUND)			\
+					| MACB_BIT(ISR_RLE)		\
+					| MACB_BIT(TXERR))
+#define MACB_TX_INT_FLAGS	(MACB_TX_ERR_FLAGS | MACB_BIT(TCOMP))
+
+/*
+ * Graceful stop timeouts in us. We should allow up to
+ * 1 frame time (10 Mbits/s, full-duplex, ignoring collisions)
+ */
+#define MACB_HALT_TIMEOUT	1230
 
 /* Ring buffer accessors */
 static unsigned int macb_tx_ring_wrap(unsigned int index)
@@ -339,66 +349,113 @@ static void macb_update_stats(struct macb *bp)
 		*p += __raw_readl(reg);
 }
 
-static void macb_tx(struct macb *bp)
+static int macb_halt_tx(struct macb *bp)
 {
-	unsigned int tail;
-	unsigned int head;
-	u32 status;
+	unsigned long	halt_time, timeout;
+	u32		status;
 
-	status = macb_readl(bp, TSR);
-	macb_writel(bp, TSR, status);
+	macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(THALT));
 
-	netdev_vdbg(bp->dev, "macb_tx status = 0x%03lx\n", (unsigned long)status);
+	timeout = jiffies + usecs_to_jiffies(MACB_HALT_TIMEOUT);
+	do {
+		halt_time = jiffies;
+		status = macb_readl(bp, TSR);
+		if (!(status & MACB_BIT(TGO)))
+			return 0;
 
-	if (status & (MACB_BIT(UND) | MACB_BIT(TSR_RLE))) {
-		int i;
-		netdev_err(bp->dev, "TX %s, resetting buffers\n",
-			   status & MACB_BIT(UND) ?
-			   "underrun" : "retry limit exceeded");
+		usleep_range(10, 250);
+	} while (time_before(halt_time, timeout));
 
-		/* Transfer ongoing, disable transmitter, to avoid confusion */
-		if (status & MACB_BIT(TGO))
-			macb_writel(bp, NCR, macb_readl(bp, NCR) & ~MACB_BIT(TE));
+	return -ETIMEDOUT;
+}
 
-		head = bp->tx_head;
+static void macb_tx_error_task(struct work_struct *work)
+{
+	struct macb	*bp = container_of(work, struct macb, tx_error_task);
+	struct macb_tx_skb	*tx_skb;
+	struct sk_buff		*skb;
+	unsigned int		tail;
 
-		/*Mark all the buffer as used to avoid sending a lost buffer*/
-		for (i = 0; i < TX_RING_SIZE; i++)
-			bp->tx_ring[i].ctrl = MACB_BIT(TX_USED);
+	netdev_vdbg(bp->dev, "macb_tx_error_task: t = %u, h = %u\n",
+		    bp->tx_tail, bp->tx_head);
 
-		/* Add wrap bit */
-		bp->tx_ring[TX_RING_SIZE - 1].ctrl |= MACB_BIT(TX_WRAP);
+	/* Make sure nobody is trying to queue up new packets */
+	netif_stop_queue(bp->dev);
 
-		/* free transmit buffer in upper layer*/
-		for (tail = bp->tx_tail; tail != head; tail++) {
-			struct macb_tx_skb	*tx_skb;
-			struct sk_buff		*skb;
+	/*
+	 * Stop transmission now
+	 * (in case we have just queued new packets)
+	 */
+	if (macb_halt_tx(bp))
+		/* Just complain for now, reinitializing TX path can be good */
+		netdev_err(bp->dev, "BUG: halt tx timed out\n");
 
-			rmb();
+	/* No need for the lock here as nobody will interrupt us anymore */
 
-			tx_skb = macb_tx_skb(bp, tail);
-			skb = tx_skb->skb;
+	/*
+	 * Treat frames in TX queue including the ones that caused the error.
+	 * Free transmit buffers in upper layer.
+	 */
+	for (tail = bp->tx_tail; tail != bp->tx_head; tail++) {
+		struct macb_dma_desc	*desc;
+		u32			ctrl;
 
-			dma_unmap_single(&bp->pdev->dev, tx_skb->mapping,
-						skb->len, DMA_TO_DEVICE);
-			tx_skb->skb = NULL;
-			dev_kfree_skb_irq(skb);
-		}
+		desc = macb_tx_desc(bp, tail);
+		ctrl = desc->ctrl;
+		tx_skb = macb_tx_skb(bp, tail);
+		skb = tx_skb->skb;
 
-		bp->tx_head = bp->tx_tail = 0;
+		if (ctrl & MACB_BIT(TX_USED)) {
+			netdev_vdbg(bp->dev, "txerr skb %u (data %p) TX complete\n",
+				    macb_tx_ring_wrap(tail), skb->data);
+			bp->stats.tx_packets++;
+			bp->stats.tx_bytes += skb->len;
+		} else {
+			/*
+			 * "Buffers exhausted mid-frame" errors may only happen
+			 * if the driver is buggy, so complain loudly about those.
+			 * Statistics are updated by hardware.
+			 */
+			if (ctrl & MACB_BIT(TX_BUF_EXHAUSTED))
+				netdev_err(bp->dev,
+					   "BUG: TX buffers exhausted mid-frame\n");
 
-		/* Enable the transmitter again */
-		if (status & MACB_BIT(TGO))
-			macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TE));
+			desc->ctrl = ctrl | MACB_BIT(TX_USED);
+		}
+
+		dma_unmap_single(&bp->pdev->dev, tx_skb->mapping, skb->len,
+				 DMA_TO_DEVICE);
+		tx_skb->skb = NULL;
+		dev_kfree_skb(skb);
 	}
 
-	if (!(status & MACB_BIT(COMP)))
-		/*
-		 * This may happen when a buffer becomes complete
-		 * between reading the ISR and scanning the
-		 * descriptors.  Nothing to worry about.
-		 */
-		return;
+	/* Make descriptor updates visible to hardware */
+	wmb();
+
+	/* Reinitialize the TX desc queue */
+	macb_writel(bp, TBQP, bp->tx_ring_dma);
+	/* Make TX ring reflect state of hardware */
+	bp->tx_head = bp->tx_tail = 0;
+
+	/* Now we are ready to start transmission again */
+	netif_wake_queue(bp->dev);
+
+	/* Housework before enabling TX IRQ */
+	macb_writel(bp, TSR, macb_readl(bp, TSR));
+	macb_writel(bp, IER, MACB_TX_INT_FLAGS);
+}
+
+static void macb_tx_interrupt(struct macb *bp)
+{
+	unsigned int tail;
+	unsigned int head;
+	u32 status;
+
+	status = macb_readl(bp, TSR);
+	macb_writel(bp, TSR, status);
+
+	netdev_vdbg(bp->dev, "macb_tx_interrupt status = 0x%03lx\n",
+		(unsigned long)status);
 
 	head = bp->tx_head;
 	for (tail = bp->tx_tail; tail != head; tail++) {
@@ -638,9 +695,14 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id)
 			}
 		}
 
-		if (status & (MACB_BIT(TCOMP) | MACB_BIT(ISR_TUND) |
-			    MACB_BIT(ISR_RLE)))
-			macb_tx(bp);
+		if (unlikely(status & (MACB_TX_ERR_FLAGS))) {
+			macb_writel(bp, IDR, MACB_TX_INT_FLAGS);
+			schedule_work(&bp->tx_error_task);
+			break;
+		}
+
+		if (status & MACB_BIT(TCOMP))
+			macb_tx_interrupt(bp);
 
 		/*
 		 * Link change detection isn't possible with RMII, so we'll
@@ -970,13 +1032,8 @@ static void macb_init_hw(struct macb *bp)
 	macb_writel(bp, NCR, MACB_BIT(RE) | MACB_BIT(TE) | MACB_BIT(MPE));
 
 	/* Enable interrupts */
-	macb_writel(bp, IER, (MACB_BIT(RCOMP)
-			      | MACB_BIT(RXUBR)
-			      | MACB_BIT(ISR_TUND)
-			      | MACB_BIT(ISR_RLE)
-			      | MACB_BIT(TXERR)
-			      | MACB_BIT(TCOMP)
-			      | MACB_BIT(ISR_ROVR)
+	macb_writel(bp, IER, (MACB_RX_INT_FLAGS
+			      | MACB_TX_INT_FLAGS
 			      | MACB_BIT(HRESP)));
 
 }
@@ -1428,6 +1485,7 @@ static int __init macb_probe(struct platform_device *pdev)
 	bp->dev = dev;
 
 	spin_lock_init(&bp->lock);
+	INIT_WORK(&bp->tx_error_task, macb_tx_error_task);
 
 	bp->pclk = clk_get(&pdev->dev, "pclk");
 	if (IS_ERR(bp->pclk)) {
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index 232dca6..4235ab8 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -538,6 +538,7 @@ struct macb {
 	struct clk		*hclk;
 	struct net_device	*dev;
 	struct napi_struct	napi;
+	struct work_struct	tx_error_task;
 	struct net_device_stats	stats;
 	union {
 		struct macb_stats	macb;
-- 
1.8.0

^ permalink raw reply related

* [PATCH v4 07/10] net/macb: ethtool interface: add register dump feature
From: Nicolas Ferre @ 2012-10-31 16:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1351690694.git.nicolas.ferre@atmel.com>

Add macb_get_regs() ethtool function and its helper function:
macb_get_regs_len().
The version field is deduced from the IP revision which gives the
"MACB or GEM" information. An additional version field is reserved.

Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Reviewed-by: Ben Hutchings <bhutchings@solarflare.com>
Tested-by: Joachim Eastwood <manabian@gmail.com>
---
 drivers/net/ethernet/cadence/macb.c | 40 +++++++++++++++++++++++++++++++++++++
 drivers/net/ethernet/cadence/macb.h |  3 +++
 2 files changed, 43 insertions(+)

diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index c432d41..78488b4 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -1273,9 +1273,49 @@ static int macb_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 	return phy_ethtool_sset(phydev, cmd);
 }
 
+static int macb_get_regs_len(struct net_device *netdev)
+{
+	return MACB_GREGS_NBR * sizeof(u32);
+}
+
+static void macb_get_regs(struct net_device *dev, struct ethtool_regs *regs,
+			  void *p)
+{
+	struct macb *bp = netdev_priv(dev);
+	unsigned int tail, head;
+	u32 *regs_buff = p;
+
+	regs->version = (macb_readl(bp, MID) & ((1 << MACB_REV_SIZE) - 1))
+			| MACB_GREGS_VERSION;
+
+	tail = macb_tx_ring_wrap(bp->tx_tail);
+	head = macb_tx_ring_wrap(bp->tx_head);
+
+	regs_buff[0]  = macb_readl(bp, NCR);
+	regs_buff[1]  = macb_or_gem_readl(bp, NCFGR);
+	regs_buff[2]  = macb_readl(bp, NSR);
+	regs_buff[3]  = macb_readl(bp, TSR);
+	regs_buff[4]  = macb_readl(bp, RBQP);
+	regs_buff[5]  = macb_readl(bp, TBQP);
+	regs_buff[6]  = macb_readl(bp, RSR);
+	regs_buff[7]  = macb_readl(bp, IMR);
+
+	regs_buff[8]  = tail;
+	regs_buff[9]  = head;
+	regs_buff[10] = macb_tx_dma(bp, tail);
+	regs_buff[11] = macb_tx_dma(bp, head);
+
+	if (macb_is_gem(bp)) {
+		regs_buff[12] = gem_readl(bp, USRIO);
+		regs_buff[13] = gem_readl(bp, DMACFG);
+	}
+}
+
 const struct ethtool_ops macb_ethtool_ops = {
 	.get_settings		= macb_get_settings,
 	.set_settings		= macb_set_settings,
+	.get_regs_len		= macb_get_regs_len,
+	.get_regs		= macb_get_regs,
 	.get_link		= ethtool_op_get_link,
 	.get_ts_info		= ethtool_op_get_ts_info,
 };
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index 024a270..232dca6 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -10,6 +10,9 @@
 #ifndef _MACB_H
 #define _MACB_H
 
+#define MACB_GREGS_NBR 16
+#define MACB_GREGS_VERSION 1
+
 /* MACB register offsets */
 #define MACB_NCR				0x0000
 #define MACB_NCFGR				0x0004
-- 
1.8.0

^ permalink raw reply related

* [PATCH v4 06/10] net/macb: clean up ring buffer logic
From: Nicolas Ferre @ 2012-10-31 16:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1351690694.git.nicolas.ferre@atmel.com>

From: Havard Skinnemoen <havard@skinnemoen.net>

Instead of masking head and tail every time we increment them, just let them
wrap through UINT_MAX and mask them when subscripting. Add simple accessor
functions to do the subscripting properly to minimize the chances of messing
this up.

This makes the code slightly smaller, and hopefully faster as well.  Also,
doing the ring buffer management this way will simplify things a lot when
making the ring sizes configurable in the future.

Available number of descriptors in ring buffer function by David Laight.

Signed-off-by: Havard Skinnemoen <havard@skinnemoen.net>
[nicolas.ferre at atmel.com: split patch in topics, adapt to newer kernel]
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Tested-by: Joachim Eastwood <manabian@gmail.com>
---
 drivers/net/ethernet/cadence/at91_ether.c |   6 +-
 drivers/net/ethernet/cadence/macb.c       | 172 +++++++++++++++++++-----------
 drivers/net/ethernet/cadence/macb.h       |  22 ++--
 3 files changed, 127 insertions(+), 73 deletions(-)

diff --git a/drivers/net/ethernet/cadence/at91_ether.c b/drivers/net/ethernet/cadence/at91_ether.c
index b92815a..0d6392d 100644
--- a/drivers/net/ethernet/cadence/at91_ether.c
+++ b/drivers/net/ethernet/cadence/at91_ether.c
@@ -156,7 +156,7 @@ static int at91ether_start(struct net_device *dev)
 	int i;
 
 	lp->rx_ring = dma_alloc_coherent(&lp->pdev->dev,
-					MAX_RX_DESCR * sizeof(struct dma_desc),
+					MAX_RX_DESCR * sizeof(struct macb_dma_desc),
 					&lp->rx_ring_dma, GFP_KERNEL);
 	if (!lp->rx_ring) {
 		netdev_err(lp->dev, "unable to alloc rx ring DMA buffer\n");
@@ -170,7 +170,7 @@ static int at91ether_start(struct net_device *dev)
 		netdev_err(lp->dev, "unable to alloc rx data DMA buffer\n");
 
 		dma_free_coherent(&lp->pdev->dev,
-					MAX_RX_DESCR * sizeof(struct dma_desc),
+					MAX_RX_DESCR * sizeof(struct macb_dma_desc),
 					lp->rx_ring, lp->rx_ring_dma);
 		lp->rx_ring = NULL;
 		return -ENOMEM;
@@ -256,7 +256,7 @@ static int at91ether_close(struct net_device *dev)
 	netif_stop_queue(dev);
 
 	dma_free_coherent(&lp->pdev->dev,
-				MAX_RX_DESCR * sizeof(struct dma_desc),
+				MAX_RX_DESCR * sizeof(struct macb_dma_desc),
 				lp->rx_ring, lp->rx_ring_dma);
 	lp->rx_ring = NULL;
 
diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index cd6d431..c432d41 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -30,25 +30,14 @@
 #include "macb.h"
 
 #define RX_BUFFER_SIZE		128
-#define RX_RING_SIZE		512
-#define RX_RING_BYTES		(sizeof(struct dma_desc) * RX_RING_SIZE)
+#define RX_RING_SIZE		512 /* must be power of 2 */
+#define RX_RING_BYTES		(sizeof(struct macb_dma_desc) * RX_RING_SIZE)
 
 /* Make the IP header word-aligned (the ethernet header is 14 bytes) */
 #define RX_OFFSET		2
 
-#define TX_RING_SIZE		128
-#define DEF_TX_RING_PENDING	(TX_RING_SIZE - 1)
-#define TX_RING_BYTES		(sizeof(struct dma_desc) * TX_RING_SIZE)
-
-#define TX_RING_GAP(bp)						\
-	(TX_RING_SIZE - (bp)->tx_pending)
-#define TX_BUFFS_AVAIL(bp)					\
-	(((bp)->tx_tail <= (bp)->tx_head) ?			\
-	 (bp)->tx_tail + (bp)->tx_pending - (bp)->tx_head :	\
-	 (bp)->tx_tail - (bp)->tx_head - TX_RING_GAP(bp))
-#define NEXT_TX(n)		(((n) + 1) & (TX_RING_SIZE - 1))
-
-#define NEXT_RX(n)		(((n) + 1) & (RX_RING_SIZE - 1))
+#define TX_RING_SIZE		128 /* must be power of 2 */
+#define TX_RING_BYTES		(sizeof(struct macb_dma_desc) * TX_RING_SIZE)
 
 /* minimum number of free TX descriptors before waking up TX process */
 #define MACB_TX_WAKEUP_THRESH	(TX_RING_SIZE / 4)
@@ -56,6 +45,51 @@
 #define MACB_RX_INT_FLAGS	(MACB_BIT(RCOMP) | MACB_BIT(RXUBR)	\
 				 | MACB_BIT(ISR_ROVR))
 
+/* Ring buffer accessors */
+static unsigned int macb_tx_ring_wrap(unsigned int index)
+{
+	return index & (TX_RING_SIZE - 1);
+}
+
+static unsigned int macb_tx_ring_avail(struct macb *bp)
+{
+	return (bp->tx_tail - bp->tx_head) & (TX_RING_SIZE - 1);
+}
+
+static struct macb_dma_desc *macb_tx_desc(struct macb *bp, unsigned int index)
+{
+	return &bp->tx_ring[macb_tx_ring_wrap(index)];
+}
+
+static struct macb_tx_skb *macb_tx_skb(struct macb *bp, unsigned int index)
+{
+	return &bp->tx_skb[macb_tx_ring_wrap(index)];
+}
+
+static dma_addr_t macb_tx_dma(struct macb *bp, unsigned int index)
+{
+	dma_addr_t offset;
+
+	offset = macb_tx_ring_wrap(index) * sizeof(struct macb_dma_desc);
+
+	return bp->tx_ring_dma + offset;
+}
+
+static unsigned int macb_rx_ring_wrap(unsigned int index)
+{
+	return index & (RX_RING_SIZE - 1);
+}
+
+static struct macb_dma_desc *macb_rx_desc(struct macb *bp, unsigned int index)
+{
+	return &bp->rx_ring[macb_rx_ring_wrap(index)];
+}
+
+static void *macb_rx_buffer(struct macb *bp, unsigned int index)
+{
+	return bp->rx_buffers + RX_BUFFER_SIZE * macb_rx_ring_wrap(index);
+}
+
 static void __macb_set_hwaddr(struct macb *bp)
 {
 	u32 bottom;
@@ -336,17 +370,18 @@ static void macb_tx(struct macb *bp)
 		bp->tx_ring[TX_RING_SIZE - 1].ctrl |= MACB_BIT(TX_WRAP);
 
 		/* free transmit buffer in upper layer*/
-		for (tail = bp->tx_tail; tail != head; tail = NEXT_TX(tail)) {
-			struct ring_info *rp = &bp->tx_skb[tail];
-			struct sk_buff *skb = rp->skb;
-
-			BUG_ON(skb == NULL);
+		for (tail = bp->tx_tail; tail != head; tail++) {
+			struct macb_tx_skb	*tx_skb;
+			struct sk_buff		*skb;
 
 			rmb();
 
-			dma_unmap_single(&bp->pdev->dev, rp->mapping, skb->len,
-							 DMA_TO_DEVICE);
-			rp->skb = NULL;
+			tx_skb = macb_tx_skb(bp, tail);
+			skb = tx_skb->skb;
+
+			dma_unmap_single(&bp->pdev->dev, tx_skb->mapping,
+						skb->len, DMA_TO_DEVICE);
+			tx_skb->skb = NULL;
 			dev_kfree_skb_irq(skb);
 		}
 
@@ -366,34 +401,38 @@ static void macb_tx(struct macb *bp)
 		return;
 
 	head = bp->tx_head;
-	for (tail = bp->tx_tail; tail != head; tail = NEXT_TX(tail)) {
-		struct ring_info *rp = &bp->tx_skb[tail];
-		struct sk_buff *skb = rp->skb;
-		u32 bufstat;
+	for (tail = bp->tx_tail; tail != head; tail++) {
+		struct macb_tx_skb	*tx_skb;
+		struct sk_buff		*skb;
+		struct macb_dma_desc	*desc;
+		u32			ctrl;
 
-		BUG_ON(skb == NULL);
+		desc = macb_tx_desc(bp, tail);
 
 		/* Make hw descriptor updates visible to CPU */
 		rmb();
 
-		bufstat = bp->tx_ring[tail].ctrl;
+		ctrl = desc->ctrl;
 
-		if (!(bufstat & MACB_BIT(TX_USED)))
+		if (!(ctrl & MACB_BIT(TX_USED)))
 			break;
 
+		tx_skb = macb_tx_skb(bp, tail);
+		skb = tx_skb->skb;
+
 		netdev_vdbg(bp->dev, "skb %u (data %p) TX complete\n",
-			   tail, skb->data);
-		dma_unmap_single(&bp->pdev->dev, rp->mapping, skb->len,
+			macb_tx_ring_wrap(tail), skb->data);
+		dma_unmap_single(&bp->pdev->dev, tx_skb->mapping, skb->len,
 				 DMA_TO_DEVICE);
 		bp->stats.tx_packets++;
 		bp->stats.tx_bytes += skb->len;
-		rp->skb = NULL;
+		tx_skb->skb = NULL;
 		dev_kfree_skb_irq(skb);
 	}
 
 	bp->tx_tail = tail;
-	if (netif_queue_stopped(bp->dev) &&
-	    TX_BUFFS_AVAIL(bp) > MACB_TX_WAKEUP_THRESH)
+	if (netif_queue_stopped(bp->dev)
+			&& macb_tx_ring_avail(bp) > MACB_TX_WAKEUP_THRESH)
 		netif_wake_queue(bp->dev);
 }
 
@@ -404,17 +443,21 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
 	unsigned int frag;
 	unsigned int offset = 0;
 	struct sk_buff *skb;
+	struct macb_dma_desc *desc;
 
-	len = MACB_BFEXT(RX_FRMLEN, bp->rx_ring[last_frag].ctrl);
+	desc = macb_rx_desc(bp, last_frag);
+	len = MACB_BFEXT(RX_FRMLEN, desc->ctrl);
 
 	netdev_vdbg(bp->dev, "macb_rx_frame frags %u - %u (len %u)\n",
-		   first_frag, last_frag, len);
+		macb_rx_ring_wrap(first_frag),
+		macb_rx_ring_wrap(last_frag), len);
 
 	skb = netdev_alloc_skb(bp->dev, len + RX_OFFSET);
 	if (!skb) {
 		bp->stats.rx_dropped++;
-		for (frag = first_frag; ; frag = NEXT_RX(frag)) {
-			bp->rx_ring[frag].addr &= ~MACB_BIT(RX_USED);
+		for (frag = first_frag; ; frag++) {
+			desc = macb_rx_desc(bp, frag);
+			desc->addr &= ~MACB_BIT(RX_USED);
 			if (frag == last_frag)
 				break;
 		}
@@ -429,7 +472,7 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
 	skb_checksum_none_assert(skb);
 	skb_put(skb, len);
 
-	for (frag = first_frag; ; frag = NEXT_RX(frag)) {
+	for (frag = first_frag; ; frag++) {
 		unsigned int frag_len = RX_BUFFER_SIZE;
 
 		if (offset + frag_len > len) {
@@ -437,11 +480,10 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
 			frag_len = len - offset;
 		}
 		skb_copy_to_linear_data_offset(skb, offset,
-					       (bp->rx_buffers +
-					        (RX_BUFFER_SIZE * frag)),
-					       frag_len);
+				macb_rx_buffer(bp, frag), frag_len);
 		offset += RX_BUFFER_SIZE;
-		bp->rx_ring[frag].addr &= ~MACB_BIT(RX_USED);
+		desc = macb_rx_desc(bp, frag);
+		desc->addr &= ~MACB_BIT(RX_USED);
 
 		if (frag == last_frag)
 			break;
@@ -467,8 +509,10 @@ static void discard_partial_frame(struct macb *bp, unsigned int begin,
 {
 	unsigned int frag;
 
-	for (frag = begin; frag != end; frag = NEXT_RX(frag))
-		bp->rx_ring[frag].addr &= ~MACB_BIT(RX_USED);
+	for (frag = begin; frag != end; frag++) {
+		struct macb_dma_desc *desc = macb_rx_desc(bp, frag);
+		desc->addr &= ~MACB_BIT(RX_USED);
+	}
 
 	/* Make descriptor updates visible to hardware */
 	wmb();
@@ -483,17 +527,18 @@ static void discard_partial_frame(struct macb *bp, unsigned int begin,
 static int macb_rx(struct macb *bp, int budget)
 {
 	int received = 0;
-	unsigned int tail = bp->rx_tail;
+	unsigned int tail;
 	int first_frag = -1;
 
-	for (; budget > 0; tail = NEXT_RX(tail)) {
+	for (tail = bp->rx_tail; budget > 0; tail++) {
+		struct macb_dma_desc *desc = macb_rx_desc(bp, tail);
 		u32 addr, ctrl;
 
 		/* Make hw descriptor updates visible to CPU */
 		rmb();
 
-		addr = bp->rx_ring[tail].addr;
-		ctrl = bp->rx_ring[tail].ctrl;
+		addr = desc->addr;
+		ctrl = desc->ctrl;
 
 		if (!(addr & MACB_BIT(RX_USED)))
 			break;
@@ -647,6 +692,8 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
 	struct macb *bp = netdev_priv(dev);
 	dma_addr_t mapping;
 	unsigned int len, entry;
+	struct macb_dma_desc *desc;
+	struct macb_tx_skb *tx_skb;
 	u32 ctrl;
 	unsigned long flags;
 
@@ -663,7 +710,7 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
 	spin_lock_irqsave(&bp->lock, flags);
 
 	/* This is a hard error, log it. */
-	if (TX_BUFFS_AVAIL(bp) < 1) {
+	if (macb_tx_ring_avail(bp) < 1) {
 		netif_stop_queue(dev);
 		spin_unlock_irqrestore(&bp->lock, flags);
 		netdev_err(bp->dev, "BUG! Tx Ring full when queue awake!\n");
@@ -672,12 +719,15 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
 		return NETDEV_TX_BUSY;
 	}
 
-	entry = bp->tx_head;
+	entry = macb_tx_ring_wrap(bp->tx_head);
+	bp->tx_head++;
 	netdev_vdbg(bp->dev, "Allocated ring entry %u\n", entry);
 	mapping = dma_map_single(&bp->pdev->dev, skb->data,
 				 len, DMA_TO_DEVICE);
-	bp->tx_skb[entry].skb = skb;
-	bp->tx_skb[entry].mapping = mapping;
+
+	tx_skb = &bp->tx_skb[entry];
+	tx_skb->skb = skb;
+	tx_skb->mapping = mapping;
 	netdev_vdbg(bp->dev, "Mapped skb data %p to DMA addr %08lx\n",
 		   skb->data, (unsigned long)mapping);
 
@@ -686,20 +736,18 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
 	if (entry == (TX_RING_SIZE - 1))
 		ctrl |= MACB_BIT(TX_WRAP);
 
-	bp->tx_ring[entry].addr = mapping;
-	bp->tx_ring[entry].ctrl = ctrl;
+	desc = &bp->tx_ring[entry];
+	desc->addr = mapping;
+	desc->ctrl = ctrl;
 
 	/* Make newly initialized descriptor visible to hardware */
 	wmb();
 
-	entry = NEXT_TX(entry);
-	bp->tx_head = entry;
-
 	skb_tx_timestamp(skb);
 
 	macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART));
 
-	if (TX_BUFFS_AVAIL(bp) < 1)
+	if (macb_tx_ring_avail(bp) < 1)
 		netif_stop_queue(dev);
 
 	spin_unlock_irqrestore(&bp->lock, flags);
@@ -735,7 +783,7 @@ static int macb_alloc_consistent(struct macb *bp)
 {
 	int size;
 
-	size = TX_RING_SIZE * sizeof(struct ring_info);
+	size = TX_RING_SIZE * sizeof(struct macb_tx_skb);
 	bp->tx_skb = kmalloc(size, GFP_KERNEL);
 	if (!bp->tx_skb)
 		goto out_err;
@@ -1412,8 +1460,6 @@ static int __init macb_probe(struct platform_device *pdev)
 		macb_or_gem_writel(bp, USRIO, MACB_BIT(MII));
 #endif
 
-	bp->tx_pending = DEF_TX_RING_PENDING;
-
 	err = register_netdev(dev);
 	if (err) {
 		dev_err(&pdev->dev, "Cannot register net device, aborting.\n");
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index 33a050f..024a270 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -362,7 +362,12 @@
 		__v; \
 	})
 
-struct dma_desc {
+/**
+ * struct macb_dma_desc - Hardware DMA descriptor
+ * @addr: DMA address of data buffer
+ * @ctrl: Control and status bits
+ */
+struct macb_dma_desc {
 	u32	addr;
 	u32	ctrl;
 };
@@ -427,7 +432,12 @@ struct dma_desc {
 #define MACB_TX_USED_OFFSET			31
 #define MACB_TX_USED_SIZE			1
 
-struct ring_info {
+/**
+ * struct macb_tx_skb - data about an skb which is being transmitted
+ * @skb: skb currently being transmitted
+ * @mapping: DMA address of the skb's data buffer
+ */
+struct macb_tx_skb {
 	struct sk_buff		*skb;
 	dma_addr_t		mapping;
 };
@@ -512,12 +522,12 @@ struct macb {
 	void __iomem		*regs;
 
 	unsigned int		rx_tail;
-	struct dma_desc		*rx_ring;
+	struct macb_dma_desc	*rx_ring;
 	void			*rx_buffers;
 
 	unsigned int		tx_head, tx_tail;
-	struct dma_desc		*tx_ring;
-	struct ring_info	*tx_skb;
+	struct macb_dma_desc	*tx_ring;
+	struct macb_tx_skb	*tx_skb;
 
 	spinlock_t		lock;
 	struct platform_device	*pdev;
@@ -535,8 +545,6 @@ struct macb {
 	dma_addr_t		tx_ring_dma;
 	dma_addr_t		rx_buffers_dma;
 
-	unsigned int		rx_pending, tx_pending;
-
 	struct mii_bus		*mii_bus;
 	struct phy_device	*phy_dev;
 	unsigned int 		link;
-- 
1.8.0

^ permalink raw reply related

* [PATCH v4 05/10] net/macb: tx status is more than 8 bits now
From: Nicolas Ferre @ 2012-10-31 16:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1351690694.git.nicolas.ferre@atmel.com>

On some revision of GEM, TSR status register has more information.

Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Tested-by: Joachim Eastwood <manabian@gmail.com>
---
 drivers/net/ethernet/cadence/macb.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index 4db52f3..cd6d431 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -314,7 +314,7 @@ static void macb_tx(struct macb *bp)
 	status = macb_readl(bp, TSR);
 	macb_writel(bp, TSR, status);
 
-	netdev_vdbg(bp->dev, "macb_tx status = %02lx\n", (unsigned long)status);
+	netdev_vdbg(bp->dev, "macb_tx status = 0x%03lx\n", (unsigned long)status);
 
 	if (status & (MACB_BIT(UND) | MACB_BIT(TSR_RLE))) {
 		int i;
-- 
1.8.0

^ permalink raw reply related

* [PATCH v4 04/10] net/macb: remove macb_get_drvinfo()
From: Nicolas Ferre @ 2012-10-31 16:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1351690694.git.nicolas.ferre@atmel.com>

This function has little meaning so remove it altogether and
let ethtool core fill in the fields automatically.

Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Reviewed-by: Ben Hutchings <bhutchings@solarflare.com>
Tested-by: Joachim Eastwood <manabian@gmail.com>
---
 drivers/net/ethernet/cadence/macb.c | 11 -----------
 1 file changed, 11 deletions(-)

diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index b161d73..4db52f3 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -1225,20 +1225,9 @@ static int macb_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 	return phy_ethtool_sset(phydev, cmd);
 }
 
-static void macb_get_drvinfo(struct net_device *dev,
-			     struct ethtool_drvinfo *info)
-{
-	struct macb *bp = netdev_priv(dev);
-
-	strcpy(info->driver, bp->pdev->dev.driver->name);
-	strcpy(info->version, "$Revision: 1.14 $");
-	strcpy(info->bus_info, dev_name(&bp->pdev->dev));
-}
-
 const struct ethtool_ops macb_ethtool_ops = {
 	.get_settings		= macb_get_settings,
 	.set_settings		= macb_set_settings,
-	.get_drvinfo		= macb_get_drvinfo,
 	.get_link		= ethtool_op_get_link,
 	.get_ts_info		= ethtool_op_get_ts_info,
 };
-- 
1.8.0

^ permalink raw reply related

* [PATCH v4 03/10] net/macb: change debugging messages
From: Nicolas Ferre @ 2012-10-31 16:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1351690694.git.nicolas.ferre@atmel.com>

From: Havard Skinnemoen <havard@skinnemoen.net>

Convert some noisy netdev_dbg() statements to netdev_vdbg(). Defining
DEBUG will no longer fill up the logs; VERBOSE_DEBUG still does.
Add one more verbose debug for ISR status.

Signed-off-by: Havard Skinnemoen <havard@skinnemoen.net>
[nicolas.ferre at atmel.com: split patch in topics, add ISR status]
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Tested-by: Joachim Eastwood <manabian@gmail.com>
---
 drivers/net/ethernet/cadence/macb.c | 22 ++++++++++++----------
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index e7f554d..b161d73 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -314,7 +314,7 @@ static void macb_tx(struct macb *bp)
 	status = macb_readl(bp, TSR);
 	macb_writel(bp, TSR, status);
 
-	netdev_dbg(bp->dev, "macb_tx status = %02lx\n", (unsigned long)status);
+	netdev_vdbg(bp->dev, "macb_tx status = %02lx\n", (unsigned long)status);
 
 	if (status & (MACB_BIT(UND) | MACB_BIT(TSR_RLE))) {
 		int i;
@@ -381,7 +381,7 @@ static void macb_tx(struct macb *bp)
 		if (!(bufstat & MACB_BIT(TX_USED)))
 			break;
 
-		netdev_dbg(bp->dev, "skb %u (data %p) TX complete\n",
+		netdev_vdbg(bp->dev, "skb %u (data %p) TX complete\n",
 			   tail, skb->data);
 		dma_unmap_single(&bp->pdev->dev, rp->mapping, skb->len,
 				 DMA_TO_DEVICE);
@@ -407,7 +407,7 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
 
 	len = MACB_BFEXT(RX_FRMLEN, bp->rx_ring[last_frag].ctrl);
 
-	netdev_dbg(bp->dev, "macb_rx_frame frags %u - %u (len %u)\n",
+	netdev_vdbg(bp->dev, "macb_rx_frame frags %u - %u (len %u)\n",
 		   first_frag, last_frag, len);
 
 	skb = netdev_alloc_skb(bp->dev, len + RX_OFFSET);
@@ -454,7 +454,7 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
 
 	bp->stats.rx_packets++;
 	bp->stats.rx_bytes += len;
-	netdev_dbg(bp->dev, "received skb of length %u, csum: %08x\n",
+	netdev_vdbg(bp->dev, "received skb of length %u, csum: %08x\n",
 		   skb->len, skb->csum);
 	netif_receive_skb(skb);
 
@@ -536,7 +536,7 @@ static int macb_poll(struct napi_struct *napi, int budget)
 
 	work_done = 0;
 
-	netdev_dbg(bp->dev, "poll: status = %08lx, budget = %d\n",
+	netdev_vdbg(bp->dev, "poll: status = %08lx, budget = %d\n",
 		   (unsigned long)status, budget);
 
 	work_done = macb_rx(bp, budget);
@@ -575,6 +575,8 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id)
 			break;
 		}
 
+		netdev_vdbg(bp->dev, "isr = 0x%08lx\n", (unsigned long)status);
+
 		if (status & MACB_RX_INT_FLAGS) {
 			/*
 			 * There's no point taking any more interrupts
@@ -586,7 +588,7 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id)
 			macb_writel(bp, IDR, MACB_RX_INT_FLAGS);
 
 			if (napi_schedule_prep(&bp->napi)) {
-				netdev_dbg(bp->dev, "scheduling RX softirq\n");
+				netdev_vdbg(bp->dev, "scheduling RX softirq\n");
 				__napi_schedule(&bp->napi);
 			}
 		}
@@ -648,8 +650,8 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
 	u32 ctrl;
 	unsigned long flags;
 
-#ifdef DEBUG
-	netdev_dbg(bp->dev,
+#if defined(DEBUG) && defined(VERBOSE_DEBUG)
+	netdev_vdbg(bp->dev,
 		   "start_xmit: len %u head %p data %p tail %p end %p\n",
 		   skb->len, skb->head, skb->data,
 		   skb_tail_pointer(skb), skb_end_pointer(skb));
@@ -671,12 +673,12 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
 	}
 
 	entry = bp->tx_head;
-	netdev_dbg(bp->dev, "Allocated ring entry %u\n", entry);
+	netdev_vdbg(bp->dev, "Allocated ring entry %u\n", entry);
 	mapping = dma_map_single(&bp->pdev->dev, skb->data,
 				 len, DMA_TO_DEVICE);
 	bp->tx_skb[entry].skb = skb;
 	bp->tx_skb[entry].mapping = mapping;
-	netdev_dbg(bp->dev, "Mapped skb data %p to DMA addr %08lx\n",
+	netdev_vdbg(bp->dev, "Mapped skb data %p to DMA addr %08lx\n",
 		   skb->data, (unsigned long)mapping);
 
 	ctrl = MACB_BF(TX_FRMLEN, len);
-- 
1.8.0

^ permalink raw reply related

* [PATCH v4 02/10] net/macb: memory barriers cleanup
From: Nicolas Ferre @ 2012-10-31 16:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1351690694.git.nicolas.ferre@atmel.com>

From: Havard Skinnemoen <havard@skinnemoen.net>

Remove a couple of unneeded barriers and document the remaining ones.

Signed-off-by: Havard Skinnemoen <havard@skinnemoen.net>
[nicolas.ferre at atmel.com: split patch in topics]
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Tested-by: Joachim Eastwood <manabian@gmail.com>
---
 drivers/net/ethernet/cadence/macb.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index 0931cb7..e7f554d 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -373,7 +373,9 @@ static void macb_tx(struct macb *bp)
 
 		BUG_ON(skb == NULL);
 
+		/* Make hw descriptor updates visible to CPU */
 		rmb();
+
 		bufstat = bp->tx_ring[tail].ctrl;
 
 		if (!(bufstat & MACB_BIT(TX_USED)))
@@ -416,7 +418,10 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
 			if (frag == last_frag)
 				break;
 		}
+
+		/* Make descriptor updates visible to hardware */
 		wmb();
+
 		return 1;
 	}
 
@@ -437,12 +442,14 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
 					       frag_len);
 		offset += RX_BUFFER_SIZE;
 		bp->rx_ring[frag].addr &= ~MACB_BIT(RX_USED);
-		wmb();
 
 		if (frag == last_frag)
 			break;
 	}
 
+	/* Make descriptor updates visible to hardware */
+	wmb();
+
 	skb->protocol = eth_type_trans(skb, bp->dev);
 
 	bp->stats.rx_packets++;
@@ -462,6 +469,8 @@ static void discard_partial_frame(struct macb *bp, unsigned int begin,
 
 	for (frag = begin; frag != end; frag = NEXT_RX(frag))
 		bp->rx_ring[frag].addr &= ~MACB_BIT(RX_USED);
+
+	/* Make descriptor updates visible to hardware */
 	wmb();
 
 	/*
@@ -480,7 +489,9 @@ static int macb_rx(struct macb *bp, int budget)
 	for (; budget > 0; tail = NEXT_RX(tail)) {
 		u32 addr, ctrl;
 
+		/* Make hw descriptor updates visible to CPU */
 		rmb();
+
 		addr = bp->rx_ring[tail].addr;
 		ctrl = bp->rx_ring[tail].ctrl;
 
@@ -675,6 +686,8 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
 	bp->tx_ring[entry].addr = mapping;
 	bp->tx_ring[entry].ctrl = ctrl;
+
+	/* Make newly initialized descriptor visible to hardware */
 	wmb();
 
 	entry = NEXT_TX(entry);
@@ -783,9 +796,6 @@ static void macb_init_rings(struct macb *bp)
 
 static void macb_reset_hw(struct macb *bp)
 {
-	/* Make sure we have the write buffer for ourselves */
-	wmb();
-
 	/*
 	 * Disable RX and TX (XXX: Should we halt the transmission
 	 * more gracefully?)
-- 
1.8.0

^ permalink raw reply related

* [PATCH v4 01/10] net/macb: Add support for Gigabit Ethernet mode
From: Nicolas Ferre @ 2012-10-31 16:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1351690694.git.nicolas.ferre@atmel.com>

From: Patrice Vilchez <patrice.vilchez@atmel.com>

Add Gigabit Ethernet mode to GEM cadence IP and enable RGMII connection.

Signed-off-by: Patrice Vilchez <patrice.vilchez@atmel.com>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Tested-by: Joachim Eastwood <manabian@gmail.com>
---
 drivers/net/ethernet/cadence/macb.c | 15 ++++++++++++---
 drivers/net/ethernet/cadence/macb.h |  4 ++++
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index 6a4f499..0931cb7 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -152,13 +152,17 @@ static void macb_handle_link_change(struct net_device *dev)
 
 			reg = macb_readl(bp, NCFGR);
 			reg &= ~(MACB_BIT(SPD) | MACB_BIT(FD));
+			if (macb_is_gem(bp))
+				reg &= ~GEM_BIT(GBE);
 
 			if (phydev->duplex)
 				reg |= MACB_BIT(FD);
 			if (phydev->speed == SPEED_100)
 				reg |= MACB_BIT(SPD);
+			if (phydev->speed == SPEED_1000)
+				reg |= GEM_BIT(GBE);
 
-			macb_writel(bp, NCFGR, reg);
+			macb_or_gem_writel(bp, NCFGR, reg);
 
 			bp->speed = phydev->speed;
 			bp->duplex = phydev->duplex;
@@ -216,7 +220,10 @@ static int macb_mii_probe(struct net_device *dev)
 	}
 
 	/* mask with MAC supported features */
-	phydev->supported &= PHY_BASIC_FEATURES;
+	if (macb_is_gem(bp))
+		phydev->supported &= PHY_GBIT_FEATURES;
+	else
+		phydev->supported &= PHY_BASIC_FEATURES;
 
 	phydev->advertising = phydev->supported;
 
@@ -1388,7 +1395,9 @@ static int __init macb_probe(struct platform_device *pdev)
 		bp->phy_interface = err;
 	}
 
-	if (bp->phy_interface == PHY_INTERFACE_MODE_RMII)
+	if (bp->phy_interface == PHY_INTERFACE_MODE_RGMII)
+		macb_or_gem_writel(bp, USRIO, GEM_BIT(RGMII));
+	else if (bp->phy_interface == PHY_INTERFACE_MODE_RMII)
 #if defined(CONFIG_ARCH_AT91)
 		macb_or_gem_writel(bp, USRIO, (MACB_BIT(RMII) |
 					       MACB_BIT(CLKEN)));
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index a362751..33a050f 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -149,6 +149,8 @@
 #define MACB_IRXFCS_SIZE			1
 
 /* GEM specific NCFGR bitfields. */
+#define GEM_GBE_OFFSET				10
+#define GEM_GBE_SIZE				1
 #define GEM_CLK_OFFSET				18
 #define GEM_CLK_SIZE				3
 #define GEM_DBW_OFFSET				21
@@ -252,6 +254,8 @@
 /* Bitfields in USRIO (AT91) */
 #define MACB_RMII_OFFSET			0
 #define MACB_RMII_SIZE				1
+#define GEM_RGMII_OFFSET			0	/* GEM gigabit mode */
+#define GEM_RGMII_SIZE				1
 #define MACB_CLKEN_OFFSET			1
 #define MACB_CLKEN_SIZE				1
 
-- 
1.8.0

^ permalink raw reply related

* [PATCH v4 00/10] net/macb: driver enhancement concerning GEM support, ring logic and cleanup
From: Nicolas Ferre @ 2012-10-31 16:04 UTC (permalink / raw)
  To: linux-arm-kernel

This is an enhancement work that began several years ago. I try to catchup with
some performance improvement that has been implemented then by Havard.
The ring index logic and the TX error path modification are the biggest changes
but some cleanup/debugging have been added along the way.
The GEM revision will benefit from the Gigabit support.
Newer pinctrl infrastructure support is added but it is optional.

The series has been tested on several Atmel AT91 SoC with the two MACB/GEM
flavors.

v4: - remove unneeded device tree header includes
    - modified the computation of available entries in ring buffer
v3: - rebased on net-next to take into account current effor to merge
      at91_ether with macb drivers
    - add additional patch to use the new pinctrl infrastructure
v2: - modify the tx error handling: now uses a workqueue
    - information provided by ethtool -i were not accurate: removed


Havard Skinnemoen (4):
  net/macb: memory barriers cleanup
  net/macb: change debugging messages
  net/macb: clean up ring buffer logic
  net/macb: Offset first RX buffer by two bytes

Jean-Christophe PLAGNIOL-VILLARD (1):
  net/macb: add pinctrl consumer support

Nicolas Ferre (4):
  net/macb: remove macb_get_drvinfo()
  net/macb: tx status is more than 8 bits now
  net/macb: ethtool interface: add register dump feature
  net/macb: better manage tx errors

Patrice Vilchez (1):
  net/macb: Add support for Gigabit Ethernet mode

 drivers/net/ethernet/cadence/at91_ether.c |   6 +-
 drivers/net/ethernet/cadence/macb.c       | 448 +++++++++++++++++++++---------
 drivers/net/ethernet/cadence/macb.h       |  30 +-
 3 files changed, 337 insertions(+), 147 deletions(-)

-- 
1.8.0

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox