1.背景介绍

近期板卡上开始使用中航光电的光模块,查阅资料发现这些光模块都可以通过I2C来获取状态信息并进行开关控制,描述如下,其中需要特别注意的是所有光模块的读写I2C地址都是一样的,不可以挂在一根总线上,要么分别单独控制,要么通过交换芯片切换控制

海光cpu进bios 海光cpu linux_海光cpu进bios

 

 

 

 

海光cpu进bios 海光cpu linux_zynq_02

海光cpu进bios 海光cpu linux_zynq_03

海光cpu进bios 海光cpu linux_光模块_04

为了实现这一点,可以考虑通过zynq的I2C控制器来对光模块进行操作。由于ZYNQ PS部分的I2C控制器只有两个,当光模块数量超过2个时使用PL部分的I2C IP核来实现较为简单。

 

2.硬件参考设计

这里使用了6个ZYNQ PL部分的I2C核来控制6个外接光模块

海光cpu进bios 海光cpu linux_i2c_05

 

每个IP核出一个中断接入PL-PS的中断引脚,这里使用了一个组合器

海光cpu进bios 海光cpu linux_linux_06

 

海光cpu进bios 海光cpu linux_海光cpu进bios_07

 

3.devicetree设置

硬件产生后导出设备树后,能看到PL部分由I2C控制器,需要修改为如下:

海光cpu进bios 海光cpu linux_linux_08

把clock-names改成如图所示,加上interrupt-names这一行,最后是添加从设备,这里写2其实并不是i2c设备地址为2,只是为了把设备挂上,随便什么地址都可以。完整的设备树信息如下:

/dts-v1/;

