Hier geht es um den NIC OS8104 von SMSC, über den die gesamte Kommunikation mit dem MOST-Ring stattfindet.

  • Anpassung DTS-File (diese Anpassung gilt für Allwinner A20, für RK3328 (Rock64) ist sie hier zu finden):

Im DT-File wird die Konfiguration des SBCs hinlegt, wie viele CPU, LEDs, die gesamte Peripherie, wie I²C, I²S, SPI, CAN, ...

Hier kommt auch die Definition des OS8104 rein. In meinem Fall hängt der OS8104 am I²C-2 Controller:

&i2c2 {
	pinctrl-names = "default";
	pinctrl-0 = <&i2c2_pins_a>;
	status = "okay";
	clock-frequency = <400000>;
	
	os8104: os8104@41 {
		compatible = "smsc,os8104";
		reg = <0x41>;
		master = <0>;					/* slave mode*/
		bypass = <0>;					/* /ABY (all bypass mode) im register bXCR */
		
		reset-gpios = <&pio 8 17 0>;	/* PI17 */
		int-gpios = <&pio 8 16 0>;		/* PI16 */
		aint-gpios = <&pio 8 19 0>;		/* PI19 */
		error-gpios = <&pio 8 18 0>;	/* PI18 */
		3db-gpios = <&pio 8 20 0>;		/* PI20 */
	};
	
	tmp275: tmp275@48 {
		compatible = "ti,tmp275";
		reg = <0x48>;		
	};
};

In der Zeile 5 wird der Takt des I²C Buses auf 400kHz festgelegt. Mit dem zusätzlichen Block (Zeile 7 bis 18) sagen wir dem Kernel, dass am I²C-2 noch ein Chip hängt, an der Adresse 0x41. Das wird beim Laden des Moduls ausgelesen.

In den Zeilen 20 bis 23 ist noch ein Temperatursensor registriert, er liefert die Temperatur im Inneren des Steuergerätes zurück. Der Chip wird vom Modul LM75 unterstützt. Die gelieferte Temperatur ist für den Funktionsblock "EnhancedTestability" notwendig. Der GPIO "3db-gpios" hat nichts direkt mit dem OS8104 zu tun, er ist dazu da um die Sendeleistung des FOTs abzusenken (auf -3dB) und wird ebenfalls von "EnhancedTestability" benötigt.

Wichtig: die Eigenschaftsnamen im GPIO-Block (Zeile 13 bis 17) haben eine gewisse Semantik, die so aussieht: <FUNKTION>-gpio oder <FUNKTION>-gpios. Mit <FUNKTION> ist die "Aufgabe" des Pins gemeint, zum Beispiel /RESET oder /INT.

  • Auslesen der Daten aus dem DeviceTree:

Wenn diese GPIOs dann im LKM ausgelesen werden, lässt man den Suffix "-gpio" (oder. "-gpios") weg, sieht dann für reset-gpios so aus:

devm_gpiod_get (&client->dev, "reset", GPIOD_OUT_HIGH);

Der Zusatz "GPIOD_OUT_HIGH" bedeutet, dass der Pin als Ausgang mit dem Zustand 1 initialisiert wird.

Für einen Eingang sieht es so aus:

devm_gpiod_get (&client->dev, "int", GPIOD_IN);

Will man den Pin als Trigger für einen Interrupt nutzen, so muss ein Interrupt-Händler

static irq_handler_t os8104_int_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs) {
        pr_info("OS8104 INT interrupt fired!!!\n");     
        
        pr_info ("- >OS8104 INT interrupt left!!!\n");
        return (irq_handler_t) IRQ_HANDLED;
}

registriert werden:

priv->irq = gpiod_to_irq (priv->int_gpios);
rc = request_irq(priv->irq
				, (irq_handler_t) os8104_int_irq_handler
				, IRQF_TRIGGER_LOW
				, "os8104_int_irq"
				, &os8104
				);

Die Variable priv->irq ist einfach nur ein Integer um die IRQ-Nummer vorzuhalten. Der eigentliche Request findet in der Zeile 2 statt, es wird die IRQ-Nummer übergeben, IRQ-Handler (also die Funktion, die aufgerufen wird, wenn Interrupt gefeuert wird), die Bedienung, die zum Feuern des Interrupts führt (wenn der betreffende Pin auf Zustand 0 wechselt) und der Name des Interrupts.

Oder alternativ:

rc = devm_request_irq(&client->dev
						, os8104->irq
						, (irq_handler_t) os8104_int_irq_handler
						, IRQF_TRIGGER_LOW
						, "os8104_int_irq"
						, client);