/ {
	#address-cells = <0x1>;
	#size-cells = <0x1>;
	compatible = "xlnx,zynq-7000";

	cpus {
		#address-cells = <0x1>;
		#size-cells = <0x0>;

		cpu@0 {
			compatible = "arm,cortex-a9";
			device_type = "cpu";
			reg = <0x0>;
			clocks = <0x1 0x3>;
			clock-latency = <0x3e8>;
			cpu0-supply = <0x2>;
			operating-points = <0xa4cb8 0xf4240 0x5265c 0xf4240>;
		};

		cpu@1 {
			compatible = "arm,cortex-a9";
			device_type = "cpu";
			reg = <0x1>;
			clocks = <0x1 0x3>;
		};
	};

	fpga-full {
		compatible = "fpga-region";
		fpga-mgr = <0x3>;
		#address-cells = <0x1>;
		#size-cells = <0x1>;
		ranges;
	};

	pmu@f8891000 {
		compatible = "arm,cortex-a9-pmu";
		interrupts = <0x0 0x5 0x4 0x0 0x6 0x4>;
		interrupt-parent = <0x4>;
		reg = <0xf8891000 0x1000 0xf8893000 0x1000>;
	};

	fixedregulator {
		compatible = "regulator-fixed";
		regulator-name = "VCCPINT";
		regulator-min-microvolt = <0xf4240>;
		regulator-max-microvolt = <0xf4240>;
		regulator-boot-on;
		regulator-always-on;
		linux,phandle = <0x2>;
		phandle = <0x2>;
	};

	amba {
		u-boot,dm-pre-reloc;
		compatible = "simple-bus";
		#address-cells = <0x1>;
		#size-cells = <0x1>;
		interrupt-parent = <0x4>;
		ranges;

		adc@f8007100 {
			compatible = "xlnx,zynq-xadc-1.00.a";
			reg = <0xf8007100 0x20>;
			interrupts = <0x0 0x7 0x4>;
			interrupt-parent = <0x4>;
			clocks = <0x1 0xc>;

			xlnx,channels {
				#address-cells = <0x1>;
				#size-cells = <0x0>;

				channel@0 {
					reg = <0x0>;
				};

				channel@1 {
					reg = <0x1>;
				};

				channel@2 {
					reg = <0x2>;
				};

				channel@3 {
					reg = <0x3>;
				};

				channel@4 {
					reg = <0x4>;
				};

				channel@5 {
					reg = <0x5>;
				};

				channel@6 {
					reg = <0x6>;
				};

				channel@7 {
					reg = <0x7>;
				};

				channel@8 {
					reg = <0x8>;
				};

				channel@9 {
					reg = <0x9>;
				};

				channel@a {
					reg = <0xa>;
				};

				channel@b {
					reg = <0xb>;
				};

				channel@c {
					reg = <0xc>;
				};

				channel@d {
					reg = <0xd>;
				};

				channel@e {
					reg = <0xe>;
				};

				channel@f {
					reg = <0xf>;
				};

				channel@10 {
					reg = <0x10>;
				};
			};
		};

		gpio@e000a000 {
			compatible = "xlnx,zynq-gpio-1.0";
			#gpio-cells = <0x2>;
			clocks = <0x1 0x2a>;
			gpio-controller;
			interrupt-controller;
			#interrupt-cells = <0x2>;
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x14 0x4>;
			reg = <0xe000a000 0x1000>;
		};

		i2c@e0004000 {
			compatible = "cdns,i2c-r1p10-slave";
			status = "okay";
			clocks = <0x1 0x26>;
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x19 0x4>;
			reg = <0xe0004000 0x1000>;
			#address-cells = <0x1>;
			#size-cells = <0x0>;
			clock-frequency = <0x61a80>;
		};

		i2c@e0005000 {
			compatible = "cdns,i2c-r1p10-slave";
			status = "okay";
			clocks = <0x1 0x27>;
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x30 0x4>;
			reg = <0xe0005000 0x1000>;
			#address-cells = <0x1>;
			#size-cells = <0x0>;
			clock-frequency = <0x61a80>;
		};

		interrupt-controller@f8f01000 {
			compatible = "arm,cortex-a9-gic";
			#interrupt-cells = <0x3>;
			interrupt-controller;
			reg = <0xf8f01000 0x1000 0xf8f00100 0x100>;
			num_cpus = <0x2>;
			num_interrupts = <0x60>;
			linux,phandle = <0x4>;
			phandle = <0x4>;
		};

		cache-controller@f8f02000 {
			compatible = "arm,pl310-cache";
			reg = <0xf8f02000 0x1000>;
			interrupts = <0x0 0x2 0x4>;
			arm,data-latency = <0x3 0x2 0x2>;
			arm,tag-latency = <0x2 0x2 0x2>;
			cache-unified;
			cache-level = <0x2>;
		};

		memory-controller@f8006000 {
			compatible = "xlnx,zynq-ddrc-a05";
			reg = <0xf8006000 0x1000>;
		};

		ocmc@f800c000 {
			compatible = "xlnx,zynq-ocmc-1.0";
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x3 0x4>;
			reg = <0xf800c000 0x1000>;
		};

		serial@e0000000 {
			compatible = "xlnx,xuartps", "cdns,uart-r1p8";
			status = "okay";
			clocks = <0x1 0x17 0x1 0x28>;
			clock-names = "uart_clk", "pclk";
			reg = <0xe0000000 0x1000>;
			interrupts = <0x0 0x1b 0x4>;
			device_type = "serial";
			port-number = <0x4>;
		};

		spi@e000d000 {
			clock-names = "ref_clk", "pclk";
			clocks = <0x1 0xa 0x1 0x2b>;
			compatible = "xlnx,zynq-qspi-1.0";
			status = "okay";
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x13 0x4>;
			reg = <0xe000d000 0x1000>;
			#address-cells = <0x1>;
			#size-cells = <0x0>;
			is-dual = <0x1>;
			num-cs = <0x1>;
		};

		ethernet@e000b000 {
			compatible = "xlnx,ps7-ethernet-1.00.a";
			reg = <0xe000b000 0x1000>;
			status = "okay";
			interrupts = <0x0 0x16 0x4>;
			clocks = <0x1 0xd 0x1 0x1e>;
			clock-names = "ref_clk", "aper_clk";
			#address-cells = <0x1>;
			#size-cells = <0x0>;
			enet-reset = <0x4 0x2f 0x0>;
			#local-mac-address = [00 0b 35 11 12 00];
			local-mac-address = [00 0b 44 00 3e 16];
			phy-mode = "rgmii";
			phy-handle = <0x7>;
			xlnx,eth-mode = <0x1>;
			xlnx,has-mdio = <0x1>;
			xlnx,ptp-enet-clock = <0x69f6bcb>;

			mdio {
				#address-cells = <0x1>;
				#size-cells = <0x0>;

				phy@2 {
					compatible = "marvell,88e1111";
					device_type = "ethernet-phy";
					reg = <0x2>;
					linux,phandle = <0x7>;
					phandle = <0x7>;
				};
			};
		};

		ethernet@e000c000 {
			compatible = "xlnx,ps7-ethernet-1.00.b";
			reg = <0xe000c000 0x1000>;
			status = "okay";
			interrupts = <0x0 0x2d 0x4>;
			clocks = <0x1 0xe 0x1 0x1f>;
			clock-names = "ref_clk", "aper_clk";
			#address-cells = <0x1>;
			#size-cells = <0x0>;
			enet-reset = <0x4 0x2f 0x0>;
			local-mac-address = [00 0b 36 11 11 00];
			phy-mode = "rgmii";
			phy-handle = <0x8>;
			xlnx,eth-mode = <0x1>;
			xlnx,has-mdio = <0x1>;
			xlnx,ptp-enet-clock = <0x69f6bcb>;

			mdio {
				#address-cells = <0x1>;
				#size-cells = <0x0>;

				phy@1 {
					compatible = "marvell,88e1111";
					device_type = "ethernet-phy";
					reg = <0x1>;
					linux,phandle = <0x8>;
					phandle = <0x8>;
				};
			};
		};

		slcr@f8000000 {
			u-boot,dm-pre-reloc;
			#address-cells = <0x1>;
			#size-cells = <0x1>;
			compatible = "xlnx,zynq-slcr", "syscon", "simple-mfd";
			reg = <0xf8000000 0x1000>;
			ranges;
			linux,phandle = <0x5>;
			phandle = <0x5>;

			clkc@100 {
				u-boot,dm-pre-reloc;
				#clock-cells = <0x1>;
				compatible = "xlnx,ps7-clkc";
				fclk-enable = <0x7>;
				clock-output-names = "armpll", "ddrpll", "iopll", "cpu_6or4x", "cpu_3or2x", "cpu_2x", "cpu_1x", "ddr2x", "ddr3x", "dci", "lqspi", "smc", "pcap", "gem0", "gem1", "fclk0", "fclk1", "fclk2", "fclk3", "can0", "can1", "sdio0", "sdio1", "uart0", "uart1", "spi0", "spi1", "dma", "usb0_aper", "usb1_aper", "gem0_aper", "gem1_aper", "sdio0_aper", "sdio1_aper", "spi0_aper", "spi1_aper", "can0_aper", "can1_aper", "i2c0_aper", "i2c1_aper", "uart0_aper", "uart1_aper", "gpio_aper", "lqspi_aper", "smc_aper", "swdt", "dbg_trc", "dbg_apb";
				reg = <0x100 0x100>;
				ps-clk-frequency = <0x2faf080>;
				linux,phandle = <0x1>;
				phandle = <0x1>;
			};

			rstc@200 {
				compatible = "xlnx,zynq-reset";
				reg = <0x200 0x48>;
				#reset-cells = <0x1>;
				syscon = <0x5>;
			};

			pinctrl@700 {
				compatible = "xlnx,pinctrl-zynq";
				reg = <0x700 0x200>;
				syscon = <0x5>;
			};
		};

		dmac@f8003000 {
			compatible = "arm,pl330", "arm,primecell";
			reg = <0xf8003000 0x1000>;
			interrupt-parent = <0x4>;
			interrupt-names = "abort", "dma0", "dma1", "dma2", "dma3", "dma4", "dma5", "dma6", "dma7";
			interrupts = <0x0 0xd 0x4 0x0 0xe 0x4 0x0 0xf 0x4 0x0 0x10 0x4 0x0 0x11 0x4 0x0 0x28 0x4 0x0 0x29 0x4 0x0 0x2a 0x4 0x0 0x2b 0x4>;
			#dma-cells = <0x1>;
			#dma-channels = <0x8>;
			#dma-requests = <0x4>;
			clocks = <0x1 0x1b>;
			clock-names = "apb_pclk";
		};

		devcfg@f8007000 {
			compatible = "xlnx,zynq-devcfg-1.0";
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x8 0x4>;
			reg = <0xf8007000 0x100>;
			clocks = <0x1 0xc 0x1 0xf 0x1 0x10 0x1 0x11 0x1 0x12>;
			clock-names = "ref_clk", "fclk0", "fclk1", "fclk2", "fclk3";
			syscon = <0x5>;
			linux,phandle = <0x3>;
			phandle = <0x3>;
		};

		efuse@f800d000 {
			compatible = "xlnx,zynq-efuse";
			reg = <0xf800d000 0x20>;
		};

		timer@f8f00200 {
			compatible = "arm,cortex-a9-global-timer";
			reg = <0xf8f00200 0x20>;
			interrupts = <0x1 0xb 0x301>;
			interrupt-parent = <0x4>;
			clocks = <0x1 0x4>;
		};

		timer@f8001000 {
			interrupt-parent = <0x4>;
			interrupts = <0x0 0xa 0x4 0x0 0xb 0x4 0x0 0xc 0x4>;
			compatible = "cdns,ttc";
			clocks = <0x1 0x6>;
			reg = <0xf8001000 0x1000>;
		};

		timer@f8002000 {
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x25 0x4 0x0 0x26 0x4 0x0 0x27 0x4>;
			compatible = "cdns,ttc";
			clocks = <0x1 0x6>;
			reg = <0xf8002000 0x1000>;
		};

		timer@f8f00600 {
			interrupt-parent = <0x4>;
			interrupts = <0x1 0xd 0x301>;
			compatible = "arm,cortex-a9-twd-timer";
			reg = <0xf8f00600 0x20>;
			clocks = <0x1 0x4>;
		};

		watchdog@f8005000 {
			clocks = <0x1 0x2d>;
			compatible = "cdns,wdt-r1p2";
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x9 0x1>;
			reg = <0xf8005000 0x1000>;
			timeout-sec = <0xa>;
		};
	};

	amba_pl {
		#address-cells = <0x1>;
		#size-cells = <0x1>;
		compatible = "simple-bus";
		ranges;

		i2c@41600000 {
			#address-cells = <0x1>;
			#size-cells = <0x0>;
			clock-names = "s_axi_aclk";
			clocks = <0x1 0xf>;
			compatible = "xlnx,axi-iic-2.0", "xlnx,xps-iic-2.00.a";
			interrupt-names = "iic2intc_irpt";
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x34 0x4>;
			reg = <0x41600000 0x10000>;
		};

		i2c@41610000 {
			#address-cells = <0x1>;
			#size-cells = <0x0>;
			clock-names = "s_axi_aclk";
			clocks = <0x1 0xf>;
			compatible = "xlnx,axi-iic-2.0", "xlnx,xps-iic-2.00.a";
			interrupt-names = "iic2intc_irpt";
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x1f 0x4>;
			reg = <0x41610000 0x10000>;

			i2ctemperature@2 {
				compatible = "temperature";
				reg = <0x2>;
			};
		};

		i2c@41620000 {
			#address-cells = <0x1>;
			#size-cells = <0x0>;
			clock-names = "s_axi_aclk";
			clocks = <0x1 0xf>;
			compatible = "xlnx,axi-iic-2.0", "xlnx,xps-iic-2.00.a";
			interrupt-names = "iic2intc_irpt";
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x20 0x4>;
			reg = <0x41620000 0x10000>;

			i2ctemperature@2 {
				compatible = "temperature";
				reg = <0x2>;
			};
		};

		i2c@41630000 {
			#address-cells = <0x1>;
			#size-cells = <0x0>;
			clock-names = "s_axi_aclk";
			clocks = <0x1 0xf>;
			compatible = "xlnx,axi-iic-2.0", "xlnx,xps-iic-2.00.a";
			interrupt-names = "iic2intc_irpt";
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x21 0x4>;
			reg = <0x41630000 0x10000>;

			i2ctemperature@2 {
				compatible = "temperature";
				reg = <0x2>;
			};
		};

		i2c@41640000 {
			#address-cells = <0x1>;
			#size-cells = <0x0>;
			clock-names = "s_axi_aclk";
			clocks = <0x1 0xf>;
			compatible = "xlnx,axi-iic-2.0", "xlnx,xps-iic-2.00.a";
			interrupt-names = "iic2intc_irpt";
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x22 0x4>;
			reg = <0x41640000 0x10000>;

			i2ctemperature@2 {
				compatible = "temperature";
				reg = <0x2>;
			};
		};

		i2c@41650000 {
			#address-cells = <0x1>;
			#size-cells = <0x0>;
			clock-names = "s_axi_aclk";
			clocks = <0x1 0xf>;
			compatible = "xlnx,axi-iic-2.0", "xlnx,xps-iic-2.00.a";
			interrupt-names = "iic2intc_irpt";
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x1d 0x4>;
			reg = <0x41650000 0x10000>;

			i2ctemperature@2 {
				compatible = "temperature";
				reg = <0x2>;
			};
		};

		i2c@41660000 {
			#address-cells = <0x1>;
			#size-cells = <0x0>;
			clock-names = "s_axi_aclk";
			clocks = <0x1 0xf>;
			compatible = "xlnx,axi-iic-2.0", "xlnx,xps-iic-2.00.a";
			interrupt-names = "iic2intc_irpt";
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x1e 0x4>;
			reg = <0x41660000 0x10000>;

			i2ctemperature@2 {
				compatible = "temperature";
				reg = <0x2>;
			};
		};

		axi_quad_spi@90000000 {
			bits-per-word = <0x8>;
			clock-names = "ext_spi_clk", "s_axi_aclk";
			clocks = <0x1 0x11 0x1 0xf>;
			compatible = "xlnx,axi_qspi-2.00.a";
			fifo-size = <0x100>;
			interrupt-names = "ip2intc_irpt";
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x23 0x1>;
			num-cs = <0x4>;
			reg = <0x90000000 0x10000>;
			xlnx,num-ss-bits = <0x4>;
			xlnx,spi-mem-addr-bits = <0x18>;
			xlnx,spi-memory = <0x2>;
			xlnx,type-of-axi4-interface = <0x0>;
			xlnx,use-startup = <0x1>;
			xlnx,spi-mode = <0x2>;
			#address-cells = <0x1>;
			#size-cells = <0x0>;
			is-dual = <0x0>;

			flash@0 {
				compatible = "n25q256,spi-nor";
				reg = <0x0>;
				spi-max-frequency = <0x9ef21b0>;
				spi-tx-bus-width = <0x1>;
				spi-rx-bus-width = <0x1>;
				#address-cells = <0x1>;
				#size-cells = <0x1>;

				partition@0x0000000 {
					label = "spi-flash1";
					reg = <0x0 0x2000000>;
				};
			};

			flash@1 {
				compatible = "n25q256,spi-nor";
				reg = <0x1>;
				spi-max-frequency = <0x9ef21b0>;
				spi-tx-bus-width = <0x1>;
				spi-rx-bus-width = <0x1>;
				#address-cells = <0x1>;
				#size-cells = <0x1>;

				partition@0x0000000 {
					label = "spi-flash2";
					reg = <0x0 0x2000000>;
				};
			};

			flash@2 {
				compatible = "n25q256,spi-nor";
				reg = <0x2>;
				spi-max-frequency = <0x9ef21b0>;
				spi-tx-bus-width = <0x1>;
				spi-rx-bus-width = <0x1>;
				#address-cells = <0x1>;
				#size-cells = <0x1>;

				partition@0x0000000 {
					label = "spi-flash3";
					reg = <0x0 0x2000000>;
				};
			};

			flash@3 {
				compatible = "n25q256,spi-nor";
				reg = <0x3>;
				spi-max-frequency = <0x9ef21b0>;
				spi-tx-bus-width = <0x1>;
				spi-rx-bus-width = <0x1>;
				#address-cells = <0x1>;
				#size-cells = <0x1>;

				partition@0x0000000 {
					label = "spi-flash4";
					reg = <0x0 0x2000000>;
				};
			};
		};

		axi_quad_spi@80000000 {
			bits-per-word = <0x8>;
			clock-names = "ext_spi_clk", "s_axi_aclk";
			clocks = <0x1 0x11 0x1 0xf>;
			compatible = "xlnx,axi_qspi-2.00.a";
			fifo-size = <0x100>;
			interrupt-names = "ip2intc_irpt";
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x24 0x1>;
			num-cs = <0x4>;
			reg = <0x80000000 0x10000>;
			xlnx,num-ss-bits = <0x4>;
			xlnx,spi-mem-addr-bits = <0x18>;
			xlnx,spi-memory = <0x2>;
			xlnx,type-of-axi4-interface = <0x0>;
			xlnx,use-startup = <0x1>;
			xlnx,spi-mode = <0x2>;
			#address-cells = <0x1>;
			#size-cells = <0x0>;
			is-dual = <0x0>;

			flash@0 {
				compatible = "n25q256,spi-nor";
				reg = <0x0>;
				spi-max-frequency = <0x9ef21b0>;
				spi-tx-bus-width = <0x1>;
				spi-rx-bus-width = <0x1>;
				#address-cells = <0x1>;
				#size-cells = <0x1>;

				partition@0x0000000 {
					label = "spi-flash5";
					reg = <0x0 0x2000000>;
				};
			};

			flash@1 {
				compatible = "n25q256,spi-nor";
				reg = <0x1>;
				spi-max-frequency = <0x9ef21b0>;
				spi-tx-bus-width = <0x1>;
				spi-rx-bus-width = <0x1>;
				#address-cells = <0x1>;
				#size-cells = <0x1>;

				partition@0x0000000 {
					label = "spi-flash6";
					reg = <0x0 0x2000000>;
				};
			};

			flash@2 {
				compatible = "n25q256,spi-nor";
				reg = <0x2>;
				spi-max-frequency = <0x9ef21b0>;
				spi-tx-bus-width = <0x1>;
				spi-rx-bus-width = <0x1>;
				#address-cells = <0x1>;
				#size-cells = <0x1>;

				partition@0x0000000 {
					label = "spi-flash7";
					reg = <0x0 0x2000000>;
				};
			};

			flash@3 {
				compatible = "n25q256,spi-nor";
				reg = <0x3>;
				spi-max-frequency = <0x9ef21b0>;
				spi-tx-bus-width = <0x1>;
				spi-rx-bus-width = <0x1>;
				#address-cells = <0x1>;
				#size-cells = <0x1>;

				partition@0x0000000 {
					label = "spi-flash8";
					reg = <0x0 0x2000000>;
				};
			};
		};
	};

	chosen {
		bootargs = "earlycon vmalloc=400M";
		stdout-path = "serial0:115200n8";
	};

	aliases {
		ethernet0 = "/amba/ethernet@e000b000";
		ethernet1 = "/amba/ethernet@e000c000";
		serial0 = "/amba/serial@e0000000";
		spi0 = "/amba/spi@e000d000";
		spi1 = "/amba_pl/axi_quad_spi@90000000";
		spi2 = "/amba_pl/axi_quad_spi@80000000";
		flash0 = "/amba_pl/axi_quad_spi@90000000/flash@0";
		flash1 = "/amba_pl/axi_quad_spi@90000000/flash@1";
		flash2 = "/amba_pl/axi_quad_spi@90000000/flash@2";
		flash3 = "/amba_pl/axi_quad_spi@90000000/flash@3";
		flash4 = "/amba_pl/axi_quad_spi@80000000/flash@0";
		flash5 = "/amba_pl/axi_quad_spi@80000000/flash@1";
		flash6 = "/amba_pl/axi_quad_spi@80000000/flash@2";
		flash7 = "/amba_pl/axi_quad_spi@80000000/flash@3";
	};

	memory {
		device_type = "memory";
		reg = <0x0 0x40000000>;
	};
};

 

4.内核配置

内核中需要增加从设备驱动,根据厂家提供的i2c读写时序仿照之前的1848驱动来做,注意由于i2c地址全都是0x50(手册上读写地址分别为0xA0,0xA1,右移一位读写位后就是0x50),所以驱动里面地址可以写成固定值,如下图:

海光cpu进bios 海光cpu linux_linux_09

 

完整代码如下:

/*
 * temperature bus driver
 *
 * Copyright (C) 2014 CGT Corp.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#define DEBUG

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/delay.h>

#include <linux/serial_core.h>

/* Each client has this additional data */
#define USER_EEPROM_SIZE	0xFFFF48
#define USER_XFER_MAX_COUNT	0x8

/* Addresses to scan */
static const unsigned short temperature_i2c[] = { 0x3, I2C_CLIENT_END };

static unsigned read_timeout = 25;
module_param(read_timeout, uint, 0);
MODULE_PARM_DESC(read_timeout, "Time (in ms) to try reads (default 25)");

static unsigned write_timeout = 25;
module_param(write_timeout, uint, 0);
MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)");

struct temperature_data {
	struct mutex	lock;
	u8	*data;
};

static ssize_t temperature_read_data( struct i2c_client *client,
	char *buf, unsigned offset, size_t count)
{
	struct i2c_msg msg[2];
	u8 msgbuf[4];
	unsigned long timeout, transfer_time;
	int status;

	memset(msg, 0, sizeof(msg));

	msgbuf[0] = (u8)(offset & 0xff);

	msg[0].addr = 0x50;//client->addr;
	msg[0].buf = msgbuf;
	msg[0].len = 1;

	msg[1].addr = 0x50;//client->addr;
	msg[1].flags = I2C_M_RD;
	msg[1].buf = buf;
	msg[1].len = count;

	/*
	 * Reads fail if the previous write didn't complete yet. We may
	 * loop a few times until this one succeeds, waiting at least
	 * long enough for one entire page write to work.
	 */
	timeout = jiffies + msecs_to_jiffies(read_timeout);
	do {
		transfer_time = jiffies;

		status = i2c_transfer(client->adapter, msg, 2);

		if (status == 2)
			status = count;

		dev_dbg(&client->dev, "read %ld@0x%lx --> %d (%ld)\n",
				count, (unsigned long)offset, status, jiffies);

		if (status == count)
			return count;

		/* REVISIT: at HZ=100, this is sloooow */
		msleep(1);
	} while (time_before(transfer_time, timeout));

	return -ETIMEDOUT;

 }


static ssize_t temperature_read(struct file *filp, struct kobject *kobj,
			    struct bin_attribute *bin_attr,
			    char *buf, loff_t offset, size_t count)
{
	struct i2c_client *client = kobj_to_i2c_client(kobj);
	struct temperature_data *data = i2c_get_clientdata(client);

	ssize_t retval = 0;

	if (offset > USER_EEPROM_SIZE)
		return 0;

	if (offset + count > USER_EEPROM_SIZE)
		count = USER_EEPROM_SIZE - offset;

	mutex_lock(&data->lock);

	dev_dbg(&client->dev, "cps226 start read %ld@0x%lx ..\n", count, (unsigned long)offset);

	while (count > 0) {
		ssize_t	status = count>USER_XFER_MAX_COUNT?USER_XFER_MAX_COUNT:count;
		status = temperature_read_data(client, buf, offset, status);
		if (status <= 0) {
			if (retval == 0)
				retval = status;
			break;
		}
		buf += status;
		offset += status;
		count -= status;
		retval += status;
	}

	dev_dbg(&client->dev, "cps226 end read %ld@0x%lx  !\n", retval, (unsigned long)offset);

	mutex_unlock(&data->lock);

	return retval;

 }

static ssize_t temperature_write_config(
	struct i2c_client *client,
	struct temperature_data *data,
	char *buf, unsigned offset, size_t count)
{
	struct i2c_msg msg[1];
	u8 *msgbuf;
	unsigned long timeout, transfer_time;
	int status;

	memset(msg, 0, sizeof(msg));

	msgbuf = data->data;

//	msgbuf[0] = (u8)((offset >> 18) & 0x3f);
//	msgbuf[1] = (u8)((offset >> 10) & 0xff);
//	msgbuf[2] = (u8)((offset >>  2) & 0xff);

	

	msgbuf[0] = (u8)(offset& 0xff);
	memcpy(msgbuf+1, buf, count);
	msg[0].addr = 0x50;//client->addr;
	msg[0].buf = msgbuf;
	msg[0].len = 1 + count;


	/*
	 * Reads fail if the previous write didn't complete yet. We may
	 * loop a few times until this one succeeds, waiting at least
	 * long enough for one entire page write to work.
	 */
	timeout = jiffies + msecs_to_jiffies(write_timeout);
	do {
		transfer_time = jiffies;

		status = i2c_transfer(client->adapter, msg, 1);

		if (status == 1)
			status = count;

		dev_dbg(&client->dev, "write %ld@0x%lx --> %d (%ld)\n",
				count, (unsigned long)offset, status, jiffies);

		if (status == count)
			return count;

		/* REVISIT: at HZ=100, this is sloooow */
		msleep(1);
	} while (time_before(transfer_time, timeout));

	return -ETIMEDOUT;
 }

static ssize_t temperature_write(struct file *filp, struct kobject *kobj,
			    struct bin_attribute *bin_attr,
			    char *buf, loff_t offset, size_t count)
{
	struct i2c_client *client = kobj_to_i2c_client(kobj);
	struct temperature_data *data = i2c_get_clientdata(client);

	ssize_t retval = 0;

	if (offset > USER_EEPROM_SIZE)
		return 0;

	if (offset + count > USER_EEPROM_SIZE)
		count = USER_EEPROM_SIZE - offset;

	mutex_lock(&data->lock);

	dev_dbg(&client->dev, "temperature start write %ld@0x%lx ..\n", count, (unsigned long)offset);

	while (count > 0) {
		ssize_t	status = count>USER_XFER_MAX_COUNT?USER_XFER_MAX_COUNT:count;
		status = temperature_write_config(client, data, buf, offset, status);
		if (status <= 0) {
			if (retval == 0)
				retval = status;
			break;
		}
		buf += status;
		offset += status;
		count -= status;
		retval += status;
	}

	dev_dbg(&client->dev, "temperature end write %ld@0x%lx  !\n", retval, (unsigned long)offset);

	mutex_unlock(&data->lock);

	return retval;

 }

static struct bin_attribute user_temperature_attr = {
	.attr = {
		.name = "temperature",
		.mode = (S_IRUSR | S_IWUSR),
	},
	.size = USER_EEPROM_SIZE,
	.read = temperature_read,
	.write = temperature_write,
};

/* Return 0 if detection is successful, -ENODEV otherwise */
static int temperature_detect(struct i2c_client *client, struct i2c_board_info *info)
{
	struct i2c_adapter *adapter = client->adapter;

	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
		dev_dbg(&client->dev, "temperature detect error for BYTE access !\n");
		return -ENODEV;
	}

	strlcpy(info->type, "temperature", I2C_NAME_SIZE);

	return 0;
 }

static int temperature_probe(struct i2c_client *client,
			 const struct i2c_device_id *id)
{
	struct i2c_adapter *adapter = client->adapter;
	struct temperature_data *data;
	int err ;

	dev_notice(&client->dev, "temperature driver\n" );

	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
		dev_err(&client->dev, "temperature data driver:  BYTE DATA not supported! \n" );
		return -ENODEV;
	}

	if (!(data = kzalloc(sizeof(struct  temperature_data), GFP_KERNEL))) {
		dev_err(&client->dev, "temperature data driver:  Memory alloc error ! \n" );
		return -ENOMEM;
	}

	/* alloc buffer */
	data->data = devm_kzalloc(&client->dev, USER_XFER_MAX_COUNT + 8, GFP_KERNEL);
	if (!data->data) {
		dev_err(&client->dev, "temperature data driver:  Memory alloc error ! \n" );
		err = -ENOMEM;
		goto exit_kfree;
	}

	/* Init real i2c_client */
	i2c_set_clientdata(client, data);
	mutex_init(&data->lock);

	err = sysfs_create_bin_file(&client->dev.kobj, &user_temperature_attr);
	if (err) {
		dev_err(&client->dev, "temperature data driver:  sysfs create error ! \n" );
		goto exit_kfree;
	}

	return 0;

exit_kfree:
	if(data->data)
		kfree(data->data);
	kfree(data);
	return err;
 }

static int temperature_remove(struct i2c_client *client)
{
	struct temperature_data *data = i2c_get_clientdata(client);

	sysfs_remove_bin_file(&client->dev.kobj, &user_temperature_attr);
	if(data->data)
		kfree(data->data);
	kfree(data);

	return 0;
 }

static const struct i2c_device_id temperature_id[] = {
	{ "temperature", 0 },
	{ }
};
MODULE_DEVICE_TABLE(i2c, temperature_id);

static struct i2c_driver temperature_driver = {
	.driver = {
		.name	= "temperature",
	},
	.probe		= temperature_probe,
	.remove		= temperature_remove,
	.id_table	= temperature_id,

	.class		= I2C_CLASS_SPD,
	.detect		= temperature_detect,
	.address_list	= temperature_i2c,
};

module_i2c_driver(temperature_driver);

MODULE_AUTHOR("RobinLee");
MODULE_DESCRIPTION("temperature driver");
MODULE_LICENSE("GPL");

 

然后将驱动编进内核

  

海光cpu进bios 海光cpu linux_linux_10

海光cpu进bios 海光cpu linux_光模块_11

最后修改config配置文件,增加CONFIG_I2C_XILINX

海光cpu进bios 海光cpu linux_海光cpu进bios_12

 

5.应用测试

应用中只需要打开设备,然后读写寄存器即可,代码如下:

#define  I2C_0   	"/sys/class/i2c-dev/i2c-3/device/3-0002/temperature" //8518,电压是0x1A,0X1B,温度是0x1E,0X1F
#define  I2C_1   	"/sys/class/i2c-dev/i2c-4/device/4-0002/temperature" //8514,电压是0x16,0X17,温度是0x14,0X15
#define  I2C_2   	"/sys/class/i2c-dev/i2c-5/device/5-0002/temperature" //8514,电压是0x16,0X17,温度是0x14,0X15
#define  I2C_3		"/sys/class/i2c-dev/i2c-6/device/6-0002/temperature" //8514,电压是0x16,0X17,温度是0x14,0X15
#define  I2C_4   	"/sys/class/i2c-dev/i2c-7/device/7-0002/temperature" //8513,电压是0x16,0X17,温度是0x12,0X13
#define  I2C_5   	"/sys/class/i2c-dev/i2c-8/device/8-0002/temperature" //8513,电压是0x16,0X17,温度是0x12,0X13

static int fp_i2c_0, fp_i2c_1, fp_i2c_2, fp_i2c_3, fp_i2c_4, fp_i2c_5;

void init_i2c_file_opt(void)
{
	fp_i2c_0 = open(I2C_0, O_RDWR);
	printf("input fd:%d !\n", fp_i2c_0);
	if (fp_i2c_0 == NULL)
		printf("open ic-0 failed..\n");
	fp_i2c_1 = open(I2C_1, O_RDWR);
	printf("input fd:%d !\n", fp_i2c_1);
	if (fp_i2c_1 == NULL)
		printf("open ic-1 failed..\n");
	fp_i2c_2 = open(I2C_2, O_RDWR);
	printf("input fd:%d !\n", fp_i2c_2);
	if (fp_i2c_2 == NULL)
		printf("open ic-2 failed..\n");
	fp_i2c_3 = open(I2C_3, O_RDWR);
	printf("input fd:%d !\n", fp_i2c_3);
	if (fp_i2c_3 == NULL)
		printf("open ic-3 failed..\n");
	fp_i2c_4 = open(I2C_4, O_RDWR);
	printf("input fd:%d !\n", fp_i2c_4);
	if (fp_i2c_4 == NULL)
		printf("open ic-4 failed..\n");
	fp_i2c_5 = open(I2C_5, O_RDWR);
	printf("input fd:%d !\n", fp_i2c_5);
	if (fp_i2c_5 == NULL)
		printf("open ic-5 failed..\n");
}

int read_temperatue(unsigned int num, unsigned int offset)
{
	int fd = -1;
	char value = -1;

	if (num == 0)
	{
		fd = fp_i2c_0;
	}
	else if (num == 1)
	{
		fd = fp_i2c_1;
	}
	else if (num == 2)
	{
		fd = fp_i2c_2;
	}
	else if (num == 3)
	{
		fd = fp_i2c_3;
	}
	else if (num == 4)
	{
		fd = fp_i2c_4;
	}
	else if (num == 5)
	{
		fd = fp_i2c_5;
	}
	else
	{
		printf("input error::Invalid param !\n");
		return 0;
	}
	if (fd < 0)
	{
		printf("Invalid device handle !\n");
		return value;
	}

	if (lseek(fd, offset, SEEK_SET) == (off_t) - 1)
	{
		printf("failed for seek to offset 0x%x !\n", offset);
		return value;
	}

	if (read(fd, &value, sizeof(value)) != sizeof(value))
	{
		printf("failed for read from offset 0x%x !\n", offset);
		return value;
	}
	return value;
}

void i2c_temp(void)
{
	unsigned int addr = 0;
	int value = 0;
	int value2 = 0;
	float temp;
	int i;
	for(i=0;i<6;i++)
	{
		//volt info
		if (i == 0) //除了8518,其他的电压计算公式和地址都一样
		{
			value =  read_temperatue(i,0x1A);
			value2 = read_temperatue(i,0x1B);
			printf("No. %d----value:%d ,value2:%d, vol:%d mV\n",i,value,value2, value*256+value2);
		}
		else
		{
			value =  read_temperatue(i,0x16);
			value2 = read_temperatue(i,0x17);
			printf("No. %d----value:%d ,value2:%d, vol:%f mV\n",i,value,value2, (float)(((float)value*256.0+(float)value2)*0.1));
		}

		sleep(1);
		//temperature info
		if ((i == 1) || (i == 2) || (i == 3)) //这三个是8514,两个温度处理
		{
			value =  read_temperatue(i,0x14);
			value2 = read_temperatue(i,0x15);
			if(value < 128)
			{
				temp = value + (float)value2/256;
			}
			else
			{
				temp = value -256 + (float)value2/256;
			}
			printf("No. %d----value:%d ,value2:%d, temp:%f cent\n",i,value,value2, temp);
		}
		else if (i == 0) //8518
		{
			value =  read_temperatue(i,0x1E);
			value2 = read_temperatue(i,0x1F);
			if(value < 128)
			{
				temp = value + (float)value2/256;
			}
			else
			{
				temp = value -256 + (float)value2/256;
			}
			printf("No. %d----value:%d ,value2:%d, temp:%f cent\n",i,value,value2, temp);
		}
		else //8513
		{
			value =  read_temperatue(i,0x12);
			value2 = read_temperatue(i,0x13);
			if(value < 128)
			{
				temp = value + (float)value2/256;
			}
			else
			{
				temp = value -256 + (float)value2/256;
			}
			printf("No. %d----value:%d ,value2:%d, temp:%f cent\n",i,value,value2, temp);
		}
		sleep(1);
		printf("\n\r");
	}
}

 

测试结果可以读写

海光cpu进bios 海光cpu linux_i2c_13