diff options
162 files changed, 43930 insertions, 1681 deletions
diff --git a/Documentation/DocBook/mtdnand.tmpl b/Documentation/DocBook/mtdnand.tmpl index fe122d6e686..a248f42a121 100644 --- a/Documentation/DocBook/mtdnand.tmpl +++ b/Documentation/DocBook/mtdnand.tmpl @@ -1224,8 +1224,6 @@ in this page</entry>  #define NAND_BBT_CREATE		0x00000200  /* Search good / bad pattern through all pages of a block */  #define NAND_BBT_SCANALLPAGES	0x00000400 -/* Scan block empty during good / bad block scan */ -#define NAND_BBT_SCANEMPTY	0x00000800  /* Write bbt if neccecary */  #define NAND_BBT_WRITE		0x00001000  /* Read and write back block contents when writing bbt */ diff --git a/Documentation/devicetree/bindings/bus/ti-gpmc.txt b/Documentation/devicetree/bindings/bus/ti-gpmc.txt index 4b87ea1194e..704be9306c9 100644 --- a/Documentation/devicetree/bindings/bus/ti-gpmc.txt +++ b/Documentation/devicetree/bindings/bus/ti-gpmc.txt @@ -95,7 +95,6 @@ GPMC chip-select settings properties for child nodes. All are optional.  - gpmc,burst-wrap	Enables wrap bursting  - gpmc,burst-read	Enables read page/burst mode  - gpmc,burst-write	Enables write page/burst mode -- gpmc,device-nand	Device is NAND  - gpmc,device-width	Total width of device(s) connected to a GPMC  			chip-select in bytes. The GPMC supports 8-bit  			and 16-bit devices and so this property must be diff --git a/Documentation/devicetree/bindings/input/atmel,maxtouch.txt b/Documentation/devicetree/bindings/input/atmel,maxtouch.txt new file mode 100644 index 00000000000..6319f07bfbc --- /dev/null +++ b/Documentation/devicetree/bindings/input/atmel,maxtouch.txt @@ -0,0 +1,25 @@ +Atmel maXTouch touchscreen/touchpad + +Required properties: +- compatible: +    atmel,maxtouch + +- reg: The I2C address of the device + +- interrupts: The sink for the touch controller CHG/IRQ output +    See ../interrupt-controller/interrupts.txt + +Optional properties for maXTouch device: + +- linux,gpio-keymap: An array of up to 4 entries indicating the Linux +    keycode generated by each GPIO. Linux keycodes are defined in +    <dt-bindings/input/input.h>. + +Example: + +	touch@4b { +		compatible = "atmel,maxtouch"; +		reg = <0x4b>; +		interrupt-parent = <&gpio>; +		interrupts = <TEGRA_GPIO(W, 3) IRQ_TYPE_LEVEL_LOW>; +	}; diff --git a/Documentation/devicetree/bindings/mtd/gpmc-nand.txt b/Documentation/devicetree/bindings/mtd/gpmc-nand.txt index 6a983c1d87c..5e1f31b5ff7 100644 --- a/Documentation/devicetree/bindings/mtd/gpmc-nand.txt +++ b/Documentation/devicetree/bindings/mtd/gpmc-nand.txt @@ -22,15 +22,26 @@ Optional properties:  				width of 8 is assumed.   - ti,nand-ecc-opt:		A string setting the ECC layout to use. One of: - -		"sw"		Software method (default) -		"hw"		Hardware method -		"hw-romcode"	gpmc hamming mode method & romcode layout +		"sw"		<deprecated> use "ham1" instead +		"hw"		<deprecated> use "ham1" instead +		"hw-romcode"	<deprecated> use "ham1" instead +		"ham1"		1-bit Hamming ecc code  		"bch4"		4-bit BCH ecc code  		"bch8"		8-bit BCH ecc code - - elm_id:	Specifies elm device node. This is required to support BCH - 		error correction using ELM module. + - ti,nand-xfer-type:		A string setting the data transfer type. One of: + +		"prefetch-polled"	Prefetch polled mode (default) +		"polled"		Polled mode, without prefetch +		"prefetch-dma"		Prefetch enabled sDMA mode +		"prefetch-irq"		Prefetch enabled irq mode + + - elm_id:	<deprecated> use "ti,elm-id" instead + - ti,elm-id:	Specifies phandle of the ELM devicetree node. +		ELM is an on-chip hardware engine on TI SoC which is used for +		locating ECC errors for BCHx algorithms. SoC devices which have +		ELM hardware engines should specify this device node in .dtsi +		Using ELM for ECC error correction frees some CPU cycles.  For inline partiton table parsing (optional): @@ -55,6 +66,7 @@ Example for an AM33xx board:  			reg = <0 0 0>; /* CS0, offset 0 */  			nand-bus-width = <16>;  			ti,nand-ecc-opt = "bch8"; +			ti,nand-xfer-type = "polled";  			gpmc,sync-clk-ps = <0>;  			gpmc,cs-on-ns = <0>; @@ -369,7 +369,7 @@ LINUXINCLUDE    := \  KBUILD_CPPFLAGS := -D__KERNEL__ -KBUILD_CFLAGS   := -Werror -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \ +KBUILD_CFLAGS   := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \  		   -fno-strict-aliasing -fno-common \  		   -Werror-implicit-function-declaration \  		   -Wno-format-security \ diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index f414eaa38e5..7f7977cf934 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1596,6 +1596,7 @@ config ARCH_NR_GPIO  	default 352 if ARCH_VT8500  	default 288 if ARCH_SUNXI  	default 264 if MACH_H4700 +	default 256 if MACH_OMAP3_H1  	default 0  	help  	  Maximum number of GPIOs in the system. diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index e2a70d9fb96..ebd3b406229 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -161,6 +161,7 @@ dtb-$(CONFIG_ARCH_OMAP2PLUS) += omap2420-h4.dtb \  	am335x-evm.dtb \  	am335x-evmsk.dtb \  	am335x-bone.dtb +dtb-$(CONFIG_MACH_OMAP3_H1) += omap3-h1-ev1.dtb  dtb-$(CONFIG_MACH_MINNOW) += omap3-minnow-p0.dtb omap3-casper-p1.dtb  dtb-$(CONFIG_ARCH_ORION5X) += orion5x-lacie-ethernet-disk-mini-v2.dtb  dtb-$(CONFIG_ARCH_PRIMA2) += prima2-evb.dtb diff --git a/arch/arm/boot/dts/omap3-h1-ev1.dts b/arch/arm/boot/dts/omap3-h1-ev1.dts new file mode 100644 index 00000000000..fc8a507b855 --- /dev/null +++ b/arch/arm/boot/dts/omap3-h1-ev1.dts @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014 Olio Devices, Inc. + * Authors: Evan Wilson <evan@oliodevices.com> + *	    Mattis Fjallstrom <mattis@oliodevices.com> + * + * Modified from omap3-beagle-xm.dts + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +/dts-v1/; + +/include/ "omap36xx.dtsi" +/ { +	model = "Olio OMAP3 H1 Board"; +	compatible = "olio,omap3-h1", "ti,omap3"; + +	tusb_phy: tusb-usb@0 { +		compatible = "ti,tusb-usb"; +		gpios = <&gpio5 14 0x1>;  /* IRQ gpio-142    GPIOF_DIR_OUT|GPIOF_INIT_HIGH */ +		gpio-names = "tusb-cs"; +		clk_in = "periph_26mhz";  /* external 26MHz clock */ +		reg = <0 0>; +	}; + +    /* Needs to be modified for Olio H1 */ + +	/* Consider setting up pad-wkup here, like in Minnow */ + +     +}; + +/* Lots of pinmux in the board file, as well */ + +&omap3_pmx_core { +	pinctrl-names = "default"; +	pinctrl-0 = <&usb_phy_pins>; + +	usb_phy_pins: pinmux_usb_phy_pins { +		pinctrl-single,pins = < +			0x140 0x4104     /* MCBSP3_CLKX, MODE4 (GPIO-142) | INPUT | OFFWAKEUP */ +			0x5ae 0x004     /* ETK_D1, MODE4 (GPIO_15) | OUTPUT */ +			0x5b0 0x004     /* ETK_D2, MODE4 (GPIO_16) | OUTPUT */ +			0x5c6 0x004     /* EDK_D13, MODE4 (GPIO_27) | OUTPUT */ +		>; +	}; +}; + + +&usb_otg_hs { +	interface-type = <0>; +	usb-phy = <&tusb_phy>; +	mode = <3>; +	power = <50>; + +}; diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi index 2b8802164c1..4d45679361b 100644 --- a/arch/arm/boot/dts/omap3.dtsi +++ b/arch/arm/boot/dts/omap3.dtsi @@ -14,17 +14,18 @@  	compatible = "ti,omap3430", "ti,omap3";  	interrupt-parent = <&intc>; -	aliases { +	aliases { /*  		serial0 = &uart1;  		serial1 = &uart2;  		serial2 = &uart3; +               */  	}; -	cpus { -		cpu@0 { +	 cpus {  +	 	cpu@0 {  			compatible = "arm,cortex-a8"; -		}; -	}; +	 	}; +	 };  	pmu {  		compatible = "arm,cortex-a8-pmu"; @@ -43,14 +44,14 @@  			ti,hwmods = "mpu";  		}; -		iva { -			compatible = "ti,iva2.2"; -			ti,hwmods = "iva"; - -			dsp { -				compatible = "ti,omap3-c64"; -			}; -		}; +	    iva { +          compatible = "ti,iva2.2"; +          ti,hwmods = "iva"; +           +          dsp { +            compatible = "ti,omap3-c64"; +          }; +        };   	};  	/* @@ -111,7 +112,7 @@  			pinctrl-single,register-width = <16>;  			pinctrl-single,function-mask = <0x7f1f>;  			#gpio-range-cells = <3>; -		}; +        };  		gpio1: gpio@48310000 {  			compatible = "ti,omap3-gpio"; @@ -123,24 +124,31 @@  			#gpio-cells = <2>;  			interrupt-controller;  			#interrupt-cells = <2>; -		}; +         }; -		gpio2: gpio@49050000 { -			compatible = "ti,omap3-gpio"; -			reg = <0x49050000 0x200>; -			interrupts = <30>; -			ti,hwmods = "gpio2"; -			gpio-controller; -			#gpio-cells = <2>; -			interrupt-controller; -			#interrupt-cells = <2>; -			/* for gpio 32 33 there is no pnctrl controller defined */ -			gpio-ranges = <&omap3_pmx_core 2 0x25 10>, -					<&omap3_pmx_core 12 0x37 8>, -					<&omap3_pmx_core 20 0x40 8>, -					<&omap3_pmx_core 28 0x4B 3>, -					<&omap3_pmx_core 31 0x4f 1>; -		}; +        /* disabling this one makes suspend work */ +        /* but it also causes the touch to NOT work */ + +         gpio2: gpio@49050000 { +		 	compatible = "ti,omap3-gpio"; +		 	reg = <0x49050000 0x200>; +		 	interrupts = <30>; +		 	ti,hwmods = "gpio2"; +		 	gpio-controller; +            #gpio-cells = <2>; +		 	interrupt-controller; +             #interrupt-cells = <2>; +		 	/* for gpio 32 33 there is no pnctrl controller defined */ +             /* gpio-ranges = <&omap3_pmx_core 2 0x25 10>, */ +             /*               <&omap3_pmx_core 12 0x37 8>, */ +             /*               <&omap3_pmx_core 20 0x40 8>, */ +		     /*               <&omap3_pmx_core 28 0x4B 3>, */ +             /*               <&omap3_pmx_core 31 0x4f 1>; */ +          }; + +        /* Removing this one disables touch, +         * suspend works. +         */  		gpio3: gpio@49052000 {  			compatible = "ti,omap3-gpio"; @@ -151,26 +159,34 @@  			#gpio-cells = <2>;  			interrupt-controller;  			#interrupt-cells = <2>; -			gpio-ranges = <&omap3_pmx_core 0 0x50 32>; -		}; +			/* gpio-ranges = <&omap3_pmx_core 0 0x50 32>; */ +         }; -		gpio4: gpio@49054000 { -			compatible = "ti,omap3-gpio"; +        /* Removing this one disables _both_ touch  +         * and suspend.  +         */ + +         gpio4: gpio@49054000 { +            compatible = "ti,omap3-gpio";  			reg = <0x49054000 0x200>;  			interrupts = <32>;  			ti,hwmods = "gpio4";  			gpio-controller;  			#gpio-cells = <2>;  			interrupt-controller; -			#interrupt-cells = <2>; +            #interrupt-cells = <2>;  			/* -			* gpio 127 in wkup mux, whcih we can not use (see +			* gpio 127 in wkup mux, which we can not use (see  			* sorce code for comment)  			*/ -			gpio-ranges = <&omap3_pmx_core 0 0x70 16>, -					<&omap3_pmx_core 16 0x82 14>, -					<&omap3_pmx_core 30 0x81 1>; -		}; +            /* gpio-ranges = <&omap3_pmx_core 0 0x70 16>,  */ +            /*               <&omap3_pmx_core 16 0x82 14>, */ +            /*               <&omap3_pmx_core 30 0x81 1>; */ +        }; + +        /* Removing this one causes touch to work but suspend to  +         * crash....  +         */  		gpio5: gpio@49056000 {  			compatible = "ti,omap3-gpio"; @@ -180,14 +196,18 @@  			gpio-controller;  			#gpio-cells = <2>;  			interrupt-controller; -			#interrupt-cells = <2>; +            #interrupt-cells = <2>;  			/* -			* gpio 128 129 in wkup mux, whcih we can not use (see -			* sorce code for comment) -			*/ -			gpio-ranges = <&omap3_pmx_core 2 0x94 30>; +             * gpio 128 129 in wkup mux, whcih we can not use (see +             * sorce code for comment) +             */ +            /* gpio-ranges = <&omap3_pmx_core 2 0x94 20>; */  		}; +        /* Removing this one causes touch to work but suspend to  +         * crash....  +         */        +  		gpio6: gpio@49058000 {  			compatible = "ti,omap3-gpio";  			reg = <0x49058000 0x200>; @@ -196,36 +216,37 @@  			gpio-controller;  			#gpio-cells = <2>;  			interrupt-controller; -			#interrupt-cells = <2>; +            #interrupt-cells = <2>;  			/* for gpio 187 there is no pnctrl controller defined */ -			gpio-ranges = <&omap3_pmx_core 0 0xb2 7>, -					<&omap3_pmx_core 7 0x80 1>, -					<&omap3_pmx_core 8 0xc7 1>, -					<&omap3_pmx_core 9 0xc0 1>, -					<&omap3_pmx_core 10 0xcb 13>, -					<&omap3_pmx_core 23 0xc8 3>, -					<&omap3_pmx_core 26 0xd9 1>, -					<&omap3_pmx_core 28 0xc1 4>; -		}; +            /* gpio-ranges = <&omap3_pmx_core 0 0xb2 7>, */ +            /*               <&omap3_pmx_core 7 0x80 1>, */ +            /*               <&omap3_pmx_core 8 0xc7 1>, */ +            /*               <&omap3_pmx_core 9 0xc0 1>, */ +            /*               <&omap3_pmx_core 10 0xcb 13>, */ +            /*               <&omap3_pmx_core 23 0xc8 3>, */ +            /*               <&omap3_pmx_core 26 0xd9 1>,  */ +            /*               <&omap3_pmx_core 28 0xc1 4>; */ +         }; -		uart1: serial@4806a000 { -			compatible = "ti,omap3-uart"; -			ti,hwmods = "uart1"; -			clock-frequency = <48000000>; -		}; +        /*  uart1: serial@4806a000 { */ +		/* 	compatible = "ti,omap3-uart"; */ +		/* 	ti,hwmods = "uart1"; */ +		/* 	clock-frequency = <48000000>; */ +		/* }; */ -		uart2: serial@4806c000 { -			compatible = "ti,omap3-uart"; -			ti,hwmods = "uart2"; -			clock-frequency = <48000000>; -		}; +		/* uart2: serial@4806c000 { */ +		/* 	compatible = "ti,omap3-uart"; */ +		/* 	ti,hwmods = "uart2"; */ +		/* 	clock-frequency = <48000000>; */ +		/* }; */ -		uart3: serial@49020000 { -			compatible = "ti,omap3-uart"; -			ti,hwmods = "uart3"; -			clock-frequency = <48000000>; -		}; +		/* uart3: serial@49020000 { */ +		/* 	compatible = "ti,omap3-uart"; */ +		/* 	ti,hwmods = "uart3"; */ +		/* 	clock-frequency = <48000000>; */ +        /*  }; */ +        /*           		i2c1: i2c@48070000 {  			compatible = "ti,omap3-i2c";  			#address-cells = <1>; @@ -245,8 +266,8 @@  			#address-cells = <1>;  			#size-cells = <0>;  			ti,hwmods = "i2c3"; -		}; - +         }; */ +          		mcspi1: spi@48098000 {  			compatible = "ti,omap2-mcspi";  			#address-cells = <1>; @@ -299,7 +320,7 @@  			ti,spi-num-cs = <1>;  			dmas = <&sdma 70>, <&sdma 71>;  			dma-names = "tx0", "rx0"; -		}; +        };               		mmc1: mmc@4809c000 {  			compatible = "ti,omap3-hsmmc"; @@ -321,7 +342,7 @@  			ti,hwmods = "mmc3";  			dmas = <&sdma 77>, <&sdma 78>;  			dma-names = "tx", "rx"; -		}; +         };  		wdt2: wdt@48314000 {  			compatible = "ti,omap3-wdt"; @@ -331,11 +352,11 @@  		mcbsp1: mcbsp@48074000 {  			compatible = "ti,omap3-mcbsp";  			reg = <0x48074000 0xff>; -			reg-names = "mpu"; -			interrupts = <16>, /* OCP compliant interrupt */ -				     <59>, /* TX interrupt */ -				     <60>; /* RX interrupt */ -			interrupt-names = "common", "tx", "rx"; +            reg-names = "mpu"; +            interrupts = <16>, /* OCP compliant interrupt */ +                         <59>, /* TX interrupt */ +                         <60>; /* RX interrupt */ +            interrupt-names = "common", "tx", "rx";  			ti,buffer-size = <128>;  			ti,hwmods = "mcbsp1";  			dmas = <&sdma 31>, @@ -348,11 +369,11 @@  			reg = <0x49022000 0xff>,  			      <0x49028000 0xff>;  			reg-names = "mpu", "sidetone"; -			interrupts = <17>, /* OCP compliant interrupt */ -				     <62>, /* TX interrupt */ -				     <63>, /* RX interrupt */ -				     <4>;  /* Sidetone */ -			interrupt-names = "common", "tx", "rx", "sidetone"; +            interrupts = <17>, /* OCP compliant interrupt */ +                         <62>, /* TX interrupt */ +                         <63>, /* RX interrupt */ +                         <4>;   /* Sidetone */ +            interrupt-names = "common", "tx", "rx", "sidetone";  			ti,buffer-size = <1280>;  			ti,hwmods = "mcbsp2", "mcbsp2_sidetone";  			dmas = <&sdma 33>, @@ -365,11 +386,11 @@  			reg = <0x49024000 0xff>,  			      <0x4902a000 0xff>;  			reg-names = "mpu", "sidetone"; -			interrupts = <22>, /* OCP compliant interrupt */ -				     <89>, /* TX interrupt */ -				     <90>, /* RX interrupt */ -				     <5>;  /* Sidetone */ -			interrupt-names = "common", "tx", "rx", "sidetone"; +            interrupts = <22>, /* OCP compliant interrupt */ +                         <89>, /* TX interrupt */ +                         <90>, /* RX interrupt */ +                         <5>;   /* Sidetone */ +            interrupt-names = "common", "tx", "rx", "sidetone";  			ti,buffer-size = <128>;  			ti,hwmods = "mcbsp3", "mcbsp3_sidetone";  			dmas = <&sdma 17>, @@ -381,10 +402,10 @@  			compatible = "ti,omap3-mcbsp";  			reg = <0x49026000 0xff>;  			reg-names = "mpu"; -			interrupts = <23>, /* OCP compliant interrupt */ -				     <54>, /* TX interrupt */ -				     <55>; /* RX interrupt */ -			interrupt-names = "common", "tx", "rx"; +            interrupts = <23>, /* OCP compliant interrupt */ +                         <54>, /* TX interrupt */ +                         <55>; /* RX interrupt */ +            interrupt-names = "common", "tx", "rx";  			ti,buffer-size = <128>;  			ti,hwmods = "mcbsp4";  			dmas = <&sdma 19>, @@ -396,16 +417,16 @@  			compatible = "ti,omap3-mcbsp";  			reg = <0x48096000 0xff>;  			reg-names = "mpu"; -			interrupts = <27>, /* OCP compliant interrupt */ -				     <81>, /* TX interrupt */ -				     <82>; /* RX interrupt */ -			interrupt-names = "common", "tx", "rx"; +            interrupts = <27>, /* OCP compliant interrupt */ +                         <81>, /* TX interrupt */ +                         <82>; /* RX interrupt */ +            interrupt-names = "common", "tx", "rx";  			ti,buffer-size = <128>;  			ti,hwmods = "mcbsp5";  			dmas = <&sdma 21>,  			       <&sdma 22>;  			dma-names = "tx", "rx"; -		}; +         };  		timer1: timer@48318000 {  			compatible = "ti,omap3430-timer"; @@ -501,13 +522,13 @@  			ti,timer-alwon;  			ti,timer-secure;  		}; - -		usbhstll: usbhstll@48062000 { -			compatible = "ti,usbhs-tll"; -			reg = <0x48062000 0x1000>; -			interrupts = <78>; -			ti,hwmods = "usb_tll_hs"; -		}; +         +        /* usbhstll: usbhstll@48062000 { */ +        /*     compatible = "ti,usbhs-tll"; */ +		/*  	reg = <0x48062000 0x1000>;  */ +		/*  	interrupts = <78>;  */ +		/*  	ti,hwmods = "usb_tll_hs"; */ +        /* }; */  		usbhshost: usbhshost@48064000 {  			compatible = "ti,usbhs-host"; @@ -517,20 +538,24 @@  			#size-cells = <1>;  			ranges; -			usbhsohci: ohci@48064400 { -				compatible = "ti,ohci-omap3", "usb-ohci"; -				reg = <0x48064400 0x400>; -				interrupt-parent = <&intc>; -				interrupts = <76>; -			}; +			/* usbhsohci: ohci@48064400 { */ +			/* 	compatible = "ti,ohci-omap3", "usb-ohci"; */ +			/* 	reg = <0x48064400 0x400>; */ +			/* 	interrupt-parent = <&intc>; */ +			/* 	interrupts = <76>; */ +			/* }; */ -			usbhsehci: ehci@48064800 { -				compatible = "ti,ehci-omap", "usb-ehci"; -				reg = <0x48064800 0x400>; -				interrupt-parent = <&intc>; -				interrupts = <77>; -			}; -		}; +			/* usbhsehci: ehci@48064800 { */ +			/* 	compatible = "ti,ehci-omap", "usb-ehci"; */ +			/* 	reg = <0x48064800 0x400>; */ +			/* 	interrupt-parent = <&intc>; */ +			/* 	interrupts = <77>; */ +			/* }; */ +         }; + +        /* GPMC needs to be initialized here, or everything  +         * falls apart. +         */  		gpmc: gpmc@6e000000 {  			compatible = "ti,omap3430-gpmc"; @@ -541,8 +566,9 @@  			gpmc,num-waitpins = <4>;  			#address-cells = <2>;  			#size-cells = <1>; -		}; +         }; +        /*  		usb_otg_hs: usb_otg_hs@480ab000 {  			compatible = "ti,omap3-musb";  			reg = <0x480ab000 0x1000>; @@ -552,11 +578,11 @@  			multipoint = <1>;  			num-eps = <16>;  			ram-bits = <12>; -		}; +         };*/  		glbl_prm: glbl_prm@48307250 { -			/* -			* disabled by default. becasue two required +                       /* +			* disabled by default. because two required  			* values should be defined in board file  			*/  			status = "disabled"; diff --git a/arch/arm/boot/dts/omap3430-sdp.dts b/arch/arm/boot/dts/omap3430-sdp.dts index 144ae43453c..6076d016b47 100644 --- a/arch/arm/boot/dts/omap3430-sdp.dts +++ b/arch/arm/boot/dts/omap3430-sdp.dts @@ -105,7 +105,6 @@  		nand-bus-width = <8>;  		ti,nand-ecc-opt = "sw"; -		gpmc,device-nand;  		gpmc,cs-on-ns = <0>;  		gpmc,cs-rd-off-ns = <36>;  		gpmc,cs-wr-off-ns = <36>; diff --git a/arch/arm/boot/dts/omap36xx.dtsi b/arch/arm/boot/dts/omap36xx.dtsi index f3447bc1b03..2a25174167c 100644 --- a/arch/arm/boot/dts/omap36xx.dtsi +++ b/arch/arm/boot/dts/omap36xx.dtsi @@ -12,27 +12,28 @@  / {  	aliases { -		serial3 = &uart4; +		/* serial3 = &uart4; */  	};  	cpus {  		/* OMAP3630/OMAP37xx 'standard device' variants OPP50 to OPP130 */ -		cpu@0 { -			operating-points = < -				/* kHz    uV */ -				300000  1012500 -				600000  1200000 -				800000  1325000 -			>; -			clock-latency = <300000>; /* From legacy driver */ -		}; +		/* cpu@0 { */ +		/* 	operating-points = < */ +		/* 		/\* kHz    uV *\/ */ +		/* 		300000  1012500 */ +		/* 		600000  1200000 */ +		/* 		800000  1325000 */ +        /*                        1000000  1375000                 */ +		/* 	>; */ +		/* 	clock-latency = <300000>; /\* From legacy driver *\/ */ +		/* }; */  	}; -	ocp { -		uart4: serial@49042000 { -			compatible = "ti,omap3-uart"; -			ti,hwmods = "uart4"; -			clock-frequency = <48000000>; -		}; -	}; +    /* ocp { */ +    /*  	uart4: serial@49042000 { */ +	/*  		compatible = "ti,omap3-uart"; */ +	/*  		ti,hwmods = "uart4"; */ +	/*  		clock-frequency = <48000000>; */ +	/*  	};  */ +	/*  };  */  }; diff --git a/arch/arm/boot/dts/omap3_h1.dts b/arch/arm/boot/dts/omap3_h1.dts new file mode 100644 index 00000000000..13a34e53ac7 --- /dev/null +++ b/arch/arm/boot/dts/omap3_h1.dts @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014 Olio Devices, Inc. + * Authors: Evan Wilson <evan@oliodevices.com> + *	    Mattis Fjallstrom <mattis@oliodevices.com> + * + * Modified from omap3-beagle-xm.dts + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +/dts-v1/; + +/include/ "omap36xx.dtsi" +/ { +	model = "Olio OMAP3 H1 Board"; +	compatible = "olio,omap3-h1", "ti,omap3"; + +	cpus { +		cpu@0 { +			device_type = "cpu"; +			operating-points = < +				/* kHz    uV */ +				 300000   975000 +				 600000  1150000 +				 800000  1275000 +				1000000  1337500 +			>; +			/* OPP tolerance in percentage */ +			voltage-tolerance = <0>; +			reset-voltage = <1275000>; +		}; +	}; +}; + +&vc { +	ti,i2c-high-speed; +	ti,i2c-pad-load = <3>; +	vc_mpu{ +		ti,master-channel; +	}; + +	vc_core{ +		ti,use-master-slave-addr; +	}; +}; + +&glbl_prm { +	status = "ok"; +	sys_clk = "osc_sys_ck"; +	autoextclkmode = <2>; +	sys_off_mode; +	auto_off; +	auto_retention; +	offmodesetup_time = <3500>; +	clksetup_time = <1000>; +}; diff --git a/arch/arm/configs/omap3_h1_defconfig b/arch/arm/configs/omap3_h1_defconfig new file mode 100644 index 00000000000..7f07b5d5c44 --- /dev/null +++ b/arch/arm/configs/omap3_h1_defconfig @@ -0,0 +1,2952 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.10.0 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_ARM_PATCH_PHYS_VIRT=y +CONFIG_GENERIC_BUG=y +# CONFIG_BOOTINFO is not set +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_IRQ_WORK=y +CONFIG_BUILDTIME_EXTABLE_SORT=y + +# +# General setup +# +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_FHANDLE is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_DEBUG=y +CONFIG_SPARSE_IRQ=y +CONFIG_KTIME_SCALAR=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y + +# +# Timers subsystem +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ_COMMON=y +# CONFIG_HZ_PERIODIC is not set +CONFIG_NO_HZ_IDLE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y + +# +# CPU/Task time and stats accounting +# +CONFIG_TICK_CPU_ACCOUNTING=y +# CONFIG_IRQ_TIME_ACCOUNTING is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +# CONFIG_TASKSTATS is not set + +# +# RCU Subsystem +# +# CONFIG_TREE_PREEMPT_RCU is not set +CONFIG_TINY_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +# CONFIG_RCU_STALL_COMMON is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_RCU_BOOST=y +CONFIG_RCU_BOOST_PRIO=1 +CONFIG_RCU_BOOST_DELAY=500 +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=18 +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +CONFIG_UIDGID_CONVERTED=y +# CONFIG_UIDGID_STRICT_TYPE_CHECKS is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_HAVE_UID16=y +CONFIG_HOTPLUG=y +CONFIG_PANIC_TIMEOUT=1 +CONFIG_EXPERT=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +CONFIG_PERF_EVENTS=y +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +# CONFIG_JUMP_LABEL is not set +# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_HW_BREAKPOINT=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y +CONFIG_SECCOMP_FILTER=y +CONFIG_HAVE_CONTEXT_TRACKING=y +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_OLD_SIGACTION=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +# CONFIG_MODULE_UNLOAD is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +# CONFIG_MODULE_SIG is not set +CONFIG_BLOCK=y +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_EFI_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_ROW=y +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_ROW=y +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="row" +CONFIG_UNINLINE_SPIN_UNLOCK=y +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +CONFIG_ARCH_MULTIPLATFORM=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C24XX is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_EXYNOS is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP1 is not set + +# +# Multiple platform selection +# + +# +# CPU Core family selection +# +# CONFIG_ARCH_MULTI_V6 is not set +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_MULTI_V6_V7=y +# CONFIG_ARCH_MULTI_CPU_AUTO is not set +# CONFIG_ARCH_MVEBU is not set +# CONFIG_ARCH_BCM is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_ARCH_HIGHBANK is not set +# CONFIG_ARCH_MXC is not set + +# +# TI OMAP Common Features +# + +# +# OMAP Feature Selections +# +CONFIG_POWER_AVS_OMAP=y +CONFIG_OMAP_RESET_CLOCKS=y +CONFIG_OMAP_MUX=y +# CONFIG_OMAP_MUX_DEBUG is not set +CONFIG_OMAP_MUX_WARNINGS=y +CONFIG_OMAP_32K_TIMER=y +# CONFIG_OMAP3_L2_AUX_SECURE_SAVE_RESTORE is not set +CONFIG_OMAP_DM_TIMER=y +CONFIG_OMAP_PM_NOOP=y +# CONFIG_MACH_OMAP_GENERIC is not set +CONFIG_ARCH_OMAP=y +CONFIG_ARCH_OMAP2PLUS=y + +# +# TI OMAP2/3/4 Specific Features +# +# CONFIG_ARCH_OMAP2PLUS_TYPICAL is not set +CONFIG_SOC_HAS_OMAP2_SDRC=y +CONFIG_ARCH_OMAP3=y +# CONFIG_ARCH_OMAP4 is not set +# CONFIG_SOC_OMAP5 is not set +# CONFIG_SOC_OMAP3430 is not set +# CONFIG_SOC_TI81XX is not set +# CONFIG_SOC_AM33XX is not set +CONFIG_OMAP_PACKAGE_CBP=y + +# +# OMAP Board Type +# +# CONFIG_MACH_OMAP3_BEAGLE is not set +CONFIG_MACH_OMAP3_H1=y +# CONFIG_MACH_OMAP3_H1_EVT1 is not set +# CONFIG_MACH_OMAP3_H1_EVT2 is not set +# CONFIG_MACH_OMAP3_H1_DVT1 is not set +CONFIG_MACH_OMAP3_H1_DVT2=y +# CONFIG_MACH_DEVKIT8000 is not set +# CONFIG_MACH_OMAP_LDP is not set +# CONFIG_MACH_OMAP3530_LV_SOM is not set +# CONFIG_MACH_OMAP3_TORPEDO is not set +# CONFIG_MACH_OVERO is not set +# CONFIG_MACH_OMAP3EVM is not set +# CONFIG_MACH_OMAP3517EVM is not set +# CONFIG_MACH_CRANEBOARD is not set +# CONFIG_MACH_OMAP3_PANDORA is not set +# CONFIG_MACH_TOUCHBOOK is not set +# CONFIG_MACH_OMAP_3430SDP is not set +# CONFIG_MACH_NOKIA_RM680 is not set +# CONFIG_MACH_NOKIA_RX51 is not set +# CONFIG_MACH_OMAP_ZOOM2 is not set +# CONFIG_MACH_OMAP_ZOOM3 is not set +# CONFIG_MACH_CM_T35 is not set +# CONFIG_MACH_CM_T3517 is not set +# CONFIG_MACH_IGEP0020 is not set +# CONFIG_MACH_IGEP0030 is not set +# CONFIG_MACH_SBC3530 is not set +# CONFIG_MACH_OMAP_3630SDP is not set +# CONFIG_MACH_MINNOW is not set +# CONFIG_OMAP3_EMU is not set +# CONFIG_OMAP3_SDRC_AC_TIMING is not set +CONFIG_OMAP3_PAD_WKUP_IO=y +CONFIG_DISABLE_OMAP_ERRATA_i583=y +# CONFIG_ARCH_SOCFPGA is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_ARCH_SUNXI is not set +# CONFIG_ARCH_SIRF is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_VIRT is not set +# CONFIG_ARCH_WM8850 is not set +# CONFIG_ARCH_ZYNQ is not set + +# +# Processor Type +# +CONFIG_CPU_V7=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +# CONFIG_ARM_LPAE is not set +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +CONFIG_ARM_VIRT_EXT=y +# CONFIG_SWP_EMULATE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT_6=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_ARM_NR_BANKS=8 +CONFIG_MULTI_IRQ_HANDLER=y +CONFIG_ARM_ERRATA_430973=y +# CONFIG_ARM_ERRATA_720789 is not set +# CONFIG_ARM_ERRATA_754322 is not set +# CONFIG_ARM_ERRATA_775420 is not set +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_HAVE_ARM_ARCH_TIMER=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_ARM_PSCI=y +CONFIG_ARCH_NR_GPIO=256 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_PREEMPT_COUNT=y +CONFIG_HZ=100 +CONFIG_SCHED_HRTICK=y +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +CONFIG_ARCH_HAS_HOLES_MEMORYMODEL=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_HW_PERF_EVENTS=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_CROSS_MEMORY_ATTACH=y +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +CONFIG_SECCOMP=y +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_XEN is not set +# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set + +# +# Boot options +# +CONFIG_USE_OF=y +# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set +CONFIG_ATAGS=y +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +# CONFIG_ARM_APPENDED_DTB is not set +CONFIG_CMDLINE="" +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +CONFIG_AUTO_ZRELADDR=y + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_FREQ_GOV_COMMON=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +# CONFIG_GENERIC_CPUFREQ_CPU0 is not set + +# +# ARM CPU frequency scaling drivers +# +# CONFIG_ARM_EXYNOS4210_CPUFREQ is not set +# CONFIG_ARM_EXYNOS4X12_CPUFREQ is not set +# CONFIG_ARM_EXYNOS5250_CPUFREQ is not set +# CONFIG_ARM_EXYNOS5440_CPUFREQ is not set +# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set +CONFIG_ARM_OMAP2PLUS_CPUFREQ=y +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_MULTIPLE_DRIVERS=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_BINFMT_SCRIPT=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_MISC=y +CONFIG_COREDUMP=y + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=100 +CONFIG_PM_WAKELOCKS_GC=y +CONFIG_PM_RUNTIME=y +CONFIG_PM=y +CONFIG_PM_DEBUG=y +CONFIG_PM_ADVANCED_DEBUG=y +# CONFIG_PM_TEST_SUSPEND is not set +CONFIG_PM_SLEEP_DEBUG=y +# CONFIG_APM_EMULATION is not set +CONFIG_ARCH_HAS_OPP=y +CONFIG_PM_OPP=y +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +CONFIG_SUSPEND_TIME=y +CONFIG_HAS_AMBIENTMODE=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_DIAG is not set +CONFIG_UNIX=y +# CONFIG_UNIX_DIAG is not set +CONFIG_XFRM=y +CONFIG_XFRM_ALGO=y +CONFIG_XFRM_USER=y +# CONFIG_XFRM_SUB_POLICY is not set +CONFIG_XFRM_MIGRATE=y +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +CONFIG_NET_KEY_MIGRATE=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +# CONFIG_IP_FIB_TRIE_STATS is not set +CONFIG_IP_MULTIPLE_TABLES=y +# CONFIG_IP_ROUTE_MULTIPATH is not set +# CONFIG_IP_ROUTE_VERBOSE is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +CONFIG_NET_IP_TUNNEL=y +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_NET_IPVTI is not set +# CONFIG_INET_AH is not set +CONFIG_INET_ESP=y +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +# CONFIG_IPV6_GRE is not set +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +# CONFIG_IPV6_MROUTE is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +CONFIG_NETWORK_SECMARK=y +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +# CONFIG_NETFILTER_NETLINK_ACCT is not set +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +# CONFIG_NF_CONNTRACK_SECMARK is not set +CONFIG_NF_CONNTRACK_PROCFS=y +CONFIG_NF_CONNTRACK_EVENTS=y +# CONFIG_NF_CONNTRACK_TIMEOUT is not set +# CONFIG_NF_CONNTRACK_TIMESTAMP is not set +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_BROADCAST=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +# CONFIG_NF_CONNTRACK_SNMP is not set +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +# CONFIG_NF_CONNTRACK_SIP is not set +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +# CONFIG_NF_CT_NETLINK_TIMEOUT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE_CT is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +# CONFIG_NF_NAT_SIP is not set +CONFIG_NF_NAT_TFTP=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +# CONFIG_NETFILTER_XT_TARGET_HL is not set +# CONFIG_NETFILTER_XT_TARGET_HMARK is not set +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +# CONFIG_NETFILTER_XT_TARGET_LOG is not set +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NETMAP=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +CONFIG_NETFILTER_XT_TARGET_REDIRECT=y +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +# CONFIG_NETFILTER_XT_TARGET_SECMARK is not set +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_ADDRTYPE is not set +# CONFIG_NETFILTER_XT_MATCH_BPF is not set +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +# CONFIG_NETFILTER_XT_MATCH_CONNBYTES is not set +# CONFIG_NETFILTER_XT_MATCH_CONNLABEL is not set +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +CONFIG_NETFILTER_XT_MATCH_ECN=y +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_SET is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +# CONFIG_IP_NF_MATCH_RPFILTER is not set +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_REJECT_SKERR=y +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT_IPV4=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PPTP=y +CONFIG_NF_NAT_H323=y +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV6=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +# CONFIG_IP6_NF_MATCH_AH is not set +# CONFIG_IP6_NF_MATCH_EUI64 is not set +# CONFIG_IP6_NF_MATCH_FRAG is not set +# CONFIG_IP6_NF_MATCH_OPTS is not set +# CONFIG_IP6_NF_MATCH_HL is not set +# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set +# CONFIG_IP6_NF_MATCH_MH is not set +# CONFIG_IP6_NF_MATCH_RPFILTER is not set +# CONFIG_IP6_NF_MATCH_RT is not set +# CONFIG_IP6_NF_TARGET_HL is not set +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_TARGET_REJECT_SKERR=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_NF_NAT_IPV6=y +CONFIG_IP6_NF_TARGET_MASQUERADE=y +# CONFIG_IP6_NF_TARGET_NPT is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +CONFIG_HAVE_NET_DSA=y +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFB is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +# CONFIG_NET_SCH_MQPRIO is not set +# CONFIG_NET_SCH_CHOKE is not set +# CONFIG_NET_SCH_QFQ is not set +# CONFIG_NET_SCH_CODEL is not set +# CONFIG_NET_SCH_FQ_CODEL is not set +# CONFIG_NET_SCH_INGRESS is not set +# CONFIG_NET_SCH_PLUG is not set + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +# CONFIG_NET_ACT_POLICE is not set +# CONFIG_NET_ACT_GACT is not set +# CONFIG_NET_ACT_MIRRED is not set +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +# CONFIG_VSOCKETS is not set +# CONFIG_NETLINK_MMAP is not set +# CONFIG_NETLINK_DIAG is not set +CONFIG_BQL=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=m +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +# CONFIG_BT_BNEP_MC_FILTER is not set +# CONFIG_BT_BNEP_PROTO_FILTER is not set +CONFIG_BT_HIDP=m + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIBTUSB is not set +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set +# CONFIG_BT_HCIUART_LL is not set +# CONFIG_BT_HCIUART_3WIRE is not set +# CONFIG_BT_HCIBCM203X is not set +# CONFIG_BT_HCIBPA10X is not set +# CONFIG_BT_HCIBFUSB is not set +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +# CONFIG_WIRELESS is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_REGULATOR is not set +CONFIG_RFKILL_GPIO=y +# CONFIG_RFKILL_WL18XX is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set +# CONFIG_NFC is not set +CONFIG_HAVE_BPF_JIT=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +CONFIG_FW_LOADER_USER_HELPER=y +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_SOC_BUS=y +CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_REGMAP_SPI=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGMAP_IRQ=y +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_CMA is not set + +# +# Bus devices +# +# CONFIG_OMAP_OCP2SCP is not set +CONFIG_OMAP_INTERCONNECT=y +CONFIG_CONNECTOR=y +CONFIG_PROC_EVENTS=y +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_OF_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_CFI_INTELEXT is not set +# CONFIG_MTD_CFI_AMDSTD is not set +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +CONFIG_MTD_COMPLEX_MAPPINGS=y +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_PHYSMAP_OF is not set +# CONFIG_MTD_IMPA7 is not set +# CONFIG_MTD_GPIO_ADDR is not set +# CONFIG_MTD_PLATRAM is not set +# CONFIG_MTD_LATCH_ADDR is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +CONFIG_BCH_CONST_M=13 +CONFIG_BCH_CONST_T=8 +CONFIG_MTD_NAND_IDS=y +CONFIG_MTD_NAND_ECC=y +# CONFIG_MTD_NAND_ECC_SMC is not set +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_BCH=y +CONFIG_MTD_NAND_ECC_BCH=y +# CONFIG_MTD_SM_COMMON is not set +# CONFIG_MTD_NAND_DENALI is not set +# CONFIG_MTD_NAND_GPIO is not set +CONFIG_MTD_NAND_OMAP2=y +CONFIG_MTD_NAND_OMAP_BCH=y +CONFIG_MTD_NAND_OMAP_BCH8=y +# CONFIG_MTD_NAND_OMAP_BCH4 is not set +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_DOCG4 is not set +# CONFIG_MTD_NAND_NANDSIM is not set +CONFIG_MTD_NAND_PLATFORM=y +# CONFIG_MTD_ALAUDA is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTD_UBI_BEB_LIMIT=20 +# CONFIG_MTD_UBI_FASTMAP is not set +# CONFIG_MTD_UBI_GLUEBI is not set +CONFIG_DTC=y +CONFIG_OF=y + +# +# Device Tree and Open Firmware support +# +CONFIG_PROC_DEVICETREE=y +# CONFIG_OF_SELFTEST is not set +CONFIG_OF_FLATTREE=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_IRQ=y +CONFIG_OF_DEVICE=y +CONFIG_OF_I2C=y +CONFIG_OF_NET=y +CONFIG_OF_MTD=y +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_DRBD is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_DUMMY_IRQ is not set +# CONFIG_ICS932S401 is not set +# CONFIG_ATMEL_SSC is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_VIB_GPIO is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_DS1682 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085_I2C is not set +# CONFIG_BMP085_SPI is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_LATTICE_ECP3_CONFIG is not set +# CONFIG_SRAM is not set +# CONFIG_C55_CTRL is not set +# CONFIG_MMI_FACTORY is not set +# CONFIG_MOT_UTAG is not set +# CONFIG_BQ5105X_CTRL is not set +# CONFIG_BQ5105X_DETECT is not set +CONFIG_WAKEUP_SOURCE_NOTIFY=y +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +# CONFIG_MII is not set +CONFIG_IFB=y +# CONFIG_NET_TEAM is not set +# CONFIG_MACVLAN is not set +# CONFIG_VXLAN is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set + +# +# CAIF transport drivers +# + +# +# Distributed Switch Architecture drivers +# +# CONFIG_NET_DSA_MV88E6XXX is not set +# CONFIG_NET_DSA_MV88E6060 is not set +# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set +# CONFIG_NET_DSA_MV88E6131 is not set +# CONFIG_NET_DSA_MV88E6123_61_65 is not set +# CONFIG_ETHERNET is not set +# CONFIG_PHYLIB is not set +# CONFIG_MICREL_KS8995MA is not set +CONFIG_PPP=y +# CONFIG_PPP_BSDCOMP is not set +# CONFIG_PPP_DEFLATE is not set +# CONFIG_PPP_FILTER is not set +# CONFIG_PPP_MPPE is not set +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPPOE is not set +# CONFIG_PPPOLAC is not set +# CONFIG_PPPOPNS is not set +CONFIG_PPP_ASYNC=y +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_SLIP is not set +CONFIG_SLHC=y + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_RTL8152 is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +# CONFIG_WLAN is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set +CONFIG_INPUT_MATRIXKMAP=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_KEYRESET=y + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_QT2160 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_LM8333 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_INPUT_MOUSE=y +# CONFIG_MOUSE_PS2 is not set +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_APPLETOUCH is not set +# CONFIG_MOUSE_BCM5974 is not set +# CONFIG_MOUSE_CYAPA is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_MOUSE_GPIO is not set +# CONFIG_MOUSE_SYNAPTICS_I2C is not set +# CONFIG_MOUSE_SYNAPTICS_USB is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_ATMXT is not set +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +CONFIG_TOUCHSCREEN_ATMEL_MXT=y +# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_EGALAX is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_ILI210X is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_WACOM_I2C is not set +# CONFIG_TOUCHSCREEN_MAX11801 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MMS114 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_PIXCIR is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC_SERIO is not set +# CONFIG_TOUCHSCREEN_TSC2005 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_BMA150 is not set +# CONFIG_INPUT_MMA8450 is not set +# CONFIG_INPUT_MPU3050 is not set +# CONFIG_INPUT_GP2A is not set +# CONFIG_INPUT_GPIO_TILT_POLLED is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYCHORD is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_KXTJ9 is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +# CONFIG_INPUT_GPIO is not set +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_IMS_PCU is not set +# CONFIG_INPUT_CMA3000 is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_TTY=y +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +CONFIG_DEVKMEM=y + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX310X is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_OMAP=y +CONFIG_SERIAL_OMAP_CONSOLE=y +# CONFIG_SERIAL_SCCNXP is not set +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_IFX6X60 is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +# CONFIG_SERIAL_ARC is not set +CONFIG_TTY_PRINTK=y +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +# CONFIG_HW_RANDOM_ATMEL is not set +# CONFIG_HW_RANDOM_EXYNOS is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=y + +# +# Multiplexer I2C Chip support +# +# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set +# CONFIG_I2C_MUX_GPIO is not set +# CONFIG_I2C_MUX_PCA9541 is not set +# CONFIG_I2C_MUX_PCA954x is not set +CONFIG_I2C_MUX_PINCTRL=y +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_CBUS_GPIO is not set +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_OCORES is not set +CONFIG_I2C_OMAP=y +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_FSL_SPI is not set +# CONFIG_SPI_OC_TINY is not set +CONFIG_SPI_OMAP24XX=y +# CONFIG_SPI_PXA2XX_PCI is not set +# CONFIG_SPI_SC18IS602 is not set +# CONFIG_SPI_XCOMM is not set +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +CONFIG_SPI_SPIDEV=y +# CONFIG_SPI_TLE62X0 is not set + +# +# Qualcomm MSM SSBI bus support +# +# CONFIG_SSBI is not set +# CONFIG_HSI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# + +# +# PTP clock support +# +# CONFIG_PTP_1588_CLOCK is not set + +# +# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. +# +# CONFIG_PTP_1588_CLOCK_PCH is not set +CONFIG_PINCTRL=y + +# +# Pin controllers +# +CONFIG_PINMUX=y +CONFIG_PINCONF=y +CONFIG_GENERIC_PINCONF=y +# CONFIG_DEBUG_PINCTRL is not set +CONFIG_PINCTRL_SINGLE=y +# CONFIG_PINCTRL_EXYNOS is not set +# CONFIG_PINCTRL_EXYNOS5440 is not set +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIO_DEVRES=y +CONFIG_GPIOLIB=y +CONFIG_OF_GPIO=y +# CONFIG_DEBUG_GPIO is not set +CONFIG_GPIO_SYSFS=y + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set +# CONFIG_GPIO_EM is not set +# CONFIG_GPIO_RCAR is not set +# CONFIG_GPIO_TS5500 is not set +# CONFIG_GPIO_GRGPIO is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set +# CONFIG_GPIO_ADNP is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +CONFIG_GPIO_TPS65910=y + +# +# USB GPIO expanders: +# +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_GENERIC_ADC_BATTERY is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2780 is not set +# CONFIG_BATTERY_DS2781 is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_SBS is not set +CONFIG_BATTERY_BQ27x00=y +CONFIG_BATTERY_BQ27X00_I2C=y +# CONFIG_BATTERY_BQ27X00_PLATFORM is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_ISP1704 is not set +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_LP8727 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_CHARGER_MANAGER is not set +# CONFIG_CHARGER_BQ2415X is not set +# CONFIG_CHARGER_SMB347 is not set +# CONFIG_BATTERY_GOLDFISH is not set +# CONFIG_POWER_RESET is not set +# CONFIG_POWER_RESET_RESTART is not set +CONFIG_POWER_AVS=y +CONFIG_POWER_TI_HARDWARE_VOLTAGE_CONTROL=y +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +# CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +CONFIG_OMAP_WATCHDOG=y +CONFIG_OMAP_WATCHDOG_AUTOPET=y +# CONFIG_MAX63XX_WATCHDOG is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +CONFIG_MFD_CORE=y +# CONFIG_MFD_AS3711 is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_CROS_EC is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_MFD_DA9055 is not set +# CONFIG_MFD_MC13XXX_SPI is not set +# CONFIG_MFD_MC13XXX_I2C is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_MFD_88PM800 is not set +# CONFIG_MFD_88PM805 is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_MAX77686 is not set +# CONFIG_MFD_MAX77693 is not set +# CONFIG_MFD_MAX8907 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_CPCAP is not set +# CONFIG_MFD_VIPERBOARD is not set +# CONFIG_MFD_RETU is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_MFD_SEC_CORE is not set +# CONFIG_MFD_SI476X_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_SMSC is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_M4SENSORHUB is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_SYSCON is not set +# CONFIG_MFD_TI_AM335X_TSCADC is not set +# CONFIG_MFD_LP8788 is not set +CONFIG_MFD_OMAP_USB_HOST=y +# CONFIG_MFD_PALMAS is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +CONFIG_MFD_TPS65910=y +# CONFIG_MFD_TPS65912 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_MFD_TPS80031 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_LM3533 is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_MFD_ARIZONA_I2C is not set +# CONFIG_MFD_ARIZONA_SPI is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DEBUG=y +CONFIG_REGULATOR_DUMMY=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_GPIO is not set +# CONFIG_REGULATOR_AD5398 is not set +# CONFIG_REGULATOR_FAN53555 is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_MAX8973 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +# CONFIG_REGULATOR_LP872X is not set +# CONFIG_REGULATOR_LP8755 is not set +# CONFIG_REGULATOR_TPS51632 is not set +# CONFIG_REGULATOR_TPS62360 is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_TPS6524X is not set +CONFIG_REGULATOR_TPS65910=y +CONFIG_REGULATOR_TI_OMAP_PMIC=y +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +# CONFIG_TEGRA_HOST1X is not set +# CONFIG_SGX_OMAP3630 is not set +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_UVESA is not set +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_TMIO is not set +# CONFIG_FB_SMSCUFX is not set +# CONFIG_FB_UDL is not set +# CONFIG_FB_GOLDFISH is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_FB_AUO_K190X is not set +# CONFIG_FB_SIMPLE is not set +CONFIG_OMAP2_VRFB=y +CONFIG_OMAP2_DSS=y +# CONFIG_OMAP2_DSS_DEBUG is not set +# CONFIG_OMAP2_DSS_DEBUGFS is not set +CONFIG_OMAP2_DSS_DPI=y +# CONFIG_OMAP2_DSS_RFBI is not set +# CONFIG_OMAP2_DSS_VENC is not set +CONFIG_OMAP4_DSS_HDMI=y +# CONFIG_OMAP2_DSS_SDI is not set +# CONFIG_OMAP2_DSS_DSI is not set +CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK=0 +# CONFIG_OMAP2_DSS_SLEEP_AFTER_VENC_RESET is not set +# CONFIG_OMAP2_DSS_RESET is not set +CONFIG_FB_OMAP2=y +CONFIG_FB_OMAP2_DEBUG_SUPPORT=y +CONFIG_FB_OMAP2_NUM_FBS=3 + +# +# OMAP2/3 Display Device Drivers +# +# CONFIG_PANEL_GENERIC_DPI is not set +# CONFIG_PANEL_TFP410 is not set +CONFIG_PANEL_ILI_9342=y +# CONFIG_PANEL_LGPHILIPS_LB035Q02 is not set +# CONFIG_PANEL_PICODLP is not set +# CONFIG_PANEL_TPO_TD043MTEA1 is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_ADF is not set +# CONFIG_LOGO is not set +# CONFIG_FB_SSD1307 is not set +CONFIG_SOUND=y +# CONFIG_SOUND_OSS_CORE is not set +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_COMPRESS_OFFLOAD=y +CONFIG_SND_JACK=y +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +# CONFIG_SND_HRTIMER is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_RAWMIDI_SEQ is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_EMU10K1_SEQ is not set +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_ALOOP is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +CONFIG_SND_ARM=y +CONFIG_SND_SPI=y +CONFIG_SND_USB=y +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_UA101 is not set +# CONFIG_SND_USB_CAIAQ is not set +# CONFIG_SND_USB_6FIRE is not set +CONFIG_SND_SOC=y +CONFIG_SND_SOC_DMAENGINE_PCM=y +# CONFIG_SND_ATMEL_SOC is not set +# CONFIG_SND_DESIGNWARE_I2S is not set +CONFIG_SND_OMAP_SOC=y +# CONFIG_SND_OMAP_SOC_C55 is not set +CONFIG_SND_OMAP_SOC_MCBSP=y +CONFIG_SND_OMAP_SOC_OMAP3_H1=y +# CONFIG_SND_OMAP_SOC_OMAP_HDMI is not set +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +CONFIG_SND_SOC_DMIC=y +# CONFIG_SND_SIMPLE_CARD is not set +# CONFIG_SOUND_PRIME is not set + +# +# HID support +# +CONFIG_HID=y +# CONFIG_HID_BATTERY_STRENGTH is not set +# CONFIG_HIDRAW is not set +# CONFIG_UHID is not set +CONFIG_HID_GENERIC=y + +# +# Special HID drivers +# +# CONFIG_HID_A4TECH is not set +# CONFIG_HID_ACRUX is not set +# CONFIG_HID_APPLE is not set +# CONFIG_HID_APPLEIR is not set +# CONFIG_HID_AUREAL is not set +# CONFIG_HID_BELKIN is not set +# CONFIG_HID_CHERRY is not set +# CONFIG_HID_CHICONY is not set +# CONFIG_HID_PRODIKEYS is not set +# CONFIG_HID_CYPRESS is not set +# CONFIG_HID_DRAGONRISE is not set +# CONFIG_HID_EMS_FF is not set +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_EZKEY is not set +# CONFIG_HID_HOLTEK is not set +# CONFIG_HID_KEYTOUCH is not set +# CONFIG_HID_KYE is not set +# CONFIG_HID_UCLOGIC is not set +# CONFIG_HID_WALTOP is not set +# CONFIG_HID_GYRATION is not set +# CONFIG_HID_ICADE is not set +# CONFIG_HID_TWINHAN is not set +# CONFIG_HID_KENSINGTON is not set +# CONFIG_HID_LCPOWER is not set +# CONFIG_HID_LENOVO_TPKBD is not set +# CONFIG_HID_LOGITECH is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_MICROSOFT is not set +# CONFIG_HID_MONTEREY is not set +# CONFIG_HID_MULTITOUCH is not set +# CONFIG_HID_NTRIG is not set +# CONFIG_HID_ORTEK is not set +# CONFIG_HID_PANTHERLORD is not set +# CONFIG_HID_PETALYNX is not set +# CONFIG_HID_PICOLCD is not set +# CONFIG_HID_PRIMAX is not set +# CONFIG_HID_PS3REMOTE is not set +# CONFIG_HID_ROCCAT is not set +# CONFIG_HID_SAITEK is not set +# CONFIG_HID_SAMSUNG is not set +# CONFIG_HID_SONY is not set +# CONFIG_HID_SPEEDLINK is not set +# CONFIG_HID_STEELSERIES is not set +# CONFIG_HID_SUNPLUS is not set +# CONFIG_HID_GREENASIA is not set +# CONFIG_HID_SMARTJOYPLUS is not set +# CONFIG_HID_TIVO is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_HID_THINGM is not set +# CONFIG_HID_THRUSTMASTER is not set +# CONFIG_HID_WACOM is not set +# CONFIG_HID_WIIMOTE is not set +# CONFIG_HID_ZEROPLUS is not set +# CONFIG_HID_ZYDACRON is not set +# CONFIG_HID_SENSOR_HUB is not set + +# +# USB HID support +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +# CONFIG_USB_HIDDEV is not set + +# +# I2C HID support +# +# CONFIG_I2C_HID is not set +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB_ARCH_HAS_EHCI=y +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEFAULT_PERSIST=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +CONFIG_USB_MON=y +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_EHCI_HCD_OMAP=y +# CONFIG_USB_EHCI_HCD_PLATFORM is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_OHCI_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_MUSB_HDRC=y +# CONFIG_USB_MUSB_TUSB6010 is not set +CONFIG_USB_MUSB_OMAP2PLUS=y +# CONFIG_USB_MUSB_AM35X is not set +# CONFIG_USB_MUSB_DSPS is not set +# CONFIG_USB_MUSB_UX500 is not set +# CONFIG_USB_INVENTRA_DMA is not set +CONFIG_MUSB_PIO_ONLY=y +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_CHIPIDEA is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +# CONFIG_USB_EZUSB_FX2 is not set +# CONFIG_USB_HSIC_USB3503 is not set +CONFIG_USB_PHY=y +# CONFIG_USB_OTG_WAKELOCK is not set +CONFIG_NOP_USB_XCEIV=y +# CONFIG_OMAP_CONTROL_USB is not set +# CONFIG_OMAP_USB2 is not set +# CONFIG_OMAP_USB3 is not set +# CONFIG_SAMSUNG_USBPHY is not set +# CONFIG_SAMSUNG_USB2PHY is not set +# CONFIG_SAMSUNG_USB3PHY is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ISP1301 is not set +# CONFIG_USB_RCAR_PHY is not set +CONFIG_USB_ULPI=y +# CONFIG_USB_TUSB is not set +# CONFIG_CPCAP_USB is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=2 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 + +# +# USB Peripheral Controller +# +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_PXA27X is not set +# CONFIG_USB_MV_UDC is not set +# CONFIG_USB_MV_U3D is not set +CONFIG_USB_GADGET_MUSB_HDRC=y +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_LIBCOMPOSITE=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_AUDIO is not set +CONFIG_USB_ETH=y +CONFIG_USB_ETH_RNDIS=y +# CONFIG_USB_ETH_EEM is not set +# CONFIG_USB_G_NCM is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_MIDI_GADGET is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_G_ANDROID is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_MMC is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +CONFIG_LEDS_LM3530=y +# CONFIG_LEDS_LM3535 is not set +# CONFIG_LEDS_LM3642 is not set +# CONFIG_LEDS_PCA9532 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_LP5562 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_LM355x is not set +# CONFIG_LEDS_OT200 is not set +# CONFIG_LEDS_BLINKM is not set + +# +# LED Triggers +# +# CONFIG_LEDS_TRIGGERS is not set +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +# CONFIG_EDAC is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_HCTOSYS is not set +CONFIG_RTC_SYSTOHC=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8523 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +CONFIG_RTC_DRV_TPS65910=y +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set +# CONFIG_RTC_DRV_RX4581 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set +# CONFIG_RTC_DRV_DS2404 is not set + +# +# on-CPU RTC drivers +# +# CONFIG_RTC_DRV_SNVS is not set + +# +# HID Sensor RTC drivers +# +# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set +CONFIG_DMADEVICES=y +# CONFIG_DMADEVICES_DEBUG is not set + +# +# DMA Devices +# +# CONFIG_DW_DMAC is not set +# CONFIG_TIMB_DMA is not set +CONFIG_DMA_OMAP=y +CONFIG_DMA_ENGINE=y +CONFIG_DMA_VIRTUAL_CHANNELS=y +CONFIG_DMA_OF=y + +# +# DMA Clients +# +# CONFIG_NET_DMA is not set +# CONFIG_ASYNC_TX_DMA is not set +# CONFIG_DMATEST is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +CONFIG_UIO_PDRV=y +CONFIG_UIO_PDRV_GENIRQ=y +# CONFIG_UIO_DMEM_GENIRQ is not set +# CONFIG_VFIO is not set +# CONFIG_VIRT_DRIVERS is not set + +# +# Virtio drivers +# +# CONFIG_VIRTIO_MMIO is not set + +# +# Microsoft Hyper-V guest support +# +CONFIG_STAGING=y +# CONFIG_USBIP_CORE is not set +# CONFIG_ECHO is not set +# CONFIG_COMEDI is not set +# CONFIG_ASUS_OLED is not set +# CONFIG_TRANZPORT is not set +# CONFIG_LINE6_USB is not set + +# +# IIO staging drivers +# + +# +# Accelerometers +# +# CONFIG_ADIS16201 is not set +# CONFIG_ADIS16203 is not set +# CONFIG_ADIS16204 is not set +# CONFIG_ADIS16209 is not set +# CONFIG_ADIS16220 is not set +# CONFIG_ADIS16240 is not set +# CONFIG_LIS3L02DQ is not set +# CONFIG_SCA3000 is not set + +# +# Analog to digital converters +# +# CONFIG_AD7291 is not set +# CONFIG_AD7606 is not set +# CONFIG_AD799X is not set +# CONFIG_AD7780 is not set +# CONFIG_AD7816 is not set +# CONFIG_AD7192 is not set +# CONFIG_AD7280 is not set + +# +# Analog digital bi-direction converters +# +# CONFIG_ADT7316 is not set + +# +# Capacitance to digital converters +# +# CONFIG_AD7150 is not set +# CONFIG_AD7152 is not set +# CONFIG_AD7746 is not set + +# +# Direct Digital Synthesis +# +# CONFIG_AD5930 is not set +# CONFIG_AD9832 is not set +# CONFIG_AD9834 is not set +# CONFIG_AD9850 is not set +# CONFIG_AD9852 is not set +# CONFIG_AD9910 is not set +# CONFIG_AD9951 is not set + +# +# Digital gyroscope sensors +# +# CONFIG_ADIS16060 is not set +# CONFIG_ADIS16130 is not set +# CONFIG_ADIS16260 is not set + +# +# Network Analyzer, Impedance Converters +# +# CONFIG_AD5933 is not set + +# +# Light sensors +# +# CONFIG_SENSORS_ISL29018 is not set +# CONFIG_SENSORS_ISL29028 is not set +# CONFIG_TSL2583 is not set +# CONFIG_TSL2x7x is not set + +# +# Magnetometer sensors +# +# CONFIG_SENSORS_HMC5843 is not set + +# +# Active energy metering IC +# +# CONFIG_ADE7753 is not set +# CONFIG_ADE7754 is not set +# CONFIG_ADE7758 is not set +# CONFIG_ADE7759 is not set +# CONFIG_ADE7854 is not set + +# +# Resolver to digital converters +# +# CONFIG_AD2S90 is not set +# CONFIG_AD2S1200 is not set +# CONFIG_AD2S1210 is not set + +# +# Triggers - standalone +# +# CONFIG_IIO_PERIODIC_RTC_TRIGGER is not set +# CONFIG_IIO_GPIO_TRIGGER is not set +# CONFIG_IIO_SYSFS_TRIGGER is not set +CONFIG_INV_MPU_IIO=y +CONFIG_OLIO_TAP_ENABLE=y +# CONFIG_INV_IIO_MPU3050_ACCEL_SLAVE_BMA250 is not set +# CONFIG_DTS_INV_MPU_IIO is not set +# CONFIG_IIO_SIMPLE_DUMMY is not set +# CONFIG_ZSMALLOC is not set +# CONFIG_BCM_WIMAX is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set +# CONFIG_STAGING_MEDIA is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_BINDER_IPC_32BIT=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES=y +CONFIG_ANDROID_INTF_ALARM_DEV=y +CONFIG_SYNC=y +CONFIG_SW_SYNC=y +# CONFIG_SW_SYNC_USER is not set +CONFIG_ION=y +# CONFIG_ION_TEST is not set +# CONFIG_USB_WPAN_HCD is not set +# CONFIG_WIMAX_GDM72XX is not set +# CONFIG_CED1401 is not set +# CONFIG_DGRP is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_HAVE_CLK_PREPARE=y +CONFIG_COMMON_CLK=y + +# +# Common Clock Framework +# +CONFIG_COMMON_CLK_DEBUG=y +# CONFIG_COMMON_CLK_SI5351 is not set + +# +# Hardware Spinlock drivers +# +CONFIG_CLKSRC_OF=y +CONFIG_CLKSRC_MMIO=y +CONFIG_ARM_ARCH_TIMER=y +# CONFIG_MAILBOX is not set +CONFIG_IOMMU_API=y +CONFIG_IOMMU_SUPPORT=y +CONFIG_OF_IOMMU=y +CONFIG_OMAP_IOMMU=y +CONFIG_OMAP_IOVMM=y +CONFIG_OMAP_IOMMU_DEBUG=y + +# +# Remoteproc drivers +# +# CONFIG_STE_MODEM_RPROC is not set + +# +# Rpmsg drivers +# +# CONFIG_PM_DEVFREQ is not set +# CONFIG_EXTCON is not set +CONFIG_MEMORY=y +# CONFIG_TI_EMIF is not set +CONFIG_IIO=y +CONFIG_IIO_BUFFER=y +# CONFIG_IIO_BUFFER_CB is not set +CONFIG_IIO_KFIFO_BUF=y +CONFIG_IIO_TRIGGER=y +CONFIG_IIO_CONSUMERS_PER_TRIGGER=2 + +# +# Accelerometers +# +# CONFIG_KXSD9 is not set +# CONFIG_IIO_ST_ACCEL_3AXIS is not set + +# +# Analog to digital converters +# +# CONFIG_AD7266 is not set +# CONFIG_AD7298 is not set +# CONFIG_AD7923 is not set +# CONFIG_AD7791 is not set +# CONFIG_AD7793 is not set +# CONFIG_AD7476 is not set +# CONFIG_AD7887 is not set +# CONFIG_EXYNOS_ADC is not set +# CONFIG_MAX1363 is not set +# CONFIG_TI_ADC081C is not set + +# +# Amplifiers +# +# CONFIG_AD8366 is not set + +# +# Hid Sensor IIO Common +# + +# +# Digital to analog converters +# +# CONFIG_AD5064 is not set +# CONFIG_AD5360 is not set +# CONFIG_AD5380 is not set +# CONFIG_AD5421 is not set +# CONFIG_AD5624R_SPI is not set +# CONFIG_AD5446 is not set +# CONFIG_AD5449 is not set +# CONFIG_AD5504 is not set +# CONFIG_AD5755 is not set +# CONFIG_AD5764 is not set +# CONFIG_AD5791 is not set +# CONFIG_AD5686 is not set +# CONFIG_MAX517 is not set +# CONFIG_MCP4725 is not set + +# +# Frequency Synthesizers DDS/PLL +# + +# +# Clock Generator/Distribution +# +# CONFIG_AD9523 is not set + +# +# Phase-Locked Loop (PLL) frequency synthesizers +# +# CONFIG_ADF4350 is not set + +# +# Digital gyroscope sensors +# +# CONFIG_ADIS16080 is not set +# CONFIG_ADIS16136 is not set +# CONFIG_ADXRS450 is not set +# CONFIG_IIO_ST_GYRO_3AXIS is not set +# CONFIG_ITG3200 is not set + +# +# Light sensors +# +# CONFIG_ADJD_S311 is not set +# CONFIG_CM32181 is not set +CONFIG_CM3391=y +# CONFIG_SENSORS_TSL2563 is not set +# CONFIG_VCNL4000 is not set + +# +# Magnetometer sensors +# +# CONFIG_AK8975 is not set +# CONFIG_IIO_ST_MAGN_3AXIS is not set +# CONFIG_PWM is not set +CONFIG_IRQCHIP=y +# CONFIG_IPACK_BUS is not set +# CONFIG_RESET_CONTROLLER is not set + +# +# File systems +# +CONFIG_DCACHE_WORD_ACCESS=y +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +CONFIG_FUSE_FS=y +# CONFIG_CUSE is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_TMPFS_XATTR is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_CONFIGFS_FS=y +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS2_FS is not set +CONFIG_UBIFS_FS=y +# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +# CONFIG_F2FS_FS is not set +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_MAC_ROMAN is not set +# CONFIG_NLS_MAC_CELTIC is not set +# CONFIG_NLS_MAC_CENTEURO is not set +# CONFIG_NLS_MAC_CROATIAN is not set +# CONFIG_NLS_MAC_CYRILLIC is not set +# CONFIG_NLS_MAC_GAELIC is not set +# CONFIG_NLS_MAC_GREEK is not set +# CONFIG_NLS_MAC_ICELAND is not set +# CONFIG_NLS_MAC_INUIT is not set +# CONFIG_NLS_MAC_ROMANIAN is not set +# CONFIG_NLS_MAC_TURKISH is not set +# CONFIG_NLS_UTF8 is not set +# CONFIG_DLM is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=1 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_READABLE_ASM is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_TOUCHSCREEN_DEBUG is not set +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_LOCKUP_DETECTOR=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_PANIC_ON_OOPS=y +CONFIG_PANIC_ON_OOPS_VALUE=1 +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120 +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +CONFIG_HAVE_DEBUG_KMEMLEAK=y +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set + +# +# RCU Debugging +# +# CONFIG_PROVE_RCU_DELAY is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set +# CONFIG_NOTIFIER_ERROR_INJECTION is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_RBTREE_TEST is not set +# CONFIG_INTERVAL_TREE_TEST is not set +CONFIG_DYNAMIC_DEBUG=y +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_STRING_HELPERS is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +# CONFIG_ARM_UNWIND is not set +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_RODATA is not set +CONFIG_DEBUG_LL=y +CONFIG_DEBUG_OMAP2PLUS_UART=y +# CONFIG_DEBUG_ICEDCC is not set +# CONFIG_DEBUG_SEMIHOSTING is not set +# CONFIG_DEBUG_OMAP2UART1 is not set +# CONFIG_DEBUG_OMAP2UART2 is not set +# CONFIG_DEBUG_OMAP2UART3 is not set +CONFIG_DEBUG_OMAP3UART3=y +# CONFIG_DEBUG_OMAP4UART3 is not set +# CONFIG_DEBUG_OMAP3UART4 is not set +# CONFIG_DEBUG_OMAP4UART4 is not set +# CONFIG_DEBUG_TI81XXUART1 is not set +# CONFIG_DEBUG_TI81XXUART2 is not set +# CONFIG_DEBUG_TI81XXUART3 is not set +# CONFIG_DEBUG_AM33XXUART1 is not set +# CONFIG_DEBUG_ZOOM_UART is not set +CONFIG_DEBUG_LL_INCLUDE="debug/omap2plus.S" +CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" +CONFIG_EARLY_PRINTK=y +CONFIG_EARLY_PRINTK_DIRECT=y +# CONFIG_PID_IN_CONTEXTIDR is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_USER is not set +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_CMAC is not set +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_CRC32 is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +# CONFIG_CRYPTO_SHA1_ARM is not set +CONFIG_CRYPTO_SHA256=y +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_AES_ARM is not set +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +CONFIG_CRYPTO_LZO=y + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +# CONFIG_CRYPTO_DEV_OMAP_SHAM is not set +# CONFIG_CRYPTO_DEV_OMAP_AES is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_XZ_DEC=y +CONFIG_XZ_DEC_X86=y +CONFIG_XZ_DEC_POWERPC=y +CONFIG_XZ_DEC_IA64=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_SPARC=y +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_BCH=y +CONFIG_BCH_CONST_PARAMS=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set +# CONFIG_DDR is not set +# CONFIG_VIRTUALIZATION is not set diff --git a/arch/arm/configs/omap3_h1_defconfig.old b/arch/arm/configs/omap3_h1_defconfig.old new file mode 100644 index 00000000000..26083d70273 --- /dev/null +++ b/arch/arm/configs/omap3_h1_defconfig.old @@ -0,0 +1,2858 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.10.0 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_ARM_PATCH_PHYS_VIRT=y +CONFIG_GENERIC_BUG=y +# CONFIG_BOOTINFO is not set +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_IRQ_WORK=y +CONFIG_BUILDTIME_EXTABLE_SORT=y + +# +# General setup +# +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_FHANDLE is not set +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y +CONFIG_AUDIT_WATCH=y +CONFIG_AUDIT_TREE=y +# CONFIG_AUDIT_LOGINUID_IMMUTABLE is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_IRQ_DOMAIN=y +# CONFIG_IRQ_DOMAIN_DEBUG is not set +CONFIG_SPARSE_IRQ=y +CONFIG_KTIME_SCALAR=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y + +# +# Timers subsystem +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ_COMMON=y +# CONFIG_HZ_PERIODIC is not set +CONFIG_NO_HZ_IDLE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y + +# +# CPU/Task time and stats accounting +# +CONFIG_TICK_CPU_ACCOUNTING=y +# CONFIG_IRQ_TIME_ACCOUNTING is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +# CONFIG_TASKSTATS is not set + +# +# RCU Subsystem +# +# CONFIG_TREE_PREEMPT_RCU is not set +CONFIG_TINY_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +# CONFIG_RCU_STALL_COMMON is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_RCU_BOOST is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_DEVICE is not set +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +# CONFIG_MEMCG is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +# CONFIG_CFS_BANDWIDTH is not set +CONFIG_RT_GROUP_SCHED=y +# CONFIG_BLK_CGROUP is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +CONFIG_UIDGID_CONVERTED=y +# CONFIG_UIDGID_STRICT_TYPE_CHECKS is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_HAVE_UID16=y +CONFIG_HOTPLUG=y +CONFIG_PANIC_TIMEOUT=1 +CONFIG_EXPERT=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_TRACEPOINTS=y +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +# CONFIG_JUMP_LABEL is not set +# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y +CONFIG_SECCOMP_FILTER=y +CONFIG_HAVE_CONTEXT_TRACKING=y +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_OLD_SIGACTION=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +# CONFIG_MODULE_UNLOAD is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +# CONFIG_MODULE_SIG is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_EFI_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_ROW=y +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_ROW=y +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="row" +CONFIG_UNINLINE_SPIN_UNLOCK=y +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +CONFIG_ARCH_MULTIPLATFORM=y +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C24XX is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_EXYNOS is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP1 is not set + +# +# Multiple platform selection +# + +# +# CPU Core family selection +# +# CONFIG_ARCH_MULTI_V6 is not set +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_MULTI_V6_V7=y +# CONFIG_ARCH_MULTI_CPU_AUTO is not set +# CONFIG_ARCH_MVEBU is not set +# CONFIG_ARCH_BCM is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_ARCH_HIGHBANK is not set +# CONFIG_ARCH_MXC is not set + +# +# TI OMAP Common Features +# + +# +# OMAP Feature Selections +# +# CONFIG_POWER_AVS_OMAP is not set +CONFIG_OMAP_RESET_CLOCKS=y +CONFIG_OMAP_MUX=y +CONFIG_OMAP_MUX_DEBUG=y +CONFIG_OMAP_MUX_WARNINGS=y +CONFIG_OMAP_32K_TIMER=y +# CONFIG_OMAP3_L2_AUX_SECURE_SAVE_RESTORE is not set +CONFIG_OMAP_DM_TIMER=y +CONFIG_OMAP_PM_NOOP=y +CONFIG_MACH_OMAP_GENERIC=y +CONFIG_ARCH_OMAP=y +CONFIG_ARCH_OMAP2PLUS=y + +# +# TI OMAP2/3/4 Specific Features +# +# CONFIG_ARCH_OMAP2PLUS_TYPICAL is not set +CONFIG_SOC_HAS_OMAP2_SDRC=y +CONFIG_ARCH_OMAP3=y +# CONFIG_ARCH_OMAP4 is not set +# CONFIG_SOC_OMAP5 is not set +CONFIG_SOC_OMAP3430=y +# CONFIG_SOC_TI81XX is not set +# CONFIG_SOC_AM33XX is not set +CONFIG_OMAP_PACKAGE_CBP=y + +# +# OMAP Board Type +# +# CONFIG_MACH_OMAP3_BEAGLE is not set +CONFIG_MACH_OMAP3_H1=y +# CONFIG_MACH_DEVKIT8000 is not set +# CONFIG_MACH_OMAP_LDP is not set +# CONFIG_MACH_OMAP3530_LV_SOM is not set +# CONFIG_MACH_OMAP3_TORPEDO is not set +# CONFIG_MACH_OVERO is not set +# CONFIG_MACH_OMAP3EVM is not set +# CONFIG_MACH_OMAP3517EVM is not set +# CONFIG_MACH_CRANEBOARD is not set +# CONFIG_MACH_OMAP3_PANDORA is not set +# CONFIG_MACH_TOUCHBOOK is not set +# CONFIG_MACH_OMAP_3430SDP is not set +# CONFIG_MACH_NOKIA_RM680 is not set +# CONFIG_MACH_NOKIA_RX51 is not set +# CONFIG_MACH_OMAP_ZOOM2 is not set +# CONFIG_MACH_OMAP_ZOOM3 is not set +# CONFIG_MACH_CM_T35 is not set +# CONFIG_MACH_CM_T3517 is not set +# CONFIG_MACH_IGEP0020 is not set +# CONFIG_MACH_IGEP0030 is not set +# CONFIG_MACH_SBC3530 is not set +# CONFIG_MACH_OMAP_3630SDP is not set +# CONFIG_MACH_MINNOW is not set +# CONFIG_OMAP3_EMU is not set +# CONFIG_OMAP3_SDRC_AC_TIMING is not set +# CONFIG_OMAP3_PAD_WKUP_IO is not set +CONFIG_DISABLE_OMAP_ERRATA_i583=y +# CONFIG_ARCH_SOCFPGA is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_ARCH_SUNXI is not set +# CONFIG_ARCH_SIRF is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_VIRT is not set +# CONFIG_ARCH_WM8850 is not set +# CONFIG_ARCH_ZYNQ is not set + +# +# Processor Type +# +CONFIG_CPU_V7=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +# CONFIG_ARM_LPAE is not set +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +CONFIG_ARM_VIRT_EXT=y +# CONFIG_SWP_EMULATE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT_6=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_ARM_NR_BANKS=8 +CONFIG_MULTI_IRQ_HANDLER=y +CONFIG_ARM_ERRATA_430973=y +# CONFIG_ARM_ERRATA_720789 is not set +# CONFIG_ARM_ERRATA_754322 is not set +# CONFIG_ARM_ERRATA_775420 is not set +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +# CONFIG_HAVE_ARM_ARCH_TIMER is not set +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_ARM_PSCI is not set +CONFIG_ARCH_NR_GPIO=0 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_PREEMPT_COUNT=y +CONFIG_HZ=100 +CONFIG_SCHED_HRTICK=y +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +CONFIG_ARCH_HAS_HOLES_MEMORYMODEL=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +# CONFIG_HIGHMEM is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_CROSS_MEMORY_ATTACH=y +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_CLEANCACHE is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +CONFIG_SECCOMP=y +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_XEN is not set +# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set + +# +# Boot options +# +CONFIG_USE_OF=y +# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set +CONFIG_ATAGS=y +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +# CONFIG_ARM_APPENDED_DTB is not set +CONFIG_CMDLINE="" +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +CONFIG_AUTO_ZRELADDR=y + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_FREQ_GOV_COMMON=y +CONFIG_CPU_FREQ_STAT=y +# CONFIG_CPU_FREQ_STAT_DETAILS is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +# CONFIG_CPU_FREQ_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +# CONFIG_GENERIC_CPUFREQ_CPU0 is not set + +# +# ARM CPU frequency scaling drivers +# +# CONFIG_ARM_EXYNOS4210_CPUFREQ is not set +# CONFIG_ARM_EXYNOS4X12_CPUFREQ is not set +# CONFIG_ARM_EXYNOS5250_CPUFREQ is not set +# CONFIG_ARM_EXYNOS5440_CPUFREQ is not set +# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set +CONFIG_ARM_OMAP2PLUS_CPUFREQ=y +CONFIG_CPU_IDLE=y +# CONFIG_CPU_IDLE_MULTIPLE_DRIVERS is not set +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_BINFMT_SCRIPT=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_MISC=y +CONFIG_COREDUMP=y + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +# CONFIG_PM_AUTOSLEEP is not set +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=100 +CONFIG_PM_WAKELOCKS_GC=y +CONFIG_PM_RUNTIME=y +CONFIG_PM=y +CONFIG_PM_DEBUG=y +# CONFIG_PM_ADVANCED_DEBUG is not set +# CONFIG_PM_TEST_SUSPEND is not set +CONFIG_PM_SLEEP_DEBUG=y +# CONFIG_APM_EMULATION is not set +CONFIG_ARCH_HAS_OPP=y +CONFIG_PM_OPP=y +CONFIG_PM_CLK=y +CONFIG_CPU_PM=y +CONFIG_SUSPEND_TIME=y +CONFIG_HAS_AMBIENTMODE=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_DIAG is not set +CONFIG_UNIX=y +# CONFIG_UNIX_DIAG is not set +CONFIG_XFRM=y +CONFIG_XFRM_ALGO=y +CONFIG_XFRM_USER=y +# CONFIG_XFRM_SUB_POLICY is not set +CONFIG_XFRM_MIGRATE=y +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +CONFIG_NET_KEY_MIGRATE=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +# CONFIG_IP_FIB_TRIE_STATS is not set +CONFIG_IP_MULTIPLE_TABLES=y +# CONFIG_IP_ROUTE_MULTIPATH is not set +# CONFIG_IP_ROUTE_VERBOSE is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +CONFIG_NET_IP_TUNNEL=y +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_NET_IPVTI is not set +# CONFIG_INET_AH is not set +CONFIG_INET_ESP=y +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +# CONFIG_IPV6_GRE is not set +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_NETLABEL is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +CONFIG_NETWORK_SECMARK=y +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +# CONFIG_NETFILTER_NETLINK_ACCT is not set +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +# CONFIG_NF_CONNTRACK_SECMARK is not set +CONFIG_NF_CONNTRACK_PROCFS=y +CONFIG_NF_CONNTRACK_EVENTS=y +# CONFIG_NF_CONNTRACK_TIMEOUT is not set +# CONFIG_NF_CONNTRACK_TIMESTAMP is not set +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_BROADCAST=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +# CONFIG_NF_CONNTRACK_SNMP is not set +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +# CONFIG_NF_CONNTRACK_SIP is not set +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +# CONFIG_NF_CT_NETLINK_TIMEOUT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE_CT is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +# CONFIG_NF_NAT_SIP is not set +CONFIG_NF_NAT_TFTP=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +# CONFIG_NETFILTER_XT_TARGET_HL is not set +# CONFIG_NETFILTER_XT_TARGET_HMARK is not set +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +# CONFIG_NETFILTER_XT_TARGET_LOG is not set +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NETMAP=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +CONFIG_NETFILTER_XT_TARGET_REDIRECT=y +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +# CONFIG_NETFILTER_XT_TARGET_SECMARK is not set +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_ADDRTYPE is not set +# CONFIG_NETFILTER_XT_MATCH_BPF is not set +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +# CONFIG_NETFILTER_XT_MATCH_CONNBYTES is not set +# CONFIG_NETFILTER_XT_MATCH_CONNLABEL is not set +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +CONFIG_NETFILTER_XT_MATCH_ECN=y +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_SET is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +# CONFIG_IP_NF_MATCH_RPFILTER is not set +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_REJECT_SKERR=y +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT_IPV4=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PPTP=y +CONFIG_NF_NAT_H323=y +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +CONFIG_IP_NF_RAW=y +# CONFIG_IP_NF_SECURITY is not set +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV6=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +# CONFIG_IP6_NF_MATCH_AH is not set +# CONFIG_IP6_NF_MATCH_EUI64 is not set +# CONFIG_IP6_NF_MATCH_FRAG is not set +# CONFIG_IP6_NF_MATCH_OPTS is not set +# CONFIG_IP6_NF_MATCH_HL is not set +# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set +# CONFIG_IP6_NF_MATCH_MH is not set +# CONFIG_IP6_NF_MATCH_RPFILTER is not set +# CONFIG_IP6_NF_MATCH_RT is not set +# CONFIG_IP6_NF_TARGET_HL is not set +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_TARGET_REJECT_SKERR=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +# CONFIG_IP6_NF_SECURITY is not set +CONFIG_NF_NAT_IPV6=y +CONFIG_IP6_NF_TARGET_MASQUERADE=y +# CONFIG_IP6_NF_TARGET_NPT is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +CONFIG_HAVE_NET_DSA=y +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFB is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +# CONFIG_NET_SCH_MQPRIO is not set +# CONFIG_NET_SCH_CHOKE is not set +# CONFIG_NET_SCH_QFQ is not set +# CONFIG_NET_SCH_CODEL is not set +# CONFIG_NET_SCH_FQ_CODEL is not set +# CONFIG_NET_SCH_INGRESS is not set +# CONFIG_NET_SCH_PLUG is not set + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +# CONFIG_NET_ACT_POLICE is not set +# CONFIG_NET_ACT_GACT is not set +# CONFIG_NET_ACT_MIRRED is not set +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set +CONFIG_DNS_RESOLVER=y +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +# CONFIG_VSOCKETS is not set +# CONFIG_NETLINK_MMAP is not set +# CONFIG_NETLINK_DIAG is not set +# CONFIG_NETPRIO_CGROUP is not set +CONFIG_BQL=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NET_DROP_MONITOR is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_CFG80211=y +CONFIG_NL80211_TESTMODE=y +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +# CONFIG_CFG80211_CERTIFICATION_ONUS is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_DEBUGFS is not set +# CONFIG_CFG80211_INTERNAL_REGDB is not set +# CONFIG_CFG80211_WEXT is not set +# CONFIG_LIB80211 is not set +# CONFIG_CFG80211_ALLOW_RECONNECT is not set +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +CONFIG_MAC80211_RC_PID=y +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_PID=y +# CONFIG_MAC80211_RC_DEFAULT_MINSTREL is not set +CONFIG_MAC80211_RC_DEFAULT="pid" +# CONFIG_MAC80211_MESH is not set +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MAC80211_DEBUGFS is not set +# CONFIG_MAC80211_MESSAGE_TRACING is not set +CONFIG_MAC80211_DEBUG_MENU=y +# CONFIG_MAC80211_NOINLINE is not set +# CONFIG_MAC80211_VERBOSE_DEBUG is not set +# CONFIG_MAC80211_MLME_DEBUG is not set +# CONFIG_MAC80211_STA_DEBUG is not set +# CONFIG_MAC80211_HT_DEBUG is not set +# CONFIG_MAC80211_IBSS_DEBUG is not set +# CONFIG_MAC80211_PS_DEBUG is not set +# CONFIG_MAC80211_TDLS_DEBUG is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_REGULATOR is not set +# CONFIG_RFKILL_GPIO is not set +CONFIG_RFKILL_WL18XX=y +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set +# CONFIG_NFC is not set +CONFIG_HAVE_BPF_JIT=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +CONFIG_FW_LOADER_USER_HELPER=y +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_SOC_BUS=y +CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_REGMAP_SPI=y +CONFIG_REGMAP_IRQ=y +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_CMA is not set + +# +# Bus devices +# +# CONFIG_OMAP_OCP2SCP is not set +CONFIG_OMAP_INTERCONNECT=y +CONFIG_CONNECTOR=y +CONFIG_PROC_EVENTS=y +CONFIG_MTD=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +CONFIG_MTD_OF_PARTS=y +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_CFI_INTELEXT is not set +# CONFIG_MTD_CFI_AMDSTD is not set +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +CONFIG_MTD_COMPLEX_MAPPINGS=y +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_PHYSMAP_OF is not set +# CONFIG_MTD_IMPA7 is not set +# CONFIG_MTD_GPIO_ADDR is not set +# CONFIG_MTD_PLATRAM is not set +# CONFIG_MTD_LATCH_ADDR is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +CONFIG_MTD_NAND_IDS=y +CONFIG_MTD_NAND_ECC=y +# CONFIG_MTD_NAND_ECC_SMC is not set +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_BCH=y +CONFIG_MTD_NAND_ECC_BCH=y +# CONFIG_MTD_SM_COMMON is not set +# CONFIG_MTD_NAND_DENALI is not set +# CONFIG_MTD_NAND_GPIO is not set +CONFIG_MTD_NAND_OMAP2=y +# CONFIG_MTD_NAND_OMAP_BCH is not set +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_DOCG4 is not set +# CONFIG_MTD_NAND_NANDSIM is not set +CONFIG_MTD_NAND_PLATFORM=y +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTD_UBI_BEB_LIMIT=20 +# CONFIG_MTD_UBI_FASTMAP is not set +# CONFIG_MTD_UBI_GLUEBI is not set +CONFIG_DTC=y +CONFIG_OF=y + +# +# Device Tree and Open Firmware support +# +CONFIG_PROC_DEVICETREE=y +# CONFIG_OF_SELFTEST is not set +CONFIG_OF_FLATTREE=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_IRQ=y +CONFIG_OF_DEVICE=y +CONFIG_OF_I2C=y +CONFIG_OF_NET=y +CONFIG_OF_MTD=y +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_DRBD is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_DUMMY_IRQ is not set +# CONFIG_ICS932S401 is not set +# CONFIG_ATMEL_SSC is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_VIB_GPIO is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_DS1682 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085_I2C is not set +# CONFIG_BMP085_SPI is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_LATTICE_ECP3_CONFIG is not set +# CONFIG_SRAM is not set +# CONFIG_C55_CTRL is not set +# CONFIG_MMI_FACTORY is not set +# CONFIG_MOT_UTAG is not set +# CONFIG_BQ5105X_CTRL is not set +# CONFIG_BQ5105X_DETECT is not set +CONFIG_WAKEUP_SOURCE_NOTIFY=y +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +# CONFIG_MII is not set +CONFIG_IFB=y +# CONFIG_NET_TEAM is not set +# CONFIG_MACVLAN is not set +# CONFIG_VXLAN is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set + +# +# CAIF transport drivers +# + +# +# Distributed Switch Architecture drivers +# +# CONFIG_NET_DSA_MV88E6XXX is not set +# CONFIG_NET_DSA_MV88E6060 is not set +# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set +# CONFIG_NET_DSA_MV88E6131 is not set +# CONFIG_NET_DSA_MV88E6123_61_65 is not set +# CONFIG_ETHERNET is not set +# CONFIG_PHYLIB is not set +# CONFIG_MICREL_KS8995MA is not set +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_MPPE=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPPOE is not set +# CONFIG_PPPOLAC is not set +# CONFIG_PPPOPNS is not set +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y +# CONFIG_SLIP is not set +CONFIG_SLHC=y +# CONFIG_WLAN is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set +CONFIG_INPUT_MATRIXKMAP=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_KEYRESET=y + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_QT2160 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_LM8333 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_INPUT_MOUSE=y +# CONFIG_MOUSE_PS2 is not set +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_CYAPA is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_MOUSE_GPIO is not set +# CONFIG_MOUSE_SYNAPTICS_I2C is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ATMXT=y +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set +# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_EGALAX is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_ILI210X is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_WACOM_I2C is not set +# CONFIG_TOUCHSCREEN_MAX11801 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MMS114 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_PIXCIR is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC_SERIO is not set +# CONFIG_TOUCHSCREEN_TSC2005 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_BMA150 is not set +# CONFIG_INPUT_MMA8450 is not set +# CONFIG_INPUT_MPU3050 is not set +# CONFIG_INPUT_GP2A is not set +# CONFIG_INPUT_GPIO_TILT_POLLED is not set +# CONFIG_INPUT_KEYCHORD is not set +# CONFIG_INPUT_KXTJ9 is not set +# CONFIG_INPUT_UINPUT is not set +# CONFIG_INPUT_GPIO is not set +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CMA3000 is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_TTY=y +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +CONFIG_DEVKMEM=y + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX310X is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_OMAP=y +CONFIG_SERIAL_OMAP_CONSOLE=y +# CONFIG_SERIAL_SCCNXP is not set +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_IFX6X60 is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +# CONFIG_SERIAL_ARC is not set +CONFIG_TTY_PRINTK=y +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +# CONFIG_HW_RANDOM_ATMEL is not set +# CONFIG_HW_RANDOM_EXYNOS is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_CBUS_GPIO is not set +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_OCORES is not set +CONFIG_I2C_OMAP=y +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +CONFIG_I2C_DEBUG_CORE=y +# CONFIG_I2C_DEBUG_ALGO is not set +CONFIG_I2C_DEBUG_BUS=y +CONFIG_SPI=y +CONFIG_SPI_DEBUG=y +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_FSL_SPI is not set +# CONFIG_SPI_OC_TINY is not set +CONFIG_SPI_OMAP24XX=y +# CONFIG_SPI_PXA2XX_PCI is not set +# CONFIG_SPI_SC18IS602 is not set +# CONFIG_SPI_XCOMM is not set +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +CONFIG_SPI_SPIDEV=y +# CONFIG_SPI_TLE62X0 is not set + +# +# Qualcomm MSM SSBI bus support +# +# CONFIG_SSBI is not set +# CONFIG_HSI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# + +# +# PTP clock support +# +# CONFIG_PTP_1588_CLOCK is not set + +# +# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. +# +# CONFIG_PTP_1588_CLOCK_PCH is not set +CONFIG_PINCTRL=y + +# +# Pin controllers +# +CONFIG_PINMUX=y +CONFIG_PINCONF=y +CONFIG_GENERIC_PINCONF=y +# CONFIG_DEBUG_PINCTRL is not set +CONFIG_PINCTRL_SINGLE=y +# CONFIG_PINCTRL_EXYNOS is not set +# CONFIG_PINCTRL_EXYNOS5440 is not set +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIO_DEVRES=y +CONFIG_GPIOLIB=y +CONFIG_OF_GPIO=y +# CONFIG_DEBUG_GPIO is not set +CONFIG_GPIO_SYSFS=y + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set +# CONFIG_GPIO_EM is not set +# CONFIG_GPIO_RCAR is not set +# CONFIG_GPIO_TS5500 is not set +# CONFIG_GPIO_GRGPIO is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set +# CONFIG_GPIO_ADNP is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_GPIO_TPS65910 is not set + +# +# USB GPIO expanders: +# +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_GENERIC_ADC_BATTERY is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2780 is not set +# CONFIG_BATTERY_DS2781 is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_SBS is not set +CONFIG_BATTERY_BQ27x00=y +CONFIG_BATTERY_BQ27X00_I2C=y +CONFIG_BATTERY_BQ27X00_PLATFORM=y +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_LP8727 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_CHARGER_MANAGER is not set +# CONFIG_CHARGER_BQ2415X is not set +# CONFIG_CHARGER_SMB347 is not set +# CONFIG_BATTERY_GOLDFISH is not set +CONFIG_POWER_RESET=y +# CONFIG_POWER_RESET_GPIO is not set +# CONFIG_POWER_RESET_RESTART is not set +# CONFIG_POWER_RESET_MINNOW is not set +CONFIG_POWER_AVS=y +# CONFIG_POWER_TI_HARDWARE_VOLTAGE_CONTROL is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +# CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +CONFIG_OMAP_WATCHDOG=y +CONFIG_OMAP_WATCHDOG_AUTOPET=y +# CONFIG_MAX63XX_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +CONFIG_MFD_CORE=y +# CONFIG_MFD_AS3711 is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_CROS_EC is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_MFD_DA9055 is not set +# CONFIG_MFD_MC13XXX_SPI is not set +# CONFIG_MFD_MC13XXX_I2C is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_MFD_88PM800 is not set +# CONFIG_MFD_88PM805 is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_MAX77686 is not set +# CONFIG_MFD_MAX77693 is not set +# CONFIG_MFD_MAX8907 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_CPCAP is not set +# CONFIG_MFD_RETU is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_MFD_SEC_CORE is not set +# CONFIG_MFD_SI476X_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_SMSC is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_M4SENSORHUB is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_SYSCON is not set +# CONFIG_MFD_TI_AM335X_TSCADC is not set +# CONFIG_MFD_LP8788 is not set +# CONFIG_MFD_PALMAS is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +CONFIG_MFD_TPS65910=y +CONFIG_MFD_TPS65912=y +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_MFD_TPS65912_DEBUGFS is not set +# CONFIG_MFD_TPS80031 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_LM3533 is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_MFD_ARIZONA_I2C is not set +# CONFIG_MFD_ARIZONA_SPI is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_DEBUG is not set +CONFIG_REGULATOR_DUMMY=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_GPIO is not set +# CONFIG_REGULATOR_AD5398 is not set +# CONFIG_REGULATOR_FAN53555 is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_MAX8973 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +# CONFIG_REGULATOR_LP872X is not set +# CONFIG_REGULATOR_LP8755 is not set +# CONFIG_REGULATOR_TPS51632 is not set +# CONFIG_REGULATOR_TPS62360 is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_TPS6524X is not set +CONFIG_REGULATOR_TPS65910=y +CONFIG_REGULATOR_TI_OMAP_PMIC=y +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +# CONFIG_TEGRA_HOST1X is not set +# CONFIG_SGX_OMAP3630 is not set +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_TILEBLITTING=y + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_UVESA is not set +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_TMIO is not set +# CONFIG_FB_GOLDFISH is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_FB_AUO_K190X is not set +# CONFIG_FB_SIMPLE is not set +CONFIG_OMAP2_VRFB=y +CONFIG_OMAP2_DSS=y +CONFIG_OMAP2_DSS_DEBUG=y +CONFIG_OMAP2_DSS_DEBUGFS=y +# CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS is not set +CONFIG_OMAP2_DSS_DPI=y +# CONFIG_OMAP2_DSS_RFBI is not set +# CONFIG_OMAP2_DSS_VENC is not set +# CONFIG_OMAP4_DSS_HDMI is not set +# CONFIG_OMAP2_DSS_SDI is not set +# CONFIG_OMAP2_DSS_DSI is not set +CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK=0 +CONFIG_OMAP2_DSS_SLEEP_AFTER_VENC_RESET=y +CONFIG_OMAP2_DSS_RESET=y +CONFIG_FB_OMAP2=y +CONFIG_FB_OMAP2_DEBUG_SUPPORT=y +CONFIG_FB_OMAP2_NUM_FBS=1 + +# +# OMAP2/3 Display Device Drivers +# +# CONFIG_PANEL_GENERIC_DPI is not set +# CONFIG_PANEL_TFP410 is not set +# CONFIG_PANEL_LGPHILIPS_LB035Q02 is not set +# CONFIG_PANEL_PICODLP is not set +# CONFIG_PANEL_TPO_TD043MTEA1 is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +# CONFIG_ADF is not set +# CONFIG_LOGO is not set +# CONFIG_FB_SSD1307 is not set +CONFIG_SOUND=y +# CONFIG_SOUND_OSS_CORE is not set +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_COMPRESS_OFFLOAD=y +CONFIG_SND_JACK=y +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +# CONFIG_SND_HRTIMER is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +# CONFIG_SND_SUPPORT_OLD_API is not set +# CONFIG_SND_VERBOSE_PROCFS is not set +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_RAWMIDI_SEQ is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_EMU10K1_SEQ is not set +# CONFIG_SND_DRIVERS is not set +# CONFIG_SND_ARM is not set +# CONFIG_SND_SPI is not set +CONFIG_SND_SOC=y +CONFIG_SND_SOC_DMAENGINE_PCM=y +# CONFIG_SND_ATMEL_SOC is not set +# CONFIG_SND_DESIGNWARE_I2S is not set +CONFIG_SND_OMAP_SOC=y +# CONFIG_SND_OMAP_SOC_C55 is not set +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +# CONFIG_SND_SIMPLE_CARD is not set +# CONFIG_SOUND_PRIME is not set + +# +# HID support +# +CONFIG_HID=y +# CONFIG_HID_BATTERY_STRENGTH is not set +# CONFIG_HIDRAW is not set +# CONFIG_UHID is not set +CONFIG_HID_GENERIC=y + +# +# Special HID drivers +# +# CONFIG_HID_A4TECH is not set +# CONFIG_HID_ACRUX is not set +# CONFIG_HID_APPLE is not set +# CONFIG_HID_AUREAL is not set +# CONFIG_HID_BELKIN is not set +# CONFIG_HID_CHERRY is not set +# CONFIG_HID_CHICONY is not set +# CONFIG_HID_PRODIKEYS is not set +# CONFIG_HID_CYPRESS is not set +# CONFIG_HID_DRAGONRISE is not set +# CONFIG_HID_EMS_FF is not set +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_EZKEY is not set +# CONFIG_HID_KEYTOUCH is not set +# CONFIG_HID_KYE is not set +# CONFIG_HID_UCLOGIC is not set +# CONFIG_HID_WALTOP is not set +# CONFIG_HID_GYRATION is not set +# CONFIG_HID_ICADE is not set +# CONFIG_HID_TWINHAN is not set +# CONFIG_HID_KENSINGTON is not set +# CONFIG_HID_LCPOWER is not set +# CONFIG_HID_LOGITECH is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_MICROSOFT is not set +# CONFIG_HID_MONTEREY is not set +# CONFIG_HID_MULTITOUCH is not set +# CONFIG_HID_ORTEK is not set +# CONFIG_HID_PANTHERLORD is not set +# CONFIG_HID_PETALYNX is not set +# CONFIG_HID_PICOLCD is not set +# CONFIG_HID_PRIMAX is not set +# CONFIG_HID_PS3REMOTE is not set +# CONFIG_HID_SAITEK is not set +# CONFIG_HID_SAMSUNG is not set +# CONFIG_HID_SPEEDLINK is not set +# CONFIG_HID_STEELSERIES is not set +# CONFIG_HID_SUNPLUS is not set +# CONFIG_HID_GREENASIA is not set +# CONFIG_HID_SMARTJOYPLUS is not set +# CONFIG_HID_TIVO is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_HID_THINGM is not set +# CONFIG_HID_THRUSTMASTER is not set +# CONFIG_HID_WACOM is not set +# CONFIG_HID_WIIMOTE is not set +# CONFIG_HID_ZEROPLUS is not set +# CONFIG_HID_ZYDACRON is not set +# CONFIG_HID_SENSOR_HUB is not set + +# +# I2C HID support +# +# CONFIG_I2C_HID is not set +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB_ARCH_HAS_EHCI=y +# CONFIG_USB_ARCH_HAS_XHCI is not set +# CONFIG_USB_SUPPORT is not set +# CONFIG_MMC is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +CONFIG_LEDS_LM3530=y +# CONFIG_LEDS_LM3535 is not set +# CONFIG_LEDS_LM3642 is not set +# CONFIG_LEDS_PCA9532 is not set +# CONFIG_LEDS_GPIO is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_LP5562 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_LM355x is not set +# CONFIG_LEDS_OT200 is not set +# CONFIG_LEDS_BLINKM is not set + +# +# LED Triggers +# +# CONFIG_LEDS_TRIGGERS is not set +CONFIG_SWITCH=y +# CONFIG_SWITCH_GPIO is not set +# CONFIG_ACCESSIBILITY is not set +# CONFIG_EDAC is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_HCTOSYS is not set +CONFIG_RTC_SYSTOHC=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8523 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +CONFIG_RTC_DRV_TPS65910=y +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set +# CONFIG_RTC_DRV_RX4581 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set +# CONFIG_RTC_DRV_DS2404 is not set + +# +# on-CPU RTC drivers +# +# CONFIG_RTC_DRV_SNVS is not set + +# +# HID Sensor RTC drivers +# +CONFIG_DMADEVICES=y +# CONFIG_DMADEVICES_DEBUG is not set + +# +# DMA Devices +# +# CONFIG_DW_DMAC is not set +# CONFIG_TIMB_DMA is not set +CONFIG_DMA_OMAP=y +CONFIG_DMA_ENGINE=y +CONFIG_DMA_VIRTUAL_CHANNELS=y +CONFIG_DMA_OF=y + +# +# DMA Clients +# +# CONFIG_NET_DMA is not set +# CONFIG_ASYNC_TX_DMA is not set +# CONFIG_DMATEST is not set +# CONFIG_AUXDISPLAY is not set +CONFIG_UIO=y +CONFIG_UIO_PDRV=y +CONFIG_UIO_PDRV_GENIRQ=y +# CONFIG_UIO_DMEM_GENIRQ is not set +# CONFIG_VIRT_DRIVERS is not set + +# +# Virtio drivers +# +# CONFIG_VIRTIO_MMIO is not set + +# +# Microsoft Hyper-V guest support +# +CONFIG_STAGING=y +<<<<<<< HEAD +# CONFIG_ECHO is not set +# CONFIG_COMEDI is not set + +# +# IIO staging drivers +# +======= +CONFIG_ECHO=y +CONFIG_IIO=y +# CONFIG_IIO_ST_HWMON is not set +CONFIG_IIO_BUFFER=y +# CONFIG_IIO_SW_RING is not set +CONFIG_IIO_KFIFO_BUF=y +CONFIG_IIO_TRIGGER=y +CONFIG_IIO_CONSUMERS_PER_TRIGGER=2 +>>>>>>> 4353894... inv_mpu: Adding invensense mpu driver + +# +# Accelerometers +# +<<<<<<< HEAD +# CONFIG_ADIS16201 is not set +# CONFIG_ADIS16203 is not set +# CONFIG_ADIS16204 is not set +# CONFIG_ADIS16209 is not set +# CONFIG_ADIS16220 is not set +# CONFIG_ADIS16240 is not set +# CONFIG_LIS3L02DQ is not set +# CONFIG_SCA3000 is not set +======= +>>>>>>> 4353894... inv_mpu: Adding invensense mpu driver + +# +# Analog to digital converters +# +# CONFIG_AD7291 is not set +# CONFIG_AD7606 is not set +# CONFIG_AD799X is not set +<<<<<<< HEAD +# CONFIG_AD7780 is not set +# CONFIG_AD7816 is not set +# CONFIG_AD7192 is not set +# CONFIG_AD7280 is not set +======= +# CONFIG_ADT7410 is not set +# CONFIG_MAX1363 is not set +>>>>>>> 4353894... inv_mpu: Adding invensense mpu driver + +# +# Analog digital bi-direction converters +# +# CONFIG_ADT7316 is not set + +# +# Capacitance to digital converters +# +# CONFIG_AD7150 is not set +# CONFIG_AD7152 is not set +# CONFIG_AD7746 is not set + +# +<<<<<<< HEAD +# Direct Digital Synthesis +# +# CONFIG_AD5930 is not set +# CONFIG_AD9832 is not set +# CONFIG_AD9834 is not set +# CONFIG_AD9850 is not set +# CONFIG_AD9852 is not set +# CONFIG_AD9910 is not set +# CONFIG_AD9951 is not set +======= +# Digital to analog converters +# +# CONFIG_AD5380 is not set +# CONFIG_MAX517 is not set + +# +# Direct Digital Synthesis +# +>>>>>>> 4353894... inv_mpu: Adding invensense mpu driver + +# +# Digital gyroscope sensors +# +<<<<<<< HEAD +# CONFIG_ADIS16060 is not set +# CONFIG_ADIS16130 is not set +# CONFIG_ADIS16260 is not set +======= +>>>>>>> 4353894... inv_mpu: Adding invensense mpu driver + +# +# Network Analyzer, Impedance Converters +# +# CONFIG_AD5933 is not set + +# +<<<<<<< HEAD +# Light sensors +# +# CONFIG_SENSORS_ISL29018 is not set +# CONFIG_SENSORS_ISL29028 is not set +# CONFIG_TSL2583 is not set +# CONFIG_TSL2x7x is not set +======= +# Inertial measurement units +# +CONFIG_INV_MPU_IIO=m +# CONFIG_INV_IIO_MPU3050_ACCEL_SLAVE_BMA250 is not set +# CONFIG_DTS_INV_MPU_IIO is not set + +# +# Light sensors +# +# CONFIG_SENSORS_ISL29018 is not set +# CONFIG_SENSORS_TSL2563 is not set +# CONFIG_TSL2583 is not set +>>>>>>> 4353894... inv_mpu: Adding invensense mpu driver + +# +# Magnetometer sensors +# +# CONFIG_SENSORS_HMC5843 is not set + +# +# Active energy metering IC +# +<<<<<<< HEAD +# CONFIG_ADE7753 is not set +# CONFIG_ADE7754 is not set +# CONFIG_ADE7758 is not set +# CONFIG_ADE7759 is not set +======= +>>>>>>> 4353894... inv_mpu: Adding invensense mpu driver +# CONFIG_ADE7854 is not set + +# +# Resolver to digital converters +# +<<<<<<< HEAD +# CONFIG_AD2S90 is not set +# CONFIG_AD2S1200 is not set +# CONFIG_AD2S1210 is not set +======= +>>>>>>> 4353894... inv_mpu: Adding invensense mpu driver + +# +# Triggers - standalone +# +# CONFIG_IIO_PERIODIC_RTC_TRIGGER is not set +# CONFIG_IIO_GPIO_TRIGGER is not set +# CONFIG_IIO_SYSFS_TRIGGER is not set +# CONFIG_IIO_SIMPLE_DUMMY is not set +<<<<<<< HEAD +# CONFIG_ZSMALLOC is not set +======= +# CONFIG_FB_SM7XX is not set +# CONFIG_TIDSPBRIDGE is not set +>>>>>>> 4353894... inv_mpu: Adding invensense mpu driver +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set +# CONFIG_STAGING_MEDIA is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_BINDER_IPC_32BIT=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES=y +CONFIG_ANDROID_INTF_ALARM_DEV=y +CONFIG_SYNC=y +CONFIG_SW_SYNC=y +# CONFIG_SW_SYNC_USER is not set +CONFIG_ION=y +# CONFIG_ION_TEST is not set +# CONFIG_WIMAX_GDM72XX is not set +# CONFIG_DGRP is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_HAVE_CLK_PREPARE=y +CONFIG_COMMON_CLK=y + +# +# Common Clock Framework +# +# CONFIG_COMMON_CLK_DEBUG is not set +# CONFIG_COMMON_CLK_SI5351 is not set + +# +# Hardware Spinlock drivers +# +CONFIG_CLKSRC_MMIO=y +# CONFIG_MAILBOX is not set +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers +# +# CONFIG_STE_MODEM_RPROC is not set + +# +# Rpmsg drivers +# +# CONFIG_PM_DEVFREQ is not set +# CONFIG_EXTCON is not set +# CONFIG_MEMORY is not set +CONFIG_IIO=y +CONFIG_IIO_BUFFER=y +# CONFIG_IIO_BUFFER_CB is not set +CONFIG_IIO_KFIFO_BUF=y +CONFIG_IIO_TRIGGER=y +CONFIG_IIO_CONSUMERS_PER_TRIGGER=2 + +# +# Accelerometers +# +# CONFIG_KXSD9 is not set +# CONFIG_IIO_ST_ACCEL_3AXIS is not set + +# +# Analog to digital converters +# +# CONFIG_AD7266 is not set +# CONFIG_AD7298 is not set +# CONFIG_AD7923 is not set +# CONFIG_AD7791 is not set +# CONFIG_AD7793 is not set +# CONFIG_AD7476 is not set +# CONFIG_AD7887 is not set +# CONFIG_EXYNOS_ADC is not set +# CONFIG_MAX1363 is not set +# CONFIG_TI_ADC081C is not set + +# +# Amplifiers +# +# CONFIG_AD8366 is not set + +# +# Hid Sensor IIO Common +# + +# +# Digital to analog converters +# +# CONFIG_AD5064 is not set +# CONFIG_AD5360 is not set +# CONFIG_AD5380 is not set +# CONFIG_AD5421 is not set +# CONFIG_AD5624R_SPI is not set +# CONFIG_AD5446 is not set +# CONFIG_AD5449 is not set +# CONFIG_AD5504 is not set +# CONFIG_AD5755 is not set +# CONFIG_AD5764 is not set +# CONFIG_AD5791 is not set +# CONFIG_AD5686 is not set +# CONFIG_MAX517 is not set +# CONFIG_MCP4725 is not set + +# +# Frequency Synthesizers DDS/PLL +# + +# +# Clock Generator/Distribution +# +# CONFIG_AD9523 is not set + +# +# Phase-Locked Loop (PLL) frequency synthesizers +# +# CONFIG_ADF4350 is not set + +# +# Digital gyroscope sensors +# +# CONFIG_ADIS16080 is not set +# CONFIG_ADIS16136 is not set +# CONFIG_ADXRS450 is not set +# CONFIG_IIO_ST_GYRO_3AXIS is not set +# CONFIG_ITG3200 is not set + +# +# Inertial measurement units +# +# CONFIG_ADIS16400 is not set +# CONFIG_ADIS16480 is not set +# CONFIG_INV_MPU6050_IIO is not set + +# +# Light sensors +# +# CONFIG_ADJD_S311 is not set +# CONFIG_SENSORS_TSL2563 is not set +# CONFIG_VCNL4000 is not set + +# +# Magnetometer sensors +# +# CONFIG_AK8975 is not set +# CONFIG_IIO_ST_MAGN_3AXIS is not set +# CONFIG_PWM is not set +CONFIG_IRQCHIP=y +# CONFIG_IPACK_BUS is not set +# CONFIG_RESET_CONTROLLER is not set + +# +# File systems +# +CONFIG_DCACHE_WORD_ACCESS=y +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +CONFIG_QUOTA=y +# CONFIG_QUOTA_NETLINK_INTERFACE is not set +CONFIG_PRINT_QUOTA_WARNING=y +# CONFIG_QUOTA_DEBUG is not set +CONFIG_QUOTA_TREE=y +# CONFIG_QFMT_V1 is not set +CONFIG_QFMT_V2=y +CONFIG_QUOTACTL=y +# CONFIG_AUTOFS4_FS is not set +CONFIG_FUSE_FS=y +# CONFIG_CUSE is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_TMPFS_XATTR is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_CONFIGFS_FS=y +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_ECRYPT_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS2_FS is not set +CONFIG_UBIFS_FS=y +# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +# CONFIG_F2FS_FS is not set +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_MAC_ROMAN is not set +# CONFIG_NLS_MAC_CELTIC is not set +# CONFIG_NLS_MAC_CENTEURO is not set +# CONFIG_NLS_MAC_CROATIAN is not set +# CONFIG_NLS_MAC_CYRILLIC is not set +# CONFIG_NLS_MAC_GAELIC is not set +# CONFIG_NLS_MAC_GREEK is not set +# CONFIG_NLS_MAC_ICELAND is not set +# CONFIG_NLS_MAC_INUIT is not set +# CONFIG_NLS_MAC_ROMANIAN is not set +# CONFIG_NLS_MAC_TURKISH is not set +# CONFIG_NLS_UTF8 is not set +# CONFIG_DLM is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=7 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_READABLE_ASM is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_TOUCHSCREEN_DEBUG is not set +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_LOCKUP_DETECTOR=y +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=1 +CONFIG_PANIC_ON_OOPS=y +CONFIG_PANIC_ON_OOPS_VALUE=1 +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120 +CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=1 +# CONFIG_SCHED_DEBUG is not set +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +CONFIG_HAVE_DEBUG_KMEMLEAK=y +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set + +# +# RCU Debugging +# +# CONFIG_PROVE_RCU_DELAY is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set +# CONFIG_NOTIFIER_ERROR_INJECTION is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_NOP_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACE_CLOCK=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +CONFIG_FUNCTION_TRACER=y +CONFIG_FUNCTION_GRAPH_TRACER=y +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_FTRACE_SYSCALLS is not set +# CONFIG_TRACER_SNAPSHOT is not set +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +CONFIG_STACK_TRACER=y +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_PROBE_EVENTS is not set +CONFIG_DYNAMIC_FTRACE=y +# CONFIG_FUNCTION_PROFILER is not set +CONFIG_FTRACE_MCOUNT_RECORD=y +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set +# CONFIG_RING_BUFFER_STARTUP_TEST is not set +# CONFIG_RBTREE_TEST is not set +# CONFIG_INTERVAL_TREE_TEST is not set +CONFIG_DYNAMIC_DEBUG=y +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_STRING_HELPERS is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +# CONFIG_ARM_UNWIND is not set +CONFIG_OLD_MCOUNT=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_RODATA is not set +CONFIG_DEBUG_LL=y +CONFIG_DEBUG_OMAP2PLUS_UART=y +# CONFIG_DEBUG_ICEDCC is not set +# CONFIG_DEBUG_SEMIHOSTING is not set +# CONFIG_DEBUG_OMAP2UART1 is not set +# CONFIG_DEBUG_OMAP2UART2 is not set +# CONFIG_DEBUG_OMAP2UART3 is not set +CONFIG_DEBUG_OMAP3UART3=y +# CONFIG_DEBUG_OMAP4UART3 is not set +# CONFIG_DEBUG_OMAP3UART4 is not set +# CONFIG_DEBUG_OMAP4UART4 is not set +# CONFIG_DEBUG_TI81XXUART1 is not set +# CONFIG_DEBUG_TI81XXUART2 is not set +# CONFIG_DEBUG_TI81XXUART3 is not set +# CONFIG_DEBUG_AM33XXUART1 is not set +# CONFIG_DEBUG_ZOOM_UART is not set +CONFIG_DEBUG_LL_INCLUDE="debug/omap2plus.S" +CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" +CONFIG_EARLY_PRINTK=y +CONFIG_EARLY_PRINTK_DIRECT=y +# CONFIG_PID_IN_CONTEXTIDR is not set + +# +# Security options +# +CONFIG_KEYS=y +# CONFIG_ENCRYPTED_KEYS is not set +# CONFIG_KEYS_DEBUG_PROC_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +CONFIG_SECURITY=y +# CONFIG_SECURITYFS is not set +CONFIG_SECURITY_NETWORK=y +# CONFIG_SECURITY_NETWORK_XFRM is not set +# CONFIG_SECURITY_PATH is not set +CONFIG_LSM_MMAP_MIN_ADDR=32768 +CONFIG_SECURITY_SELINUX=y +# CONFIG_SECURITY_SELINUX_BOOTPARAM is not set +# CONFIG_SECURITY_SELINUX_DISABLE is not set +# CONFIG_SECURITY_SELINUX_DEVELOP is not set +# CONFIG_SECURITY_SELINUX_AVC_STATS is not set +CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 +# CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set +# CONFIG_SECURITY_SMACK is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_SECURITY_APPARMOR is not set +# CONFIG_SECURITY_YAMA is not set +# CONFIG_IMA is not set +# CONFIG_EVM is not set +CONFIG_DEFAULT_SECURITY_SELINUX=y +# CONFIG_DEFAULT_SECURITY_DAC is not set +CONFIG_DEFAULT_SECURITY="selinux" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_USER is not set +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_CMAC is not set +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_CRC32 is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +# CONFIG_CRYPTO_SHA1_ARM is not set +CONFIG_CRYPTO_SHA256=y +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_AES_ARM is not set +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +CONFIG_CRYPTO_LZO=y + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +# CONFIG_CRYPTO_DEV_OMAP_SHAM is not set +# CONFIG_CRYPTO_DEV_OMAP_AES is not set +# CONFIG_ASYMMETRIC_KEY_TYPE is not set +CONFIG_BINARY_PRINTF=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +# CONFIG_CRC8 is not set +CONFIG_AUDIT_GENERIC=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_XZ_DEC=y +CONFIG_XZ_DEC_X86=y +CONFIG_XZ_DEC_POWERPC=y +CONFIG_XZ_DEC_IA64=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_SPARC=y +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_BCH=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set +# CONFIG_DDR is not set +# CONFIG_VIRTUALIZATION is not set diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index 0168b07ce0c..c3fdbc0f08a 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -207,6 +207,34 @@ config MACH_OMAP3_BEAGLE  	depends on ARCH_OMAP3  	default y  	select OMAP_PACKAGE_CBB +	 +config MACH_OMAP3_H1 +	bool "OMAP3 H1 board" +	depends on ARCH_OMAP3 +	default y +	select OMAP_PACKAGE_CBP + +choice +       prompt "Olio H1B build version" +       default MACH_OMAP3_H1_EVT2 + +config MACH_OMAP3_H1_EVT1 +       bool "EVT1 Build of the H1 board" +       depends on MACH_OMAP3_H1 + +config MACH_OMAP3_H1_EVT2 +       bool "EVT2 Build of the H1 board" +       depends on MACH_OMAP3_H1 + +config MACH_OMAP3_H1_DVT1 +       bool "DVT1 Build of the H1 board" +       depends on MACH_OMAP3_H1 +        +config MACH_OMAP3_H1_DVT2 +	   bool "DVT2 Build of the H1 board" +	   depends on MACH_OMAP3_H1 + +endchoice  config MACH_DEVKIT8000  	bool "DEVKIT8000 board" diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index ff5c4457325..9245b2356df 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -225,6 +225,8 @@ obj-$(CONFIG_MACH_OMAP_GENERIC)		+= board-generic.o  obj-$(CONFIG_MACH_OMAP_H4)		+= board-h4.o  obj-$(CONFIG_MACH_OMAP_2430SDP)		+= board-2430sdp.o  obj-$(CONFIG_MACH_OMAP3_BEAGLE)		+= board-omap3beagle.o +obj-$(CONFIG_MACH_OMAP3_H1)		+= board-omap3h1.o +obj-$(CONFIG_MACH_OMAP3_H1)		+= board-omap3h1-bluetooth.o  obj-$(CONFIG_MACH_DEVKIT8000)     	+= board-devkit8000.o  obj-$(CONFIG_MACH_OMAP_LDP)		+= board-ldp.o  obj-$(CONFIG_MACH_OMAP3530_LV_SOM)      += board-omap3logic.o diff --git a/arch/arm/mach-omap2/board-flash.c b/arch/arm/mach-omap2/board-flash.c index c33adea0247..bb25fff9ee6 100644 --- a/arch/arm/mach-omap2/board-flash.c +++ b/arch/arm/mach-omap2/board-flash.c @@ -139,7 +139,7 @@ __init board_nand_init(struct mtd_partition *nand_parts, u8 nr_parts, u8 cs,  	board_nand_data.nr_parts	= nr_parts;  	board_nand_data.devsize		= nand_type; -	board_nand_data.ecc_opt = OMAP_ECC_HAMMING_CODE_DEFAULT; +	board_nand_data.ecc_opt = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;  	gpmc_nand_init(&board_nand_data, gpmc_t);  }  #endif /* CONFIG_MTD_NAND_OMAP2 || CONFIG_MTD_NAND_OMAP2_MODULE */ diff --git a/arch/arm/mach-omap2/board-omap3h1-bluetooth.c b/arch/arm/mach-omap2/board-omap3h1-bluetooth.c new file mode 100644 index 00000000000..52bf419b549 --- /dev/null +++ b/arch/arm/mach-omap2/board-omap3h1-bluetooth.c @@ -0,0 +1,387 @@ +/* + * Bluetooth Broadcomm  and low power control via GPIO + * + *  Copyright (C) 2011 Google, 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ +  + /* +  * Adapted from board-tuna-bluetooth.c by Evan Wilson <evan@oliodevices.com +  * +  */ +  + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/gpio.h> +#include <linux/hrtimer.h> +#include <linux/irq.h> +#include <linux/rfkill.h> +#include <linux/platform_device.h> +#include <linux/wakelock.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <asm/mach-types.h> +#include "serial.h" +#include "board-omap3h1.h" +#include <linux/regulator/driver.h> + +static void update_host_wake_locked(int); + +#define BT_REG_GPIO 180 + +#define BT_WAKE_GPIO 93 +#define BT_HOST_WAKE_GPIO 11 + +static struct rfkill *bt_rfkill; +static struct regulator *clk32ksys_reg; +static bool bt_enabled; +static bool host_wake_uart_enabled; +static bool wake_uart_enabled; +static struct dentry *btdebugdent; + +struct bcm_bt_lpm { +	int wake; +	int host_wake; + +	struct hrtimer enter_lpm_timer; +	ktime_t enter_lpm_delay; + +	struct uart_port *uport; + +	struct wake_lock wake_lock; +	char wake_lock_name[100]; +} bt_lpm; + +static int bcm20702_bt_rfkill_set_power(void *data, bool blocked) +{ +	// rfkill_ops callback. Turn transmitter on when blocked is false +	if (!blocked) { +		if (clk32ksys_reg && !bt_enabled) +			regulator_enable(clk32ksys_reg); + +		gpio_set_value(BT_REG_GPIO, 1); + +	} else { +		// Chip won't toggle host_wake after reset.  Make sure +		// we don't hold the wake_lock until chip wakes up again. +		update_host_wake_locked(0); +		 +		gpio_set_value(BT_REG_GPIO, 0); +		if (clk32ksys_reg && bt_enabled) +			regulator_disable(clk32ksys_reg); +	} + +	bt_enabled = !blocked; + +	return 0; +} + +static const struct rfkill_ops bcm20702_bt_rfkill_ops = { +	.set_block = bcm20702_bt_rfkill_set_power, +}; + +static void set_wake_locked(int wake) +{ +	bt_lpm.wake = wake; + +	if (!wake) +		wake_unlock(&bt_lpm.wake_lock); + +	if (!wake_uart_enabled && wake) +		//omap_uart_enable(2); + +	gpio_set_value(BT_WAKE_GPIO, wake); + +	if (wake_uart_enabled && !wake) +		//omap_uart_disable(2); + +	wake_uart_enabled = wake; +} + +static enum hrtimer_restart enter_lpm(struct hrtimer *timer) { +	unsigned long flags; +	spin_lock_irqsave(&bt_lpm.uport->lock, flags); +	set_wake_locked(0); +	spin_unlock_irqrestore(&bt_lpm.uport->lock, flags); + +	return HRTIMER_NORESTART; +} + +void bcm_bt_lpm_exit_lpm_locked(struct uart_port *uport) { +	bt_lpm.uport = uport; + +	hrtimer_try_to_cancel(&bt_lpm.enter_lpm_timer); + +	set_wake_locked(1); + +	hrtimer_start(&bt_lpm.enter_lpm_timer, bt_lpm.enter_lpm_delay, +		HRTIMER_MODE_REL); +} +EXPORT_SYMBOL(bcm_bt_lpm_exit_lpm_locked); + +static void update_host_wake_locked(int host_wake) +{ +	if (host_wake == bt_lpm.host_wake) +		return; + +	bt_lpm.host_wake = host_wake; + +	if (host_wake) { +		wake_lock(&bt_lpm.wake_lock); +		if (!host_wake_uart_enabled) { +			//omap_uart_enable(2); +		} +	} else  { +		if (host_wake_uart_enabled) { +			//omap_uart_disable(2); +		} +		// Take a timed wakelock, so that upper layers can take it. +		// The chipset deasserts the hostwake lock, when there is no +		// more data to send. +		wake_lock_timeout(&bt_lpm.wake_lock, HZ/2); +	} + +	host_wake_uart_enabled = host_wake; + +} + +static irqreturn_t host_wake_isr(int irq, void *dev) +{ +	int host_wake; +	unsigned long flags; + +	host_wake = gpio_get_value(BT_HOST_WAKE_GPIO); +	irq_set_irq_type(irq, host_wake ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + +	if (!bt_lpm.uport) { +		bt_lpm.host_wake = host_wake; +		return IRQ_HANDLED; +	} + +	spin_lock_irqsave(&bt_lpm.uport->lock, flags); +	update_host_wake_locked(host_wake); +	spin_unlock_irqrestore(&bt_lpm.uport->lock, flags); + +	return IRQ_HANDLED; +} + +static int bcm_bt_lpm_init(struct platform_device *pdev) +{ +	int irq; +	int ret; +	int rc; + +	rc = gpio_request(BT_WAKE_GPIO, "bcm20702_wake_gpio"); +	if (unlikely(rc)) { +		return rc; +	} + +	rc = gpio_request(BT_HOST_WAKE_GPIO, "bcm20702_host_wake_gpio"); +	if (unlikely(rc)) { +		gpio_free(BT_WAKE_GPIO); +		return rc; +	} + +	hrtimer_init(&bt_lpm.enter_lpm_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); +	bt_lpm.enter_lpm_delay = ktime_set(1, 0);  /* 1 sec */ +	bt_lpm.enter_lpm_timer.function = enter_lpm; + +	bt_lpm.host_wake = 0; + +	irq = gpio_to_irq(BT_HOST_WAKE_GPIO); +	ret = request_irq(irq, host_wake_isr, IRQF_TRIGGER_HIGH, +		"bt host_wake", NULL); +	if (ret) { +		gpio_free(BT_WAKE_GPIO); +		gpio_free(BT_HOST_WAKE_GPIO); +		return ret; +	} + +	ret = irq_set_irq_wake(irq, 1); +	if (ret) { +		gpio_free(BT_WAKE_GPIO); +		gpio_free(BT_HOST_WAKE_GPIO); +		return ret; +	} + +	gpio_direction_output(BT_WAKE_GPIO, 0); +	gpio_direction_input(BT_HOST_WAKE_GPIO); + +	snprintf(bt_lpm.wake_lock_name, sizeof(bt_lpm.wake_lock_name), +			"BTLowPower"); +	wake_lock_init(&bt_lpm.wake_lock, WAKE_LOCK_SUSPEND, +			 bt_lpm.wake_lock_name); +	return 0; +} + +static int btdebug_dump(struct seq_file *sf, void *private) +{ +	seq_printf(sf, "en=%d bt_wake=%d lpm.w=%d w_uart_en=%d\n", +		   bt_enabled, gpio_get_value(BT_WAKE_GPIO), +		   bt_lpm.wake, wake_uart_enabled); +	seq_printf(sf, "bt_host_wake=%d lpm.hw=%d hw_uart_en=%d\n", +		   gpio_get_value(BT_HOST_WAKE_GPIO), bt_lpm.host_wake, +		   host_wake_uart_enabled); +	return 0; +} + +static int btdebug_open(struct inode *inode, struct file *file) +{ +	return single_open(file, btdebug_dump, NULL); +} + +static const struct file_operations btdebug_fops = { +	.open = btdebug_open, +	.read = seq_read, +	.llseek = seq_lseek, +	.release = single_release, +}; + +static int bcm20702_bluetooth_probe(struct platform_device *pdev) +{ +	int rc = 0; +	int ret = 0; + +//	rc = gpio_request(BT_RESET_GPIO, "bcm20702_nreset_gpip"); +//	if (unlikely(rc)) { +//		return rc; +//	} + +	rc = gpio_request(BT_REG_GPIO, "bcm20702_nshutdown_gpio"); +	if (unlikely(rc)) { +		//gpio_free(BT_RESET_GPIO); +		return rc; +	} + +	clk32ksys_reg = regulator_get(0, "clk32ksys"); +	if (IS_ERR(clk32ksys_reg)) { +		pr_err("clk32ksys reg not found!\n"); +		clk32ksys_reg = NULL; +	} + +	gpio_direction_output(BT_REG_GPIO, 1); +	//gpio_direction_output(BT_RESET_GPIO, 1); + +	bt_rfkill = rfkill_alloc("bcm20702 Bluetooth", &pdev->dev, +				RFKILL_TYPE_BLUETOOTH, &bcm20702_bt_rfkill_ops, +				NULL); + +	if (unlikely(!bt_rfkill)) { +		//gpio_free(BT_RESET_GPIO); +		gpio_free(BT_REG_GPIO); +		return -ENOMEM; +	} + +	rfkill_set_states(bt_rfkill, true, false); +	rc = rfkill_register(bt_rfkill); + +	if (unlikely(rc)) { +		rfkill_destroy(bt_rfkill); +		//gpio_free(BT_RESET_GPIO); +		gpio_free(BT_REG_GPIO); +		return -1; +	} + +	ret = bcm_bt_lpm_init(pdev); +	if (ret) { +		rfkill_unregister(bt_rfkill); +		rfkill_destroy(bt_rfkill); + +		//gpio_free(BT_RESET_GPIO); +		gpio_free(BT_REG_GPIO); +	} + +	btdebugdent = debugfs_create_file("bt", S_IRUGO, NULL, NULL, +					  &btdebug_fops); +	if (IS_ERR_OR_NULL(btdebugdent)) +		pr_err("%s: failed to create debugfs file\n", __func__); + +	return ret; +} + +static int bcm20702_bluetooth_remove(struct platform_device *pdev) +{ +	rfkill_unregister(bt_rfkill); +	rfkill_destroy(bt_rfkill); + +	if (!IS_ERR_OR_NULL(btdebugdent)) +		debugfs_remove(btdebugdent); + +	gpio_free(BT_REG_GPIO); +	//gpio_free(BT_RESET_GPIO); +	gpio_free(BT_WAKE_GPIO); +	gpio_free(BT_HOST_WAKE_GPIO); +	regulator_put(clk32ksys_reg); + +	wake_lock_destroy(&bt_lpm.wake_lock); +	return 0; +} + +int bcm4430_bluetooth_suspend(struct platform_device *pdev, pm_message_t state) +{ +	int irq = gpio_to_irq(BT_HOST_WAKE_GPIO); +	int host_wake; + +	disable_irq(irq); +	host_wake = gpio_get_value(BT_HOST_WAKE_GPIO); + +	if (host_wake) { +		enable_irq(irq); +		return -EBUSY; +	} + +	return 0; +} + +int bcm4430_bluetooth_resume(struct platform_device *pdev) +{ +	int irq = gpio_to_irq(BT_HOST_WAKE_GPIO); +	enable_irq(irq); +	return 0; +} + +static struct platform_driver bcm20702_bluetooth_platform_driver = { +	.probe = bcm20702_bluetooth_probe, +	.remove = bcm20702_bluetooth_remove, +	.suspend = bcm4430_bluetooth_suspend, +	.resume = bcm4430_bluetooth_resume, +	.driver = { +		   .name = "bcm20702_bluetooth", +		   .owner = THIS_MODULE, +		   }, +}; + +static int __init bcm20702_bluetooth_init(void) +{ +	bt_enabled = false; +	return platform_driver_register(&bcm20702_bluetooth_platform_driver); +} + +static void __exit bcm20702_bluetooth_exit(void) +{ +	platform_driver_unregister(&bcm20702_bluetooth_platform_driver); +} + + +module_init(bcm20702_bluetooth_init); +module_exit(bcm20702_bluetooth_exit); + +MODULE_ALIAS("platform:bcm20702"); +MODULE_DESCRIPTION("bcm20702_bluetooth"); +MODULE_AUTHOR("Jaikumar Ganesh <jaikumar@google.com>"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-omap2/board-omap3h1.c b/arch/arm/mach-omap2/board-omap3h1.c new file mode 100644 index 00000000000..61c01c90a8f --- /dev/null +++ b/arch/arm/mach-omap2/board-omap3h1.c @@ -0,0 +1,1267 @@ +/* + * Copyright (C) 2014 Olio Devices, Inc. + * Authors: Evan Wilson <evan@oliodevices.com> + *			Mattis Fjallstrom <mattis@oliodevices.com> + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * + * Modified from the original mach-omap/omap2/board-generic.c did by Paul + * to support the OMAP2+ device tree boards with an unique board file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/opp.h> +#include <linux/cpu.h> +#include <linux/mpu.h> +#include <linux/spi/spi.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/nand.h> + +#include <linux/usb/musb.h> +#include <linux/usb/phy.h> +#include <linux/usb/nop-usb-xceiv.h> + +#include <linux/i2c/atmel_mxt_ts.h> +#include <linux/gpio.h> + +#include <linux/regulator/machine.h> +#include <linux/regulator/fixed.h> +#include <linux/mfd/tps65910.h> + +#include <linux/wakelock.h> /* used in interrupt, waking up.  */ +#include <linux/debugfs.h> + +#include <linux/led-lm3530.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/flash.h> + +#include <video/omapdss.h> +#include <video/omap-panel-data.h> +#include <linux/platform_data/mtd-nand-omap2.h> +#include <linux/platform_data/serial-omap.h> +#include <linux/interrupt.h> + +#include <linux/of_irq.h> +#include <linux/of_platform.h> + + +#include "common.h" +#include "omap_device.h" +#include "gpmc.h" +#include "soc.h" +#include "mux.h" +#include "pm.h" +#include "board-flash.h" +#include "common-board-devices.h" +#include "board-omap3h1.h" + +#include "sdram-micron-mt29c4g48.h" + +#define NAND_CS 0 + +#define MPUIRQ_GPIO 31 +#define ATMEL_MXT_GPIO 105 +#define TPS_SYS_NIRQ 0 +#define USB_IRQ 124 +#define USB_ON_ETK_D7 21 + +#define DEFAULT_RXDMA_POLLRATE		1	/* RX DMA polling rate (us) */ +#define DEFAULT_RXDMA_BUFSIZE		4096	/* RX DMA buffer size */ +#define DEFAULT_RXDMA_TIMEOUT		(3 * HZ)/* RX DMA timeout (jiffies) */ + +#if defined(CONFIG_MACH_OMAP3_H1_DVT1) || defined(CONFIG_MACH_OMAP3_H1_DVT2) +#define LCD_RESET_GPIO 94 +#else  +#define LCD_RESET_GPIO 122 +#endif + +static struct accelerometer_wake { +  int awake; +  struct wake_lock lock; +  char lock_name[100]; +  int user_pid; +} acc_wake; + +static struct mtd_partition omap3h1_nand_partitions[] = { +	/* All the partition sizes are listed in terms of NAND block size */ +	{ +		.name		= "X-Loader", +		.offset		= 0, +		.size		= 4 * NAND_BLOCK_SIZE, +		.mask_flags	= MTD_WRITEABLE,	/* force read-only */ +	}, +	{ +		.name		= "U-Boot", +		.offset		= MTDPART_OFS_APPEND,	/* Offset = 0x80000 */ +		.size		= 15 * NAND_BLOCK_SIZE, +		.mask_flags	= MTD_WRITEABLE,	/* force read-only */ +	}, +	{ +		.name		= "U-Boot Env", +		.offset		= MTDPART_OFS_APPEND,	/* Offset = 0x260000 */ +		.size		= 1 * NAND_BLOCK_SIZE, +	}, +	{ +		.name		= "kernel", +		.offset		= MTDPART_OFS_APPEND,	/* Offset = 0x280000 */ +		.size		= 40 * NAND_BLOCK_SIZE, +	}, +	{ +		.name		= "initramfs", +		.offset		= MTDPART_OFS_APPEND, 	/* Offset = 0xC80000 */ +		.size		= 80 * NAND_BLOCK_SIZE, +	}, +	{ +		.name		= "ramdisk", +		.offset		= MTDPART_OFS_APPEND,	/* Offset = 0x1180000 */ +		.size		= 40 * NAND_BLOCK_SIZE, +	}, +	{ +		.name		= "system", +		.offset		= MTDPART_OFS_APPEND,	/* Offset = 0x1680000 */ +		.size		= 2000 * NAND_BLOCK_SIZE, +	}, +	{ +		.name		= "userdata", +		.offset		= MTDPART_OFS_APPEND,	/* Offset = 0x11180000 */ +		.size		= MTDPART_SIZ_FULL, +	}, +}; + +static struct omap_dss_device omap3h1_lcd_device = { +	.type				= OMAP_DISPLAY_TYPE_DPI, +	.name				= "olio_h1_panel", +	.driver_name 		= "ili9342_panel", +	.phy.dpi.data_lines = 18, +	.reset_gpio			= LCD_RESET_GPIO, +}; + +static struct omap_dss_device *omap3h1_dss_devices[] = { +		&omap3h1_lcd_device, +}; + +static struct omap_dss_board_info omap3h1_dss_data = { +	.num_devices 	= 1, +	.devices = omap3h1_dss_devices, +	.default_device = &omap3h1_lcd_device, +}; + +static struct spi_board_info omap3h1_spi_board_info[] __initdata = { +	{ +		.modalias		= "ili9342-spi", +		.bus_num		= 1, +		.chip_select 	= 1, +		.max_speed_hz 	= 375000, +		.platform_data	= &omap3h1_lcd_device, +		.mode			= SPI_MODE_0, +	} +}; + +static int __init omap3h1_spi_init(void) { +	spi_register_board_info(omap3h1_spi_board_info, +			ARRAY_SIZE(omap3h1_spi_board_info)); +	return 0; +} + + +/*  + * int_config is interrupt config, INT_ENABLE register. To enable _all_  + * interrupts, set to 0x59. I think. --mfj + *  + * Level shifter seems to set power rail between two options - 1 means VDD. + * (0 means VLogic, which I'm not sure what it is). + */ + +static struct mpu_platform_data mpu_data = { +	.int_config  = 0x00, +	.level_shifter = 1, +	.orientation = {  0, -1,  0, +					 -1,  0,  0, +					  0,  0,  1 }, +}; + +static struct lm3530_platform_data omap3h1_backlight_platform_data = { +	.mode = LM3530_BL_MODE_MANUAL, +	//.als_input_mode = LM3530_INPUT_ALS1, +	.max_current = LM3530_FS_CURR_29mA, +	//.pwm_pol_hi = true, +	//.als_avrg_time = LM3530_ALS_AVRG_TIME_512ms, +	.brt_ramp_law = 0, +	.brt_ramp_fall = LM3530_RAMP_TIME_1s, /* LM3530_RAMP_TIME_1s, */ +	.brt_ramp_rise = LM3530_RAMP_TIME_1s, /* LM3530_RAMP_TIME_1s, */ +	//.als1_resistor_sel = LM3530_ALS_IMPD_13_53kOhm, +	//.als2_resistor_sel = LM3530_ALS_IMPD_Z, +	//.als_vmin = 730,	    /* mV */ +	//.als_vmax = 10a20,	/* mV */ +	.brt_val = 0x64,	/* initial brightness */ +}; + + + +static struct mxt_platform_data mxt_data = { +    .irqflags	= IRQF_TRIGGER_FALLING, +};  + +/* +static struct platform_device bcm20702_bluetooth_device = { +	.name = "bcm20702_bluetooth", +	.id   = -1, + }; +*/ + +static struct platform_device omap3h1_dmic_codec = { +		.name = "dmic-codec", +		.id = -1, +}; + +/* --------------------------------------------------------------------------- */ +/* USB settings  + */ + + + +static struct omap_musb_board_data musb_board_data = { +	.interface_type		= MUSB_INTERFACE_ULPI, +	.mode   			= MUSB_OTG, +	.power	    		= 100, +}; + +static struct nop_usb_xceiv_platform_data nop_plat_data = { +   .type     = USB_PHY_TYPE_USB2, +   .clk_rate = 60000000, /* 60 MHz */ +   .needs_vcc = 1, +   .needs_reset = 0, +}; + +static struct platform_device nop_phy_device = { +  .name = "nop_usb_xceiv", +  .id   = -1, +  .dev  = { +      .platform_data = &nop_plat_data, +  }, +}; + + +/* static struct usbhs_omap_platform_data usbhs_bdata __initdata = { */ +/* 	.port_mode[0] = OMAP_EHCI_PORT_MODE_PHY, */ +/* }; */ + + +/* --------------------------------------------------------------------------- */ +/* REGULATOR - Fixed first + */ + +static struct regulator_init_data omap3h1_vbat_data = { +    .constraints.always_on = true, +    .constraints.valid_modes_mask = REGULATOR_MODE_NORMAL, +    .constraints.valid_ops_mask = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, +}; + +static struct fixed_voltage_config omap3h1_vbat_pdata = { +	.supply_name	= "VBAT", +	.microvolts   	= 3800000, +	.init_data	    = &omap3h1_vbat_data, +	.gpio		    = -EINVAL, +}; + +static struct platform_device omap3h1_vbat = { +	.name	= "reg-fixed-voltage", +	.id		= -1, +	.dev = { +		.platform_data = &omap3h1_vbat_pdata, +	}, +}; + + +/* --------------------------------------------------------------------------- */ + +static struct regulator_init_data tps65910_dummy = { +	.supply_regulator = "VBAT", +    .constraints.always_on  = false, +    .constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS, +}; + +/* --------------------------------------------------------------------------- */ +/* This one is required for the display to work. + */ + +static struct regulator_consumer_supply tps65910_touch3_supply[] = { +    REGULATOR_SUPPLY("avdd", "2-004a"), +    REGULATOR_SUPPLY("vdd", "spi1.1"), +}; + +static struct regulator_init_data tps65910_touch3 = { +	.supply_regulator = "VBAT", +    .constraints = { +        .min_uV            = 3300000, +        .max_uV            = 3300000, +        .valid_modes_mask  = REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY, +        .valid_ops_mask    = REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_MODE, +        .always_on         = false, +        .apply_uV		   = true, + +        .state_mem         = { +            .uV = 0, +            .mode = REGULATOR_MODE_STANDBY, +            .disabled = 1, +        }, +        .state_disk        = { +            .uV = 0, +            .mode = REGULATOR_MODE_STANDBY, +            .disabled = 1, +        }, +        .state_standby     = { +            .uV = 0, +            .mode = REGULATOR_MODE_STANDBY, +            .disabled = 1, +        }, +    }, + +    .num_consumer_supplies = ARRAY_SIZE(tps65910_touch3_supply), +    .consumer_supplies     = tps65910_touch3_supply, +}; + +/* --------------------------------------------------------------------------- */ +/* This regulator is for the vibrator. We currently lack a way of enabling / + * disabling this since we lack a driver for the vibrator (we're just accessing + * it's registers through the i2c bus).  + *  + * Hence, it's always on. + */ + +static struct regulator_consumer_supply tps65910_vibrator_supply[] = { +      REGULATOR_SUPPLY("vaux2", "drv2605"), +}; + +static struct regulator_init_data tps65910_vibrator = { +	.supply_regulator = "VBAT", +    .constraints = { +        .min_uV            = 3300000, +        .max_uV            = 3300000, +        .valid_modes_mask  = REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY, +        .valid_ops_mask    = REGULATOR_CHANGE_MODE, +        .always_on         = false, +        .apply_uV		   = true, + +        .state_mem         = { +            .disabled = 1, +        }, +        .state_disk        = { +            .disabled = 1, +        }, +        .state_standby     = { +            .disabled = 1, +        }, +    }, +    .num_consumer_supplies = ARRAY_SIZE(tps65910_vibrator_supply), +    .consumer_supplies     = tps65910_vibrator_supply, +}; + + + +/* --------------------------------------------------------------------------- */ +/* Seems like this one has to be on. It doesn't have any "official" consumers + * ... but it's definitely needed. Bluetooth relies on it. + */ + +/* +static struct regulator_consumer_supply tps65910_bluetooth_3v3[] = { +      REGULATOR_SUPPLY("clk32ksys", 0), +}; +*/ + +static struct regulator_init_data tps65910_3v3 = { +	.supply_regulator = "VBAT", +    .constraints = { +        .min_uV            = 3300000, +        .max_uV            = 3300000, +        .valid_modes_mask  = REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY, +        .valid_ops_mask    = REGULATOR_CHANGE_MODE    |  +                             REGULATOR_CHANGE_STATUS, +        .always_on         = false, +        .apply_uV		   = true, + +        .state_mem         = { +            .disabled = 1, +        }, +        .state_disk        = { +            .disabled = 1, +        }, +        .state_standby     = { +            .disabled = 1, +        }, +    }, +     +}; + + +/* --------------------------------------------------------------------------- */ +/* This is the OMAP display subsystem. Part of the OMAP processor OCP stuff. + * USB also depends on this, so if you want to transfer files it has to be  + * always on. + * NAND flash and UART3 (which we don't use) requires it  + * as well. And the i2c bus, and lots of other stuff. + *  + * Setting always_on to "false" resulted in almost no power savings, at + * least in the current setting.  + */ + +static struct regulator_consumer_supply tps65910_1v8_supply[] = { +    REGULATOR_SUPPLY("vdds_dsi", "omapdss"), +}; + +static struct regulator_init_data tps65910_1v8 = { +	.supply_regulator = "VBAT", +    .constraints = { +        .min_uV            = 1800000, +        .max_uV            = 1800000, +        .valid_modes_mask  = REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE |  REGULATOR_MODE_STANDBY, +        .valid_ops_mask    = REGULATOR_CHANGE_MODE,  +        .always_on         = false, +        .apply_uV		   = true, +    }, +    .num_consumer_supplies = ARRAY_SIZE(tps65910_1v8_supply), +    .consumer_supplies     = tps65910_1v8_supply, +}; + +/* --------------------------------------------------------------------------- */ +/* There's no driver explicitly using this one, but it goes to VDDA_DPLL_PER  + * on the OMAP chip. So it's used internally by the DPLL devices in PER, I think. + */ + +static struct regulator_consumer_supply tps65910_vpll_supply[] = { + +}; + +static struct regulator_init_data tps65910_vpll = { +	.supply_regulator = "VBAT", +    .constraints = { +        .min_uV            = 1800000, +        .max_uV            = 1800000, +        .valid_modes_mask  = REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY, +        .valid_ops_mask    = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, +        .always_on         = true, +        .apply_uV		   = true, +    }, +    .num_consumer_supplies = ARRAY_SIZE(tps65910_vpll_supply), +    .consumer_supplies     = tps65910_vpll_supply, +}; + + +/* --------------------------------------------------------------------------- */ +/* Touch power supply part 2. + */ + +static struct regulator_consumer_supply tps65910_touch_supply[] = { +    REGULATOR_SUPPLY("vdd", "2-004a" ), +}; + +static struct regulator_init_data tps65910_touch = { +	.supply_regulator = "VBAT", +    .constraints = { +        .min_uV            = 1800000, +        .max_uV            = 1800000, +        .valid_modes_mask  = REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY, +        .valid_ops_mask    = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, +        .always_on         = false, +        .apply_uV		   = true, + +        .state_mem         = { +            .uV = 1, +            .mode = REGULATOR_MODE_STANDBY, +            .disabled = 1, +        }, +        .state_disk        = { +            .uV = 1, +            .mode = REGULATOR_MODE_STANDBY, +            .disabled = 1, +        }, +        .state_standby     = { +            .uV = 1, +            .mode = REGULATOR_MODE_STANDBY, +            .disabled = 1, +        }, + +    }, +    .num_consumer_supplies = ARRAY_SIZE(tps65910_touch_supply), +    .consumer_supplies     = tps65910_touch_supply, +}; + +/* =========================================================================== */ + +/* --------------------------------------------------------------------------- */ + +/* This one only works if 'always_on' is set to true. Do I need to do something + * special otherwise to turn it on? Maybe the driver doesn't enable it. + *  + * The accelerometer driver isn't using regulators properly, hence this needs to + * be always on. + */ + +static struct regulator_consumer_supply tps65910_accel_supply[] = { +     REGULATOR_SUPPLY("vdd", "mpu6515"), +}; + +static struct regulator_init_data tps65910_accel = { +	.supply_regulator = "VBAT", +    .constraints = { +        .min_uV            = 1800000, +        .max_uV            = 1800000, +        .valid_modes_mask  = REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE | REGULATOR_MODE_STANDBY, +        .valid_ops_mask    = REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_STATUS, +        .always_on         = true, +        .apply_uV		   = true, + +        .state_mem         = { +            .uV = 1, +            .mode = REGULATOR_MODE_STANDBY, +            .disabled = 1, +        }, +        .state_disk        = { +            .uV = 1, +            .mode = REGULATOR_MODE_STANDBY, +            .disabled = 1, +        }, +        .state_standby     = { +            .uV = 1, +            .mode = REGULATOR_MODE_STANDBY, +            .disabled = 1, +        }, +    }, +    .num_consumer_supplies = ARRAY_SIZE(tps65910_accel_supply), +    .consumer_supplies     = tps65910_accel_supply, +}; + +/* --------------------------------------------------------------------------- */ +/* Supplies the MPU PD and friends.  + */ + +static struct regulator_consumer_supply tps65910_vdd1_supply[] = { +    REGULATOR_SUPPLY("vcc", "cpu0"), +}; + +static struct regulator_init_data tps65910_vdd1 = { +	.supply_regulator = "VBAT", +    .constraints = { +        .name              = "vdd_mpu_iva", +        .min_uV            = 900000, +        .max_uV            = 1350000, +        .valid_modes_mask  = REGULATOR_MODE_NORMAL, +        .valid_ops_mask    = REGULATOR_CHANGE_VOLTAGE, +        .always_on         = false, + +        .state_mem         = { +            .uV = 1, +            .mode = REGULATOR_MODE_STANDBY, +            .disabled = 1, +        }, +        .state_disk        = { +            .uV = 1, +            .mode = REGULATOR_MODE_STANDBY, +            .disabled = 1, +        }, +        .state_standby     = { +            .uV = 1, +            .mode = REGULATOR_MODE_STANDBY, +            .disabled = 1, +        }, +    }, + +    .num_consumer_supplies = ARRAY_SIZE(tps65910_vdd1_supply), +    .consumer_supplies     = tps65910_vdd1_supply, +}; + +/* --------------------------------------------------------------------------- */ +/* I seem to need always on, but the TWL code gets by without it. So...  + */ + +static struct regulator_consumer_supply tps65910_vdd2_supply[] = { +    REGULATOR_SUPPLY("vdd_core", "l3_main.0"), +}; + +static struct regulator_init_data tps65910_vdd2 = { +	.supply_regulator = "VBAT", +    .constraints = { +        .name              = "vdd_core", +        .min_uV            = 900000, +        .max_uV            = 1200000, +        .valid_modes_mask  = REGULATOR_MODE_NORMAL, +        .valid_ops_mask    = REGULATOR_CHANGE_VOLTAGE, +        .always_on         = false, + +        .state_mem         = { +            .uV = 1, +            .mode = REGULATOR_MODE_STANDBY, +            .disabled = 1, +        }, +        .state_disk        = { +            .uV = 1, +            .mode = REGULATOR_MODE_STANDBY, +            .disabled = 1, +        }, +        .state_standby     = { +            .uV = 1, +            .mode = REGULATOR_MODE_STANDBY, +            .disabled = 1, +        }, +    }, +     +    .num_consumer_supplies = ARRAY_SIZE(tps65910_vdd2_supply), +    .consumer_supplies     = tps65910_vdd2_supply, +}; + +/* --------------------------------------------------------------------------- */ + +/* The clock has to be ON, probably because we're not enabling it properly. */ +/* If I set i2chs_keepon to 6, UBIfs fails to mount (!?). Again, probably + * not enabling it properly.  + *  + * And even with therm set, the screen acts weird (looking really dark during + * boot, touch not working when up).  + */ + +/* +static struct tps65910_sleep_keepon_data olio_slp_keepon = { +    .therm_keepon = 0, +    .clkout32k_keepon = 0,   +    .i2chs_keepon = 1, + };*/ + + + +static struct tps65910_board omap3h1_tps65910_pdata = { +    /* .irq = 7 + OMAP_INTC_START, */ /* Getting this at runtime instead. */ +	.en_ck32k_xtal = true, +    /* .en_dev_slp = true, */ +     +    /* .slp_keepon = &olio_slp_keepon, */ + +    .tps65910_pmic_init_data[TPS65910_REG_VIO]      = &tps65910_1v8, +    .tps65910_pmic_init_data[TPS65910_REG_VDD1]     = &tps65910_vdd1, +    .tps65910_pmic_init_data[TPS65910_REG_VDD2]     = &tps65910_vdd2, +    .tps65910_pmic_init_data[TPS65910_REG_VDIG1]    = &tps65910_accel, +    .tps65910_pmic_init_data[TPS65910_REG_VDIG2]    = &tps65910_touch, +    .tps65910_pmic_init_data[TPS65910_REG_VPLL]     = &tps65910_vpll, + +    .tps65910_pmic_init_data[TPS65910_REG_VMMC]     = &tps65910_touch3, +    .tps65910_pmic_init_data[TPS65910_REG_VAUX2]    = &tps65910_vibrator, +     +    /* This one is connected to BT ... maybe more? */ +     +    .tps65910_pmic_init_data[TPS65910_REG_VAUX33]   = &tps65910_3v3, + +    /* not actually used */ + +    .tps65910_pmic_init_data[TPS65910_REG_VRTC]     = &tps65910_dummy, +    .tps65910_pmic_init_data[TPS65910_REG_VDAC]     = &tps65910_dummy, +    .tps65910_pmic_init_data[TPS65910_REG_VAUX1]    = &tps65910_dummy, /* 2v85 */ +    .tps65910_pmic_init_data[TPS65910_REG_VDD3]     = &tps65910_dummy, +     +    .pm_off = true, +}; + + +/* ====================================================================== */ +/* Voltage domain settings  */ + + +#define OMAP3_SRI2C_SLAVE_ADDR		0x12 +#define OMAP3_VDD_MPU_SR_CONTROL_REG	0x00 +#define OMAP3_VDD_CORE_SR_CONTROL_REG	0x01 +#define OMAP3_VP_CONFIG_ERROROFFSET	0x00 +#define OMAP3_VP_VSTEPMIN_VSTEPMIN	0x1 +#define OMAP3_VP_VSTEPMAX_VSTEPMAX	0x04 +#define OMAP3_VP_VLIMITTO_TIMEOUT_US	200 + + +static unsigned long olio_vsel_to_uv(const u8 vsel) +{ +	return (((vsel * 125) + 6000)) * 100; +} + +static u8 olio_uv_to_vsel(unsigned long uv) +{ +	return DIV_ROUND_UP(uv - 600000, 12500); +} + + +static struct omap_voltdm_pmic omap3_mpu_pmic = { +	.slew_rate		= 12500, +	.step_size		= 12500, +	.vp_erroroffset		= OMAP3_VP_CONFIG_ERROROFFSET, +	.vp_vstepmin		= OMAP3_VP_VSTEPMIN_VSTEPMIN, +	.vp_vstepmax		= OMAP3_VP_VSTEPMAX_VSTEPMAX, +	.vddmin			= 900000, +	.vddmax			= 1350000, +	.vp_timeout_us		= OMAP3_VP_VLIMITTO_TIMEOUT_US, +	.i2c_slave_addr		= OMAP3_SRI2C_SLAVE_ADDR, +	.volt_reg_addr		= OMAP3_VDD_MPU_SR_CONTROL_REG, +	.i2c_high_speed		= true, +	.vsel_to_uv		= olio_vsel_to_uv, +	.uv_to_vsel		= olio_uv_to_vsel, +}; + +static struct omap_voltdm_pmic omap3_core_pmic = { +	.slew_rate		= 12500, +	.step_size		= 12500, +	.vp_erroroffset		= OMAP3_VP_CONFIG_ERROROFFSET, +	.vp_vstepmin		= OMAP3_VP_VSTEPMIN_VSTEPMIN, +	.vp_vstepmax		= OMAP3_VP_VSTEPMAX_VSTEPMAX, +	.vddmin			= 900000, +	.vddmax			= 1200000, +	.vp_timeout_us		= OMAP3_VP_VLIMITTO_TIMEOUT_US, +	.i2c_slave_addr		= OMAP3_SRI2C_SLAVE_ADDR, +	.volt_reg_addr		= OMAP3_VDD_CORE_SR_CONTROL_REG, +	.i2c_high_speed		= true, +	.vsel_to_uv		= olio_vsel_to_uv, +	.uv_to_vsel		= olio_uv_to_vsel, +}; + +int __init omap3_voltdm_init(void) +{ +	struct voltagedomain *voltdm; + +	if (!cpu_is_omap34xx()) +		return -ENODEV; + +	voltdm = voltdm_lookup("mpu_iva"); +	omap_voltage_register_pmic(voltdm, &omap3_mpu_pmic); + +	voltdm = voltdm_lookup("core"); +	omap_voltage_register_pmic(voltdm, &omap3_core_pmic); + +	return 0; +} + + + +/* ====================================================================== */ + + +static struct i2c_board_info __initdata omap3h1_i2c1_board_info[] = { +         { +            I2C_BOARD_INFO("tps65910", 0x2d), +            .platform_data = &omap3h1_tps65910_pdata, +         }, +#ifdef CONFIG_MACH_OMAP3_H1_DVT2 +}; +static struct i2c_board_info __initdata omap3h1_i2c2_board_info[] = { +#endif +    { +        I2C_BOARD_INFO("mpu6515", 0x68), +        // This is needed for the interrupt wake. IH_GPIO_BASE changed in 3.10  +        // .irq = (IH_GPIO_BASE + MPUIRQ_GPIO), +        .platform_data = &mpu_data, +    }, +    {  +        /* Backlight */ +        I2C_BOARD_INFO("lm3530-led", 0x38), +        .platform_data = &omap3h1_backlight_platform_data, +    }, +    { +        I2C_BOARD_INFO("mXT224", 0x4a), +        .platform_data = &mxt_data, +    }, +    { +        I2C_BOARD_INFO("cm3391", 0x10), +    }, +#ifdef CONFIG_MACH_OMAP3_H1_EVT1 +}; +static struct i2c_board_info __initdata omap3h1_i2c3_board_info[] = { +#endif +    { +        I2C_BOARD_INFO("bq274xx", 0x55), +    }, +}; + +#ifdef ONLY_KEEPING_THIS_STUFF_FOR_NOSTALGIC_REASONS + +/* --------------------------------------------------------------------------- */ +/* Create debugfs entry  + *  + * We need to have this so that our wake-up thread can tell us it's PID.  + * That PID will then be used when waking the system up. + */ + +struct dentry * pidfile; + +#define SIG_OLIO_WAKE 44	// we choose 44 as our signal number (real-time signals are in the range of 33 to 64) + +static ssize_t write_pid(struct file *file,  +                         const char __user *buf, +                         size_t count,  +                         loff_t *ppos) +{ +	char mybuf[10]; +	int pid = 0; + +	/* read the value from user space */ + +	if(count > 10) +      return -EINVAL; +	if (copy_from_user(mybuf, buf, count) != 0) { +      /* nothing read?? */ +      printk ("OLIO: write_pid: Error??\n"); +      return 0; +    } + +	sscanf(mybuf, "%d", &pid); +	 +    printk("pid read = %d\n", pid); +     +    acc_wake.user_pid = pid; + +	return count; +} + +static ssize_t send_wake_signal (void) { +    int ret; +	struct siginfo info; +	struct task_struct *t; + +	/* send the signal */ + +    printk ("Now sending signal!\n"); + +	memset(&info, 0, sizeof(struct siginfo)); + +    /* this is bit of a trickery: SI_QUEUE is normally used by sigqueue from user space, +     * and kernel space should use SI_KERNEL. But if SI_KERNEL is used the real_time data  +     * is not delivered to the user space signal handler function.  +     */ + +	info.si_signo = SIG_OLIO_WAKE; +	info.si_code  = SI_QUEUE; + +  	/* real time signals may have 32 bits of data. */ + +	info.si_int = 1234; + +	rcu_read_lock(); + +	t = pid_task(find_pid_ns(acc_wake.user_pid, &init_pid_ns), PIDTYPE_PID);	 + +	if(t == NULL){ +        rcu_read_unlock(); +		printk("no such pid\n"); +		return -ENODEV; +	} + +    rcu_read_unlock(); + +    /* send the signal */ + +	ret = send_sig_info(SIG_OLIO_WAKE, &info, t); + +	if (ret < 0) { +		printk("error sending signal\n"); +	} +    return ret; +} + +static const struct file_operations h1_fops = { +    .write = write_pid, +}; + + +/*************************************************************************** + * acc_irq_handler - handle interrupt from accelerometer + *  + * This routine gets called when an interrupt from the accelerometer  + * happens. Time out is in milliseconds. + *  + * TODO: BREAK this all out, place it in it's own kernel driver together + * with the configuration stuff for the accelerometer (the low power sleep  + * mode stuff). + */ + +#define OLIO_DOUBLE_TAP 500 +#define OLIO_ONE_SECOND 1000 +#define OLIO_DT_WAIT 3000 + +static unsigned long previous; +static unsigned long previous_double; + +static irqreturn_t acc_irq_handler_thr(int irq, void * omap3h1_d) { +  /* struct platform_device** omap3h1_devs = (struct platform_device **) omap3h1_d; */ + +  int ret = 99; +  unsigned long now; + +  printk (KERN_DEBUG "acc_irq_handler_thr: Entered!\n"); + +  now = jiffies; + +  if (jiffies_to_msecs(now - previous) < OLIO_DOUBLE_TAP) { + +    /* ignore double clicks that happens too soon from the previous one */ +    if (jiffies_to_msecs (now - previous_double) < OLIO_DT_WAIT) +    { +      previous = 0; +      return IRQ_HANDLED; +    } + +    printk (KERN_DEBUG "Double tap detected!\n"); + +    /* We want to wake up. Take wake lock (and send signal to user space?) +     * BT driver only takes wake lock, then lets higher levels handle  +     * everything ... but what if there's nothing there to take the lock? +     */ + +    send_wake_signal();     +    wake_lock_timeout (&acc_wake.lock, msecs_to_jiffies (20 * OLIO_ONE_SECOND)); +     +    previous_double = now; +    previous = 0; +  } else { +    previous = now; +  } + +  printk (KERN_DEBUG "acc_irq_handler_thr: Leaving! (ret = %d)\n", ret); + +  return IRQ_HANDLED; +} + +static irqreturn_t acc_irq_handler(int irq, void * omap3h1_d) { +  printk (KERN_DEBUG "acc_irq_handler: Entered!\n"); +   +  /* nothing to do, other than wake the main thread */ +   +  printk (KERN_DEBUG "acc_irq_handler: Leaving!\n"); +  return IRQ_WAKE_THREAD; +} + +static int __init omap3_acc_irq_init (int irq, void * dev_id) { +    irq_set_irq_wake(irq, 1); + +    if (request_threaded_irq (irq, acc_irq_handler,  +                              acc_irq_handler_thr,  +                              IRQF_SHARED | IRQF_TRIGGER_RISING,  +                              "acc_wake",  +                              dev_id)) { +      printk (KERN_DEBUG "Couldn't register handler for accelerometer IRQ\n"); +    } + +    /* Should check the return value ... but if it fails, what do we do? */ +     +    return 0; +} + +static int __init omap3_wake_init (void) { +  pidfile = debugfs_create_file("wake_signal_pid", 0200, NULL, NULL, &h1_fops); +  acc_wake.user_pid = 0; /* No user thread yet */ + +  snprintf(acc_wake.lock_name, sizeof(acc_wake.lock_name), +           "Accelerometer"); +  wake_lock_init(&acc_wake.lock, WAKE_LOCK_SUSPEND, +                 acc_wake.lock_name); +  return 0; +} + + +#endif /* ONLY_KEEPING_THIS_STUFF_FOR_NOSTALGIC_REASONS */ + + +/*************************************************************************** + * omap_uart data  + */ + +/* Some notes: +		.dma_enabled	= false, +		.dma_rx_buf_size = DEFAULT_RXDMA_BUFSIZE, +		.dma_rx_poll_rate = DEFAULT_RXDMA_POLLRATE, +		.dma_rx_timeout = DEFAULT_RXDMA_TIMEOUT, +		.autosuspend_timeout = DEFAULT_AUTOSUSPEND_DELAY, ( -1 or X ms. ) +         +        .wakelock_timeout - for minnow, this is 150 (BT) or 50 (M4 debug). +              Can be left out. + +        .DTR_* -> can be left out. I can't find anyone using it. + +        int			    DTR_gpio; +        int			    DTR_inverted; +        int			    DTR_present; +        bool			wakeup_capable; +         +        bool			open_close_pm; - Minnow uses this for c55 only +        unsigned int	rx_trig;  Minnow, BT & debug only +*/ + +static struct omap_uart_port_info omap_uart_ports[] = { +    { +        .dma_enabled = false,	/* To specify DMA Mode */ +        .dma_rx_buf_size  = DEFAULT_RXDMA_BUFSIZE, +        .dma_rx_timeout   = DEFAULT_RXDMA_TIMEOUT, +        .dma_rx_poll_rate = DEFAULT_RXDMA_POLLRATE, +        .autosuspend_timeout = 5000, +        .wakeup_capable = false, +    }, +    { +        .dma_enabled = false,	/* To specify DMA Mode */ +        .dma_rx_buf_size  = DEFAULT_RXDMA_BUFSIZE, +        .dma_rx_timeout   = DEFAULT_RXDMA_TIMEOUT, +        .dma_rx_poll_rate = DEFAULT_RXDMA_POLLRATE, +        .autosuspend_timeout = 5000, +        .wakeup_capable = false, +    }, +    { +        .dma_enabled = false,	/* To specify DMA Mode */ +        .dma_rx_buf_size  = DEFAULT_RXDMA_BUFSIZE, +        .dma_rx_timeout   = DEFAULT_RXDMA_TIMEOUT, +        .dma_rx_poll_rate = DEFAULT_RXDMA_POLLRATE, +        .autosuspend_timeout = -1, +        .wakelock_timeout = -1, +        .wakeup_capable = true, +    }, +    { +        .dma_enabled = false,	/* To specify DMA Mode */ +        .dma_rx_buf_size  = DEFAULT_RXDMA_BUFSIZE, +        .dma_rx_timeout   = DEFAULT_RXDMA_TIMEOUT, +        .dma_rx_poll_rate = DEFAULT_RXDMA_POLLRATE, +        .autosuspend_timeout = 5000, +        .wakeup_capable = false, +    }, +}; + + +/*************************************************************************** + * DEVICE TREE STUFF + *  + * Trying to include some DT stuff with our board file. Not sure how this  + * will work out... + */ + +static struct of_device_id omap_dt_match_table[] __initdata = { +	{ .compatible = "olio,omap3-h1", }, +	{ .compatible = "simple-bus", }, +	{ .compatible = "ti,omap-infra", }, +	{ } +}; + + +/*************************************************************************** + * omap3_h1_i2c_init - init the i2c buses  + * + */ + +static int __init omap3_h1_i2c_init(void) +{ +    int acc_irq; + +    /* In Linux 3.10 we need to request an IRQ through  +     * gpio_to_irq. This means it can't be set at compile time, and +     * must be done at runtime. In other words, here. +     */ +     +    gpio_request_one(ATMEL_MXT_GPIO, GPIOF_IN, "atmel_mxt_ts CHG"); +	gpio_request_one(MPUIRQ_GPIO, GPIOF_IN, "mpu6515 IRQ pin"); + +    acc_irq = gpio_to_irq(MPUIRQ_GPIO); + +#ifdef CONFIG_MACH_OMAP3_H1_DVT2 +	omap3h1_i2c2_board_info[2].irq = gpio_to_irq(ATMEL_MXT_GPIO); +    omap3h1_i2c2_board_info[0].irq = acc_irq; + +    ((struct tps65910_board *) (omap3h1_i2c1_board_info[0].platform_data))->irq =  +     gpio_to_irq (TPS_SYS_NIRQ); +#else +	omap3h1_i2c1_board_info[3].irq = gpio_to_irq(ATMEL_MXT_GPIO); +    omap3h1_i2c1_board_info[1].irq = acc_irq; +#endif + +	gpio_request_one(USB_ON_ETK_D7, GPIOF_OUT_INIT_HIGH, "USB on"); +     +    /* Register buses */ + +	omap_register_i2c_bus(1, 400, omap3h1_i2c1_board_info, ARRAY_SIZE(omap3h1_i2c1_board_info)); +#ifdef CONFIG_MACH_OMAP3_H1_DVT2 +	omap_register_i2c_bus(2, 400, omap3h1_i2c2_board_info, ARRAY_SIZE(omap3h1_i2c2_board_info)); +#else +	omap_register_i2c_bus(2, 400, NULL, 0); +#endif +#ifdef CONFIG_MACH_OMAP3_H1_EVT1 +	omap_register_i2c_bus(3, 400, omap3h1_i2c3_board_info, ARRAY_SIZE(omap3h1_i2c3_board_info)); +#else +	omap_register_i2c_bus(3, 400, NULL, 0); +#endif +	return 0; +} + + +static struct platform_device *omap3h1_devices[] __initdata = { +		&omap3h1_vbat, +        // &bcm20702_bluetooth_device, +        &nop_phy_device, +		&omap3h1_dmic_codec, +}; + +#ifdef CONFIG_OMAP_MUX +static struct omap_board_mux board_mux[] __initdata = { + +#if defined(CONFIG_MACH_OMAP3_H1_DVT1) || defined(CONFIG_MACH_OMAP3_H1_DVT2) +	OMAP3_MUX(CAM_HS, OMAP_MUX_MODE4 | OMAP_PIN_OUTPUT), +#else +	OMAP3_MUX(HSUSB0_DIR, OMAP_MUX_MODE4 | OMAP_PIN_OUTPUT), +#endif +	OMAP3_MUX(DSS_PCLK, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_HSYNC, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_VSYNC, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA0, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA1, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA2, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA3, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA4, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA5, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA6, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA7, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA8, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA9, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA10, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA11, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA12, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA13, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA14, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA15, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA16, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(DSS_DATA17, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), + +	OMAP3_MUX(MCSPI1_CLK, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(MCSPI1_SIMO, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), +	OMAP3_MUX(MCSPI1_SOMI, OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP), +	OMAP3_MUX(MCSPI1_CS1, OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT), + +	/* TOUCH_RESET */ + +	OMAP3_MUX(CAM_D6, OMAP_MUX_MODE4 | OMAP_PIN_INPUT_PULLUP), + +#if defined(CONFIG_MACH_OMAP3_H1_DVT1) || defined(CONFIG_MACH_OMAP3_H1_DVT2) +    /* USB pin settings (mUSB) */ + +    OMAP3_MUX(HSUSB0_CLK,   (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)), +    OMAP3_MUX(HSUSB0_DATA0, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)), +    OMAP3_MUX(HSUSB0_DATA1, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)), +    OMAP3_MUX(HSUSB0_DATA2, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)), +    OMAP3_MUX(HSUSB0_DATA3, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)), +    OMAP3_MUX(HSUSB0_DATA4, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)), +    OMAP3_MUX(HSUSB0_DATA5, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)), +    OMAP3_MUX(HSUSB0_DATA6, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)), +    OMAP3_MUX(HSUSB0_DATA7, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)), +    OMAP3_MUX(HSUSB0_DIR,   (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)), +    OMAP3_MUX(HSUSB0_NXT,   (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)), +    OMAP3_MUX(HSUSB0_STP,   (OMAP_MUX_MODE0 | OMAP_PIN_OUTPUT)), +#endif /* CONFIG_MACH_OMAP3_H1_DVT */ + +    /* accelerometer */ +      +    OMAP3_MUX(JTAG_EMU1, (OMAP_MUX_MODE4 | OMAP_PIN_INPUT  +                          | OMAP_PIN_OFF_WAKEUPENABLE)), +     +    /* USB on */ +     +    OMAP3_MUX(ETK_D7, (OMAP_MUX_MODE4 | OMAP_PIN_OUTPUT)), + +    OMAP3_MUX(SYS_NIRQ, (OMAP_MUX_MODE0 | OMAP_PIN_INPUT)), + +	{ .reg_offset = OMAP_MUX_TERMINATOR }, +}; +#endif + +static void __init omap3_h1_init(void) +{ +    /* Read what we can from the device tree */ + +    of_platform_populate(NULL, omap_dt_match_table, NULL, NULL); + +	board_nand_init(omap3h1_nand_partitions, +                    ARRAY_SIZE(omap3h1_nand_partitions), NAND_CS, +                    NAND_BUSWIDTH_16, NULL); + +	omap_sdrc_init(mt29c4g48_sdrc_params, mt29c4g48_sdrc_params); + +    omap3_mux_init(board_mux, OMAP_PACKAGE_CBP); + +	omap3_h1_i2c_init(); + +    /* Set up the voltage domains */ +    omap3_voltdm_init(); + +	platform_add_devices(omap3h1_devices, ARRAY_SIZE(omap3h1_devices)); + +	omap3h1_spi_init(); + +	omap_serial_board_init(omap_uart_ports); + +    omap_display_init(&omap3h1_dss_data); + +	usb_bind_phy("musb-hdrc.0.auto", 0, "nop_usb_xceiv"); /* "tusb-usb-h1" */ + +	usb_musb_init(&musb_board_data); + +    /* #ifdef ONLY_KEEPING_THIS_STUFF_FOR_NOSTALGIC_REASONS */ + +    /* For handling interrupts from the accelerometer */ + +    /* previous = jiffies; */ +      +    /* Init the code that handles wake ups */ + +    /* +    omap3_wake_init(); + +    omap3_acc_irq_init(omap3h1_i2c2_board_info[0].irq, (void *) &omap3h1_devices); +     */ + +    /* #endif  ONLY_KEEPING_THIS_STUFF_FOR_NOSTALGIC_REASONS*/ + +} + +static const char *omap3_h1_boards_compat[] __initdata = { +	"olio,omap3-h1", +	NULL, +}; + +DT_MACHINE_START(OMAP3_H1, "Olio OMAP3 H1 (Flattened Device Tree)") +    .atag_offset = 0x100, +	.reserve	= omap_reserve, +	.map_io		= omap3_map_io, +	.init_early	= omap3630_init_early, +	/* .init_irq		= omap3_init_irq, */ +    .init_irq	= omap_intc_of_init, +	.handle_irq	= omap3_intc_handle_irq, +	.init_machine	= omap3_h1_init, +	.init_late	= omap3630_init_late, +    .init_time  = omap3_sync32k_timer_init, +	/* .init_time	= omap3_secure_sync32k_timer_init, */ +	.dt_compat	= omap3_h1_boards_compat, +	.restart	= omap3xxx_restart, +MACHINE_END + +#if 0 /* removing ... for now */ +MACHINE_START(OMAP3_H1, "Olio OMAP3 H1 Board") +	.atag_offset	= 0x100, +	.reserve		= omap_reserve, +	.map_io			= omap3_map_io, +	.init_early		= omap3630_init_early, +	.init_irq		= omap3_init_irq, +	.handle_irq		= omap3_intc_handle_irq, +	.init_machine	= omap3_h1_init, +	.init_late		= omap3630_init_late, +    .init_time      = omap3_sync32k_timer_init, +	/* .init_time		= omap3_secure_sync32k_timer_init, */ +	/* .dt_compat		= omap3_h1_boards_compat, */ +	.restart		= omap3xxx_restart, +MACHINE_END +#endif diff --git a/arch/arm/mach-omap2/board-omap3h1.h b/arch/arm/mach-omap2/board-omap3h1.h new file mode 100644 index 00000000000..343eaeaaf86 --- /dev/null +++ b/arch/arm/mach-omap2/board-omap3h1.h @@ -0,0 +1,30 @@ +/* + * Bluetooth Broadcomm  and low power control via GPIO + * + *  Copyright (C) 2011 Samsung, Inc. + *  Copyright (C) 2011 Google, 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +#ifndef __BOARD_OMAP3H1__H__ +#define __BOARD_OMAP3H1__H__ + +#include <linux/serial_core.h> + +extern void bcm_bt_lpm_exit_lpm_locked(struct uart_port *uport); + +#endif /*  __BOARD_OMAP3H1_H__  */ diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c index 9017d5ea9f3..81a30ac94a8 100644 --- a/arch/arm/mach-omap2/devices.c +++ b/arch/arm/mach-omap2/devices.c @@ -66,7 +66,7 @@ static int __init omap3_l3_init(void)  	WARN(IS_ERR(pdev), "could not build omap_device for %s\n", oh_name); -	return IS_ERR(pdev) ? PTR_ERR(pdev) : 0; +	return PTR_RET(pdev);  }  omap_postcore_initcall(omap3_l3_init); @@ -100,7 +100,7 @@ static int __init omap4_l3_init(void)  	WARN(IS_ERR(pdev), "could not build omap_device for %s\n", oh_name); -	return IS_ERR(pdev) ? PTR_ERR(pdev) : 0; +	return PTR_RET(pdev);  }  omap_postcore_initcall(omap4_l3_init); diff --git a/arch/arm/mach-omap2/fb.c b/arch/arm/mach-omap2/fb.c index 190ae493c6e..2ca33cc0c48 100644 --- a/arch/arm/mach-omap2/fb.c +++ b/arch/arm/mach-omap2/fb.c @@ -83,10 +83,7 @@ static int __init omap_init_vrfb(void)  	pdev = platform_device_register_resndata(NULL, "omapvrfb", -1,  			res, num_res, NULL, 0); -	if (IS_ERR(pdev)) -		return PTR_ERR(pdev); -	else -		return 0; +	return PTR_RET(pdev);  }  omap_arch_initcall(omap_init_vrfb); diff --git a/arch/arm/mach-omap2/gpmc-nand.c b/arch/arm/mach-omap2/gpmc-nand.c index d9c27195caf..aeca5507820 100644 --- a/arch/arm/mach-omap2/gpmc-nand.c +++ b/arch/arm/mach-omap2/gpmc-nand.c @@ -140,8 +140,6 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data,  		if (gpmc_nand_data->of_node) {  			gpmc_read_settings_dt(gpmc_nand_data->of_node, &s);  		} else { -			s.device_nand = true; -  			/* Enable RD PIN Monitoring Reg */  			if (gpmc_nand_data->dev_ready) {  				s.wait_on_read = true; @@ -149,6 +147,8 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data,  			}  		} +		s.device_nand = true; +  		if (gpmc_nand_data->devsize == NAND_BUSWIDTH_16)  			s.device_width = GPMC_DEVWIDTH_16BIT;  		else diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index 6c4da1254f5..c877129142b 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c @@ -30,6 +30,7 @@  #include <linux/of_mtd.h>  #include <linux/of_device.h>  #include <linux/mtd/nand.h> +#include <linux/pm_runtime.h>  #include <linux/platform_data/mtd-nand-omap2.h> @@ -148,13 +149,14 @@ struct omap3_gpmc_regs {  static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ];  static struct irq_chip gpmc_irq_chip; -static unsigned gpmc_irq_start; +static int gpmc_irq_start;  static struct resource	gpmc_mem_root;  static struct resource	gpmc_cs_mem[GPMC_CS_NUM];  static DEFINE_SPINLOCK(gpmc_mem_lock);  /* Define chip-selects as reserved by default until probe completes */  static unsigned int gpmc_cs_map = ((1 << GPMC_CS_NUM) - 1); +static unsigned int gpmc_cs_num = GPMC_CS_NUM;  static unsigned int gpmc_nr_waitpins;  static struct device *gpmc_dev;  static int gpmc_irq; @@ -521,8 +523,10 @@ static int gpmc_cs_remap(int cs, u32 base)  	int ret;  	u32 old_base, size; -	if (cs > GPMC_CS_NUM) +	if (cs > gpmc_cs_num) { +		pr_err("%s: requested chip-select is disabled\n", __func__);  		return -ENODEV; +	}  	gpmc_cs_get_memconf(cs, &old_base, &size);  	if (base == old_base)  		return 0; @@ -545,9 +549,10 @@ int gpmc_cs_request(int cs, unsigned long size, unsigned long *base)  	struct resource *res = &gpmc_cs_mem[cs];  	int r = -1; -	if (cs > GPMC_CS_NUM) +	if (cs > gpmc_cs_num) { +		pr_err("%s: requested chip-select is disabled\n", __func__);  		return -ENODEV; - +	}  	size = gpmc_mem_align(size);  	if (size > (1 << GPMC_SECTION_SHIFT))  		return -ENOMEM; @@ -582,7 +587,7 @@ EXPORT_SYMBOL(gpmc_cs_request);  void gpmc_cs_free(int cs)  {  	spin_lock(&gpmc_mem_lock); -	if (cs >= GPMC_CS_NUM || cs < 0 || !gpmc_cs_reserved(cs)) { +	if (cs >= gpmc_cs_num || cs < 0 || !gpmc_cs_reserved(cs)) {  		printk(KERN_ERR "Trying to free non-reserved GPMC CS%d\n", cs);  		BUG();  		spin_unlock(&gpmc_mem_lock); @@ -777,7 +782,7 @@ static void gpmc_mem_exit(void)  {  	int cs; -	for (cs = 0; cs < GPMC_CS_NUM; cs++) { +	for (cs = 0; cs < gpmc_cs_num; cs++) {  		if (!gpmc_cs_mem_enabled(cs))  			continue;  		gpmc_cs_delete_mem(cs); @@ -798,7 +803,7 @@ static void gpmc_mem_init(void)  	gpmc_mem_root.end = GPMC_MEM_END;  	/* Reserve all regions that has been set up by bootloader */ -	for (cs = 0; cs < GPMC_CS_NUM; cs++) { +	for (cs = 0; cs < gpmc_cs_num; cs++) {  		u32 base, size;  		if (!gpmc_cs_mem_enabled(cs)) @@ -1245,7 +1250,6 @@ void gpmc_read_settings_dt(struct device_node *np, struct gpmc_settings *p)  	p->sync_read = of_property_read_bool(np, "gpmc,sync-read");  	p->sync_write = of_property_read_bool(np, "gpmc,sync-write"); -	p->device_nand = of_property_read_bool(np, "gpmc,device-nand");  	of_property_read_u32(np, "gpmc,device-width", &p->device_width);  	of_property_read_u32(np, "gpmc,mux-add-data", &p->mux_add_data); @@ -1337,12 +1341,11 @@ static void __maybe_unused gpmc_read_timings_dt(struct device_node *np,  #ifdef CONFIG_MTD_NAND -static const char * const nand_ecc_opts[] = { -	[OMAP_ECC_HAMMING_CODE_DEFAULT]		= "sw", -	[OMAP_ECC_HAMMING_CODE_HW]		= "hw", -	[OMAP_ECC_HAMMING_CODE_HW_ROMCODE]	= "hw-romcode", -	[OMAP_ECC_BCH4_CODE_HW]			= "bch4", -	[OMAP_ECC_BCH8_CODE_HW]			= "bch8", +static const char * const nand_xfer_types[] = { +	[NAND_OMAP_PREFETCH_POLLED]		= "prefetch-polled", +	[NAND_OMAP_POLLED]			= "polled", +	[NAND_OMAP_PREFETCH_DMA]		= "prefetch-dma", +	[NAND_OMAP_PREFETCH_IRQ]		= "prefetch-irq",  };  static int gpmc_probe_nand_child(struct platform_device *pdev, @@ -1367,10 +1370,45 @@ static int gpmc_probe_nand_child(struct platform_device *pdev,  	gpmc_nand_data->cs = val;  	gpmc_nand_data->of_node = child; -	if (!of_property_read_string(child, "ti,nand-ecc-opt", &s)) -		for (val = 0; val < ARRAY_SIZE(nand_ecc_opts); val++) -			if (!strcasecmp(s, nand_ecc_opts[val])) { -				gpmc_nand_data->ecc_opt = val; +	/* Detect availability of ELM module */ +	gpmc_nand_data->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0); +	if (gpmc_nand_data->elm_of_node == NULL) +		gpmc_nand_data->elm_of_node = +					of_parse_phandle(child, "elm_id", 0); +	if (gpmc_nand_data->elm_of_node == NULL) +		pr_warn("%s: ti,elm-id property not found\n", __func__); + +	/* select ecc-scheme for NAND */ +	if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) { +		pr_err("%s: ti,nand-ecc-opt not found\n", __func__); +		return -ENODEV; +	} +	if (!strcmp(s, "ham1") || !strcmp(s, "sw") || +		!strcmp(s, "hw") || !strcmp(s, "hw-romcode")) +		gpmc_nand_data->ecc_opt = +				OMAP_ECC_HAM1_CODE_HW; +	else if (!strcmp(s, "bch4")) +		if (gpmc_nand_data->elm_of_node) +			gpmc_nand_data->ecc_opt = +				OMAP_ECC_BCH4_CODE_HW; +		else +			gpmc_nand_data->ecc_opt = +				OMAP_ECC_BCH4_CODE_HW_DETECTION_SW; +	else if (!strcmp(s, "bch8")) +		if (gpmc_nand_data->elm_of_node) +			gpmc_nand_data->ecc_opt = +				OMAP_ECC_BCH8_CODE_HW; +		else +			gpmc_nand_data->ecc_opt = +				OMAP_ECC_BCH8_CODE_HW_DETECTION_SW; +	else +		pr_err("%s: ti,nand-ecc-opt invalid value\n", __func__); + +	/* select data transfer mode for NAND controller */ +	if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) +		for (val = 0; val < ARRAY_SIZE(nand_xfer_types); val++) +			if (!strcasecmp(s, nand_xfer_types[val])) { +				gpmc_nand_data->xfer_type = val;  				break;  			} @@ -1473,8 +1511,8 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,  	 */  	ret = gpmc_cs_remap(cs, res.start);  	if (ret < 0) { -		dev_err(&pdev->dev, "cannot remap GPMC CS %d to 0x%x\n", -			cs, res.start); +		dev_err(&pdev->dev, "cannot remap GPMC CS %d to %pa\n", +			cs, &res.start);  		goto err;  	} @@ -1513,6 +1551,20 @@ static int gpmc_probe_dt(struct platform_device *pdev)  	if (!of_id)  		return 0; +	ret = of_property_read_u32(pdev->dev.of_node, "gpmc,num-cs", +				   &gpmc_cs_num); +	if (ret < 0) { +		pr_err("%s: number of chip-selects not defined\n", __func__); +		return ret; +	} else if (gpmc_cs_num < 1) { +		pr_err("%s: all chip-selects are disabled\n", __func__); +		return -EINVAL; +	} else if (gpmc_cs_num > GPMC_CS_NUM) { +		pr_err("%s: number of supported chip-selects cannot be > %d\n", +					 __func__, GPMC_CS_NUM); +		return -EINVAL; +	} +  	ret = of_property_read_u32(pdev->dev.of_node, "gpmc,num-waitpins",  				   &gpmc_nr_waitpins);  	if (ret < 0) { @@ -1577,7 +1629,8 @@ static int gpmc_probe(struct platform_device *pdev)  		return PTR_ERR(gpmc_l3_clk);  	} -	clk_prepare_enable(gpmc_l3_clk); +	pm_runtime_enable(&pdev->dev); +	pm_runtime_get_sync(&pdev->dev);  	gpmc_dev = &pdev->dev; @@ -1610,12 +1663,14 @@ static int gpmc_probe(struct platform_device *pdev)  	/* Now the GPMC is initialised, unreserve the chip-selects */  	gpmc_cs_map = 0; -	if (!pdev->dev.of_node) +	if (!pdev->dev.of_node) { +		gpmc_cs_num	 = GPMC_CS_NUM;  		gpmc_nr_waitpins = GPMC_NR_WAITPINS; +	}  	rc = gpmc_probe_dt(pdev);  	if (rc < 0) { -		clk_disable_unprepare(gpmc_l3_clk); +		pm_runtime_put_sync(&pdev->dev);  		clk_put(gpmc_l3_clk);  		dev_err(gpmc_dev, "failed to probe DT parameters\n");  		return rc; @@ -1628,10 +1683,30 @@ static int gpmc_remove(struct platform_device *pdev)  {  	gpmc_free_irq();  	gpmc_mem_exit(); +	pm_runtime_put_sync(&pdev->dev); +	pm_runtime_disable(&pdev->dev);  	gpmc_dev = NULL;  	return 0;  } +#ifdef CONFIG_PM_SLEEP +static int gpmc_suspend(struct device *dev) +{ +	omap3_gpmc_save_context(); +	pm_runtime_put_sync(dev); +	return 0; +} + +static int gpmc_resume(struct device *dev) +{ +	pm_runtime_get_sync(dev); +	omap3_gpmc_restore_context(); +	return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(gpmc_pm_ops, gpmc_suspend, gpmc_resume); +  static struct platform_driver gpmc_driver = {  	.probe		= gpmc_probe,  	.remove		= gpmc_remove, @@ -1639,6 +1714,7 @@ static struct platform_driver gpmc_driver = {  		.name	= DEVICE_NAME,  		.owner	= THIS_MODULE,  		.of_match_table = of_match_ptr(gpmc_dt_ids), +		.pm	= &gpmc_pm_ops,  	},  }; @@ -1678,7 +1754,7 @@ static int __init omap_gpmc_init(void)  	pdev = omap_device_build(DEVICE_NAME, -1, oh, NULL, 0);  	WARN(IS_ERR(pdev), "could not build omap_device for %s\n", oh_name); -	return IS_ERR(pdev) ? PTR_ERR(pdev) : 0; +	return PTR_RET(pdev);  }  omap_postcore_initcall(omap_gpmc_init); @@ -1701,7 +1777,6 @@ static irqreturn_t gpmc_handle_irq(int irq, void *dev)  	return IRQ_HANDLED;  } -#ifdef CONFIG_ARCH_OMAP3  static struct omap3_gpmc_regs gpmc_context;  void omap3_gpmc_save_context(void) @@ -1715,7 +1790,7 @@ void omap3_gpmc_save_context(void)  	gpmc_context.prefetch_config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1);  	gpmc_context.prefetch_config2 = gpmc_read_reg(GPMC_PREFETCH_CONFIG2);  	gpmc_context.prefetch_control = gpmc_read_reg(GPMC_PREFETCH_CONTROL); -	for (i = 0; i < GPMC_CS_NUM; i++) { +	for (i = 0; i < gpmc_cs_num; i++) {  		gpmc_context.cs_context[i].is_valid = gpmc_cs_mem_enabled(i);  		if (gpmc_context.cs_context[i].is_valid) {  			gpmc_context.cs_context[i].config1 = @@ -1747,7 +1822,7 @@ void omap3_gpmc_restore_context(void)  	gpmc_write_reg(GPMC_PREFETCH_CONFIG1, gpmc_context.prefetch_config1);  	gpmc_write_reg(GPMC_PREFETCH_CONFIG2, gpmc_context.prefetch_config2);  	gpmc_write_reg(GPMC_PREFETCH_CONTROL, gpmc_context.prefetch_control); -	for (i = 0; i < GPMC_CS_NUM; i++) { +	for (i = 0; i < gpmc_cs_num; i++) {  		if (gpmc_context.cs_context[i].is_valid) {  			gpmc_cs_write_reg(i, GPMC_CS_CONFIG1,  				gpmc_context.cs_context[i].config1); @@ -1766,4 +1841,3 @@ void omap3_gpmc_restore_context(void)  		}  	}  } -#endif /* CONFIG_ARCH_OMAP3 */ diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c index 7c5a6b81875..f9b8a4c965a 100644 --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c @@ -3818,6 +3818,7 @@ static struct omap_hwmod_ocp_if *omap34xx_gp_hwmod_ocp_ifs[] __initdata = {  	NULL  }; +  static struct omap_hwmod_ocp_if *omap36xx_gp_hwmod_ocp_ifs[] __initdata = {  	&omap3xxx_l4_sec__timer12,  	&omap3xxx_l4_core__sham, @@ -3975,7 +3976,8 @@ int __init omap3xxx_hwmod_init(void)  	} else if (rev == OMAP3630_REV_ES1_0 || rev == OMAP3630_REV_ES1_1 ||  		   rev == OMAP3630_REV_ES1_2) {  		h = omap36xx_hwmod_ocp_ifs; -		h_gp = omap36xx_gp_hwmod_ocp_ifs; +        /* OLIO: We don't want these clocks running */ +	 	/* h_gp = omap36xx_gp_hwmod_ocp_ifs; */  	} else {  		WARN(1, "OMAP3 hwmod family init: unknown chip type\n");  		return -EINVAL; diff --git a/arch/arm/mach-omap2/opp.c b/arch/arm/mach-omap2/opp.c index 82fd8c72f75..7149f838be4 100644 --- a/arch/arm/mach-omap2/opp.c +++ b/arch/arm/mach-omap2/opp.c @@ -41,8 +41,10 @@ int __init omap_init_opp_table(struct omap_opp_def *opp_def,  {  	int i, r; +    /* OLIO HACK  	if (of_have_populated_dt())  		return -EINVAL; +     */  	if (!opp_def || !opp_def_size) {  		pr_err("%s: invalid params!\n", __func__); diff --git a/arch/arm/mach-omap2/opp3xxx_data.c b/arch/arm/mach-omap2/opp3xxx_data.c index fc67add7644..0f5af3377af 100644 --- a/arch/arm/mach-omap2/opp3xxx_data.c +++ b/arch/arm/mach-omap2/opp3xxx_data.c @@ -130,9 +130,9 @@ static struct omap_opp_def __initdata omap36xx_opp_def_list[] = {  	/* MPU OPP2 - OPP100 */  	OPP_INITIALIZER("mpu", true,  600000000, OMAP3630_VDD_MPU_OPP100_UV),  	/* MPU OPP3 - OPP-Turbo */ -	OPP_INITIALIZER("mpu", false, 800000000, OMAP3630_VDD_MPU_OPP120_UV), +	OPP_INITIALIZER("mpu", true, 800000000, OMAP3630_VDD_MPU_OPP120_UV),  	/* MPU OPP4 - OPP-SB */ -	OPP_INITIALIZER("mpu", false, 1000000000, OMAP3630_VDD_MPU_OPP1G_UV), +	OPP_INITIALIZER("mpu", true, 1000000000, OMAP3630_VDD_MPU_OPP1G_UV),  	/* L3 OPP1 - OPP50 */  	OPP_INITIALIZER("l3_main", true, 100000000, OMAP3630_VDD_CORE_OPP50_UV), diff --git a/arch/arm/mach-omap2/pm-debug-regs.h b/arch/arm/mach-omap2/pm-debug-regs.h index f70bbe5c6a8..65da3221b13 100644 --- a/arch/arm/mach-omap2/pm-debug-regs.h +++ b/arch/arm/mach-omap2/pm-debug-regs.h @@ -27,7 +27,7 @@ extern void pm_dbg_regs_copy(int tgt, int src);  static inline int pm_dbg_regs_init(struct dentry *d) { return 0; }  static inline void pm_dbg_regs_save(int reg_set) {};  static inline void pm_dbg_regs_dump(int reg_set) {}; -static inline void pm_dbg_regs_dump_delta(int current, int ref) {} +static inline void pm_dbg_regs_dump_delta(int curr, int ref) {};  static inline void pm_dbg_show_wakeup_source(void) {};  static inline void pm_dbg_regs_copy(int tgt, int src) {};  #endif diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c index dd7931c2d54..b2dc18a6b82 100644 --- a/arch/arm/mach-omap2/pm.c +++ b/arch/arm/mach-omap2/pm.c @@ -276,6 +276,8 @@ static inline void omap_init_cpufreq(void)  		devinfo.name = "omap-cpufreq";  	else  		devinfo.name = "cpufreq-cpu0"; + +    devinfo.name = "omap-cpufreq"; /* OLIO HACK */  	platform_device_register_full(&devinfo);  } diff --git a/arch/arm/mach-omap2/pmu.c b/arch/arm/mach-omap2/pmu.c index 9ace8eae7ee..33c8846b419 100644 --- a/arch/arm/mach-omap2/pmu.c +++ b/arch/arm/mach-omap2/pmu.c @@ -54,10 +54,7 @@ static int __init omap2_init_pmu(unsigned oh_num, char *oh_names[])  	WARN(IS_ERR(omap_pmu_dev), "Can't build omap_device for %s.\n",  	     dev_name); -	if (IS_ERR(omap_pmu_dev)) -		return PTR_ERR(omap_pmu_dev); - -	return 0; +	return PTR_RET(omap_pmu_dev);  }  static int __init omap_init_pmu(void) diff --git a/arch/arm/mach-omap2/sdram-micron-mt29c4g48.h b/arch/arm/mach-omap2/sdram-micron-mt29c4g48.h new file mode 100644 index 00000000000..aa012b090f9 --- /dev/null +++ b/arch/arm/mach-omap2/sdram-micron-mt29c4g48.h @@ -0,0 +1,63 @@ +/* + * SDRC register values for the Micron MT29C4G48MAYAPAKQ-5 + * + * Copyright (C) 2015 Olio Devices + * Copyright (C) 2008 Texas Instruments, Inc. + * Copyright (C) 2008-2009 Nokia Corporation + * + * Mattis Fjallstrom (mattis at oliodevices.com) + * Paul Walmsley + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef ARCH_ARM_MACH_OMAP2_SDRAM_MICRON_MT29C4G48 +#define ARCH_ARM_MACH_OMAP2_SDRAM_MICRON_MT29C4G48 + +#include "sdrc.h" + +/* Micron MT29C4G48MAYAPAKQ-5 */ +/* 5ns -> 200MHz */ + + +/* 5.4ns -> 185.185185185185 MHz */ +/* 6ns -> 166.666667 MHz */ +/* 7.5ns -> 133.33333333333 */ + +static struct omap_sdrc_params mt29c4g48_sdrc_params[] = { +	[0] = { +		.rate        = 200000000, +		.actim_ctrla = 0x7AE1B4C6, +		.actim_ctrlb = 0x00021217, +		.rfr_ctrl    = 0x0005E601, +		.mr	         = 0x00000034, +	}, +	[1] = { +		.rate	     = 185185185, +		.actim_ctrla = 0x72E1B4C6, +		.actim_ctrlb = 0x00021215, +		.rfr_ctrl    = 0x00057201, +		.mr  	     = 0x00000034, +	}, +	[2] = { +        .rate	     = 166000000, +        .actim_ctrla = 0x629DB4C6, +		.actim_ctrlb = 0x00011113, +		.rfr_ctrl    = 0x0004E201, +		.mr	         = 0x00000034, +	}, +	[3] = { +		.rate	     = 133333333, +		.actim_ctrla = 0x5259B485, +		.actim_ctrlb = 0x0001110F, +		.rfr_ctrl    = 0x0003DE01, +		.mr	         = 0x00000034, +	}, +	[4] = { +		.rate	     = 0, +	}, +}; + +#endif diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c index 30b24ad84f4..8ebc41a7243 100644 --- a/arch/arm/mach-s5pv210/mach-goni.c +++ b/arch/arm/mach-s5pv210/mach-goni.c @@ -239,14 +239,6 @@ static void __init goni_radio_init(void)  /* TSP */  static struct mxt_platform_data qt602240_platform_data = { -	.x_line		= 17, -	.y_line		= 11, -	.x_size		= 800, -	.y_size		= 480, -	.blen		= 0x21, -	.threshold	= 0x28, -	.voltage	= 2800000,              /* 2.8V */ -	.orient		= MXT_DIAGONAL,  	.irqflags	= IRQF_TRIGGER_FALLING,  }; diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types index e4203b7eeba..c2fa3ad5270 100644 --- a/arch/arm/tools/mach-types +++ b/arch/arm/tools/mach-types @@ -274,6 +274,7 @@ omap2evm		MACH_OMAP2EVM		OMAP2EVM		1534  omap3evm		MACH_OMAP3EVM		OMAP3EVM		1535  dns323			MACH_DNS323		DNS323			1542  omap3_beagle		MACH_OMAP3_BEAGLE	OMAP3_BEAGLE		1546 +omap3_h1		MACH_OMAP3_H1		OMAP3_H1	1547  nokia_n810		MACH_NOKIA_N810		NOKIA_N810		1548  pcm038			MACH_PCM038		PCM038			1551  sg310			MACH_SG310		SG310			1564 diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index ef13ad08afb..f8bc937728b 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -958,6 +958,7 @@ int __pm_runtime_resume(struct device *dev, int rpmflags)  	spin_lock_irqsave(&dev->power.lock, flags);  	retval = rpm_resume(dev, rpmflags); +    BUG_ON (dev == NULL); /* OLIO: I get weird crashes here */  	spin_unlock_irqrestore(&dev->power.lock, flags);  	return retval; diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c index 06146219d9d..d64de51db97 100644 --- a/drivers/gpio/gpio-tps65910.c +++ b/drivers/gpio/gpio-tps65910.c @@ -82,7 +82,7 @@ static int tps65910_gpio_input(struct gpio_chip *gc, unsigned offset)  						GPIO_CFG_MASK);  } -#ifdef CONFIG_OF +#if 0 /* #ifdef CONFIG_OF - olio: We DO have a DT, but no tps info there. */  static struct tps65910_board *tps65910_parse_dt_for_gpio(struct device *dev,  		struct tps65910 *tps65910, int chip_ngpio)  { @@ -149,7 +149,7 @@ static int tps65910_gpio_probe(struct platform_device *pdev)  	tps65910_gpio->gpio_chip.set	= tps65910_gpio_set;  	tps65910_gpio->gpio_chip.get	= tps65910_gpio_get;  	tps65910_gpio->gpio_chip.dev = &pdev->dev; -#ifdef CONFIG_OF_GPIO +#if 0 /* #ifdef CONFIG_OF_GPIO */  	tps65910_gpio->gpio_chip.of_node = tps65910->dev->of_node;  #endif  	if (pdata && pdata->gpio_base) diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index b2f963be399..a37b074755d 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -4,7 +4,6 @@  menuconfig IIO  	tristate "Industrial I/O support" -	depends on GENERIC_HARDIRQS  	help  	  The industrial I/O subsystem provides a unified framework for  	  drivers for many different types of embedded sensors using a @@ -67,7 +66,7 @@ source "drivers/iio/common/Kconfig"  source "drivers/iio/dac/Kconfig"  source "drivers/iio/frequency/Kconfig"  source "drivers/iio/gyro/Kconfig" -source "drivers/iio/imu/Kconfig" +# source "drivers/iio/imu/Kconfig"  source "drivers/iio/light/Kconfig"  source "drivers/iio/magnetometer/Kconfig" diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index a0e8cdd67e4..35a17226827 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -18,6 +18,6 @@ obj-y += common/  obj-y += dac/  obj-y += gyro/  obj-y += frequency/ -obj-y += imu/ +# obj-y += imu/  obj-y += light/  obj-y += magnetometer/ diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu-aosp/Kconfig index 4f40a10cb74..c332d4918b1 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu-aosp/Kconfig @@ -38,3 +38,5 @@ config IIO_ADIS_LIB_BUFFER  	  family.  source "drivers/iio/imu/inv_mpu6050/Kconfig" +# source "drivers/iio/imu/inv_mpu6515/Kconfig" + diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu-aosp/Makefile index f2f56ceaed2..2fb21dd4a54 100644 --- a/drivers/iio/imu/Makefile +++ b/drivers/iio/imu-aosp/Makefile @@ -13,3 +13,4 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o  obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o  obj-y += inv_mpu6050/ +obj-y += inv_mpu6515/ diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu-aosp/adis.c index 911255d41c1..911255d41c1 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu-aosp/adis.c diff --git a/drivers/iio/imu/adis16400.h b/drivers/iio/imu-aosp/adis16400.h index 2f8f9d63238..2f8f9d63238 100644 --- a/drivers/iio/imu/adis16400.h +++ b/drivers/iio/imu-aosp/adis16400.h diff --git a/drivers/iio/imu/adis16400_buffer.c b/drivers/iio/imu-aosp/adis16400_buffer.c index 054c01d6e73..054c01d6e73 100644 --- a/drivers/iio/imu/adis16400_buffer.c +++ b/drivers/iio/imu-aosp/adis16400_buffer.c diff --git a/drivers/iio/imu/adis16400_core.c b/drivers/iio/imu-aosp/adis16400_core.c index f60591f0b92..f60591f0b92 100644 --- a/drivers/iio/imu/adis16400_core.c +++ b/drivers/iio/imu-aosp/adis16400_core.c diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu-aosp/adis16480.c index b7db3837629..b7db3837629 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu-aosp/adis16480.c diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu-aosp/adis_buffer.c index 99d8e0b0dd3..99d8e0b0dd3 100644 --- a/drivers/iio/imu/adis_buffer.c +++ b/drivers/iio/imu-aosp/adis_buffer.c diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu-aosp/adis_trigger.c index e0017c22bb9..e0017c22bb9 100644 --- a/drivers/iio/imu/adis_trigger.c +++ b/drivers/iio/imu-aosp/adis_trigger.c diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu-aosp/inv_mpu6050/Kconfig index 361b2328453..361b2328453 100644 --- a/drivers/iio/imu/inv_mpu6050/Kconfig +++ b/drivers/iio/imu-aosp/inv_mpu6050/Kconfig diff --git a/drivers/iio/imu/inv_mpu6050/Makefile b/drivers/iio/imu-aosp/inv_mpu6050/Makefile index 3a677c778af..3a677c778af 100644 --- a/drivers/iio/imu/inv_mpu6050/Makefile +++ b/drivers/iio/imu-aosp/inv_mpu6050/Makefile diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu-aosp/inv_mpu6050/inv_mpu_core.c index fe4c61e219f..fe4c61e219f 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu-aosp/inv_mpu6050/inv_mpu_core.c diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu-aosp/inv_mpu6050/inv_mpu_iio.h index f38395529a4..f38395529a4 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu-aosp/inv_mpu6050/inv_mpu_iio.h diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu-aosp/inv_mpu6050/inv_mpu_ring.c index 7da0832f187..7da0832f187 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +++ b/drivers/iio/imu-aosp/inv_mpu6050/inv_mpu_ring.c diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu-aosp/inv_mpu6050/inv_mpu_trigger.c index 03b9372c121..03b9372c121 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c +++ b/drivers/iio/imu-aosp/inv_mpu6050/inv_mpu_trigger.c diff --git a/drivers/iio/imu-aosp/inv_mpu6515/Kconfig b/drivers/iio/imu-aosp/inv_mpu6515/Kconfig new file mode 100755 index 00000000000..18ccddb1f4e --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/Kconfig @@ -0,0 +1,40 @@ +# +# inv-mpu-iio driver for Invensense MPU devices and combos +# + +config INV_MPU_IIO +	tristate "Invensense MPU devices" +	depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF && IIO_TRIGGER && !INV_MPU +	default n +	help +	  This driver supports the Invensense MPU devices. +	  This includes MPU6050/MPU3050/MPU9150/ITG3500/MPU6500/MPU9250. +	  This driver can be built as a module. The module will be called +	  inv-mpu-iio. + +config INV_IIO_MPU3050_ACCEL_SLAVE_BMA250 +	bool  "Invensense MPU3050 slave accelerometer device for bma250" +	depends on INV_MPU_IIO +	default n +	help +	  This is slave device enable MPU3050 accelerometer slave device. +	  Right now, it is only bma250. For other acceleromter device, +	  it can be added to this menu if the proper interface is filled. +	  There are some interface function to be defined. + +config DTS_INV_MPU_IIO +	bool  "Invensense MPU driver using Device Tree Structure (DTS)" +	depends on INV_MPU_IIO +	default n +	help +	  This enables Invensense MPU devices to use Device Tree Structure. +	  DTS must be enabled in the system. + +config INV_KERNEL_3_10 +	bool  "Invensense MPU driver support for 3.10 kernel" +	depends on INV_MPU_IIO +	default n +	help +	  This enables Invensense MPU devices for 3.10 kernel + + diff --git a/drivers/iio/imu-aosp/inv_mpu6515/Makefile b/drivers/iio/imu-aosp/inv_mpu6515/Makefile new file mode 100755 index 00000000000..6e3c2475889 --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/Makefile @@ -0,0 +1,56 @@ +# +# Makefile for Invensense inv-mpu-iio device. +# + +obj-$(CONFIG_INV_MPU_IIO) += inv-mpu-iio.o + +inv-mpu-iio-objs := inv_mpu_core.o +inv-mpu-iio-objs += inv_mpu_ring.o +inv-mpu-iio-objs += inv_mpu_trigger.o +inv-mpu-iio-objs += inv_mpu_misc.o +inv-mpu-iio-objs += inv_mpu3050_iio.o +inv-mpu-iio-objs += dmpDefaultMPU6050.o +inv-mpu-iio-objs += inv_slave_compass.o +inv-mpu-iio-objs += inv_slave_pressure.o + +ifeq ($(CONFIG_INV_KERNEL_3_10), y) +CFLAGS_inv_mpu_core.o      += -Idrivers/iio -Iinclude/linux/iio +CFLAGS_inv_mpu_ring.o      += -Idrivers/iio -Iinclude/linux/iio +CFLAGS_inv_mpu_trigger.o   += -Idrivers/iio -Iinclude/linux/iio +CFLAGS_inv_mpu_misc.o      += -Idrivers/iio -Iinclude/linux/iio +CFLAGS_inv_mpu3050_iio.o   += -Idrivers/iio -Iinclude/linux/iio +CFLAGS_dmpDefaultMPU6050.o += -Idrivers/iio -Iinclude/linux/iio +CFLAGS_inv_slave_compass.o   += -Idrivers/iio -Iinclude/linux/iio +CFLAGS_inv_slave_pressure.o   += -Idrivers/iio -Iinclude/linux/iio +else +CFLAGS_inv_mpu_core.o      += -Idrivers/staging/iio +CFLAGS_inv_mpu_ring.o      += -Idrivers/staging/iio +CFLAGS_inv_mpu_trigger.o   += -Idrivers/staging/iio +CFLAGS_inv_mpu_misc.o      += -Idrivers/staging/iio +CFLAGS_inv_mpu3050_iio.o   += -Idrivers/staging/iio +CFLAGS_dmpDefaultMPU6050.o += -Idrivers/staging/iio +CFLAGS_inv_slave_compass.o   += -Idrivers/staging/iio +CFLAGS_inv_slave_pressure.o   += -Idrivers/staging/iio +endif + +# the Bosch BMA250 driver is added to the inv-mpu device driver because it +# must be connected to an MPU3050 device on the secondary slave bus. +ifeq ($(CONFIG_INV_IIO_MPU3050_ACCEL_SLAVE_BMA250), y) +inv-mpu-iio-objs += inv_slave_bma250.o +ifeq ($(CONFIG_INV_KERNEL_3_10), y) +CFLAGS_inv_slave_bma250.o   += -Idrivers/iio +else +CFLAGS_inv_slave_bma250.o   += -Idrivers/staging/iio +endif +endif + +# compile Invensense MPU IIO driver as DTS +ifeq ($(CONFIG_DTS_INV_MPU_IIO), y) +inv-mpu-iio-objs += inv_mpu_dts.o +ifeq ($(CONFIG_INV_KERNEL_3_10), y) +CFLAGS_inv_mpu_dts.o   += -Idrivers/iio +else +CFLAGS_inv_mpu_dts.o   += -Idrivers/staging/iio +endif +endif + diff --git a/drivers/iio/imu-aosp/inv_mpu6515/README b/drivers/iio/imu-aosp/inv_mpu6515/README new file mode 100755 index 00000000000..0963954a844 --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/README @@ -0,0 +1,659 @@ +Kernel driver inv-mpu-iio +Author: Invensense <http://invensense.com> + +Table of Contents: +================== +- Description +- Integrating the Driver in the Linux Kernel +- Board and Platform Data +    > Interrupt Pin +    > Platform Data +- Board File Modifications for Secondary I2C Configuration +    > MPU-6050 + AKM8963 on the secondary I2C interface +    > MPU-6500 + AKM8963 on the secondary I2C interface +    > MPU-6515 + AKM8975 and BMP280 on the secondary I2C interface +    > MPU-9150 +    > MPU-9250 +    > MPU-3050 + BMA250 on the secondary I2C interface +- Board File Modifications for Invensense Devices +    > MPU-3050 +    > ITG-3500 +    > MPU-6050 +    > MPU-6515 +    > MPU-6500 +    > MPU-6XXX +    > MPU-9150 +    > MPU-9250 +- IIO Subsystem +    > Communicating with the Driver in Userspace +    > ITG-3500 +    > MPU-6050 and MPU-6500 +    > MPU-9150 +    > MPU-9250 +    > MPU-3050 + BMA250 on the secondary I2C interface +- Suspend and Resume +- Wake up CPU using event interrupt +- DMP Event +- Motion Event +- Streaming Data to an Userspace Application +- Recommended Sysfs Entry Setup Sequence +    > With DMP Firmware +    > Without DMP Firmware +- Test Applications +    > Running Test Applications with MPU-9150/MPU-6050/MPU-6500/MPU-9250 +    > Running Test Applications with MPU-3050/ITG-3500 + + +Description +=========== +This document describes how to install the Invensense device driver into a +Linux kernel. The Invensense driver currently supports the following sensors: +- ITG-3500 +- MPU-6050 +- MPU-9150 +- MPU-6500 +- MPU-9250 +- MPU-3050 +- MPU-6XXX(either MPU6050 or MPU6500, driver to do auto detection) + +The slave address of each device is either 0x68 or 0x69, depending on the AD0 +pin value of the device. Please refer to the appropriate product specification +document for further information regarding the AD0 pin. The driver supports both +addresses. + +The following files are included in this package: +- Kconfig +- Makefile +- inv_mpu_core.c +- inv_mpu_misc.c +- inv_mpu_trigger.c +- inv_mpu3050_iio.c +- inv_mpu_iio.h +- inv_mpu_ring.c +- inv_slave_bma250.c +- inv_slave_compass.c +- dmpDefaultMPU6050.c +- dmpkey.h +- dmpmap.h +- mpu.h + + +Integrating the Driver in the Linux Kernel +========================================== +Please add the files as follows: +- Add mpu.h to "kernel/include/linux". +- Add all other files to drivers/staging/iio/imu/inv_mpu +(another directory is acceptable, but this is the recommended destination) + +In order to see the driver in menuconfig when building the kernel, please +make modifications as shown below: + +    modify "drivers/staging/iio/imu/Kconfig" with: +    >> source "drivers/staging/iio/imu/inv_mpu/Kconfig" + +    modify "drivers/staging/iio/imu/Makefile" with: +    >> obj-y += inv_mpu/ + + +Board and Platform Data +======================= +In order to recognize the Invensense device on the I2C bus, the board file must +be modified. +The i2c_board_info instance must be defined as shown below. + +Interrupt Pin +------------- +The hardcoded value of 140 corresponds to the GPIO input pin connected to the +Invensense device's interrupt pin. +This pin will most likely be different for your platform, and the value should +be changed accordingly. + +Platform Data +------------- +The platform data (orientation matrix and secondary bus configurations) must be +modified as show below, according to your particular platform configuration. + +Please note that the MPU-9150 it is treated as a MPU-6050 with AKM8975 on the +device's secondary I2C interface. Thus the secondary I2C address must be +provided. + +Please note that the MPU-9250 it is treated as a MPU-6500 with AKM8963 on the +device's secondary I2C interface. Thus the secondary I2C address must be +provided. + +Board File Modifications for Secondary I2C Configuration +======================================================== +For the Panda Board, the board file can be found at +arch/arm/mach-omap2/board-omap4panda.c. +Please modify the pertinent baord file in your system according to the examples +shown below: + +MPU-6050 + AKM8963 on the secondary I2C interface +------------------------------------------------- +static struct mpu_platform_data gyro_platform_data = { +	.int_config  = 0x00, +	.level_shifter = 0, +	.orientation = {  -1,  0,  0, +			   0,  1,  0, +			   0,  0, -1 }, +	.sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS, +	.sec_slave_id   = COMPASS_ID_AK8963, +	.secondary_i2c_addr = 0x0E, +        .secondary_orientation = { 0,  1, 0, +                                   1,  0,  0, +                                   0,  0, -1 }, +}; + +MPU-6500 + AKM8963 on the secondary I2C interface +------------------------------------------------- +static struct mpu_platform_data gyro_platform_data = { +        .int_config  = 0x00, +        .level_shifter = 0, +        .orientation = {  -1,  0,  0, +                           0,  1,  0, +                           0,  0, -1 }, +        .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS, +        .sec_slave_id   = COMPASS_ID_AK8963, +        .secondary_i2c_addr = 0x0E, +        .secondary_orientation = { 0,  1, 0, +                                   1,  0,  0, +                                   0,  0, -1 }, +}; + +MPU-6500 + AK09911 on the secondary I2C interface +------------------------------------------------- +static struct mpu_platform_data gyro_platform_data = { +        .int_config  = 0x00, +        .level_shifter = 0, +        .orientation = {  -1,  0,  0, +                           0,  1,  0, +                           0,  0, -1 }, +        .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS, +        .sec_slave_id   = COMPASS_ID_AK09911, +        .secondary_i2c_addr = 0x0D, +        .secondary_orientation = { 0,  1, 0, +                                   1,  0,  0, +                                   0,  0, -1 }, +}; + +MPU-6515 + AK8975 + BMP280 on the secondary I2C interface +Note: sec_slave_XXX can only be compass or accelerometer in 3050 case. +      aux_slave_XXX can only be pressure. +-------------------------------------------------- +static struct mpu_platform_data mpu_data = { +        .int_config  = 0x00, +        .level_shifter = 0, +        .orientation = {  -1,  0,  0, +                           0,  1,  0, +                           0,  0, -1 }, +        .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS, +        .sec_slave_id   = COMPASS_ID_AK8975, +        .secondary_i2c_addr = 0x0E, +        .secondary_orientation = { 0,  1, 0, +                                   1,  0,  0, +                                   0,  0, -1 }, +        .aux_slave_type = SECONDARY_SLAVE_TYPE_PRESSURE, +        .aux_slave_id   = PRESSURE_ID_BMP280, +        .aux_i2c_addr = 0x77, +}; +static struct i2c_board_info __initdata chip_board_info[] = { +        { +                I2C_BOARD_INFO("mpu6515", 0x68), +                .irq = (IH_GPIO_BASE + MPUIRQ_GPIO), +                .platform_data = &mpu_data, +        } +}; + +MPU-9150 +-------- +For MPU-9150, please provide the following secondary I2C bus information. + +static struct mpu_platform_data gyro_platform_data = { +	.int_config  = 0x10, +	.level_shifter = 0, +	.orientation = {   1,  0,  0, +			   0,  1,  0, +			   0,  0,  1 }, +	.sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS, +	.sec_slave_id   = COMPASS_ID_AK8975, +	.secondary_i2c_addr = 0x0C, +        .secondary_orientation = { 0,  1, 0, +                                   1,  0,  0, +                                   0,  0, -1 }, +}; + +MPU-9250 +-------- +For MPU-9250, please provide the following secondary I2C bus information. + +static struct mpu_platform_data gyro_platform_data = { +        .int_config  = 0x00, +        .level_shifter = 0, +        .orientation = {   1,  0,  0, +                           0,  1,  0, +                           0,  0, 1 }, +        .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS, +        .sec_slave_id   = COMPASS_ID_AK8963, +        .secondary_i2c_addr = 0x0C, +        .secondary_orientation = { 0,  1, 0, +                                   -1, 0,  0, +                                   0,  0,  1 }, +}; + +MPU-3050 + BMA250 on the secondary I2C interface +------------------------------------------------ +For BMA250 on the secondary I2C bus, please provide the following information. + +static struct mpu_platform_data gyro_platform_data = { +	.int_config  = 0x10, +	.level_shifter = 0, +	.orientation = {  -1,  0,  0, +			   0,  1,  0, +			   0,  0, -1 }, +	.sec_slave_type = SECONDARY_SLAVE_TYPE_ACCEL, +	.sec_slave_id   = ACCEL_ID_BMA250, +	.secondary_i2c_addr = 0x18, +}; + + +Board File Modifications for Invensense Devices +=============================================== +For Invensense devices, please provide the i2c init data as shown in the +examples below. + +In the _i2c_init function, the device is registered in the following manner: + +    // arch/arm/mach-omap2/board-omap4panda.c +    // in static int __init omap4_panda_i2c_init(void) +    omap_register_i2c_bus(4, 400, +                          single_chip_board_info, +                          ARRAY_SIZE(single_chip_board_info)); + +MPU-3050 +-------- +static struct i2c_board_info __initdata single_chip_board_info[] = { +	{ +		I2C_BOARD_INFO("mpu3050", 0x68), +		.irq = (IH_GPIO_BASE + MPUIRQ_GPIO), +		.platform_data = &gyro_platform_data, +	}, +}; + +ITG-3500 +-------- +static struct i2c_board_info __initdata single_chip_board_info[] = { +	{ +		I2C_BOARD_INFO("itg3500", 0x68), +		.irq = (IH_GPIO_BASE + MPUIRQ_GPIO), +                .platform_data = &gyro_platform_data, +	}, +}; + +MPU6050 +------- +static struct i2c_board_info __initdata single_chip_board_info[] = { +	{ +		I2C_BOARD_INFO("mpu6050", 0x68), +		.irq = (IH_GPIO_BASE + MPUIRQ_GPIO), +		.platform_data = &gyro_platform_data, +	}, +}; + +MPU6500 +------- +static struct i2c_board_info __initdata single_chip_board_info[] = { +        { +                I2C_BOARD_INFO("mpu6500", 0x68), +                .irq = (IH_GPIO_BASE + MPUIRQ_GPIO), +                .platform_data = &gyro_platform_data, +        }, +}; + +MPU6XXX +------- +static struct i2c_board_info __initdata single_chip_board_info[] = { +        { +                I2C_BOARD_INFO("mpu6xxx", 0x68), +                .irq = (IH_GPIO_BASE + MPUIRQ_GPIO), +                .platform_data = &gyro_platform_data, +        }, +}; + +MPU9150 +------- +arch/arm/mach-omap2/board-omap4panda.c +static struct i2c_board_info __initdata single_chip_board_info[] = { +	{ +		I2C_BOARD_INFO("mpu9150", 0x68), +		.irq = (IH_GPIO_BASE + MPUIRQ_GPIO), +		.platform_data = &gyro_platform_data, +	}, +}; + +MPU9250 +------- +arch/arm/mach-omap2/board-omap4panda.c +static struct i2c_board_info __initdata single_chip_board_info[] = { +        { +                I2C_BOARD_INFO("mpu9250", 0x68), +                .irq = (IH_GPIO_BASE + MPUIRQ_GPIO), +                .platform_data = &gyro_platform_data, +        }, +}; + +IIO subsystem +============= +A successful installation will create the following two new directories under +/sys/bus/iio/devices: +    - iio:device0 +    - trigger0 + +Also, a new file, "iio:device0", will be created in the /dev/ diretory. +(if you have more than one IIO device, the file will be named "iio:deviceX", +where X is a number) + + +Communicating with the Driver in Userspace +------------------------------------------ +The driver generates several files in sysfs upon installation. +These files are used to communicate with the driver. The files can be found +at /sys/bus/iio/devices/iio:device0 (or ../iio:deviceX as shown above). + +A brief description of the pertinent files for each Invensense device is shown +below: + +ITG-3500 +-------- +temperature (Read-only) +--Read temperature data directly from the temperature register. + +sampling_frequency (Read/write) +--Configure the ADC sampling rate and FIFO output rate. + +sampling_frequency_available(read-only) +--show commonly used frequency + +clock_source (Read-only) +--Check which clock-source is used by the chip. + +power_state (Read/write) +--turn on/off the power supply + +self_test (read-only) +--read this entry trigger self test. The return value is D. +D is the success/fail. +For different chip, the result is different for success/fail. +1 means success 0 means fail. The LSB of D is for gyro; the bit +next to LSB of D is for accel. The bit 2 of D is for compass result. + +key (read-only) +--show the key value of this driver. Used by MPL. + +gyro_matrix (read-only) +--show the orientation matrix obtained from the board file. + +MPU-6050 and MPU-6500 +--------------------- +MPU-6050 and MPU-6500 have all sysfs files belonging to ITG-3500 (shown above). +In addition, it has the files below: + +gyro_enable (read/write) +--enable/disable gyro functionality. Affects raw_gyro. Turning this off this +  will shut down gyro and save power. + +accl_enable (read/write) +--enable/disable accelerometer functionality. Affects raw_accl. +Turning this off this will shut down accel and save power. + +firmware_loaded (read/write) +--Flag indicating the whether firmware is loaded or not in the DMP engine. +0 means no firmware loaded. 1 means firmware is already loaded . This +flag can only be written as 0. It internally updates to 1. + +dmp_on(read/write) +--This entry controls whether to run DMP or not. +Write 1 to enable DMP and write 0 to disable dmp. +Please note that firmware_loaded must be 1 in order to enable DMP. + +dmp_int_on(read/write) +--This entry controls whether dmp interrupt is on/off. +Please note that firmware_loaded must be 1. +Also, we'd like to remind you that it is sometimes advantageous to +turn interrupts off while the DMP is running. + +dmp_output_rate +--control dmp output rate when dmp is on. + +dmp_event_int_on(read/write) +--This entry controls whether dmp event interrupt is on/off. +Please note that turning this on will turn off the data interrupt. +Interrupts will be generated only when events occur. +This is useful for saving power when the system is waiting for a special event +to wake up. + +dmp_firmware (write only binary file) +--DMP firmware code is loaded into this entry. +If loading is successful, the firmware_loaded flag will be updated to 1. +In order to load new firmware, the firmware_loaded flag must be first set to 0. + +accel_matrix +--orientation matrix for accelerometer. + +quaternion_on +--Turn on/off quaterniion data output. DMP is required for this feature. + +pedometer_time +pedometer_steps, +--Pedometer related entries + +event_tap +event_display_orientation +event_accel_motion +event_smd +--Event related entries. +Please poll these entries to read their values. Direct reads will yield +meaningless results. +Further details are provided in the DMP Events section of this README. + +tap_on +--Controls tap function of DMP + +tap_time +tap_min_count +tap_threshold +--Tap related entries. Controls various parameters of tap function. + +display_orientation_on +--Turn on/off display orientation function of DMP. + +smd_enable +enable SMD(Significant Motion Detection) detection. + +smd_threshold +This set the threshold of the motion when SMD start to be triggered. The +value is in acclerometer counts. + +smd_delay_threshold +This sets the threshold of time after which SMD can be triggered. +The value is in seconds. + +smd_delay_threshold2 +This sets the threshold of time during which SMD can be triggered (after the +smd_delay_threshold timer has expired). +The value is in seconds. + +quaternion_on +--Turn on/off quaterniion data output. DMP is required for this feature. + +Low power accel motion interrupt related settings. +if motion_lpa_on is set, this will disable all engines except accel. Accel will +enter low power mode and the whole chip will be turned on/off at specific frequency. +----------------------------------------------------------------------------- +motion_lpa_threshold +--set motion threshold. in mg. The maximum is 1020mg and resolution is 32mg. + +motion_lpa_on +--turn on/off motion function. + +motion_lpa_freq +--motion lpa frequency. which determines power on/off frequency. +------------------------------------------------------------------------------ +MPU-9150 +-------- +MPU-9150 has all of MPU-6050's entries. It also has two additional entries, +described below. + +compass_enable (read/write) +--Enables compass function. + +compass_matrix (read-only) +--Compass orientation matrix + +MPU-3050 with BMA250 on secondary I2C interface +----------------------------------------------- +MPU-3050 with BMA250 on the secondary I2C interface has ever ITG-3500 entry. +It also has two additional entries, shown below: + +accl_matrix + +accl_enable + +Suspend and Resume +=================================================== +The suspend and resume functions are call backs registered to the system +and executed when the system goes in suspend and resumes. +It is enabled when CONFIG_PM is defined. +The current behavior is simple: +- suspend will turn off the chip +- resume will turn on the chip + +However, it is possible for the driver to do more complex things; +for example, leaving pedometers running when system is off. This can save whole +system power while letting pedometer working. Other behaviors are possible +too. + +Wake up CPU using event interrupt +==================================================== +It is common practice in current mobile device to save power as much as +possible. Ususally the device can sleep when not use and only be wake up when in use. +Invensense MPU series provides an efficient way of waking up CPU by events. +The events could be pedometer steps, tap action, SMD(significant Motion Detection), +or any customer defined actions. CPU can sleep to save power, when event happens, +DMP will calculate the motion data to detemine whether a pre-defined event happens +or not. If an pre-defined happens, an interrupt is generated to wake up the CPU +to do further processing.  +The requirements of doing event wake is the following: +1. Add enable_irq_wake() either in the driver or in the board file. +2. Connect the interrupt pin to the CPU wake up pin. +3. Configure CPU to be woken up by wake up pin. + +DMP Event +========= +A DMP Event is an event that is output by the DMP unit within the Invensense +device (MPU). +Only the MPU-6050, MPU-6500, MPU-9250, MPU-9150, MPU-9250 feature the DMP. + +There are four sysfs entries for DMP events: +- event_tap +- event_display_orientation +- event_accel_motion +- event_smd + +These events must be polled before reading. + +The proper method to poll sysfs is as follows: +1. open file. +2. dummy read. +3. poll. +4. once the poll passed, use fopen and fread to read the sysfs entry. +5. interpret the data. + +Streaming Data to an Userspace Application +========================================== +When streaming data to an userspace application, we recommend that you access +gyro/accel/compass data via /dev/iio:device0. + +Please follow the steps below to read data at a constant rate from the driver: + +1. Write a 1 to power_state to turn on the chip. This is the default setting +   after installing the driver. +2. Write the desired output rate to fifo_rate. +3. Write 1 to enable to turn on the event. +4. Read /dev/iio:device0 to get a string of gyro/accel/compass data. +5. Parse this string to obtain each gyro/accel/compass element. +6. If dmp firmware code is loaded, use "dmp_on" to enable/disable dmp. +7. If compass is enabled, the output will contain compass data. + + +Recommended Sysfs Entry Setup Senquence +======================================= + +Without DMP Firmware +-------------------- +1. Set "power_state" to 1, +2. Set the scale and fifo rate values according to your needs. +3. Set gyro_enable, accel_enable, and compass_enable according to your needs. +   For example: +    - If you only want gyro data, set accel_enable to 0 (and compass_enable to +      0, if applicable). +    - If you only want accel data, set gyro_enable to 0 (and compass_enable to +      0, if applicable). +    - If you only want compass data, set gyro_enable to 0 and accel_enable to 0. +4. Set "enable" to 1. +5. You will now get the output that you want. + +With DMP Firmware +----------------- +1. Set "power_state" to 1. +2. Write "0" to firmware_loaded if it is not zero already. +3. Load firmware into "dmp_firmware" as a whole. Don't split the DMP firmware +   image. +4. Make sure firmware_loaded is 1 after loading the DMP image. +5. Make appropriate configurations as shown above in the "without DMP firmware" +   case. +6. Set dmp_on to 1. +7. Set "enable" to 1. + +Please note that the enable function uses the enable entry under +"/sys/bus/iio/devices/iio:device0/buffer" + +Test Applications +================= +A test application is located under software/simple_apps/mpu_iio. +This application is stand-alone in that it cannot be run concurrently with other +entities trying to access the device node(s) or sysfs entries; in particular, +the + +Running Test Applications with MPU-9150/MPU-6050/MPU-6500/MPU-9250 +--------------------------------------------------------- +To run test applications with MPU-9150, MPU-9250, MPU-6050, or MPU-6500 devices, +please use the following commands: + +1. For tap/display orientation events: +   mpu_iio -c 10 -l 3 -p + +2. In addition, to test the motion interrupt (and no_motion on MPU6050) use: +   mpu_iio -c 10 -l 3 -p -m + +3. For printing data normally: +   mpu_iio -c 10 -l 3 -r + +Running Test Applications with MPU-3050/ITG-3500 +------------------------------------------------ +To run test applications with MPU-3050 or ITG-3500 devices, +please use the following command: + +1. For printing data normally: +   mpu_iio -c 10 -l 3 -r + +Please use mpu_iio.c and iio_utils.h as example code for your development +purposes. + +Stress test application +================================= +A stress test application is located under software/simple_apps/stress_iio. +This application simulates HAL's usage calls to the driver. It creates three +threads. One for data read; one for event read; one for sysfs control. +It can run without any parameters or run with some control parameters. Please +see README in the same directories for details. + diff --git a/drivers/iio/imu-aosp/inv_mpu6515/dmpDefaultMPU6050.c b/drivers/iio/imu-aosp/inv_mpu6515/dmpDefaultMPU6050.c new file mode 100755 index 00000000000..892e62dc14f --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/dmpDefaultMPU6050.c @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2012 Invensense, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include "inv_mpu_iio.h" +#include "dmpKey.h" +#include "dmpmap.h" + +#define CFG_OUT_PRESS       (1872) +#define CFG_PED_ENABLE      (1985) +#define CFG_OUT_GYRO        (1804) +#define CFG_PEDSTEP_DET     (2807) +#define OUT_GYRO_DAT        (1813) +#define CFG_FIFO_INT        (1983) +#define OUT_CPASS_DAT       (1847) +#define CFG_AUTH            (1099) +#define OUT_ACCL_DAT        (1779) +#define FCFG_1              (1127) +#define FCFG_3              (1152) +#define FCFG_2              (1131) +#define CFG_OUT_CPASS       (1838) +#define FCFG_7              (1138) +#define CFG_OUT_3QUAT       (1666) +#define OUT_PRESS_DAT       (1881) +#define OUT_3QUAT_DAT       (1676) +#define CFG_7               (1349) +#define OUT_PQUAT_DAT       (1745) +#define CFG_OUT_6QUAT       (1701) +#define CFG_PED_INT         (2796) +#define SMD_TP2             (1314) +#define SMD_TP1             (1293) +#define CFG_MOTION_BIAS     (1351) +#define CFG_OUT_ACCL        (1770) +#define CFG_OUT_STEPDET     (1636) +#define OUT_6QUAT_DAT       (1711) +#define CFG_OUT_PQUAT       (1736) + +#define D_0_22                  (22+512) +#define D_0_24                  (24+512) + +#define D_0_36                  (36) +#define D_0_52                  (52) +#define D_0_96                  (96) +#define D_0_104                 (104) +#define D_0_108                 (108) +#define D_0_163                 (163) +#define D_0_188                 (188) +#define D_0_192                 (192) +#define D_0_224                 (224) +#define D_0_228                 (228) +#define D_0_232                 (232) +#define D_0_236                 (236) + +#define D_1_2                   (256 + 2) +#define D_1_4                   (256 + 4) +#define D_1_8                   (256 + 8) +#define D_1_10                  (256 + 10) +#define D_1_24                  (256 + 24) +#define D_1_28                  (256 + 28) +#define D_1_36                  (256 + 36) +#define D_1_40                  (256 + 40) +#define D_1_44                  (256 + 44) +#define D_1_72                  (256 + 72) +#define D_1_74                  (256 + 74) +#define D_1_79                  (256 + 79) +#define D_1_88                  (256 + 88) +#define D_1_90                  (256 + 90) +#define D_1_92                  (256 + 92) +#define D_1_96                  (256 + 96) +#define D_1_98                  (256 + 98) +#define D_1_106                 (256 + 106) +#define D_1_108                 (256 + 108) +#define D_1_112                 (256 + 112) +#define D_1_128                 (256 + 144) +#define D_1_152                 (256 + 12) +#define D_1_160                 (256 + 160) +#define D_1_176                 (256 + 176) +#define D_1_178                 (256 + 178) +#define D_1_218                 (256 + 218) +#define D_1_232                 (256 + 232) +#define D_1_236                 (256 + 236) +#define D_1_240                 (256 + 240) +#define D_1_244                 (256 + 244) +#define D_1_250                 (256 + 250) +#define D_1_252                 (256 + 252) +#define D_2_12                  (512 + 12) +#define D_2_96                  (512 + 96) +#define D_2_108                 (512 + 108) +#define D_2_208                 (512 + 208) +#define D_2_224                 (512 + 224) +#define D_2_236                 (512 + 236) +#define D_2_244                 (512 + 244) +#define D_2_248                 (512 + 248) +#define D_2_252                 (512 + 252) +#define CPASS_BIAS_X            (30 * 16 + 4) +#define CPASS_BIAS_Y            (30 * 16 + 8) +#define CPASS_BIAS_Z            (30 * 16 + 12) +#define CPASS_MTX_00            (32 * 16 + 4) +#define CPASS_MTX_01            (32 * 16 + 8) +#define CPASS_MTX_02            (36 * 16 + 12) +#define CPASS_MTX_10            (33 * 16) +#define CPASS_MTX_11            (33 * 16 + 4) +#define CPASS_MTX_12            (33 * 16 + 8) +#define CPASS_MTX_20            (33 * 16 + 12) +#define CPASS_MTX_21            (34 * 16 + 4) +#define CPASS_MTX_22            (34 * 16 + 8) +#define D_EXT_GYRO_BIAS_X       (61 * 16) +#define D_EXT_GYRO_BIAS_Y       (61 * 16 + 4) +#define D_EXT_GYRO_BIAS_Z       (61 * 16 + 8) +#define D_ACT0                  (40 * 16) +#define D_ACSX                  (40 * 16 + 4) +#define D_ACSY                  (40 * 16 + 8) +#define D_ACSZ                  (40 * 16 + 12) + +#define FLICK_MSG               (45 * 16 + 4) +#define FLICK_COUNTER           (45 * 16 + 8) +#define FLICK_LOWER             (45 * 16 + 12) +#define FLICK_UPPER             (46 * 16 + 12) + +#define D_SMD_ENABLE            (18 * 16) +#define D_SMD_MOT_THLD          (21 * 16 + 8) +#define D_SMD_DELAY_THLD        (21 * 16 + 4) +#define D_SMD_DELAY2_THLD       (21 * 16 + 12) +#define D_SMD_EXE_STATE         (22 * 16) +#define D_SMD_DELAY_CNTR        (21 * 16) + +#define D_WF_GESTURE_TIME_THRSH	(25 * 16 + 8) +#define D_WF_GESTURE_TILT_ERROR	(25 * 16 + 12) +#define D_WF_GESTURE_TILT_THRSH	(26 * 16 + 8) +#define D_WF_GESTURE_TILT_REJECT_THRSH	(26 * 16 + 12) + +#define D_AUTH_OUT              (992) +#define D_AUTH_IN               (996) +#define D_AUTH_A                (1000) +#define D_AUTH_B                (1004) + +#define D_PEDSTD_BP_B           (768 + 0x1C) +#define D_PEDSTD_BP_A4          (768 + 0x40) +#define D_PEDSTD_BP_A3          (768 + 0x44) +#define D_PEDSTD_BP_A2          (768 + 0x48) +#define D_PEDSTD_BP_A1          (768 + 0x4C) +#define D_PEDSTD_SB             (768 + 0x28) +#define D_PEDSTD_SB_TIME        (768 + 0x2C) +#define D_PEDSTD_PEAKTHRSH      (768 + 0x98) +#define D_PEDSTD_TIML           (768 + 0x2A) +#define D_PEDSTD_TIMH           (768 + 0x2E) +#define D_PEDSTD_PEAK           (768 + 0X94) +#define D_PEDSTD_STEPCTR        (768 + 0x60) +#define D_PEDSTD_STEPCTR2       (58 * 16 + 8) +#define D_PEDSTD_TIMECTR        (964) +#define D_PEDSTD_DECI           (768 + 0xA0) +#define D_PEDSTD_SB2			(60 * 16 + 14) +#define D_STPDET_TIMESTAMP      (28 * 16 + 8) +#define D_PEDSTD_DRIVE_STATE    (58) + +#define D_HOST_NO_MOT           (976) +#define D_ACCEL_BIAS            (660) + +#define D_BM_BATCH_CNTR         (27 * 16 + 4) +#define D_BM_BATCH_THLD         (27 * 16 + 12) +#define D_BM_ENABLE             (28 * 16 + 6) +#define D_BM_NUMWORD_TOFILL     (28 * 16 + 4) + +#define D_SO_DATA               (4 * 16 + 2) + +#define D_P_HW_ID               (22 * 16 + 10) +#define D_P_INIT                (22 * 16 + 2) + +#define D_DMP_RUN_CNTR          (24*16) + +#define D_ODR_S0                (45*16+8) +#define D_ODR_S1                (45*16+12) +#define D_ODR_S2                (45*16+10) +#define D_ODR_S3                (45*16+14) +#define D_ODR_S4                (46*16+8) +#define D_ODR_S5                (46*16+12) +#define D_ODR_S6                (46*16+10) +#define D_ODR_S7                (46*16+14) +#define D_ODR_S8                (42*16+8) +#define D_ODR_S9                (42*16+12) + +#define D_ODR_CNTR_S0           (45*16) +#define D_ODR_CNTR_S1           (45*16+4) +#define D_ODR_CNTR_S2           (45*16+2) +#define D_ODR_CNTR_S3           (45*16+6) +#define D_ODR_CNTR_S4           (46*16) +#define D_ODR_CNTR_S5           (46*16+4) +#define D_ODR_CNTR_S6           (46*16+2) +#define D_ODR_CNTR_S7           (46*16+6) +#define D_ODR_CNTR_S8           (42*16) +#define D_ODR_CNTR_S9           (42*16+4) + +#define D_FS_LPQ0               (59*16) +#define D_FS_LPQ1               (59*16 + 4) +#define D_FS_LPQ2               (59*16 + 8) +#define D_FS_LPQ3               (59*16 + 12) + +#define D_FS_Q0                 (12*16) +#define D_FS_Q1                 (12*16 + 4) +#define D_FS_Q2                 (12*16 + 8) +#define D_FS_Q3                 (12*16 + 12) + +#define D_FS_9Q0                (2*16) +#define D_FS_9Q1                (2*16 + 4) +#define D_FS_9Q2                (2*16 + 8) +#define D_FS_9Q3                (2*16 + 12) + +#define D_CPASS_STATUS_CHK		(22*16 + 8) + +static const struct tKeyLabel dmpTConfig[] = { +	{KEY_CFG_OUT_ACCL,              CFG_OUT_ACCL}, +	{KEY_CFG_OUT_GYRO,              CFG_OUT_GYRO}, +	{KEY_CFG_OUT_3QUAT,             CFG_OUT_3QUAT}, +	{KEY_CFG_OUT_6QUAT,             CFG_OUT_6QUAT}, +	{KEY_CFG_OUT_PQUAT,             CFG_OUT_PQUAT}, +	{KEY_CFG_PED_ENABLE,            CFG_PED_ENABLE}, +	{KEY_CFG_FIFO_INT,              CFG_FIFO_INT}, +	{KEY_CFG_AUTH,                  CFG_AUTH}, +	{KEY_FCFG_1,                    FCFG_1}, +	{KEY_FCFG_3,                    FCFG_3}, +	{KEY_FCFG_2,                    FCFG_2}, +	{KEY_FCFG_7,                    FCFG_7}, +	{KEY_CFG_7,                     CFG_7}, +	{KEY_CFG_MOTION_BIAS,           CFG_MOTION_BIAS}, +	{KEY_CFG_PEDSTEP_DET,           CFG_PEDSTEP_DET}, +	{KEY_D_0_22,                    D_0_22}, +	{KEY_D_0_96,                    D_0_96}, +	{KEY_D_0_104,                   D_0_104}, +	{KEY_D_0_108,                   D_0_108}, +	{KEY_D_1_36,                    D_1_36}, +	{KEY_D_1_40,                    D_1_40}, +	{KEY_D_1_44,                    D_1_44}, +	{KEY_D_1_72,                    D_1_72}, +	{KEY_D_1_74,                    D_1_74}, +	{KEY_D_1_79,                    D_1_79}, +	{KEY_D_1_88,                    D_1_88}, +	{KEY_D_1_90,                    D_1_90}, +	{KEY_D_1_92,                    D_1_92}, +	{KEY_D_1_160,                   D_1_160}, +	{KEY_D_1_176,                   D_1_176}, +	{KEY_D_1_218,                   D_1_218}, +	{KEY_D_1_232,                   D_1_232}, +	{KEY_D_1_250,                   D_1_250}, +	{KEY_DMP_SH_TH_Y,               DMP_SH_TH_Y}, +	{KEY_DMP_SH_TH_X,               DMP_SH_TH_X}, +	{KEY_DMP_SH_TH_Z,               DMP_SH_TH_Z}, +	{KEY_DMP_ORIENT,                DMP_ORIENT}, +	{KEY_D_AUTH_OUT,                D_AUTH_OUT}, +	{KEY_D_AUTH_IN,                 D_AUTH_IN}, +	{KEY_D_AUTH_A,                  D_AUTH_A}, +	{KEY_D_AUTH_B,                  D_AUTH_B}, +	{KEY_CPASS_BIAS_X,          CPASS_BIAS_X}, +	{KEY_CPASS_BIAS_Y,          CPASS_BIAS_Y}, +	{KEY_CPASS_BIAS_Z,          CPASS_BIAS_Z}, +	{KEY_CPASS_MTX_00,          CPASS_MTX_00}, +	{KEY_CPASS_MTX_01,          CPASS_MTX_01}, +	{KEY_CPASS_MTX_02,          CPASS_MTX_02}, +	{KEY_CPASS_MTX_10,          CPASS_MTX_10}, +	{KEY_CPASS_MTX_11,          CPASS_MTX_11}, +	{KEY_CPASS_MTX_12,          CPASS_MTX_12}, +	{KEY_CPASS_MTX_20,          CPASS_MTX_20}, +	{KEY_CPASS_MTX_21,          CPASS_MTX_21}, +	{KEY_CPASS_MTX_22,          CPASS_MTX_22}, +	{KEY_D_ACT0,                    D_ACT0}, +	{KEY_D_ACSX,                    D_ACSX}, +	{KEY_D_ACSY,                    D_ACSY}, +	{KEY_D_ACSZ,                    D_ACSZ}, +	{KEY_D_PEDSTD_BP_B,             D_PEDSTD_BP_B}, +	{KEY_D_PEDSTD_BP_A4,            D_PEDSTD_BP_A4}, +	{KEY_D_PEDSTD_BP_A3,            D_PEDSTD_BP_A3}, +	{KEY_D_PEDSTD_BP_A2,            D_PEDSTD_BP_A2}, +	{KEY_D_PEDSTD_BP_A1,            D_PEDSTD_BP_A1}, +	{KEY_D_PEDSTD_SB,               D_PEDSTD_SB}, +	{KEY_D_PEDSTD_SB_TIME,          D_PEDSTD_SB_TIME}, +	{KEY_D_PEDSTD_PEAKTHRSH,        D_PEDSTD_PEAKTHRSH}, +	{KEY_D_PEDSTD_TIML,             D_PEDSTD_TIML}, +	{KEY_D_PEDSTD_TIMH,             D_PEDSTD_TIMH}, +	{KEY_D_PEDSTD_PEAK,             D_PEDSTD_PEAK}, +	{KEY_D_PEDSTD_STEPCTR,          D_PEDSTD_STEPCTR}, +	{KEY_D_PEDSTD_STEPCTR2,         D_PEDSTD_STEPCTR2}, +	{KEY_D_PEDSTD_TIMECTR,          D_PEDSTD_TIMECTR}, +	{KEY_D_PEDSTD_DECI,             D_PEDSTD_DECI}, +	{KEY_D_PEDSTD_SB2,				D_PEDSTD_SB2}, +	{KEY_D_PEDSTD_DRIVE_STATE,      D_PEDSTD_DRIVE_STATE}, +	{KEY_D_STPDET_TIMESTAMP,		D_STPDET_TIMESTAMP}, +	{KEY_D_HOST_NO_MOT,             D_HOST_NO_MOT}, +	{KEY_D_ACCEL_BIAS,              D_ACCEL_BIAS}, +	{KEY_CFG_EXT_GYRO_BIAS_X,       D_EXT_GYRO_BIAS_X}, +	{KEY_CFG_EXT_GYRO_BIAS_Y,       D_EXT_GYRO_BIAS_Y}, +	{KEY_CFG_EXT_GYRO_BIAS_Z,       D_EXT_GYRO_BIAS_Z}, +	{KEY_CFG_PED_INT,               CFG_PED_INT}, +	{KEY_SMD_ENABLE,                D_SMD_ENABLE}, +	{KEY_SMD_ACCEL_THLD,            D_SMD_MOT_THLD}, +	{KEY_SMD_DELAY_THLD,            D_SMD_DELAY_THLD}, +	{KEY_SMD_DELAY2_THLD,           D_SMD_DELAY2_THLD}, +	{KEY_SMD_ENABLE_TESTPT1,        SMD_TP1}, +	{KEY_SMD_ENABLE_TESTPT2,        SMD_TP2}, +	{KEY_SMD_EXE_STATE,             D_SMD_EXE_STATE}, +	{KEY_SMD_DELAY_CNTR,            D_SMD_DELAY_CNTR}, +	{KEY_BM_ENABLE,                 D_BM_ENABLE}, +	{KEY_BM_BATCH_CNTR,             D_BM_BATCH_CNTR}, +	{KEY_BM_BATCH_THLD,             D_BM_BATCH_THLD}, +	{KEY_BM_NUMWORD_TOFILL,         D_BM_NUMWORD_TOFILL}, +	{KEY_SO_DATA,                   D_SO_DATA}, +	{KEY_P_INIT,                    D_P_INIT}, +	{KEY_CFG_OUT_CPASS,             CFG_OUT_CPASS}, +	{KEY_CFG_OUT_PRESS,             CFG_OUT_PRESS}, +	{KEY_CFG_OUT_STEPDET,           CFG_OUT_STEPDET}, +	{KEY_CFG_3QUAT_ODR,             D_ODR_S1}, +	{KEY_CFG_6QUAT_ODR,             D_ODR_S2}, +	{KEY_CFG_PQUAT6_ODR,            D_ODR_S3}, +	{KEY_CFG_ACCL_ODR,              D_ODR_S4}, +	{KEY_CFG_GYRO_ODR,              D_ODR_S5}, +	{KEY_CFG_CPASS_ODR,             D_ODR_S6}, +	{KEY_CFG_PRESS_ODR,             D_ODR_S7}, +	{KEY_CFG_9QUAT_ODR,             D_ODR_S8}, +	{KEY_CFG_PQUAT9_ODR,            D_ODR_S9}, +	{KEY_ODR_CNTR_3QUAT,            D_ODR_CNTR_S1}, +	{KEY_ODR_CNTR_6QUAT,            D_ODR_CNTR_S2}, +	{KEY_ODR_CNTR_PQUAT,            D_ODR_CNTR_S3}, +	{KEY_ODR_CNTR_ACCL,             D_ODR_CNTR_S4}, +	{KEY_ODR_CNTR_GYRO,             D_ODR_CNTR_S5}, +	{KEY_ODR_CNTR_CPASS,            D_ODR_CNTR_S6}, +	{KEY_ODR_CNTR_PRESS,            D_ODR_CNTR_S7}, +	{KEY_ODR_CNTR_9QUAT,			D_ODR_CNTR_S8}, +	{KEY_ODR_CNTR_PQUAT9,			D_ODR_CNTR_S9}, +	{KEY_DMP_RUN_CNTR,              D_DMP_RUN_CNTR}, +	{KEY_DMP_LPQ0,                  D_FS_LPQ0}, +	{KEY_DMP_LPQ1,                  D_FS_LPQ1}, +	{KEY_DMP_LPQ2,                  D_FS_LPQ2}, +	{KEY_DMP_LPQ3,                  D_FS_LPQ3}, +	{KEY_DMP_Q0,                    D_FS_Q0}, +	{KEY_DMP_Q1,                    D_FS_Q1}, +	{KEY_DMP_Q2,                    D_FS_Q2}, +	{KEY_DMP_Q3,                    D_FS_Q3}, +	{KEY_DMP_9Q0,					D_FS_9Q0}, +	{KEY_DMP_9Q1,					D_FS_9Q1}, +	{KEY_DMP_9Q2,					D_FS_9Q2}, +	{KEY_DMP_9Q3,					D_FS_9Q3}, +	{KEY_CPASS_STATUS_CHK,          D_CPASS_STATUS_CHK}, +	{KEY_TEST_01,                   OUT_ACCL_DAT}, +	{KEY_TEST_02,                   OUT_GYRO_DAT}, +	{KEY_TEST_03,                   OUT_CPASS_DAT}, +	{KEY_TEST_04,                   OUT_PRESS_DAT}, +	{KEY_TEST_05,                   OUT_3QUAT_DAT}, +	{KEY_TEST_06,                   OUT_6QUAT_DAT}, +	{KEY_TEST_07,                   OUT_PQUAT_DAT} +}; +#define NUM_LOCAL_KEYS (sizeof(dmpTConfig)/sizeof(dmpTConfig[0])) + +static struct tKeyLabel keys[NUM_KEYS]; + +unsigned short inv_dmp_get_address(unsigned short key) +{ +	static int isSorted; + +	if (!isSorted) { +		int kk; +		for (kk = 0; kk < NUM_KEYS; ++kk) { +			keys[kk].addr = 0xffff; +			keys[kk].key = kk; +		} +		for (kk = 0; kk < NUM_LOCAL_KEYS; ++kk) +			keys[dmpTConfig[kk].key].addr = dmpTConfig[kk].addr; +		isSorted = 1; +	} +	if (key >= NUM_KEYS) { +		pr_err("ERROR!! key not exist=%d!\n", key); +		return 0xffff; +	} +	if (0xffff == keys[key].addr) +		pr_err("ERROR!!key not local=%d!\n", key); +	return keys[key].addr; +} diff --git a/drivers/iio/imu-aosp/inv_mpu6515/dmpKey.h b/drivers/iio/imu-aosp/inv_mpu6515/dmpKey.h new file mode 100755 index 00000000000..20b28478785 --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/dmpKey.h @@ -0,0 +1,607 @@ +/* + * Copyright (C) 2012 Invensense, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ +#ifndef DMPKEY_H__ +#define DMPKEY_H__ + +#define KEY_CFG_25                  (0) +#define KEY_CFG_24                  (KEY_CFG_25 + 1) +#define KEY_CFG_26                  (KEY_CFG_24 + 1) +#define KEY_CFG_27                  (KEY_CFG_26 + 1) +#define KEY_CFG_21                  (KEY_CFG_27 + 1) +#define KEY_CFG_20                  (KEY_CFG_21 + 1) +#define KEY_CFG_TAP4                (KEY_CFG_20 + 1) +#define KEY_CFG_TAP5                (KEY_CFG_TAP4 + 1) +#define KEY_CFG_TAP6                (KEY_CFG_TAP5 + 1) +#define KEY_CFG_TAP7                (KEY_CFG_TAP6 + 1) +#define KEY_CFG_TAP0                (KEY_CFG_TAP7 + 1) +#define KEY_CFG_TAP1                (KEY_CFG_TAP0 + 1) +#define KEY_CFG_TAP2                (KEY_CFG_TAP1 + 1) +#define KEY_CFG_TAP3                (KEY_CFG_TAP2 + 1) +#define KEY_CFG_TAP_QUANTIZE        (KEY_CFG_TAP3 + 1) +#define KEY_CFG_TAP_JERK            (KEY_CFG_TAP_QUANTIZE + 1) +#define KEY_CFG_DR_INT              (KEY_CFG_TAP_JERK + 1) +#define KEY_CFG_AUTH                (KEY_CFG_DR_INT + 1) +#define KEY_CFG_TAP_SAVE_ACCB       (KEY_CFG_AUTH + 1) +#define KEY_CFG_TAP_CLEAR_STICKY    (KEY_CFG_TAP_SAVE_ACCB + 1) +#define KEY_CFG_FIFO_ON_EVENT       (KEY_CFG_TAP_CLEAR_STICKY + 1) +#define KEY_FCFG_ACCEL_INPUT        (KEY_CFG_FIFO_ON_EVENT + 1) +#define KEY_FCFG_ACCEL_INIT         (KEY_FCFG_ACCEL_INPUT + 1) +#define KEY_CFG_23                  (KEY_FCFG_ACCEL_INIT + 1) +#define KEY_FCFG_1                  (KEY_CFG_23 + 1) +#define KEY_FCFG_3                  (KEY_FCFG_1 + 1) +#define KEY_FCFG_2                  (KEY_FCFG_3 + 1) +#define KEY_CFG_3D                  (KEY_FCFG_2 + 1) +#define KEY_CFG_3B                  (KEY_CFG_3D + 1) +#define KEY_CFG_3C                  (KEY_CFG_3B + 1) +#define KEY_FCFG_5                  (KEY_CFG_3C + 1) +#define KEY_FCFG_4                  (KEY_FCFG_5 + 1) +#define KEY_FCFG_7                  (KEY_FCFG_4 + 1) +#define KEY_FCFG_FSCALE             (KEY_FCFG_7 + 1) +#define KEY_FCFG_AZ                 (KEY_FCFG_FSCALE + 1) +#define KEY_FCFG_6                  (KEY_FCFG_AZ + 1) +#define KEY_FCFG_LSB4               (KEY_FCFG_6 + 1) +#define KEY_CFG_12                  (KEY_FCFG_LSB4 + 1) +#define KEY_CFG_14                  (KEY_CFG_12 + 1) +#define KEY_CFG_15                  (KEY_CFG_14 + 1) +#define KEY_CFG_16                  (KEY_CFG_15 + 1) +#define KEY_CFG_18                  (KEY_CFG_16 + 1) +#define KEY_CFG_6                   (KEY_CFG_18 + 1) +#define KEY_CFG_7                   (KEY_CFG_6 + 1) +#define KEY_CFG_4                   (KEY_CFG_7 + 1) +#define KEY_CFG_5                   (KEY_CFG_4 + 1) +#define KEY_CFG_2                   (KEY_CFG_5 + 1) +#define KEY_CFG_3                   (KEY_CFG_2 + 1) +#define KEY_CFG_1                   (KEY_CFG_3 + 1) +#define KEY_CFG_EXTERNAL            (KEY_CFG_1 + 1) +#define KEY_CFG_8                   (KEY_CFG_EXTERNAL + 1) +#define KEY_CFG_9                   (KEY_CFG_8 + 1) +#define KEY_CFG_ORIENT_3            (KEY_CFG_9 + 1) +#define KEY_CFG_ORIENT_2            (KEY_CFG_ORIENT_3 + 1) +#define KEY_CFG_ORIENT_1            (KEY_CFG_ORIENT_2 + 1) +#define KEY_CFG_GYRO_SOURCE         (KEY_CFG_ORIENT_1 + 1) +#define KEY_CFG_ORIENT_IRQ_1        (KEY_CFG_GYRO_SOURCE + 1) +#define KEY_CFG_ORIENT_IRQ_2        (KEY_CFG_ORIENT_IRQ_1 + 1) +#define KEY_CFG_ORIENT_IRQ_3        (KEY_CFG_ORIENT_IRQ_2 + 1) +#define KEY_FCFG_MAG_VAL            (KEY_CFG_ORIENT_IRQ_3 + 1) +#define KEY_FCFG_MAG_MOV            (KEY_FCFG_MAG_VAL + 1) +#define KEY_CFG_LP_QUAT             (KEY_FCFG_MAG_MOV + 1) +#define KEY_CFG_GYRO_RAW_DATA       (KEY_CFG_LP_QUAT + 1) +#define KEY_CFG_EXT_GYRO_BIAS       (KEY_CFG_GYRO_RAW_DATA + 1) +#define KEY_CFG_EXT_GYRO_BIAS_X     (KEY_CFG_EXT_GYRO_BIAS + 1) +#define KEY_CFG_EXT_GYRO_BIAS_Y     (KEY_CFG_EXT_GYRO_BIAS_X + 1) +#define KEY_CFG_EXT_GYRO_BIAS_Z     (KEY_CFG_EXT_GYRO_BIAS_Y + 1) +#define KEY_bad_compass				(KEY_CFG_EXT_GYRO_BIAS_Z + 1) +#define KEY_COMPASS_CHG_SENSITIVITY (KEY_bad_compass + 1) +#define KEY_CCS_HEADING_THLD        (KEY_COMPASS_CHG_SENSITIVITY + 1) +#define KEY_CCS_TIME_THLD           (KEY_CCS_HEADING_THLD + 1) +#define KEY_CCS_DOTP_THLD           (KEY_CCS_TIME_THLD + 1) +#define KEY_CCS_COMP_CNTR           (KEY_CCS_DOTP_THLD + 1) +#define KEY_CFG_NM_DET              (KEY_CCS_COMP_CNTR + 1) +#define KEY_SMD_ENABLE              (KEY_CFG_NM_DET + 1) +#define KEY_SMD_ACCEL_THLD          (KEY_SMD_ENABLE + 1) +#define KEY_SMD_DELAY_THLD          (KEY_SMD_ACCEL_THLD + 1) +#define KEY_SMD_DELAY2_THLD         (KEY_SMD_DELAY_THLD + 1) +#define KEY_SMD_ENABLE_TESTPT1      (KEY_SMD_DELAY2_THLD + 1) +#define KEY_SMD_ENABLE_TESTPT2      (KEY_SMD_ENABLE_TESTPT1 + 1) +#define KEY_SMD_EXE_STATE           (KEY_SMD_ENABLE_TESTPT2 + 1) +#define KEY_SMD_DELAY_CNTR          (KEY_SMD_EXE_STATE + 1) + +#define KEY_BREAK (81) +#if KEY_SMD_DELAY_CNTR != KEY_BREAK +#error +#endif + +/* MPU6050 keys */ +#define KEY_CFG_ACCEL_FILTER        (KEY_BREAK + 1) +#define KEY_CFG_MOTION_BIAS         (KEY_CFG_ACCEL_FILTER + 1) +#define KEY_TEMPLABEL               (KEY_CFG_MOTION_BIAS + 1) + +#define KEY_D_0_22                  (KEY_TEMPLABEL + 1) +#define KEY_D_0_24                  (KEY_D_0_22 + 1) +#define KEY_D_0_36                  (KEY_D_0_24 + 1) +#define KEY_D_0_52                  (KEY_D_0_36 + 1) +#define KEY_D_0_96                  (KEY_D_0_52 + 1) +#define KEY_D_0_104                 (KEY_D_0_96 + 1) +#define KEY_D_0_108                 (KEY_D_0_104 + 1) +#define KEY_D_0_163                 (KEY_D_0_108 + 1) +#define KEY_D_0_188                 (KEY_D_0_163 + 1) +#define KEY_D_0_192                 (KEY_D_0_188 + 1) +#define KEY_D_0_224                 (KEY_D_0_192 + 1) +#define KEY_D_0_228                 (KEY_D_0_224 + 1) +#define KEY_D_0_232                 (KEY_D_0_228 + 1) +#define KEY_D_0_236                 (KEY_D_0_232 + 1) + +#define KEY_DMP_PREVPTAT            (KEY_D_0_236 + 1) +#define KEY_D_1_2                   (KEY_DMP_PREVPTAT + 1) +#define KEY_D_1_4                   (KEY_D_1_2 + 1) +#define KEY_D_1_8                   (KEY_D_1_4 + 1) +#define KEY_D_1_10                  (KEY_D_1_8 + 1) +#define KEY_D_1_24                  (KEY_D_1_10 + 1) +#define KEY_D_1_28                  (KEY_D_1_24 + 1) +#define KEY_D_1_36                  (KEY_D_1_28 + 1) +#define KEY_D_1_40                  (KEY_D_1_36 + 1) +#define KEY_D_1_44                  (KEY_D_1_40 + 1) +#define KEY_D_1_72                  (KEY_D_1_44 + 1) +#define KEY_D_1_74                  (KEY_D_1_72 + 1) +#define KEY_D_1_79                  (KEY_D_1_74 + 1) +#define KEY_D_1_88                  (KEY_D_1_79 + 1) +#define KEY_D_1_90                  (KEY_D_1_88 + 1) +#define KEY_D_1_92                  (KEY_D_1_90 + 1) +#define KEY_D_1_96                  (KEY_D_1_92 + 1) +#define KEY_D_1_98                  (KEY_D_1_96 + 1) +#define KEY_D_1_100                 (KEY_D_1_98 + 1) +#define KEY_D_1_106                 (KEY_D_1_100 + 1) +#define KEY_D_1_108                 (KEY_D_1_106 + 1) +#define KEY_D_1_112                 (KEY_D_1_108 + 1) +#define KEY_D_1_128                 (KEY_D_1_112 + 1) +#define KEY_D_1_152                 (KEY_D_1_128 + 1) +#define KEY_D_1_160                 (KEY_D_1_152 + 1) +#define KEY_D_1_168                 (KEY_D_1_160 + 1) +#define KEY_D_1_175                 (KEY_D_1_168 + 1) +#define KEY_D_1_176                 (KEY_D_1_175 + 1) +#define KEY_D_1_178                 (KEY_D_1_176 + 1) +#define KEY_D_1_179                 (KEY_D_1_178 + 1) +#define KEY_D_1_218                 (KEY_D_1_179 + 1) +#define KEY_D_1_232                 (KEY_D_1_218 + 1) +#define KEY_D_1_236                 (KEY_D_1_232 + 1) +#define KEY_D_1_240                 (KEY_D_1_236 + 1) +#define KEY_D_1_244                 (KEY_D_1_240 + 1) +#define KEY_D_1_250                 (KEY_D_1_244 + 1) +#define KEY_D_1_252                 (KEY_D_1_250 + 1) +#define KEY_D_2_12                  (KEY_D_1_252 + 1) +#define KEY_D_2_96                  (KEY_D_2_12 + 1) +#define KEY_D_2_108                 (KEY_D_2_96 + 1) +#define KEY_D_2_208                 (KEY_D_2_108 + 1) +#define KEY_FLICK_MSG               (KEY_D_2_208 + 1) +#define KEY_FLICK_COUNTER           (KEY_FLICK_MSG + 1) +#define KEY_FLICK_LOWER             (KEY_FLICK_COUNTER + 1) +#define KEY_CFG_FLICK_IN            (KEY_FLICK_LOWER + 1) +#define KEY_FLICK_UPPER             (KEY_CFG_FLICK_IN + 1) +#define KEY_CGNOTICE_INTR           (KEY_FLICK_UPPER + 1) +#define KEY_D_2_224                 (KEY_CGNOTICE_INTR + 1) +#define KEY_D_2_244                 (KEY_D_2_224 + 1) +#define KEY_D_2_248                 (KEY_D_2_244 + 1) +#define KEY_D_2_252                 (KEY_D_2_248 + 1) + +#define KEY_D_GYRO_BIAS_X               (KEY_D_2_252 + 1) +#define KEY_D_GYRO_BIAS_Y               (KEY_D_GYRO_BIAS_X + 1) +#define KEY_D_GYRO_BIAS_Z               (KEY_D_GYRO_BIAS_Y + 1) +#define KEY_D_ACC_BIAS_X                (KEY_D_GYRO_BIAS_Z + 1) +#define KEY_D_ACC_BIAS_Y                (KEY_D_ACC_BIAS_X + 1) +#define KEY_D_ACC_BIAS_Z                (KEY_D_ACC_BIAS_Y + 1) +#define KEY_D_GYRO_ENABLE               (KEY_D_ACC_BIAS_Z + 1) +#define KEY_D_ACCEL_ENABLE              (KEY_D_GYRO_ENABLE + 1) +#define KEY_D_QUAT_ENABLE               (KEY_D_ACCEL_ENABLE + 1) +#define KEY_D_OUTPUT_ENABLE             (KEY_D_QUAT_ENABLE + 1) +#define KEY_D_ACCEL_CNTR                (KEY_D_OUTPUT_ENABLE + 1) +#define KEY_D_GYRO_CNTR                 (KEY_D_ACCEL_CNTR + 1) +#define KEY_D_QUAT0_CNTR                (KEY_D_GYRO_CNTR + 1) +#define KEY_D_QUAT1_CNTR                (KEY_D_QUAT0_CNTR + 1) +#define KEY_D_QUAT2_CNTR                (KEY_D_QUAT1_CNTR + 1) +#define KEY_D_CR_TIME_G                 (KEY_D_QUAT2_CNTR + 1) +#define KEY_D_CR_TIME_A                 (KEY_D_CR_TIME_G + 1) +#define KEY_D_CR_TIME_Q                 (KEY_D_CR_TIME_A + 1) +#define KEY_D_CS_TAX                    (KEY_D_CR_TIME_Q + 1) +#define KEY_D_CS_TAY                    (KEY_D_CS_TAX + 1) +#define KEY_D_CS_TAZ                    (KEY_D_CS_TAY + 1) +#define KEY_D_CS_TGX                    (KEY_D_CS_TAZ + 1) +#define KEY_D_CS_TGY                    (KEY_D_CS_TGX + 1) +#define KEY_D_CS_TGZ                    (KEY_D_CS_TGY + 1) +#define KEY_D_CS_TQ0                    (KEY_D_CS_TGZ + 1) +#define KEY_D_CS_TQ1                    (KEY_D_CS_TQ0 + 1) +#define KEY_D_CS_TQ2                    (KEY_D_CS_TQ1 + 1) +#define KEY_D_CS_TQ3                    (KEY_D_CS_TQ2 + 1) + +/* Compass keys */ +#define KEY_CPASS_GAIN              (KEY_D_CS_TQ3 + 1) +#define KEY_CPASS_BIAS_X            (KEY_CPASS_GAIN + 1) +#define KEY_CPASS_BIAS_Y            (KEY_CPASS_BIAS_X + 1) +#define KEY_CPASS_BIAS_Z            (KEY_CPASS_BIAS_Y + 1) +#define KEY_CPASS_MTX_00            (KEY_CPASS_BIAS_Z + 1) +#define KEY_CPASS_MTX_01            (KEY_CPASS_MTX_00 + 1) +#define KEY_CPASS_MTX_02            (KEY_CPASS_MTX_01 + 1) +#define KEY_CPASS_MTX_10            (KEY_CPASS_MTX_02 + 1) +#define KEY_CPASS_MTX_11            (KEY_CPASS_MTX_10 + 1) +#define KEY_CPASS_MTX_12            (KEY_CPASS_MTX_11 + 1) +#define KEY_CPASS_MTX_20            (KEY_CPASS_MTX_12 + 1) +#define KEY_CPASS_MTX_21            (KEY_CPASS_MTX_20 + 1) +#define KEY_CPASS_MTX_22            (KEY_CPASS_MTX_21 + 1) +#define KEY_CPASS_STATUS_CHK		(KEY_CPASS_MTX_22 + 1) +/* Tap Keys */ +#define KEY_DMP_TAP_GATE              (KEY_CPASS_STATUS_CHK + 1) +#define KEY_DMP_TAPW_MIN              (KEY_DMP_TAP_GATE + 1) +#define KEY_DMP_TAP_THR_Z             (KEY_DMP_TAPW_MIN + 1) +#define KEY_DMP_TAP_PREV_JERK_Z       (KEY_DMP_TAP_THR_Z + 1) +#define KEY_DMP_TAP_MIN_TAPS          (KEY_DMP_TAP_PREV_JERK_Z + 1) +#define KEY_DMP_TAP_NEXT_TAP_THRES    (KEY_DMP_TAP_MIN_TAPS + 1) +#define KEY_DMP_TAP_SHAKE_REJECT      (KEY_DMP_TAP_NEXT_TAP_THRES + 1) +#define KEY_DMP_TAP_SHAKE_COUNT_MAX   (KEY_DMP_TAP_SHAKE_REJECT + 1) +#define KEY_DMP_TAP_SHAKE_TIMEOUT_MAX (KEY_DMP_TAP_SHAKE_COUNT_MAX + 1) +#define KEY_DMP_TAP_DIRECTION         (KEY_DMP_TAP_SHAKE_TIMEOUT_MAX + 1) +#define KEY_DMP_TAP_COUNT             (KEY_DMP_TAP_DIRECTION + 1) +/* Gesture Keys */ +#define KEY_DMP_SH_TH_Y             (KEY_DMP_TAP_COUNT + 1) +#define KEY_DMP_SH_TH_X             (KEY_DMP_SH_TH_Y + 1) +#define KEY_DMP_SH_TH_Z             (KEY_DMP_SH_TH_X + 1) +#define KEY_DMP_ORIENT              (KEY_DMP_SH_TH_Z + 1) +#define KEY_D_ACT0                  (KEY_DMP_ORIENT + 1) +#define KEY_D_ACSX                  (KEY_D_ACT0 + 1) +#define KEY_D_ACSY                  (KEY_D_ACSX + 1) +#define KEY_D_ACSZ                  (KEY_D_ACSY + 1) + +#define KEY_CFG_DISPLAY_ORIENT_INT  (KEY_D_ACSZ + 1) +#define KEY_NO_ORIENT_INTERRUPT     (KEY_CFG_DISPLAY_ORIENT_INT + 1) +#define KEY_X_GRT_Y_TMP2            (KEY_NO_ORIENT_INTERRUPT + 1) + +/*Shake Keys */ +#define KEY_D_0_64                  (KEY_X_GRT_Y_TMP2 + 1) +#define KEY_D_2_4                   (KEY_D_0_64 + 1) +#define KEY_D_2_8                   (KEY_D_2_4 + 1) +#define KEY_D_2_48                  (KEY_D_2_8 + 1) +#define KEY_D_2_92                  (KEY_D_2_48 + 1) +#define KEY_D_2_94                  (KEY_D_2_92 + 1) +#define KEY_D_2_160                 (KEY_D_2_94 + 1) +#define KEY_D_3_180                 (KEY_D_2_160 + 1) +#define KEY_D_3_184                 (KEY_D_3_180 + 1) +#define KEY_D_3_188                 (KEY_D_3_184 + 1) +#define KEY_D_3_208                 (KEY_D_3_188 + 1) +#define KEY_D_3_240                 (KEY_D_3_208 + 1) +#define KEY_RETRACTION_1            (KEY_D_3_240 + 1) +#define KEY_RETRACTION_2            (KEY_RETRACTION_1 + 1) +#define KEY_RETRACTION_3            (KEY_RETRACTION_2 + 1) +#define KEY_RETRACTION_4            (KEY_RETRACTION_3 + 1) +#define KEY_CFG_SHAKE_INT           (KEY_RETRACTION_4 + 1) + +/* Authenticate Keys */ +#define KEY_D_AUTH_OUT              (KEY_CFG_SHAKE_INT + 1) +#define KEY_D_AUTH_IN               (KEY_D_AUTH_OUT + 1) +#define KEY_D_AUTH_A                (KEY_D_AUTH_IN + 1) +#define KEY_D_AUTH_B                (KEY_D_AUTH_A + 1) + +/* Pedometer standalone only keys */ +#define KEY_D_PEDSTD_BP_B           (KEY_D_AUTH_B + 1) +#define KEY_D_PEDSTD_HP_A           (KEY_D_PEDSTD_BP_B + 1) +#define KEY_D_PEDSTD_HP_B           (KEY_D_PEDSTD_HP_A + 1) +#define KEY_D_PEDSTD_BP_A4          (KEY_D_PEDSTD_HP_B + 1) +#define KEY_D_PEDSTD_BP_A3          (KEY_D_PEDSTD_BP_A4 + 1) +#define KEY_D_PEDSTD_BP_A2          (KEY_D_PEDSTD_BP_A3 + 1) +#define KEY_D_PEDSTD_BP_A1          (KEY_D_PEDSTD_BP_A2 + 1) +#define KEY_D_PEDSTD_INT_THRSH      (KEY_D_PEDSTD_BP_A1 + 1) +#define KEY_D_PEDSTD_CLIP           (KEY_D_PEDSTD_INT_THRSH + 1) +#define KEY_D_PEDSTD_SB             (KEY_D_PEDSTD_CLIP + 1) +#define KEY_D_PEDSTD_SB_TIME        (KEY_D_PEDSTD_SB + 1) +#define KEY_D_PEDSTD_PEAKTHRSH      (KEY_D_PEDSTD_SB_TIME + 1) +#define KEY_D_PEDSTD_TIML           (KEY_D_PEDSTD_PEAKTHRSH + 1) +#define KEY_D_PEDSTD_TIMH           (KEY_D_PEDSTD_TIML + 1) +#define KEY_D_PEDSTD_PEAK           (KEY_D_PEDSTD_TIMH + 1) +#define KEY_D_PEDSTD_TIMECTR        (KEY_D_PEDSTD_PEAK + 1) +#define KEY_D_PEDSTD_STEPCTR        (KEY_D_PEDSTD_TIMECTR + 1) +#define KEY_D_PEDSTD_STEPCTR2		(KEY_D_PEDSTD_STEPCTR + 1) +#define KEY_D_PEDSTD_WALKTIME       (KEY_D_PEDSTD_STEPCTR2 + 1) +#define KEY_D_PEDSTD_DECI           (KEY_D_PEDSTD_WALKTIME + 1) +#define KEY_D_PEDSTD_SB2			(KEY_D_PEDSTD_DECI + 1) +#define KEY_D_PEDSTD_DRIVE_STATE    (KEY_D_PEDSTD_SB2 + 1) +#define KEY_CFG_PED_INT             (KEY_D_PEDSTD_DRIVE_STATE + 1) +#define KEY_CFG_PED_ENABLE          (KEY_CFG_PED_INT + 1) +#define KEY_D_STPDET_TIMESTAMP      (KEY_CFG_PED_ENABLE + 1) + +/*Host Based No Motion*/ +#define KEY_D_HOST_NO_MOT           (KEY_D_STPDET_TIMESTAMP + 1) + +/*Host Based Accel Bias*/ +#define KEY_D_ACCEL_BIAS            (KEY_D_HOST_NO_MOT + 1) + +/*Screen/Display Orientation Keys*/ +#define KEY_D_ORIENT_GAP            (KEY_D_ACCEL_BIAS + 1) +#define KEY_D_TILT0_H               (KEY_D_ORIENT_GAP + 1) +#define KEY_D_TILT0_L               (KEY_D_TILT0_H + 1) +#define KEY_D_TILT1_H               (KEY_D_TILT0_L + 1) +#define KEY_D_TILT1_L               (KEY_D_TILT1_H + 1) +#define KEY_D_TILT2_H               (KEY_D_TILT1_L + 1) +#define KEY_D_TILT2_L               (KEY_D_TILT2_H  + 1) +#define KEY_D_TILT3_H               (KEY_D_TILT2_L + 1) +#define KEY_D_TILT3_L               (KEY_D_TILT3_H + 1) + +#define KEY_STREAM_P_ACCEL_Z        (KEY_D_TILT3_L + 1) + +/* Batch mode */ +#define KEY_BM_ENABLE               (KEY_STREAM_P_ACCEL_Z + 1) +#define KEY_BM_BATCH_THLD           (KEY_BM_ENABLE + 1) +#define KEY_BM_BATCH_CNTR           (KEY_BM_BATCH_THLD + 1) +#define KEY_BM_NUMWORD_TOFILL       (KEY_BM_BATCH_CNTR + 1) + +/* Watermark */ +#define KEY_CFG_WATERMARK_H         (KEY_BM_NUMWORD_TOFILL + 1) +#define KEY_CFG_WATERMARK_L         (KEY_CFG_WATERMARK_H + 1) + +/* FIFO output control */ +#define KEY_CFG_OUT_ACCL            (KEY_CFG_WATERMARK_L + 1) +#define KEY_CFG_OUT_GYRO            (KEY_CFG_OUT_ACCL + 1) +#define KEY_CFG_OUT_3QUAT           (KEY_CFG_OUT_GYRO + 1) +#define KEY_CFG_OUT_6QUAT           (KEY_CFG_OUT_3QUAT + 1) +#define KEY_CFG_OUT_9QUAT           (KEY_CFG_OUT_6QUAT + 1) +#define KEY_CFG_OUT_PQUAT           (KEY_CFG_OUT_9QUAT + 1) +#define KEY_CFG_OUT_PQUAT9          (KEY_CFG_OUT_PQUAT + 1) +#define KEY_CFG_OUT_CPASS           (KEY_CFG_OUT_PQUAT9 + 1) +#define KEY_CFG_OUT_PRESS           (KEY_CFG_OUT_CPASS + 1) +#define KEY_CFG_OUT_STEPDET         (KEY_CFG_OUT_PRESS + 1) +#define KEY_CFG_FIFO_INT            (KEY_CFG_OUT_STEPDET + 1) + +/* Ped Step detection */ +#define KEY_CFG_PEDSTEP_DET         (KEY_CFG_FIFO_INT + 1) + +/* Screen Orientation data */ +#define KEY_SO_DATA                 (KEY_CFG_PEDSTEP_DET + 1) + +/* MPU for DMP Android K */ +#define KEY_P_INIT                  (KEY_SO_DATA + 1) +#define KEY_P_HW_ID                 (KEY_P_INIT + 1) + +/* DMP running counter */ +#define KEY_DMP_RUN_CNTR            (KEY_P_HW_ID + 1) + +/* Sensor's ODR */ +#define KEY_CFG_3QUAT_ODR           (KEY_DMP_RUN_CNTR + 1) +#define KEY_CFG_6QUAT_ODR           (KEY_CFG_3QUAT_ODR + 1) +#define KEY_CFG_9QUAT_ODR           (KEY_CFG_6QUAT_ODR + 1) +#define KEY_CFG_PQUAT6_ODR          (KEY_CFG_9QUAT_ODR + 1) +#define KEY_CFG_PQUAT9_ODR          (KEY_CFG_PQUAT6_ODR + 1) +#define KEY_CFG_ACCL_ODR            (KEY_CFG_PQUAT9_ODR + 1) +#define KEY_CFG_GYRO_ODR            (KEY_CFG_ACCL_ODR + 1) +#define KEY_CFG_CPASS_ODR           (KEY_CFG_GYRO_ODR + 1) +#define KEY_CFG_PRESS_ODR           (KEY_CFG_CPASS_ODR + 1) + +#define KEY_ODR_CNTR_3QUAT          (KEY_CFG_PRESS_ODR + 1) +#define KEY_ODR_CNTR_6QUAT          (KEY_ODR_CNTR_3QUAT + 1) +#define KEY_ODR_CNTR_9QUAT          (KEY_ODR_CNTR_6QUAT + 1) +#define KEY_ODR_CNTR_PQUAT          (KEY_ODR_CNTR_9QUAT + 1) +#define KEY_ODR_CNTR_PQUAT9         (KEY_ODR_CNTR_PQUAT + 1) +#define KEY_ODR_CNTR_ACCL           (KEY_ODR_CNTR_PQUAT9 + 1) +#define KEY_ODR_CNTR_GYRO           (KEY_ODR_CNTR_ACCL + 1) +#define KEY_ODR_CNTR_CPASS          (KEY_ODR_CNTR_GYRO + 1) +#define KEY_ODR_CNTR_PRESS          (KEY_ODR_CNTR_CPASS + 1) + +/* DMP fusion LP-Quat */ +#define KEY_DMP_LPQ0                (KEY_ODR_CNTR_PRESS + 1) +#define KEY_DMP_LPQ1                (KEY_DMP_LPQ0 + 1) +#define KEY_DMP_LPQ2                (KEY_DMP_LPQ1 + 1) +#define KEY_DMP_LPQ3                (KEY_DMP_LPQ2 + 1) + +/* DMP fusion 6-axis Quat */ +#define KEY_DMP_Q0                  (KEY_DMP_LPQ3 + 1) +#define KEY_DMP_Q1                  (KEY_DMP_Q0 + 1) +#define KEY_DMP_Q2                  (KEY_DMP_Q1 + 1) +#define KEY_DMP_Q3                  (KEY_DMP_Q2 + 1) + +/* 9-axis fusion */ +#define KEY_CPASS_VALID             (KEY_DMP_Q3 + 1) +#define KEY_9AXIS_ACCURACY          (KEY_CPASS_VALID + 1) +#define KEY_DMP_9Q0                 (KEY_9AXIS_ACCURACY + 1) +#define KEY_DMP_9Q1                 (KEY_DMP_9Q0 + 1) +#define KEY_DMP_9Q2                 (KEY_DMP_9Q1 + 1) +#define KEY_DMP_9Q3                 (KEY_DMP_9Q2 + 1) + +/* Test key */ +#define KEY_TEST_01                 (KEY_DMP_9Q3 + 1) +#define KEY_TEST_02                 (KEY_TEST_01 + 1) +#define KEY_TEST_03                 (KEY_TEST_02 + 1) +#define KEY_TEST_04                 (KEY_TEST_03 + 1) +#define KEY_TEST_05                 (KEY_TEST_04 + 1) +#define KEY_TEST_06                 (KEY_TEST_05 + 1) +#define KEY_TEST_07                 (KEY_TEST_06 + 1) +#define KEY_TEST_XX                 (KEY_TEST_07 + 1) + +#define NUM_KEYS                    (KEY_TEST_XX + 1) + +struct tKeyLabel  { +	unsigned short key; +	unsigned short addr; +}; + +#define DINA0A 0x0a +#define DINA22 0x22 +#define DINA42 0x42 +#define DINA5A 0x5a + +#define DINA06 0x06 +#define DINA0E 0x0e +#define DINA16 0x16 +#define DINA1E 0x1e +#define DINA26 0x26 +#define DINA2E 0x2e +#define DINA36 0x36 +#define DINA3E 0x3e +#define DINA46 0x46 +#define DINA4E 0x4e +#define DINA56 0x56 +#define DINA5E 0x5e +#define DINA66 0x66 +#define DINA6E 0x6e +#define DINA76 0x76 +#define DINA7E 0x7e + +#define DINA00 0x00 +#define DINA08 0x08 +#define DINA10 0x10 +#define DINA18 0x18 +#define DINA20 0x20 +#define DINA28 0x28 +#define DINA30 0x30 +#define DINA38 0x38 +#define DINA40 0x40 +#define DINA48 0x48 +#define DINA50 0x50 +#define DINA58 0x58 +#define DINA60 0x60 +#define DINA68 0x68 +#define DINA70 0x70 +#define DINA78 0x78 + +#define DINA04 0x04 +#define DINA0C 0x0c +#define DINA14 0x14 +#define DINA1C 0x1C +#define DINA24 0x24 +#define DINA2C 0x2c +#define DINA34 0x34 +#define DINA3C 0x3c +#define DINA44 0x44 +#define DINA4C 0x4c +#define DINA54 0x54 +#define DINA5C 0x5c +#define DINA64 0x64 +#define DINA6C 0x6c +#define DINA74 0x74 +#define DINA7C 0x7c + +#define DINA01 0x01 +#define DINA09 0x09 +#define DINA11 0x11 +#define DINA19 0x19 +#define DINA21 0x21 +#define DINA29 0x29 +#define DINA31 0x31 +#define DINA39 0x39 +#define DINA41 0x41 +#define DINA49 0x49 +#define DINA51 0x51 +#define DINA59 0x59 +#define DINA61 0x61 +#define DINA69 0x69 +#define DINA71 0x71 +#define DINA79 0x79 + +#define DINA25 0x25 +#define DINA2D 0x2d +#define DINA35 0x35 +#define DINA3D 0x3d +#define DINA4D 0x4d +#define DINA55 0x55 +#define DINA5D 0x5D +#define DINA6D 0x6d +#define DINA75 0x75 +#define DINA7D 0x7d + +#define DINADC 0xdc +#define DINAF2 0xf2 +#define DINAAB 0xab +#define DINAAA 0xaa +#define DINAF1 0xf1 +#define DINADF 0xdf +#define DINADA 0xda +#define DINAB1 0xb1 +#define DINAB9 0xb9 +#define DINAF3 0xf3 +#define DINA8B 0x8b +#define DINAA3 0xa3 +#define DINA91 0x91 +#define DINAB6 0xb6 +#define DINAB4 0xb4 + +#define DINC00 0x00 +#define DINC01 0x01 +#define DINC02 0x02 +#define DINC03 0x03 +#define DINC08 0x08 +#define DINC09 0x09 +#define DINC0A 0x0a +#define DINC0B 0x0b +#define DINC10 0x10 +#define DINC11 0x11 +#define DINC12 0x12 +#define DINC13 0x13 +#define DINC18 0x18 +#define DINC19 0x19 +#define DINC1A 0x1a +#define DINC1B 0x1b + +#define DINC20 0x20 +#define DINC21 0x21 +#define DINC22 0x22 +#define DINC23 0x23 +#define DINC28 0x28 +#define DINC29 0x29 +#define DINC2A 0x2a +#define DINC2B 0x2b +#define DINC30 0x30 +#define DINC31 0x31 +#define DINC32 0x32 +#define DINC33 0x33 +#define DINC38 0x38 +#define DINC39 0x39 +#define DINC3A 0x3a +#define DINC3B 0x3b + +#define DINC40 0x40 +#define DINC41 0x41 +#define DINC42 0x42 +#define DINC43 0x43 +#define DINC48 0x48 +#define DINC49 0x49 +#define DINC4A 0x4a +#define DINC4B 0x4b +#define DINC50 0x50 +#define DINC51 0x51 +#define DINC52 0x52 +#define DINC53 0x53 +#define DINC58 0x58 +#define DINC59 0x59 +#define DINC5A 0x5a +#define DINC5B 0x5b + +#define DINC60 0x60 +#define DINC61 0x61 +#define DINC62 0x62 +#define DINC63 0x63 +#define DINC68 0x68 +#define DINC69 0x69 +#define DINC6A 0x6a +#define DINC6B 0x6b +#define DINC70 0x70 +#define DINC71 0x71 +#define DINC72 0x72 +#define DINC73 0x73 +#define DINC78 0x78 +#define DINC79 0x79 +#define DINC7A 0x7a +#define DINC7B 0x7b +#define DIND40 0x40 +#define DINA80 0x80 +#define DINA90 0x90 +#define DINAA0 0xa0 +#define DINAC9 0xc9 +#define DINACB 0xcb +#define DINACD 0xcd +#define DINACF 0xcf +#define DINAC8 0xc8 +#define DINACA 0xca +#define DINACC 0xcc +#define DINACE 0xce +#define DINAD8 0xd8 +#define DINADD 0xdd +#define DINAF8 0xf0 +#define DINAFE 0xfe + +#define DINBF8 0xf8 +#define DINAC0 0xb0 +#define DINAC1 0xb1 +#define DINAC2 0xb4 +#define DINAC3 0xb5 +#define DINAC4 0xb8 +#define DINAC5 0xb9 +#define DINBC0 0xc0 +#define DINBC2 0xc2 +#define DINBC4 0xc4 +#define DINBC6 0xc6 + +#endif diff --git a/drivers/iio/imu-aosp/inv_mpu6515/dmpmap.h b/drivers/iio/imu-aosp/inv_mpu6515/dmpmap.h new file mode 100755 index 00000000000..92936372224 --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/dmpmap.h @@ -0,0 +1,263 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ + +#ifndef DMPMAP_H +#define DMPMAP_H + +#define DMP_PTAT    0 +#define DMP_XGYR    2 +#define DMP_YGYR    4 +#define DMP_ZGYR    6 +#define DMP_XACC    8 +#define DMP_YACC    10 +#define DMP_ZACC    12 +#define DMP_ADC1    14 +#define DMP_ADC2    16 +#define DMP_ADC3    18 +#define DMP_BIASUNC    20 +#define DMP_FIFORT    22 +#define DMP_INVGSFH    24 +#define DMP_INVGSFL    26 +#define DMP_1H    28 +#define DMP_1L    30 +#define DMP_BLPFSTCH    32 +#define DMP_BLPFSTCL    34 +#define DMP_BLPFSXH    36 +#define DMP_BLPFSXL    38 +#define DMP_BLPFSYH    40 +#define DMP_BLPFSYL    42 +#define DMP_BLPFSZH    44 +#define DMP_BLPFSZL    46 +#define DMP_BLPFMTC    48 +#define DMP_SMC    50 +#define DMP_BLPFMXH    52 +#define DMP_BLPFMXL    54 +#define DMP_BLPFMYH    56 +#define DMP_BLPFMYL    58 +#define DMP_BLPFMZH    60 +#define DMP_BLPFMZL    62 +#define DMP_BLPFC    64 +#define DMP_SMCTH    66 +#define DMP_TAP_DIRECTION 68 +#define DMP_TAP_COUNT 70 +#define DMP_TAP_GATE    72 +#define DMP_TAP_MIN_TAPS    74 +#define DMP_BERR2NH    76 +#define DMP_SMCINC    78 +#define DMP_TAP_SHAKE_REJECT    80 +#define DMP_ANGVBXL    82 +#define DMP_TAP_SHAKE_COUNT_MAX    84 +#define DMP_TAP_SHAKE_TIMEOUT_MAX    86 +#define DMP_TAP_THZ    88 +#define DMP_TAPW_MIN    90 +#define DMP_TAP_PREV_JERK_Z    92 +#define DMP_TAP_NEXT_TAP_THRES    94 +#define DMP_ATCH    96 +#define DMP_BIASUNCSF    98 +#define DMP_ACT2H    100 +#define DMP_ACT2L    102 +#define DMP_GSFH    104 +#define DMP_GSFL    106 +#define DMP_GH    108 +#define DMP_GL    110 +#define DMP_0_5H    112 +#define DMP_0_5L    114 +#define DMP_0_0H    116 +#define DMP_0_0L    118 +#define DMP_1_0H    120 +#define DMP_1_0L    122 +#define DMP_1_5H    124 +#define DMP_1_5L    126 +#define DMP_TMP1AH    128 +#define DMP_TMP1AL    130 +#define DMP_TMP2AH    132 +#define DMP_TMP2AL    134 +#define DMP_TMP3AH    136 +#define DMP_TMP3AL    138 +#define DMP_TMP4AH    140 +#define DMP_TMP4AL    142 +#define DMP_XACCW    144 +#define DMP_TMP5    146 +#define DMP_XACCB    148 +#define DMP_TMP8    150 +#define DMP_YACCB    152 +#define DMP_TMP9    154 +#define DMP_ZACCB    156 +#define DMP_TMP10    158 +#define DMP_DZH    160 +#define DMP_DZL    162 +#define DMP_XGCH    164 +#define DMP_XGCL    166 +#define DMP_YGCH    168 +#define DMP_YGCL    170 +#define DMP_ZGCH    172 +#define DMP_ZGCL    174 +#define DMP_YACCW    176 +#define DMP_TMP7    178 +#define DMP_AFB1H    180 +#define DMP_AFB1L    182 +#define DMP_AFB2H    184 +#define DMP_AFB2L    186 +#define DMP_MAGFBH    188 +#define DMP_MAGFBL    190 +#define DMP_QT1H    192 +#define DMP_QT1L    194 +#define DMP_QT2H    196 +#define DMP_QT2L    198 +#define DMP_QT3H    200 +#define DMP_QT3L    202 +#define DMP_QT4H    204 +#define DMP_QT4L    206 +#define DMP_CTRL1H    208 +#define DMP_CTRL1L    210 +#define DMP_CTRL2H    212 +#define DMP_CTRL2L    214 +#define DMP_CTRL3H    216 +#define DMP_CTRL3L    218 +#define DMP_CTRL4H    220 +#define DMP_CTRL4L    222 +#define DMP_CTRLS1    224 +#define DMP_CTRLSF1    226 +#define DMP_CTRLS2    228 +#define DMP_CTRLSF2    230 +#define DMP_CTRLS3    232 +#define DMP_CTRLSFNLL    234 +#define DMP_CTRLS4    236 +#define DMP_CTRLSFNL2    238 +#define DMP_CTRLSFNL    240 +#define DMP_TMP30    242 +#define DMP_CTRLSFJT    244 +#define DMP_TMP31    246 +#define DMP_TMP11    248 +#define DMP_CTRLSF2_2    250 +#define DMP_TMP12    252 +#define DMP_CTRLSF1_2    254 +#define DMP_PREVPTAT    256 +#define DMP_ACCZB    258 +#define DMP_ACCXB    264 +#define DMP_ACCYB    266 +#define DMP_1HB    272 +#define DMP_1LB    274 +#define DMP_0H    276 +#define DMP_0L    278 +#define DMP_ASR22H    280 +#define DMP_ASR22L    282 +#define DMP_ASR6H    284 +#define DMP_ASR6L    286 +#define DMP_TMP13    288 +#define DMP_TMP14    290 +#define DMP_FINTXH    292 +#define DMP_FINTXL    294 +#define DMP_FINTYH    296 +#define DMP_FINTYL    298 +#define DMP_FINTZH    300 +#define DMP_FINTZL    302 +#define DMP_TMP1BH    304 +#define DMP_TMP1BL    306 +#define DMP_TMP2BH    308 +#define DMP_TMP2BL    310 +#define DMP_TMP3BH    312 +#define DMP_TMP3BL    314 +#define DMP_TMP4BH    316 +#define DMP_TMP4BL    318 +#define DMP_STXG    320 +#define DMP_ZCTXG    322 +#define DMP_STYG    324 +#define DMP_ZCTYG    326 +#define DMP_STZG    328 +#define DMP_ZCTZG    330 +#define DMP_CTRLSFJT2    332 +#define DMP_CTRLSFJTCNT    334 +#define DMP_PVXG    336 +#define DMP_TMP15    338 +#define DMP_PVYG    340 +#define DMP_TMP16    342 +#define DMP_PVZG    344 +#define DMP_TMP17    346 +#define DMP_MNMFLAGH    352 +#define DMP_MNMFLAGL    354 +#define DMP_MNMTMH    356 +#define DMP_MNMTML    358 +#define DMP_MNMTMTHRH    360 +#define DMP_MNMTMTHRL    362 +#define DMP_MNMTHRH    364 +#define DMP_MNMTHRL    366 +#define DMP_ACCQD4H    368 +#define DMP_ACCQD4L    370 +#define DMP_ACCQD5H    372 +#define DMP_ACCQD5L    374 +#define DMP_ACCQD6H    376 +#define DMP_ACCQD6L    378 +#define DMP_ACCQD7H    380 +#define DMP_ACCQD7L    382 +#define DMP_ACCQD0H    384 +#define DMP_ACCQD0L    386 +#define DMP_ACCQD1H    388 +#define DMP_ACCQD1L    390 +#define DMP_ACCQD2H    392 +#define DMP_ACCQD2L    394 +#define DMP_ACCQD3H    396 +#define DMP_ACCQD3L    398 +#define DMP_XN2H    400 +#define DMP_XN2L    402 +#define DMP_XN1H    404 +#define DMP_XN1L    406 +#define DMP_YN2H    408 +#define DMP_YN2L    410 +#define DMP_YN1H    412 +#define DMP_YN1L    414 +#define DMP_YH    416 +#define DMP_YL    418 +#define DMP_B0H    420 +#define DMP_B0L    422 +#define DMP_A1H    424 +#define DMP_A1L    426 +#define DMP_A2H    428 +#define DMP_A2L    430 +#define DMP_SEM1    432 +#define DMP_FIFOCNT    434 +#define DMP_SH_TH_X    436 +#define DMP_PACKET    438 +#define DMP_SH_TH_Y    440 +#define DMP_FOOTER    442 +#define DMP_SH_TH_Z    444 +#define DMP_TEMP29    448 +#define DMP_TEMP30    450 +#define DMP_XACCB_PRE    452 +#define DMP_XACCB_PREL    454 +#define DMP_YACCB_PRE    456 +#define DMP_YACCB_PREL    458 +#define DMP_ZACCB_PRE    460 +#define DMP_ZACCB_PREL    462 +#define DMP_TMP22    464 +#define DMP_TAP_TIMER    466 +#define DMP_TAP_THX    468 +#define DMP_TAP_THY    472 +#define DMP_TMP25    480 +#define DMP_TMP26    482 +#define DMP_TMP27    484 +#define DMP_TMP28    486 +#define DMP_ORIENT    488 +#define DMP_THRSH    490 +#define DMP_ENDIANH    492 +#define DMP_ENDIANL    494 +#define DMP_BLPFNMTCH    496 +#define DMP_BLPFNMTCL    498 +#define DMP_BLPFNMXH    500 +#define DMP_BLPFNMXL    502 +#define DMP_BLPFNMYH    504 +#define DMP_BLPFNMYL    506 +#define DMP_BLPFNMZH    508 +#define DMP_BLPFNMZL    510 + +#endif diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu3050_iio.c b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu3050_iio.c new file mode 100755 index 00000000000..1fc5ff98097 --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu3050_iio.c @@ -0,0 +1,271 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +* GNU General Public License for more details. +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> + +#include "inv_mpu_iio.h" +#define MPU3050_NACK_MIN_TIME (2 * 1000) +#define MPU3050_NACK_MAX_TIME (3 * 1000) + +#define MPU3050_ONE_MPU_TIME 20 +#define MPU3050_BOGUS_ADDR  0x7F +int __attribute__((weak)) inv_register_mpu3050_slave(struct inv_mpu_state *st) +{ +	return 0; +} + +int set_3050_bypass(struct inv_mpu_state *st, bool enable) +{ +	struct inv_reg_map_s *reg; +	int result; +	u8 b; + +	reg = &st->reg; +	result = inv_i2c_read(st, reg->user_ctrl, 1, &b); +	if (result) +		return result; +	if (((b & BIT_3050_AUX_IF_EN) == 0) && enable) +		return 0; +	if ((b & BIT_3050_AUX_IF_EN) && (enable == 0)) +		return 0; +	b &= ~BIT_3050_AUX_IF_EN; +	if (!enable) { +		b |= BIT_3050_AUX_IF_EN; +		result = inv_i2c_single_write(st, reg->user_ctrl, b); +		return result; +	} else { +		/* Coming out of I2C is tricky due to several erratta.  Do not +		* modify this algorithm +		*/ +		/* +		* 1) wait for the right time and send the command to change +		* the aux i2c slave address to an invalid address that will +		* get nack'ed +		* +		* 0x00 is broadcast.  0x7F is unlikely to be used by any aux. +		*/ +		result = inv_i2c_single_write(st, REG_3050_SLAVE_ADDR, +						MPU3050_BOGUS_ADDR); +		if (result) +			return result; +		/* +		* 2) wait enough time for a nack to occur, then go into +		*    bypass mode: +		*/ +		usleep_range(MPU3050_NACK_MIN_TIME, MPU3050_NACK_MAX_TIME); +		result = inv_i2c_single_write(st, reg->user_ctrl, b); +		if (result) +			return result; +		/* +		* 3) wait for up to one MPU cycle then restore the slave +		*    address +		*/ +		msleep(MPU3050_ONE_MPU_TIME); + +		result = inv_i2c_single_write(st, REG_3050_SLAVE_ADDR, +			st->plat_data.secondary_i2c_addr); +		if (result) +			return result; +		result = inv_i2c_single_write(st, reg->user_ctrl, b); +		if (result) +			return result; +		usleep_range(MPU3050_NACK_MIN_TIME, MPU3050_NACK_MAX_TIME); +	} +	return 0; +} + +void inv_setup_reg_mpu3050(struct inv_reg_map_s *reg) +{ +	reg->fifo_en         = REG_3050_FIFO_EN; +	reg->sample_rate_div = REG_3050_SAMPLE_RATE_DIV; +	reg->lpf             = REG_3050_LPF; +	reg->fifo_count_h    = REG_3050_FIFO_COUNT_H; +	reg->fifo_r_w        = REG_3050_FIFO_R_W; +	reg->user_ctrl       = REG_3050_USER_CTRL; +	reg->pwr_mgmt_1      = REG_3050_PWR_MGMT_1; +	reg->raw_accel        = REG_3050_AUX_XOUT_H; +	reg->temperature     = REG_3050_TEMPERATURE; +	reg->int_enable      = REG_3050_INT_ENABLE; +	reg->int_status      = REG_3050_INT_STATUS; +} + +int inv_switch_3050_gyro_engine(struct inv_mpu_state *st, bool en) +{ +	struct inv_reg_map_s *reg; +	u8 data, p; +	int result; +	reg = &st->reg; +	if (en) { +		data = INV_CLK_PLL; +		p = (BITS_3050_POWER1 | data); +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p); +		if (result) +			return result; +		p = (BITS_3050_POWER2 | data); +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p); +		if (result) +			return result; +		p = data; +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p); +		msleep(SENSOR_UP_TIME); +	} else { +		p = BITS_3050_GYRO_STANDBY; +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p); +	} + +	return result; +} + +int inv_switch_3050_accel_engine(struct inv_mpu_state *st, bool en) +{ +	int result; +	if (NULL == st->slave_accel) +		return -EPERM; +	if (en) +		result = st->slave_accel->resume(st); +	else +		result = st->slave_accel->suspend(st); + +	return result; +} + +/** + *  inv_init_config_mpu3050() - Initialize hardware, disable FIFO. + *  @st:	Device driver instance. + *  Initial configuration: + *  FSR: +/- 2000DPS + *  DLPF: 42Hz + *  FIFO rate: 50Hz + *  Clock source: Gyro PLL + */ +int inv_init_config_mpu3050(struct iio_dev *indio_dev) +{ +	struct inv_reg_map_s *reg; +	int result; +	u8 data; +	struct inv_mpu_state *st = iio_priv(indio_dev); + +	if (st->chip_config.is_asleep) +		return -EPERM; +	/*reading AUX VDDIO register */ +	result = inv_i2c_read(st, REG_3050_AUX_VDDIO, 1, &data); +	if (result) +		return result; +	data &= ~BIT_3050_VDDIO; +	if (st->plat_data.level_shifter) +		data |= BIT_3050_VDDIO; +	result = inv_i2c_single_write(st, REG_3050_AUX_VDDIO, data); +	if (result) +		return result; + +	reg = &st->reg; +	/*2000dps full scale range*/ +	result = inv_i2c_single_write(st, reg->lpf, +				(INV_FSR_2000DPS << GYRO_CONFIG_FSR_SHIFT) +				| INV_FILTER_42HZ); +	if (result) +		return result; +	st->chip_config.fsr = INV_FSR_2000DPS; +	st->chip_config.lpf = INV_FILTER_42HZ; +	st->chip_info.multi = 1; +	result = inv_i2c_single_write(st, reg->sample_rate_div, +					ONE_K_HZ/INIT_FIFO_RATE - 1); +	if (result) +		return result; +	st->chip_config.fifo_rate = INIT_FIFO_RATE; +	st->irq_dur_ns            = INIT_DUR_TIME; +	st->chip_config.prog_start_addr = DMP_START_ADDR; +	if ((SECONDARY_SLAVE_TYPE_ACCEL == st->plat_data.sec_slave_type) && +		st->slave_accel) { +		result = st->slave_accel->setup(st); +		if (result) +			return result; +		result = st->slave_accel->set_fs(st, INV_FS_02G); +		if (result) +			return result; +		result = st->slave_accel->set_lpf(st, INIT_FIFO_RATE); +		if (result) +			return result; +	} + +	return 0; +} + +/** + *  set_power_mpu3050() - set power of mpu3050. + *  @st:	Device driver instance. + *  @power_on:  on/off + */ +int set_power_mpu3050(struct inv_mpu_state *st, bool power_on) +{ +	struct inv_reg_map_s *reg; +	u8 data, p; +	int result; +	reg = &st->reg; +	if (power_on) { +		data = 0; +	} else { +		if (st->slave_accel) { +			result = st->slave_accel->suspend(st); +			if (result) +				return result; +		} +		data = BIT_SLEEP; +	} +	if (st->chip_config.gyro_enable) { +		p = (BITS_3050_POWER1 | INV_CLK_PLL); +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p); +		if (result) +			return result; + +		p = (BITS_3050_POWER2 | INV_CLK_PLL); +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p); +		if (result) +			return result; + +		p = INV_CLK_PLL; +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p); +		if (result) +			return result; +	} else { +		data |= (BITS_3050_GYRO_STANDBY | INV_CLK_INTERNAL); +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data); +		if (result) +			return result; +	} +	if (power_on) { +		msleep(POWER_UP_TIME); +		if (st->slave_accel) { +			result = st->slave_accel->resume(st); +			if (result) +				return result; +		} +	} +	st->chip_config.is_asleep = !power_on; + +	return 0; +} + diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_core.c b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_core.c new file mode 100755 index 00000000000..6f5a131dac5 --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_core.c @@ -0,0 +1,3135 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> + +#include "inv_mpu_iio.h" +#include "sysfs.h" +#include "inv_test/inv_counters.h" + +#ifdef CONFIG_DTS_INV_MPU_IIO +#include "inv_mpu_dts.h" +#endif + +s64 get_time_ns(void) +{ +	struct timespec ts; +	ts = CURRENT_TIME; +	return timespec_to_ns(&ts); +} + +/* This is for compatibility for power state. Should remove once HAL +   does not use power_state sysfs entry */ +static bool fake_asleep; + +static const struct inv_hw_s hw_info[INV_NUM_PARTS] = { +	{119, "ITG3500"}, +	{ 63, "MPU3050"}, +	{117, "MPU6050"}, +	{118, "MPU9150"}, +	{128, "MPU6500"}, +	{128, "MPU9250"}, +	{128, "MPU9350"}, +	{128, "MPU6515"}, +}; + +static const u8 reg_gyro_offset[] = {REG_XG_OFFS_USRH, +					REG_XG_OFFS_USRH + 2, +					REG_XG_OFFS_USRH + 4}; + +const u8 reg_6050_accel_offset[] = {REG_XA_OFFS_H, +					REG_XA_OFFS_H + 2, +					REG_XA_OFFS_H + 4}; + +const u8 reg_6500_accel_offset[] = {REG_6500_XA_OFFS_H, +					REG_6500_YA_OFFS_H, +					REG_6500_ZA_OFFS_H}; +#ifdef CONFIG_INV_TESTING +static bool suspend_state; +static int inv_mpu_suspend(struct device *dev); +static int inv_mpu_resume(struct device *dev); +struct test_data_out { +	bool gyro; +	bool accel; +	bool compass; +	bool pressure; +	bool LPQ; +	bool SIXQ; +	bool PEDQ; +}; +static struct test_data_out data_out_control; +#endif + +static void inv_setup_reg(struct inv_reg_map_s *reg) +{ +	reg->sample_rate_div	= REG_SAMPLE_RATE_DIV; +	reg->lpf		= REG_CONFIG; +	reg->bank_sel		= REG_BANK_SEL; +	reg->user_ctrl		= REG_USER_CTRL; +	reg->fifo_en		= REG_FIFO_EN; +	reg->gyro_config	= REG_GYRO_CONFIG; +	reg->accel_config	= REG_ACCEL_CONFIG; +	reg->fifo_count_h	= REG_FIFO_COUNT_H; +	reg->fifo_r_w		= REG_FIFO_R_W; +	reg->raw_accel		= REG_RAW_ACCEL; +	reg->temperature	= REG_TEMPERATURE; +	reg->int_enable		= REG_INT_ENABLE; +	reg->int_status		= REG_INT_STATUS; +	reg->pwr_mgmt_1		= REG_PWR_MGMT_1; +	reg->pwr_mgmt_2		= REG_PWR_MGMT_2; +	reg->mem_start_addr	= REG_MEM_START_ADDR; +	reg->mem_r_w		= REG_MEM_RW; +	reg->prgm_strt_addrh	= REG_PRGM_STRT_ADDRH; +}; + +/** + *  inv_i2c_read_base() - Read one or more bytes from the device registers. + *  @st:	Device driver instance. + *  @i2c_addr:  i2c address of device. + *  @reg:	First device register to be read from. + *  @length:	Number of bytes to read. + *  @data:	Data read from device. + *  NOTE:This is not re-implementation of i2c_smbus_read because i2c + *       address could be specified in this case. We could have two different + *       i2c address due to secondary i2c interface. + */ +int inv_i2c_read_base(struct inv_mpu_state *st, u16 i2c_addr, +	u8 reg, u16 length, u8 *data) +{ +	struct i2c_msg msgs[2]; +	int res; + +	if (!data) +		return -EINVAL; + +	msgs[0].addr = i2c_addr; +	msgs[0].flags = 0;	/* write */ +	msgs[0].buf = ® +	msgs[0].len = 1; + +	msgs[1].addr = i2c_addr; +	msgs[1].flags = I2C_M_RD; +	msgs[1].buf = data; +	msgs[1].len = length; + +	res = i2c_transfer(st->sl_handle, msgs, 2); + +	if (res < 2) { +		if (res >= 0) +			res = -EIO; +	} else +		res = 0; + +	INV_I2C_INC_MPUWRITE(3); +	INV_I2C_INC_MPUREAD(length); +#if CONFIG_DYNAMIC_DEBUG +	{ +		char *read = 0; +		pr_debug("%s RD%02X%02X%02X -> %s%s\n", st->hw->name, +			 i2c_addr, reg, length, +			 wr_pr_debug_begin(data, length, read), +			 wr_pr_debug_end(read)); +	} +#endif +	return res; +} + +/** + *  inv_i2c_single_write_base() - Write a byte to a device register. + *  @st:	Device driver instance. + *  @i2c_addr:  I2C address of the device. + *  @reg:	Device register to be written to. + *  @data:	Byte to write to device. + *  NOTE:This is not re-implementation of i2c_smbus_write because i2c + *       address could be specified in this case. We could have two different + *       i2c address due to secondary i2c interface. + */ +int inv_i2c_single_write_base(struct inv_mpu_state *st, +	u16 i2c_addr, u8 reg, u8 data) +{ +	u8 tmp[2]; +	struct i2c_msg msg; +	int res; +	tmp[0] = reg; +	tmp[1] = data; + +	msg.addr = i2c_addr; +	msg.flags = 0;	/* write */ +	msg.buf = tmp; +	msg.len = 2; + +	pr_debug("%s WR%02X%02X%02X\n", st->hw->name, i2c_addr, reg, data); +	INV_I2C_INC_MPUWRITE(3); + +	res = i2c_transfer(st->sl_handle, &msg, 1); +	if (res < 1) { +		if (res == 0) +			res = -EIO; +		return res; +	} else +		return 0; +} + +static int inv_switch_engine(struct inv_mpu_state *st, bool en, u32 mask) +{ +	struct inv_reg_map_s *reg; +	u8 data, mgmt_1; +	int result; + +	reg = &st->reg; +	/* Only when gyro is on, can +	   clock source be switched to gyro. Otherwise, it must be set to +	   internal clock */ +	if (BIT_PWR_GYRO_STBY == mask) { +		result = inv_i2c_read(st, reg->pwr_mgmt_1, 1, &mgmt_1); +		if (result) +			return result; + +		mgmt_1 &= ~BIT_CLK_MASK; +	} + +	if ((BIT_PWR_GYRO_STBY == mask) && (!en)) { +		/* turning off gyro requires switch to internal clock first. +		   Then turn off gyro engine */ +		mgmt_1 |= INV_CLK_INTERNAL; +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, +						mgmt_1); +		if (result) +			return result; +	} + +	result = inv_i2c_read(st, reg->pwr_mgmt_2, 1, &data); +	if (result) +		return result; +	if (en) +		data &= (~mask); +	else +		data |= mask; +	result = inv_i2c_single_write(st, reg->pwr_mgmt_2, data); +	if (result) +		return result; + +	if ((BIT_PWR_GYRO_STBY == mask) && en) { +		/* only gyro on needs sensor up time */ +		msleep(SENSOR_UP_TIME); +		/* after gyro is on & stable, switch internal clock to PLL */ +		mgmt_1 |= INV_CLK_PLL; +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, +						mgmt_1); +		if (result) +			return result; +	} +	if ((BIT_PWR_ACCEL_STBY == mask) && en) +		msleep(REG_UP_TIME); + +	return 0; +} + +/* + *  inv_lpa_freq() - store current low power frequency setting. + */ +static int inv_lpa_freq(struct inv_mpu_state *st, int lpa_freq) +{ +	unsigned long result; +	u8 d; +	/* 2, 4, 6, 7 corresponds to 0.98, 3.91, 15.63, 31.25 */ +	const u8 mpu6500_lpa_mapping[] = {2, 4, 6, 7}; + +	if (lpa_freq > MAX_LPA_FREQ_PARAM) +		return -EINVAL; + +	if (INV_MPU6500 == st->chip_type) { +		d = mpu6500_lpa_mapping[lpa_freq]; +		result = inv_i2c_single_write(st, REG_6500_LP_ACCEL_ODR, d); +		if (result) +			return result; +	} +	st->chip_config.lpa_freq = lpa_freq; + +	return 0; +} + +static int set_power_itg(struct inv_mpu_state *st, bool power_on) +{ +	struct inv_reg_map_s *reg; +	u8 data; +	int result; + +	if ((!power_on) == st->chip_config.is_asleep) +		return 0; +	reg = &st->reg; +	if (power_on) +		data = 0; +	else +		data = BIT_SLEEP; +	result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data); +	if (result) +		return result; + +	if (power_on) +		msleep(REG_UP_TIME); + +	st->chip_config.is_asleep = !power_on; + +	return 0; +} + +/** + *  inv_init_config() - Initialize hardware, disable FIFO. + *  @indio_dev:	Device driver instance. + *  Initial configuration: + *  FSR: +/- 2000DPS + *  DLPF: 42Hz + *  FIFO rate: 50Hz + */ +static int inv_init_config(struct iio_dev *indio_dev) +{ +	struct inv_reg_map_s *reg; +	int result, i; +	struct inv_mpu_state *st = iio_priv(indio_dev); +	const u8 *ch; +	u8 d[2]; + +	reg = &st->reg; + +	result = inv_i2c_single_write(st, reg->gyro_config, +		INV_FSR_2000DPS << GYRO_CONFIG_FSR_SHIFT); +	if (result) +		return result; + +	st->chip_config.fsr = INV_FSR_2000DPS; + +	result = inv_i2c_single_write(st, reg->lpf, INV_FILTER_42HZ); +	if (result) +		return result; +	st->chip_config.lpf = INV_FILTER_42HZ; + +	result = inv_i2c_single_write(st, reg->sample_rate_div, +					ONE_K_HZ / INIT_FIFO_RATE - 1); +	if (result) +		return result; +	st->chip_config.fifo_rate = INIT_FIFO_RATE; +	st->irq_dur_ns            = INIT_DUR_TIME; +	st->chip_config.prog_start_addr = DMP_START_ADDR; +	if (INV_MPU6050 == st->chip_type) +		st->self_test.samples = INIT_ST_MPU6050_SAMPLES; +	else +		st->self_test.samples = INIT_ST_SAMPLES; +	st->self_test.threshold = INIT_ST_THRESHOLD; +	st->batch.wake_fifo_on = true; +	st->suspend_state = false; +	if (INV_ITG3500 != st->chip_type) { +		st->chip_config.accel_fs = INV_FS_02G; +		result = inv_i2c_single_write(st, reg->accel_config, +			(INV_FS_02G << ACCEL_CONFIG_FSR_SHIFT)); +		if (result) +			return result; +		st->tap.time = INIT_TAP_TIME; +		st->tap.thresh = INIT_TAP_THRESHOLD; +		st->tap.min_count = INIT_TAP_MIN_COUNT; +		st->sample_divider = INIT_SAMPLE_DIVIDER; +		st->smd.threshold = MPU_INIT_SMD_THLD; +		st->smd.delay     = MPU_INIT_SMD_DELAY_THLD; +		st->smd.delay2    = MPU_INIT_SMD_DELAY2_THLD; +		st->ped.int_thresh = INIT_PED_INT_THRESH; +		st->ped.step_thresh = INIT_PED_THRESH; +		st->sensor[SENSOR_STEP].rate = MAX_DMP_OUTPUT_RATE; + +		result = inv_i2c_single_write(st, REG_ACCEL_MOT_DUR, +						INIT_MOT_DUR); +		if (result) +			return result; +		st->mot_int.mot_dur = INIT_MOT_DUR; + +		result = inv_i2c_single_write(st, REG_ACCEL_MOT_THR, +						INIT_MOT_THR); +		if (result) +			return result; +		st->mot_int.mot_thr = INIT_MOT_THR; + +		for (i = 0; i < 3; i++) { +			result = inv_i2c_read(st, reg_gyro_offset[i], 2, d); +			if (result) +				return result; +			st->rom_gyro_offset[i] = +					(short)be16_to_cpup((__be16 *)(d)); +			st->input_gyro_offset[i] = 0; +			st->input_gyro_dmp_bias[i] = 0; +		} +		if (INV_MPU6050 == st->chip_type) +			ch = reg_6050_accel_offset; +		else +			ch = reg_6500_accel_offset; +		for (i = 0; i < 3; i++) { +			result = inv_i2c_read(st, ch[i], 2, d); +			if (result) +				return result; +			st->rom_accel_offset[i] = +					(short)be16_to_cpup((__be16 *)(d)); +			st->input_accel_offset[i] = 0; +			st->input_accel_dmp_bias[i] = 0; +		} +		st->ped.step = 0; +		st->ped.time = 0; +	} + +	return 0; +} + +/* + *  inv_write_fsr() - Configure the gyro's scale range. + */ +static int inv_write_fsr(struct inv_mpu_state *st, int fsr) +{ +	struct inv_reg_map_s *reg; +	int result; + +	reg = &st->reg; +	if ((fsr < 0) || (fsr > MAX_GYRO_FS_PARAM)) +		return -EINVAL; +	if (fsr == st->chip_config.fsr) +		return 0; + +	if (INV_MPU3050 == st->chip_type) +		result = inv_i2c_single_write(st, reg->lpf, +			(fsr << GYRO_CONFIG_FSR_SHIFT) | st->chip_config.lpf); +	else +		result = inv_i2c_single_write(st, reg->gyro_config, +			fsr << GYRO_CONFIG_FSR_SHIFT); + +	if (result) +		return result; +	st->chip_config.fsr = fsr; + +	return 0; +} + +/* + *  inv_write_accel_fs() - Configure the accelerometer's scale range. + */ +static int inv_write_accel_fs(struct inv_mpu_state *st, int fs) +{ +	int result; +	struct inv_reg_map_s *reg; + +	reg = &st->reg; +	if (fs < 0 || fs > MAX_ACCEL_FS_PARAM) +		return -EINVAL; +	if (fs == st->chip_config.accel_fs) +		return 0; +	if (INV_MPU3050 == st->chip_type) +		result = st->slave_accel->set_fs(st, fs); +	else +		result = inv_i2c_single_write(st, reg->accel_config, +				(fs << ACCEL_CONFIG_FSR_SHIFT)); +	if (result) +		return result; + +	st->chip_config.accel_fs = fs; + +	return 0; +} + +static int inv_set_offset_reg(struct inv_mpu_state *st, int reg, int val) +{ +	int result; +	u8 d; + +	d = ((val >> 8) & 0xff); +	result = inv_i2c_single_write(st, reg, d); +	if (result) +		return result; + +	d = (val & 0xff); +	result = inv_i2c_single_write(st, reg + 1, d); + +	return result; +} + +int inv_reset_offset_reg(struct inv_mpu_state *st, bool en) +{ +	const u8 *ch; +	int i, result; +	s16 gyro[3], accel[3]; + +	if (en) { +		for (i = 0; i < 3; i++) { +			gyro[i] = st->rom_gyro_offset[i]; +			accel[i] = st->rom_accel_offset[i]; +		} +	} else { +		for (i = 0; i < 3; i++) { +			gyro[i] = st->rom_gyro_offset[i] + +						st->input_gyro_offset[i]; +			accel[i] = st->rom_accel_offset[i] + +					(st->input_accel_offset[i] << 1); +		} +	} +	if (INV_MPU6050 == st->chip_type) +		ch = reg_6050_accel_offset; +	else +		ch = reg_6500_accel_offset; + +	for (i = 0; i < 3; i++) { +		result = inv_set_offset_reg(st, reg_gyro_offset[i], gyro[i]); +		if (result) +			return result; +		result = inv_set_offset_reg(st, ch[i], accel[i]); +		if (result) +			return result; +	} + +	return 0; +} +/* + *  inv_fifo_rate_store() - Set fifo rate. + */ +static int inv_fifo_rate_store(struct inv_mpu_state *st, int fifo_rate) +{ +	if ((fifo_rate < MIN_FIFO_RATE) || (fifo_rate > MAX_FIFO_RATE)) +		return -EINVAL; +	if (fifo_rate == st->chip_config.fifo_rate) +		return 0; + +	st->irq_dur_ns = NSEC_PER_SEC / fifo_rate; +	st->chip_config.fifo_rate = fifo_rate; + +	return 0; +} + +/* + *  inv_reg_dump_show() - Register dump for testing. + */ +static ssize_t inv_reg_dump_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	int ii; +	char data; +	ssize_t bytes_printed = 0; +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); + +	mutex_lock(&indio_dev->mlock); +	for (ii = 0; ii < st->hw->num_reg; ii++) { +		/* don't read fifo r/w register */ +		if (ii == st->reg.fifo_r_w) +			data = 0; +		else +			inv_i2c_read(st, ii, 1, &data); +		bytes_printed += sprintf(buf + bytes_printed, "%#2x: %#2x\n", +					 ii, data); +	} +	mutex_unlock(&indio_dev->mlock); + +	return bytes_printed; +} + +int write_be32_key_to_mem(struct inv_mpu_state *st, +					u32 data, int key) +{ +	cpu_to_be32s(&data); +	return mem_w_key(key, sizeof(data), (u8 *)&data); +} + +int inv_write_2bytes(struct inv_mpu_state *st, int k, int data) +{ +	u8 d[2]; + +	if (data < 0 || data > USHRT_MAX) +		return -EINVAL; + +	d[0] = (u8)((data >> 8) & 0xff); +	d[1] = (u8)(data & 0xff); + +	return mem_w_key(k, ARRAY_SIZE(d), d); +} + +static ssize_t _dmp_bias_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int result, data, tmp; + +	if (!st->chip_config.firmware_loaded) +		return -EINVAL; + +	if (!st->chip_config.enable) { +		result = st->set_power_state(st, true); +		if (result) +			return result; +	} + +	result = kstrtoint(buf, 10, &data); +	if (result) +		goto dmp_bias_store_fail; +	switch (this_attr->address) { +	case ATTR_DMP_ACCEL_X_DMP_BIAS: +		tmp = st->input_accel_dmp_bias[0]; +		st->input_accel_dmp_bias[0] = data; +		result = inv_set_accel_bias_dmp(st); +		if (result) +			st->input_accel_dmp_bias[0] = tmp; +		break; +	case ATTR_DMP_ACCEL_Y_DMP_BIAS: +		tmp = st->input_accel_dmp_bias[1]; +		st->input_accel_dmp_bias[1] = data; +		result = inv_set_accel_bias_dmp(st); +		if (result) +			st->input_accel_dmp_bias[1] = tmp; +		break; +	case ATTR_DMP_ACCEL_Z_DMP_BIAS: +		tmp = st->input_accel_dmp_bias[2]; +		st->input_accel_dmp_bias[2] = data; +		result = inv_set_accel_bias_dmp(st); +		if (result) +			st->input_accel_dmp_bias[2] = tmp; +		break; +	case ATTR_DMP_GYRO_X_DMP_BIAS: +		result = write_be32_key_to_mem(st, data, +					KEY_CFG_EXT_GYRO_BIAS_X); +		if (result) +			goto dmp_bias_store_fail; +		st->input_gyro_dmp_bias[0] = data; +		break; +	case ATTR_DMP_GYRO_Y_DMP_BIAS: +		result = write_be32_key_to_mem(st, data, +					KEY_CFG_EXT_GYRO_BIAS_Y); +		if (result) +			goto dmp_bias_store_fail; +		st->input_gyro_dmp_bias[1] = data; +		break; +	case ATTR_DMP_GYRO_Z_DMP_BIAS: +		result = write_be32_key_to_mem(st, data, +					KEY_CFG_EXT_GYRO_BIAS_Z); +		if (result) +			goto dmp_bias_store_fail; +		st->input_gyro_dmp_bias[2] = data; +		break; +	default: +		break; +	} + +dmp_bias_store_fail: +	if (!st->chip_config.enable) +		result |= st->set_power_state(st, false); +	if (result) +		return result; + +	return count; +} + +static ssize_t inv_dmp_bias_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	int result; + +	mutex_lock(&indio_dev->mlock); +	result = _dmp_bias_store(dev, attr, buf, count); +	mutex_unlock(&indio_dev->mlock); + +	return result; +} + +static ssize_t _dmp_attr_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int result, data; + +	if (st->chip_config.enable) +		return -EBUSY; +	result = kstrtoint(buf, 10, &data); +	if (result) +		return -EINVAL; +	switch (this_attr->address) { +	/* power of chip is not turned on */ +	case ATTR_DMP_ON: +		st->chip_config.dmp_on = !!data; +		break; +	case ATTR_DMP_INT_ON: +		st->chip_config.dmp_int_on = !!data; +		break; +	case ATTR_DMP_EVENT_INT_ON: +		st->chip_config.dmp_event_int_on = !!data; +		break; +	case ATTR_DMP_STEP_INDICATOR_ON: +		st->chip_config.step_indicator_on = !!data; +		break; +	case ATTR_DMP_BATCHMODE_TIMEOUT: +		if (data < 0 || data > INT_MAX) +			return -EINVAL; +		st->batch.timeout = data; +		break; +	case ATTR_DMP_BATCHMODE_WAKE_FIFO_FULL: +		st->batch.wake_fifo_on = !!data; +		st->batch.overflow_on = 0; +		break; +	case ATTR_DMP_SIX_Q_ON: +		st->sensor[SENSOR_SIXQ].on = !!data; +		break; +	case ATTR_DMP_SIX_Q_RATE: +		if (data > MPU_DEFAULT_DMP_FREQ || data < 0) +			return -EINVAL; +		st->sensor[SENSOR_SIXQ].rate = data; +		st->sensor[SENSOR_SIXQ].dur = MPU_DEFAULT_DMP_FREQ / data; +		st->sensor[SENSOR_SIXQ].dur *= DMP_INTERVAL_INIT; +		break; +	case ATTR_DMP_LPQ_ON: +		st->sensor[SENSOR_LPQ].on = !!data; +		break; +	case ATTR_DMP_LPQ_RATE: +		if (data > MPU_DEFAULT_DMP_FREQ || data < 0) +			return -EINVAL; +		st->sensor[SENSOR_LPQ].rate = data; +		st->sensor[SENSOR_LPQ].dur = MPU_DEFAULT_DMP_FREQ / data; +		st->sensor[SENSOR_LPQ].dur *= DMP_INTERVAL_INIT; +		break; +	case ATTR_DMP_PED_Q_ON: +		st->sensor[SENSOR_PEDQ].on = !!data; +		break; +	case ATTR_DMP_PED_Q_RATE: +		if (data > MPU_DEFAULT_DMP_FREQ || data < 0) +			return -EINVAL; +		st->sensor[SENSOR_PEDQ].rate = data; +		st->sensor[SENSOR_PEDQ].dur = MPU_DEFAULT_DMP_FREQ / data; +		st->sensor[SENSOR_PEDQ].dur *= DMP_INTERVAL_INIT; +		break; +	case ATTR_DMP_STEP_DETECTOR_ON: +		st->sensor[SENSOR_STEP].on = !!data; +		break; +	default: +		return -EINVAL; +	} + +	return count; +} + +/* + * inv_dmp_attr_store() -  calling this function will store DMP attributes + */ +static ssize_t inv_dmp_attr_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	int result; + +	mutex_lock(&indio_dev->mlock); +	result = _dmp_attr_store(dev, attr, buf, count); +	mutex_unlock(&indio_dev->mlock); + +	return result; +} + +static ssize_t _dmp_mem_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int result, data; + +	if (st->chip_config.enable) +		return -EBUSY; +	if (!st->chip_config.firmware_loaded) +		return -EINVAL; +	result = st->set_power_state(st, true); +	if (result) +		return result; + +	result = kstrtoint(buf, 10, &data); +	if (result) +		goto dmp_mem_store_fail; +	switch (this_attr->address) { +	case ATTR_DMP_PED_INT_ON: +		result = inv_enable_pedometer_interrupt(st, !!data); +		if (result) +			goto dmp_mem_store_fail; +		st->ped.int_on = !!data; +		break; +	case ATTR_DMP_PED_ON: +	{ +		result = inv_enable_pedometer(st, !!data); +		if (result) +			goto dmp_mem_store_fail; +		st->ped.on = !!data; +		break; +	} +	case ATTR_DMP_PED_STEP_THRESH: +	{ +		result = inv_write_2bytes(st, KEY_D_PEDSTD_SB, data); +		if (result) +			goto dmp_mem_store_fail; +		st->ped.step_thresh = data; +		break; +	} +	case ATTR_DMP_PED_INT_THRESH: +	{ +		result = inv_write_2bytes(st, KEY_D_PEDSTD_SB2, data); +		if (result) +			goto dmp_mem_store_fail; +		st->ped.int_thresh = data; +		break; +	} +	case ATTR_DMP_SMD_ENABLE: +		result = inv_write_2bytes(st, KEY_SMD_ENABLE, !!data); +		if (result) +			goto dmp_mem_store_fail; +		st->chip_config.smd_enable = !!data; +		break; +	case ATTR_DMP_SMD_THLD: +		if (data < 0 || data > SHRT_MAX) +			goto dmp_mem_store_fail; +		result = write_be32_key_to_mem(st, data << 16, +						KEY_SMD_ACCEL_THLD); +		if (result) +			goto dmp_mem_store_fail; +		st->smd.threshold = data; +		break; +	case ATTR_DMP_SMD_DELAY_THLD: +		if (data < 0 || data > INT_MAX / MPU_DEFAULT_DMP_FREQ) +			goto dmp_mem_store_fail; +		result = write_be32_key_to_mem(st, data * MPU_DEFAULT_DMP_FREQ, +						KEY_SMD_DELAY_THLD); +		if (result) +			goto dmp_mem_store_fail; +		st->smd.delay = data; +		break; +	case ATTR_DMP_SMD_DELAY_THLD2: +		if (data < 0 || data > INT_MAX / MPU_DEFAULT_DMP_FREQ) +			goto dmp_mem_store_fail; +		result = write_be32_key_to_mem(st, data * MPU_DEFAULT_DMP_FREQ, +						KEY_SMD_DELAY2_THLD); +		if (result) +			goto dmp_mem_store_fail; +		st->smd.delay2 = data; +		break; +	case ATTR_DMP_TAP_ON: +		result = inv_enable_tap_dmp(st, !!data); +		if (result) +			goto dmp_mem_store_fail; +		st->tap.on = !!data; +		break; +	case ATTR_DMP_TAP_THRESHOLD: +		if (data < 0 || data > USHRT_MAX) { +			result = -EINVAL; +			goto dmp_mem_store_fail; +		} +		result = inv_set_tap_threshold_dmp(st, data); +		if (result) +			goto dmp_mem_store_fail; +		st->tap.thresh = data; +		break; +	case ATTR_DMP_TAP_MIN_COUNT: +		if (data < 0 || data > USHRT_MAX) { +			result = -EINVAL; +			goto dmp_mem_store_fail; +		} +		result = inv_set_min_taps_dmp(st, data); +		if (result) +			goto dmp_mem_store_fail; +		st->tap.min_count = data; +		break; +	case ATTR_DMP_TAP_TIME: +		if (data < 0 || data > USHRT_MAX) { +			result = -EINVAL; +			goto dmp_mem_store_fail; +		} +		result = inv_set_tap_time_dmp(st, data); +		if (result) +			goto dmp_mem_store_fail; +		st->tap.time = data; +		break; +	case ATTR_DMP_DISPLAY_ORIENTATION_ON: +		result = inv_set_display_orient_interrupt_dmp(st, !!data); +		if (result) +			goto dmp_mem_store_fail; +		st->chip_config.display_orient_on = !!data; +		break; +#ifdef CONFIG_INV_TESTING +	case ATTR_DEBUG_SMD_ENABLE_TESTP1: +	{ +		u8 d[] = {0x42}; +		result = st->set_power_state(st, true); +		if (result) +			goto dmp_mem_store_fail; +		result = mem_w_key(KEY_SMD_ENABLE_TESTPT1, ARRAY_SIZE(d), d); +		if (result) +			goto dmp_mem_store_fail; +	} +		break; +	case ATTR_DEBUG_SMD_ENABLE_TESTP2: +	{ +		u8 d[] = {0x42}; +		result = st->set_power_state(st, true); +		if (result) +			goto dmp_mem_store_fail; +		result = mem_w_key(KEY_SMD_ENABLE_TESTPT2, ARRAY_SIZE(d), d); +		if (result) +			goto dmp_mem_store_fail; +	} +		break; +#endif +	default: +		result = -EINVAL; +		goto dmp_mem_store_fail; +	} + +dmp_mem_store_fail: +	result |= st->set_power_state(st, false); +	if (result) +		return result; + +	return count; +} + +/* + * inv_dmp_mem_store() -  calling this function will store DMP memory data + */ +static ssize_t inv_dmp_mem_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	int result; + +	mutex_lock(&indio_dev->mlock); +	result = _dmp_mem_store(dev, attr, buf, count); +	mutex_unlock(&indio_dev->mlock); + +	return result; +} + +static ssize_t inv_attr64_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int result; +	u64 tmp; +	u32 ped; + +	mutex_lock(&indio_dev->mlock); +	if (!st->chip_config.enable || !st->chip_config.dmp_on) { +		mutex_unlock(&indio_dev->mlock); +		return -EINVAL; +	} +	result = 0; +	switch (this_attr->address) { +	case ATTR_DMP_PEDOMETER_STEPS: +		result = inv_get_pedometer_steps(st, &ped); +		result |= inv_read_pedometer_counter(st); +		tmp = st->ped.step + ped; +		break; +	case ATTR_DMP_PEDOMETER_TIME: +		result = inv_get_pedometer_time(st, &ped); +		tmp = (u64)st->ped.time + ((u64)ped) * MS_PER_PED_TICKS; +		break; +	case ATTR_DMP_PEDOMETER_COUNTER: +		tmp = st->ped.last_step_time; +		break; +	default: +		result = -EINVAL; +		break; +	} +	mutex_unlock(&indio_dev->mlock); +	if (result) +		return -EINVAL; +	return sprintf(buf, "%lld\n", tmp); +} + +static ssize_t inv_attr64_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int result; +	u64 data; + +	mutex_lock(&indio_dev->mlock); +	if (st->chip_config.enable || (!st->chip_config.firmware_loaded)) { +		mutex_unlock(&indio_dev->mlock); +		return -EINVAL; +	} +	result = st->set_power_state(st, true); +	if (result) { +		mutex_unlock(&indio_dev->mlock); +		return result; +	} +	result = kstrtoull(buf, 10, &data); +	if (result) +		goto attr64_store_fail; +	switch (this_attr->address) { +	case ATTR_DMP_PEDOMETER_STEPS: +		result = write_be32_key_to_mem(st, 0, KEY_D_PEDSTD_STEPCTR); +		if (result) +			goto attr64_store_fail; +		st->ped.step = data; +		break; +	case ATTR_DMP_PEDOMETER_TIME: +		result = write_be32_key_to_mem(st, 0, KEY_D_PEDSTD_TIMECTR); +		if (result) +			goto attr64_store_fail; +		st->ped.time = data; +		break; +	default: +		result = -EINVAL; +		break; +	} +attr64_store_fail: +	mutex_unlock(&indio_dev->mlock); +	result = st->set_power_state(st, false); +	if (result) +		return result; + +	return count; +} +/* + * inv_attr_show() -  calling this function will show current + *                        dmp parameters. + */ +static ssize_t inv_attr_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int result, axis; +	s8 *m; + +	switch (this_attr->address) { +	case ATTR_GYRO_SCALE: +	{ +		const s16 gyro_scale[] = {250, 500, 1000, 2000}; + +		return sprintf(buf, "%d\n", gyro_scale[st->chip_config.fsr]); +	} +	case ATTR_ACCEL_SCALE: +	{ +		const s16 accel_scale[] = {2, 4, 8, 16}; +		return sprintf(buf, "%d\n", +					accel_scale[st->chip_config.accel_fs] * +					st->chip_info.multi); +	} +	case ATTR_COMPASS_SCALE: +		st->slave_compass->get_scale(st, &result); + +		return sprintf(buf, "%d\n", result); +	case ATTR_ACCEL_X_CALIBBIAS: +	case ATTR_ACCEL_Y_CALIBBIAS: +	case ATTR_ACCEL_Z_CALIBBIAS: +		axis = this_attr->address - ATTR_ACCEL_X_CALIBBIAS; +		return sprintf(buf, "%d\n", st->accel_bias[axis] * +						st->chip_info.multi); +	case ATTR_GYRO_X_CALIBBIAS: +	case ATTR_GYRO_Y_CALIBBIAS: +	case ATTR_GYRO_Z_CALIBBIAS: +		axis = this_attr->address - ATTR_GYRO_X_CALIBBIAS; +		return sprintf(buf, "%d\n", st->gyro_bias[axis]); +	case ATTR_SELF_TEST_GYRO_SCALE: +		return sprintf(buf, "%d\n", SELF_TEST_GYRO_FULL_SCALE); +	case ATTR_SELF_TEST_ACCEL_SCALE: +		if (INV_MPU6500 == st->chip_type) +			return sprintf(buf, "%d\n", SELF_TEST_ACCEL_6500_SCALE); +		else +			return sprintf(buf, "%d\n", SELF_TEST_ACCEL_FULL_SCALE); +	case ATTR_GYRO_X_OFFSET: +	case ATTR_GYRO_Y_OFFSET: +	case ATTR_GYRO_Z_OFFSET: +		axis = this_attr->address - ATTR_GYRO_X_OFFSET; +		return sprintf(buf, "%d\n", st->input_gyro_offset[axis]); +	case ATTR_ACCEL_X_OFFSET: +	case ATTR_ACCEL_Y_OFFSET: +	case ATTR_ACCEL_Z_OFFSET: +		axis = this_attr->address - ATTR_ACCEL_X_OFFSET; +		return sprintf(buf, "%d\n", st->input_accel_offset[axis]); +	case ATTR_DMP_ACCEL_X_DMP_BIAS: +		return sprintf(buf, "%d\n", st->input_accel_dmp_bias[0]); +	case ATTR_DMP_ACCEL_Y_DMP_BIAS: +		return sprintf(buf, "%d\n", st->input_accel_dmp_bias[1]); +	case ATTR_DMP_ACCEL_Z_DMP_BIAS: +		return sprintf(buf, "%d\n", st->input_accel_dmp_bias[2]); +	case ATTR_DMP_GYRO_X_DMP_BIAS: +		return sprintf(buf, "%d\n", st->input_gyro_dmp_bias[0]); +	case ATTR_DMP_GYRO_Y_DMP_BIAS: +		return sprintf(buf, "%d\n", st->input_gyro_dmp_bias[1]); +	case ATTR_DMP_GYRO_Z_DMP_BIAS: +		return sprintf(buf, "%d\n", st->input_gyro_dmp_bias[2]); +	case ATTR_DMP_PED_INT_ON: +		return sprintf(buf, "%d\n", st->ped.int_on); +	case ATTR_DMP_PED_ON: +		return sprintf(buf, "%d\n", st->ped.on); +	case ATTR_DMP_PED_STEP_THRESH: +		return sprintf(buf, "%d\n", st->ped.step_thresh); +	case ATTR_DMP_PED_INT_THRESH: +		return sprintf(buf, "%d\n", st->ped.int_thresh); +	case ATTR_DMP_SMD_ENABLE: +		return sprintf(buf, "%d\n", st->chip_config.smd_enable); +	case ATTR_DMP_SMD_THLD: +		return sprintf(buf, "%d\n", st->smd.threshold); +	case ATTR_DMP_SMD_DELAY_THLD: +		return sprintf(buf, "%d\n", st->smd.delay); +	case ATTR_DMP_SMD_DELAY_THLD2: +		return sprintf(buf, "%d\n", st->smd.delay2); +	case ATTR_DMP_TAP_ON: +		return sprintf(buf, "%d\n", st->tap.on); +	case ATTR_DMP_TAP_THRESHOLD: +		return sprintf(buf, "%d\n", st->tap.thresh); +	case ATTR_DMP_TAP_MIN_COUNT: +		return sprintf(buf, "%d\n", st->tap.min_count); +	case ATTR_DMP_TAP_TIME: +		return sprintf(buf, "%d\n", st->tap.time); +	case ATTR_DMP_DISPLAY_ORIENTATION_ON: +		return sprintf(buf, "%d\n", +			st->chip_config.display_orient_on); +	case ATTR_DMP_ON: +		return sprintf(buf, "%d\n", st->chip_config.dmp_on); +	case ATTR_DMP_INT_ON: +		return sprintf(buf, "%d\n", st->chip_config.dmp_int_on); +	case ATTR_DMP_EVENT_INT_ON: +		return sprintf(buf, "%d\n", st->chip_config.dmp_event_int_on); +	case ATTR_DMP_STEP_INDICATOR_ON: +		return sprintf(buf, "%d\n", st->chip_config.step_indicator_on); +	case ATTR_DMP_BATCHMODE_TIMEOUT: +		return sprintf(buf, "%d\n", +				st->batch.timeout); +	case ATTR_DMP_BATCHMODE_WAKE_FIFO_FULL: +		return sprintf(buf, "%d\n", +				st->batch.wake_fifo_on); +	case ATTR_DMP_SIX_Q_ON: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_SIXQ].on); +	case ATTR_DMP_SIX_Q_RATE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_SIXQ].rate); +	case ATTR_DMP_LPQ_ON: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_LPQ].on); +	case ATTR_DMP_LPQ_RATE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_LPQ].rate); +	case ATTR_DMP_PED_Q_ON: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_PEDQ].on); +	case ATTR_DMP_PED_Q_RATE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_PEDQ].rate); +	case ATTR_DMP_STEP_DETECTOR_ON: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_STEP].on); +	case ATTR_MOTION_LPA_ON: +		return sprintf(buf, "%d\n", st->mot_int.mot_on); +	case ATTR_MOTION_LPA_FREQ:{ +		const char *f[] = {"1.25", "5", "20", "40"}; +		return sprintf(buf, "%s\n", f[st->chip_config.lpa_freq]); +	} +	case ATTR_MOTION_LPA_THRESHOLD: +		return sprintf(buf, "%d\n", st->mot_int.mot_thr); + +	case ATTR_SELF_TEST_SAMPLES: +		return sprintf(buf, "%d\n", st->self_test.samples); +	case ATTR_SELF_TEST_THRESHOLD: +		return sprintf(buf, "%d\n", st->self_test.threshold); +	case ATTR_GYRO_ENABLE: +		return sprintf(buf, "%d\n", st->chip_config.gyro_enable); +	case ATTR_GYRO_FIFO_ENABLE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_GYRO].on); +	case ATTR_GYRO_RATE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_GYRO].rate); +	case ATTR_ACCEL_ENABLE: +		return sprintf(buf, "%d\n", st->chip_config.accel_enable); +	case ATTR_ACCEL_FIFO_ENABLE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_ACCEL].on); +	case ATTR_ACCEL_RATE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_ACCEL].rate); +	case ATTR_COMPASS_ENABLE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_COMPASS].on); +	case ATTR_COMPASS_RATE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_COMPASS].rate); +	case ATTR_PRESSURE_ENABLE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_PRESSURE].on); +	case ATTR_PRESSURE_RATE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_PRESSURE].rate); +	case ATTR_POWER_STATE: +		return sprintf(buf, "%d\n", !fake_asleep); +	case ATTR_FIRMWARE_LOADED: +		return sprintf(buf, "%d\n", st->chip_config.firmware_loaded); +	case ATTR_SAMPLING_FREQ: +		return sprintf(buf, "%d\n", st->chip_config.fifo_rate); +	case ATTR_SELF_TEST: +		mutex_lock(&indio_dev->mlock); +		if (st->chip_config.enable) { +			mutex_unlock(&indio_dev->mlock); +			return -EBUSY; +		} +		if (INV_MPU3050 == st->chip_type) +			result = 1; +		else +			result = inv_hw_self_test(st); +		mutex_unlock(&indio_dev->mlock); +		return sprintf(buf, "%d\n", result); +	case ATTR_GYRO_MATRIX: +		m = st->plat_data.orientation; +		return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n", +			m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]); +	case ATTR_ACCEL_MATRIX: +		if (st->plat_data.sec_slave_type == +						SECONDARY_SLAVE_TYPE_ACCEL) +			m = +			st->plat_data.secondary_orientation; +		else +			m = st->plat_data.orientation; +		return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n", +			m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]); +	case ATTR_COMPASS_MATRIX: +		if (st->plat_data.sec_slave_type == +				SECONDARY_SLAVE_TYPE_COMPASS) +			m = +			st->plat_data.secondary_orientation; +		else +			return -ENODEV; +		return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n", +			m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]); +	case ATTR_SECONDARY_NAME: +	{ +		const char *n[] = {"NULL", "AK8975", "AK8972", "AK8963", +					"BMA250", "MLX90399", "AK09911"}; +		switch (st->plat_data.sec_slave_id) { +		case COMPASS_ID_AK8975: +			return sprintf(buf, "%s\n", n[1]); +		case COMPASS_ID_AK8972: +			return sprintf(buf, "%s\n", n[2]); +		case COMPASS_ID_AK8963: +			return sprintf(buf, "%s\n", n[3]); +		case ACCEL_ID_BMA250: +			return sprintf(buf, "%s\n", n[4]); +		case COMPASS_ID_MLX90399: +			return sprintf(buf, "%s\n", n[5]); +		case COMPASS_ID_AK09911: +			return sprintf(buf, "%s\n", n[6]); +		default: +			return sprintf(buf, "%s\n", n[0]); +		} +	} +#ifdef CONFIG_INV_TESTING +	case ATTR_REG_WRITE: +		return sprintf(buf, "1\n"); +	case ATTR_COMPASS_SENS: +	{ +		/* these 2 conditions should never be met, since the +		   'compass_sens' sysfs entry should be hidden if the compass +		   is not an AKM */ +		if (st->plat_data.sec_slave_type != +					SECONDARY_SLAVE_TYPE_COMPASS) +			return -ENODEV; +		if (st->plat_data.sec_slave_id != COMPASS_ID_AK8975 && +		    st->plat_data.sec_slave_id != COMPASS_ID_AK8972 && +		    st->plat_data.sec_slave_id != COMPASS_ID_AK8963) +			return -ENODEV; +		m = st->chip_info.compass_sens; +		return sprintf(buf, "%d,%d,%d\n", m[0], m[1], m[2]); +	} +	case ATTR_DEBUG_SMD_EXE_STATE: +	{ +		u8 d[2]; + +		result = st->set_power_state(st, true); +		mpu_memory_read(st, st->i2c_addr, +				inv_dmp_get_address(KEY_SMD_EXE_STATE), 2, d); +		return sprintf(buf, "%d\n", (short)be16_to_cpup((__be16 *)(d))); +	} +	case ATTR_DEBUG_SMD_DELAY_CNTR: +	{ +		u8 d[4]; + +		result = st->set_power_state(st, true); +		mpu_memory_read(st, st->i2c_addr, +				inv_dmp_get_address(KEY_SMD_DELAY_CNTR), 4, d); +		return sprintf(buf, "%d\n", (int)be32_to_cpup((__be32 *)(d))); +	} +#endif +	default: +		return -EPERM; +	} +} + +/* + * inv_dmp_display_orient_show() -  calling this function will + *			show orientation This event must use poll. + */ +static ssize_t inv_dmp_display_orient_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct inv_mpu_state *st = iio_priv(dev_get_drvdata(dev)); + +	return sprintf(buf, "%d\n", st->display_orient_data); +} + +/* + * inv_accel_motion_show() -  calling this function showes motion interrupt. + *                         This event must use poll. + */ +static ssize_t inv_accel_motion_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	return sprintf(buf, "1\n"); +} + +/* + * inv_smd_show() -  calling this function showes smd interrupt. + *                         This event must use poll. + */ +static ssize_t inv_smd_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	return sprintf(buf, "1\n"); +} + +/* + * inv_ped_show() -  calling this function showes pedometer interrupt. + *                         This event must use poll. + */ +static ssize_t inv_ped_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	return sprintf(buf, "1\n"); +} + +/* + * inv_dmp_tap_show() -  calling this function will show tap + *                         This event must use poll. + */ +static ssize_t inv_dmp_tap_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct inv_mpu_state *st = iio_priv(dev_get_drvdata(dev)); +	return sprintf(buf, "%d\n", st->tap_data); +} + +/* + *  inv_temperature_show() - Read temperature data directly from registers. + */ +static ssize_t inv_temperature_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ + +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct inv_reg_map_s *reg; +	int result, cur_scale, cur_off; +	short temp; +	long scale_t; +	u8 data[2]; +	const long scale[] = {3834792L, 3158064L, 3340827L}; +	const long offset[] = {5383314L, 2394184L, 1376256L}; + +	reg = &st->reg; +	mutex_lock(&indio_dev->mlock); +	if (!st->chip_config.enable) +		result = st->set_power_state(st, true); +	else +		result = 0; +	if (result) { +		mutex_unlock(&indio_dev->mlock); +		return result; +	} +	result = inv_i2c_read(st, reg->temperature, 2, data); +	if (!st->chip_config.enable) +		result |= st->set_power_state(st, false); +	mutex_unlock(&indio_dev->mlock); +	if (result) { +		pr_err("Could not read temperature register.\n"); +		return result; +	} +	temp = (signed short)(be16_to_cpup((short *)&data[0])); +	switch (st->chip_type) { +	case INV_MPU3050: +		cur_scale = scale[0]; +		cur_off   = offset[0]; +		break; +	case INV_MPU6050: +		cur_scale = scale[1]; +		cur_off   = offset[1]; +		break; +	case INV_MPU6500: +		cur_scale = scale[2]; +		cur_off   = offset[2]; +		break; +	default: +		return -EINVAL; +	}; +	scale_t = cur_off + +		inv_q30_mult((int)temp << MPU_TEMP_SHIFT, cur_scale); + +	INV_I2C_INC_TEMPREAD(1); + +	return sprintf(buf, "%ld %lld\n", scale_t, get_time_ns()); +} + +static ssize_t inv_flush_batch_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	int result; +	bool has_data; + +	mutex_lock(&indio_dev->mlock); +	result = inv_flush_batch_data(indio_dev, &has_data); +	mutex_unlock(&indio_dev->mlock); +	if (result) +		return sprintf(buf, "%d\n", result); +	else +		return sprintf(buf, "%d\n", has_data); +} + +/* + * inv_firmware_loaded() -  calling this function will change + *                        firmware load + */ +static int inv_firmware_loaded(struct inv_mpu_state *st, int data) +{ +	if (data) +		return -EINVAL; +	st->chip_config.firmware_loaded = 0; + +	return 0; +} + +static int inv_switch_gyro_engine(struct inv_mpu_state *st, bool en) +{ +	return inv_switch_engine(st, en, BIT_PWR_GYRO_STBY); +} + +static int inv_switch_accel_engine(struct inv_mpu_state *st, bool en) +{ +	return inv_switch_engine(st, en, BIT_PWR_ACCEL_STBY); +} + +static ssize_t _attr_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int data; +	u8 d, axis; +	int result; + +	result = 0; +	if (st->chip_config.enable) +		return -EBUSY; +	if (this_attr->address <= ATTR_MOTION_LPA_THRESHOLD) { +		result = st->set_power_state(st, true); +		if (result) +			return result; +	} + +	/* check the input and validate it's format */ +	switch (this_attr->address) { +#ifdef CONFIG_INV_TESTING +	/* these inputs are strings */ +	case ATTR_COMPASS_MATRIX: +	case ATTR_COMPASS_SENS: +		break; +#endif +	/* these inputs are integers */ +	default: +		result = kstrtoint(buf, 10, &data); +		if (result) +			goto attr_store_fail; +		break; +	} + +	switch (this_attr->address) { +	case ATTR_GYRO_X_OFFSET: +	case ATTR_GYRO_Y_OFFSET: +	case ATTR_GYRO_Z_OFFSET: +		if ((data > MPU_MAX_G_OFFSET_VALUE) || +				(data < MPU_MIN_G_OFFSET_VALUE)) +			return -EINVAL; +		axis = this_attr->address - ATTR_GYRO_X_OFFSET; +		result = inv_set_offset_reg(st, +				reg_gyro_offset[axis], +				st->rom_gyro_offset[axis] + data); + +		if (result) +			goto attr_store_fail; +		st->input_gyro_offset[axis] = data; +		break; +	case ATTR_ACCEL_X_OFFSET: +	case ATTR_ACCEL_Y_OFFSET: +	case ATTR_ACCEL_Z_OFFSET: +	{ +		const u8 *ch; + +		if ((data > MPU_MAX_A_OFFSET_VALUE) || +			(data < MPU_MIN_A_OFFSET_VALUE)) +			return -EINVAL; + +		axis = this_attr->address - ATTR_ACCEL_X_OFFSET; +		if (INV_MPU6050 == st->chip_type) +			ch = reg_6050_accel_offset; +		else +			ch = reg_6500_accel_offset; + +		result = inv_set_offset_reg(st, ch[axis], +			st->rom_accel_offset[axis] + (data << 1)); +		if (result) +			goto attr_store_fail; +		st->input_accel_offset[axis] = data; +		break; +	} +	case ATTR_GYRO_SCALE: +		result = inv_write_fsr(st, data); +		break; +	case ATTR_ACCEL_SCALE: +		result = inv_write_accel_fs(st, data); +		break; +	case ATTR_COMPASS_SCALE: +		result = st->slave_compass->set_scale(st, data); +		break; +	case ATTR_MOTION_LPA_ON: +		if (INV_MPU6500 == st->chip_type) { +			if (data) +				/* enable and put in MPU6500 mode */ +				d = BIT_ACCEL_INTEL_ENABLE +					| BIT_ACCEL_INTEL_MODE; +			else +				d = 0; +			result = inv_i2c_single_write(st, +						REG_6500_ACCEL_INTEL_CTRL, d); +			if (result) +				goto attr_store_fail; +		} +		st->mot_int.mot_on = !!data; +		st->chip_config.lpa_mode = !!data; +		break; +	case ATTR_MOTION_LPA_FREQ: +		result = inv_lpa_freq(st, data); +		break; +	case ATTR_MOTION_LPA_THRESHOLD: +		if ((data > MPU6XXX_MAX_MOTION_THRESH) || (data < 0)) { +			result = -EINVAL; +			goto attr_store_fail; +		} +		if (INV_MPU6500 == st->chip_type) { +			d = (u8)(data >> MPU6500_MOTION_THRESH_SHIFT); +			data = (d << MPU6500_MOTION_THRESH_SHIFT); +		} else { +			d = (u8)(data >> MPU6050_MOTION_THRESH_SHIFT); +			data = (d << MPU6050_MOTION_THRESH_SHIFT); +		} + +		result = inv_i2c_single_write(st, REG_ACCEL_MOT_THR, d); +		if (result) +			goto attr_store_fail; +		st->mot_int.mot_thr = data; +		break; +	/* from now on, power is not turned on */ +	case ATTR_SELF_TEST_SAMPLES: +		if (data > ST_MAX_SAMPLES || data < 0) { +			result = -EINVAL; +			goto attr_store_fail; +		} +		st->self_test.samples = data; +		break; +	case ATTR_SELF_TEST_THRESHOLD: +		if (data > ST_MAX_THRESHOLD || data < 0) { +			result = -EINVAL; +			goto attr_store_fail; +		} +		st->self_test.threshold = data; +	case ATTR_GYRO_ENABLE: +		st->chip_config.gyro_enable = !!data; +		break; +	case ATTR_GYRO_FIFO_ENABLE: +		st->sensor[SENSOR_GYRO].on = !!data; +		break; +	case ATTR_GYRO_RATE: +		st->sensor[SENSOR_GYRO].rate = data; +		st->sensor[SENSOR_GYRO].dur  = MPU_DEFAULT_DMP_FREQ / data; +		st->sensor[SENSOR_GYRO].dur  *= DMP_INTERVAL_INIT; +		break; +	case ATTR_ACCEL_ENABLE: +		st->chip_config.accel_enable = !!data; +		break; +	case ATTR_ACCEL_FIFO_ENABLE: +		st->sensor[SENSOR_ACCEL].on = !!data; +		break; +	case ATTR_ACCEL_RATE: +		st->sensor[SENSOR_ACCEL].rate = data; +		st->sensor[SENSOR_ACCEL].dur  = MPU_DEFAULT_DMP_FREQ / data; +		st->sensor[SENSOR_ACCEL].dur  *= DMP_INTERVAL_INIT; +		break; +	case ATTR_COMPASS_ENABLE: +		st->sensor[SENSOR_COMPASS].on = !!data; +		break; +	case ATTR_COMPASS_RATE: +		if (data <= 0) { +			result = -EINVAL; +			goto attr_store_fail; +		} +		if ((MSEC_PER_SEC / st->slave_compass->rate_scale) < data) +			data = MSEC_PER_SEC / st->slave_compass->rate_scale; + +		st->sensor[SENSOR_COMPASS].rate = data; +		st->sensor[SENSOR_COMPASS].dur  = MPU_DEFAULT_DMP_FREQ / data; +		st->sensor[SENSOR_COMPASS].dur  *= DMP_INTERVAL_INIT; +		break; +	case ATTR_PRESSURE_ENABLE: +		st->sensor[SENSOR_PRESSURE].on = !!data; +		break; +	case ATTR_PRESSURE_RATE: +		if (data <= 0) { +			result = -EINVAL; +			goto attr_store_fail; +		} +		if ((MSEC_PER_SEC / st->slave_pressure->rate_scale) < data) +			data = MSEC_PER_SEC / st->slave_pressure->rate_scale; + +		st->sensor[SENSOR_PRESSURE].rate = data; +		st->sensor[SENSOR_PRESSURE].dur  = MPU_DEFAULT_DMP_FREQ / data; +		st->sensor[SENSOR_PRESSURE].dur  *= DMP_INTERVAL_INIT; +		break; +	case ATTR_POWER_STATE: +		fake_asleep = !data; +		break; +	case ATTR_FIRMWARE_LOADED: +		result = inv_firmware_loaded(st, data); +		break; +	case ATTR_SAMPLING_FREQ: +		result = inv_fifo_rate_store(st, data); +		break; +#ifdef CONFIG_INV_TESTING +	case ATTR_COMPASS_MATRIX: +	{ +		char *str; +		__s8 m[9]; +		d = 0; +		if (st->plat_data.sec_slave_type == SECONDARY_SLAVE_TYPE_NONE) +			return -ENODEV; +		while ((str = strsep((char **)&buf, ","))) { +			if (d >= 9) { +				result = -EINVAL; +				goto attr_store_fail; +			} +			result = kstrtoint(str, 10, &data); +			if (result) +				goto attr_store_fail; +			if (data < -1 || data > 1) { +				result = -EINVAL; +				goto attr_store_fail; +			} +			m[d] = data; +			d++; +		} +		if (d < 9) { +			result = -EINVAL; +			goto attr_store_fail; +		} +		memcpy(st->plat_data.secondary_orientation, m, sizeof(m)); +		pr_debug(KERN_INFO +			 "compass_matrix: %d,%d,%d,%d,%d,%d,%d,%d,%d\n", +			 m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]); +		break; +	} +	case ATTR_COMPASS_SENS: +	{ +		char *str; +		__s8 s[3]; +		d = 0; +		/* these 2 conditions should never be met, since the +		   'compass_sens' sysfs entry should be hidden if the compass +		   is not an AKM */ +		if (st->plat_data.sec_slave_type == SECONDARY_SLAVE_TYPE_NONE) +			return -ENODEV; +		if (st->plat_data.sec_slave_id != COMPASS_ID_AK8975 && +		    st->plat_data.sec_slave_id != COMPASS_ID_AK8972 && +		    st->plat_data.sec_slave_id != COMPASS_ID_AK8963) +			return -ENODEV; +		/* read the input data, expecting 3 comma separated values */ +		while ((str = strsep((char **)&buf, ","))) { +			if (d >= 3) { +				result = -EINVAL; +				goto attr_store_fail; +			} +			result = kstrtoint(str, 10, &data); +			if (result) +				goto attr_store_fail; +			if (data < 0 || data > 255) { +				result = -EINVAL; +				goto attr_store_fail; +			} +			s[d] = data; +			d++; +		} +		if (d < 3) { +			result = -EINVAL; +			goto attr_store_fail; +		} +		/* store the new compass sensitivity adjustment */ +		memcpy(st->chip_info.compass_sens, s, sizeof(s)); +		pr_debug(KERN_INFO +			 "compass_sens: %d,%d,%d\n", s[0], s[1], s[2]); +		break; +	} +#endif +	default: +		result = -EINVAL; +		goto attr_store_fail; +	}; + +attr_store_fail: +	if (this_attr->address <= ATTR_MOTION_LPA_THRESHOLD) +		result |= st->set_power_state(st, false); +	if (result) +		return result; + +	return count; +} + +/* + * inv_attr_store() -  calling this function will store current + *                        non-dmp parameter settings + */ +static ssize_t inv_attr_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	int result; + +	mutex_lock(&indio_dev->mlock); +	result = _attr_store(dev, attr, buf, count); +	mutex_unlock(&indio_dev->mlock); + +	return result; +} + +static ssize_t inv_master_enable_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	int data; +	int result; + +	result = kstrtoint(buf, 10, &data); +	if (result) +		return result; + +	mutex_lock(&indio_dev->mlock); +	if (st->chip_config.enable == (!!data)) { +		result = count; +		goto end_enable; +	} +	if (!!data) { +		result = st->set_power_state(st, true); +		if (result) +			goto end_enable; +	} +	result = set_inv_enable(indio_dev, !!data); +	if (result) +		goto end_enable; +	if (!data) { +		result = st->set_power_state(st, false); +		if (result) +			goto end_enable; +	} +	result = count; + +end_enable: +	mutex_unlock(&indio_dev->mlock); + +	return result; +} + +static ssize_t inv_master_enable_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct inv_mpu_state *st = iio_priv(dev_get_drvdata(dev)); + +	return sprintf(buf, "%d\n", st->chip_config.enable); +} + +#ifdef CONFIG_INV_TESTING +static ssize_t inv_test_suspend_resume_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	int data; +	int result; + +	result = kstrtoint(buf, 10, &data); +	if (result) +		return result; +	if (data) +		inv_mpu_suspend(dev); +	else +		inv_mpu_resume(dev); +	suspend_state = !!data; + +	return count; +} + +static ssize_t inv_test_suspend_resume_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ + +	return sprintf(buf, "%d\n", suspend_state); +} + +/* + * inv_reg_write_store() - register write command for testing. + *                         Format: WSRRDD, where RR is the register in hex, + *                                         and DD is the data in hex. + */ +static ssize_t inv_reg_write_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	u32 result; +	u8 wreg, wval; +	int temp; +	char local_buf[10]; + +	if ((buf[0] != 'W' && buf[0] != 'w') || +	    (buf[1] != 'S' && buf[1] != 's')) +		return -EINVAL; +	if (strlen(buf) < 6) +		return -EINVAL; + +	strncpy(local_buf, buf, 7); +	local_buf[6] = 0; +	result = sscanf(&local_buf[4], "%x", &temp); +	if (result == 0) +		return -EINVAL; +	wval = temp; +	local_buf[4] = 0; +	sscanf(&local_buf[2], "%x", &temp); +	if (result == 0) +		return -EINVAL; +	wreg = temp; + +	result = inv_i2c_single_write(st, wreg, wval); +	if (result) +		return result; + +	return count; +} + +static ssize_t inv_test_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int data; +	u8 *m; +	int result; + +	if (st->chip_config.enable) +		return -EBUSY; +	result = kstrtoint(buf, 10, &data); +	if (result) +		return -EINVAL; + +	result = st->set_power_state(st, true); +	if (result) +		return result; + +	switch (this_attr->address) { +	case ATTR_DEBUG_ACCEL_COUNTER: +	{ +		u8 D[6] = {0xf2, 0xb0, 0x80, 0xc0, 0xc8, 0xc2}; +		u8 E[6] = {0xf3, 0xb1, 0x88, 0xc0, 0xc0, 0xc0}; + +		if (data) +			m = E; +		else +			m = D; +		result = mem_w_key(KEY_TEST_01, ARRAY_SIZE(D), m); +		data_out_control.accel = !!data; +		break; +	} +	case ATTR_DEBUG_GYRO_COUNTER: +	{ +		u8 D[6] = {0xf2, 0xb0, 0x80, 0xc4, 0xcc, 0xc6}; +		u8 E[6] = {0xf3, 0xb1, 0x88, 0xc0, 0xc0, 0xc0}; + +		if (data) +			m = E; +		else +			m = D; +		result = mem_w_key(KEY_TEST_02, ARRAY_SIZE(D), m); +		data_out_control.gyro = !!data; +		break; +	} +	case ATTR_DEBUG_COMPASS_COUNTER: +	{ +		u8 D[6] = {0xf2, 0xb0, 0x81, 0xc0, 0xc8, 0xc2}; +		u8 E[6] = {0xf3, 0xb1, 0x88, 0xc0, 0xc0, 0xc0}; + +		if (data) +			m = E; +		else +			m = D; +		result = mem_w_key(KEY_TEST_03, ARRAY_SIZE(D), m); +		data_out_control.compass = !!data; +		break; +	} +	case ATTR_DEBUG_PRESSURE_COUNTER: +	{ +		u8 D[6] = {0xf2, 0xb0, 0x81, 0xc4, 0xcc, 0xc6}; +		u8 E[6] = {0xf3, 0xb1, 0x88, 0xc0, 0xc0, 0xc0}; + +		if (data) +			m = E; +		else +			m = D; +		result = mem_w_key(KEY_TEST_04, ARRAY_SIZE(D), m); +		data_out_control.pressure = !!data; +		break; +	} +	case ATTR_DEBUG_LPQ_COUNTER: +	{ +		u8 D[6] = {0xf1, 0xb1, 0x83, 0xc2, 0xc4, 0xc6}; +		u8 E[6] = {0xf1, 0xb1, 0x88, 0xc0, 0xc0, 0xc0}; + +		if (data) +			m = E; +		else +			m = D; +		result = mem_w_key(KEY_TEST_05, ARRAY_SIZE(D), m); +		data_out_control.LPQ = !!data; +		break; +	} +	case ATTR_DEBUG_SIXQ_COUNTER: +	{ +		u8 D[6] = {0xf1, 0xb1, 0x89, 0xc2, 0xc4, 0xc6}; +		u8 E[6] = {0xf1, 0xb1, 0x88, 0xc0, 0xc0, 0xc0}; + +		if (data) +			m = E; +		else +			m = D; +		result = mem_w_key(KEY_TEST_06, ARRAY_SIZE(D), m); +		data_out_control.SIXQ = !!data; +		break; +	} +	case ATTR_DEBUG_PEDQ_COUNTER: +	{ +		u8 D[6] = {0xf2, 0xf2, 0x88, 0xc2, 0xc4, 0xc6}; +		u8 E[6] = {0xf3, 0xb1, 0x88, 0xc0, 0xc0, 0xc0}; + +		if (data) +			m = E; +		else +			m = D; +		result = mem_w_key(KEY_TEST_07, ARRAY_SIZE(D), m); +		data_out_control.PEDQ = !!data; +		break; +	} +	default: +		return -EINVAL; +	} + +	return count; +} + +static ssize_t inv_test_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + +	switch (this_attr->address) { +	case ATTR_DEBUG_ACCEL_COUNTER: +		return sprintf(buf, "%d\n", data_out_control.accel); +	case ATTR_DEBUG_GYRO_COUNTER: +		return sprintf(buf, "%d\n", data_out_control.gyro); +	case ATTR_DEBUG_COMPASS_COUNTER: +		return sprintf(buf, "%d\n", data_out_control.compass); +	case ATTR_DEBUG_PRESSURE_COUNTER: +		return sprintf(buf, "%d\n", data_out_control.pressure); +	case ATTR_DEBUG_LPQ_COUNTER: +		return sprintf(buf, "%d\n", data_out_control.LPQ); +	case ATTR_DEBUG_SIXQ_COUNTER: +		return sprintf(buf, "%d\n", data_out_control.SIXQ); +	case ATTR_DEBUG_PEDQ_COUNTER: +		return sprintf(buf, "%d\n", data_out_control.PEDQ); +	default: +		return -EINVAL; +	} +} + +#endif /* CONFIG_INV_TESTING */ + +static const struct iio_chan_spec inv_mpu_channels[] = { +	IIO_CHAN_SOFT_TIMESTAMP(INV_MPU_SCAN_TIMESTAMP), +}; + +/*constant IIO attribute */ +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 20 50 100 200 500"); + +/* special sysfs */ +static DEVICE_ATTR(reg_dump, S_IRUGO, inv_reg_dump_show, NULL); +static DEVICE_ATTR(temperature, S_IRUGO, inv_temperature_show, NULL); + +/* event based sysfs, needs poll to read */ +static DEVICE_ATTR(event_tap, S_IRUGO, inv_dmp_tap_show, NULL); +static DEVICE_ATTR(event_display_orientation, S_IRUGO, +	inv_dmp_display_orient_show, NULL); +static DEVICE_ATTR(event_accel_motion, S_IRUGO, inv_accel_motion_show, NULL); +static DEVICE_ATTR(event_smd, S_IRUGO, inv_smd_show, NULL); +static DEVICE_ATTR(event_pedometer, S_IRUGO, inv_ped_show, NULL); + +/* master enable method */ +static DEVICE_ATTR(master_enable, S_IRUGO | S_IWUSR, inv_master_enable_show, +					inv_master_enable_store); + +/* special run time sysfs entry, read only */ +static DEVICE_ATTR(flush_batch, S_IRUGO, inv_flush_batch_show, NULL); + +/* DMP sysfs with power on/off */ +static IIO_DEVICE_ATTR(in_accel_x_dmp_bias, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_bias_store, ATTR_DMP_ACCEL_X_DMP_BIAS); +static IIO_DEVICE_ATTR(in_accel_y_dmp_bias, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_bias_store, ATTR_DMP_ACCEL_Y_DMP_BIAS); +static IIO_DEVICE_ATTR(in_accel_z_dmp_bias, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_bias_store, ATTR_DMP_ACCEL_Z_DMP_BIAS); + +static IIO_DEVICE_ATTR(in_anglvel_x_dmp_bias, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_bias_store, ATTR_DMP_GYRO_X_DMP_BIAS); +static IIO_DEVICE_ATTR(in_anglvel_y_dmp_bias, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_bias_store, ATTR_DMP_GYRO_Y_DMP_BIAS); +static IIO_DEVICE_ATTR(in_anglvel_z_dmp_bias, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_bias_store, ATTR_DMP_GYRO_Z_DMP_BIAS); + +static IIO_DEVICE_ATTR(pedometer_int_on, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_mem_store, ATTR_DMP_PED_INT_ON); +static IIO_DEVICE_ATTR(pedometer_on, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_mem_store, ATTR_DMP_PED_ON); +static IIO_DEVICE_ATTR(pedometer_step_thresh, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_mem_store, ATTR_DMP_PED_STEP_THRESH); +static IIO_DEVICE_ATTR(pedometer_int_thresh, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_mem_store, ATTR_DMP_PED_INT_THRESH); + +static IIO_DEVICE_ATTR(smd_enable, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_mem_store, ATTR_DMP_SMD_ENABLE); +static IIO_DEVICE_ATTR(smd_threshold, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_mem_store, ATTR_DMP_SMD_THLD); +static IIO_DEVICE_ATTR(smd_delay_threshold, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_mem_store, ATTR_DMP_SMD_DELAY_THLD); +static IIO_DEVICE_ATTR(smd_delay_threshold2, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_mem_store, ATTR_DMP_SMD_DELAY_THLD2); + +static IIO_DEVICE_ATTR(pedometer_steps, S_IRUGO | S_IWUSR, inv_attr64_show, +	inv_attr64_store, ATTR_DMP_PEDOMETER_STEPS); +static IIO_DEVICE_ATTR(pedometer_time, S_IRUGO | S_IWUSR, inv_attr64_show, +	inv_attr64_store, ATTR_DMP_PEDOMETER_TIME); +static IIO_DEVICE_ATTR(pedometer_counter, S_IRUGO | S_IWUSR, inv_attr64_show, +	NULL, ATTR_DMP_PEDOMETER_COUNTER); + +static IIO_DEVICE_ATTR(tap_on, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_mem_store, ATTR_DMP_TAP_ON); +static IIO_DEVICE_ATTR(tap_threshold, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_mem_store, ATTR_DMP_TAP_THRESHOLD); +static IIO_DEVICE_ATTR(tap_min_count, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_mem_store, ATTR_DMP_TAP_MIN_COUNT); +static IIO_DEVICE_ATTR(tap_time, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_mem_store, ATTR_DMP_TAP_TIME); +static IIO_DEVICE_ATTR(display_orientation_on, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_mem_store, ATTR_DMP_DISPLAY_ORIENTATION_ON); + +/* DMP sysfs without power on/off */ +static IIO_DEVICE_ATTR(dmp_on, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_ON); +static IIO_DEVICE_ATTR(dmp_int_on, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_INT_ON); +static IIO_DEVICE_ATTR(dmp_event_int_on, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_EVENT_INT_ON); +static IIO_DEVICE_ATTR(step_indicator_on, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_STEP_INDICATOR_ON); +static IIO_DEVICE_ATTR(batchmode_timeout, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_attr_store, ATTR_DMP_BATCHMODE_TIMEOUT); +static IIO_DEVICE_ATTR(batchmode_wake_fifo_full_on, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_attr_store, ATTR_DMP_BATCHMODE_WAKE_FIFO_FULL); + +static IIO_DEVICE_ATTR(six_axes_q_on, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_SIX_Q_ON); +static IIO_DEVICE_ATTR(six_axes_q_rate, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_SIX_Q_RATE); + +static IIO_DEVICE_ATTR(three_axes_q_on, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_LPQ_ON); +static IIO_DEVICE_ATTR(three_axes_q_rate, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_LPQ_RATE); + +static IIO_DEVICE_ATTR(ped_q_on, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_PED_Q_ON); +static IIO_DEVICE_ATTR(ped_q_rate, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_PED_Q_RATE); + +static IIO_DEVICE_ATTR(step_detector_on, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_STEP_DETECTOR_ON); + +/* non DMP sysfs with power on/off */ +static IIO_DEVICE_ATTR(motion_lpa_on, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_MOTION_LPA_ON); +static IIO_DEVICE_ATTR(motion_lpa_freq, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_MOTION_LPA_FREQ); +static IIO_DEVICE_ATTR(motion_lpa_threshold, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_MOTION_LPA_THRESHOLD); + +static IIO_DEVICE_ATTR(in_accel_scale, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_ACCEL_SCALE); +static IIO_DEVICE_ATTR(in_anglvel_scale, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_GYRO_SCALE); +static IIO_DEVICE_ATTR(in_magn_scale, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_COMPASS_SCALE); + +static IIO_DEVICE_ATTR(in_anglvel_x_offset, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_GYRO_X_OFFSET); +static IIO_DEVICE_ATTR(in_anglvel_y_offset, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_GYRO_Y_OFFSET); +static IIO_DEVICE_ATTR(in_anglvel_z_offset, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_GYRO_Z_OFFSET); + +static IIO_DEVICE_ATTR(in_accel_x_offset, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_ACCEL_X_OFFSET); +static IIO_DEVICE_ATTR(in_accel_y_offset, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_ACCEL_Y_OFFSET); +static IIO_DEVICE_ATTR(in_accel_z_offset, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_ACCEL_Z_OFFSET); + +/* non DMP sysfs without power on/off */ +static IIO_DEVICE_ATTR(self_test_samples, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_SELF_TEST_SAMPLES); +static IIO_DEVICE_ATTR(self_test_threshold, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_SELF_TEST_THRESHOLD); + +static IIO_DEVICE_ATTR(gyro_enable, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_GYRO_ENABLE); +static IIO_DEVICE_ATTR(gyro_fifo_enable, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_GYRO_FIFO_ENABLE); +static IIO_DEVICE_ATTR(gyro_rate, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_GYRO_RATE); + +static IIO_DEVICE_ATTR(accel_enable, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_ACCEL_ENABLE); +static IIO_DEVICE_ATTR(accel_fifo_enable, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_ACCEL_FIFO_ENABLE); +static IIO_DEVICE_ATTR(accel_rate, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_ACCEL_RATE); + +static IIO_DEVICE_ATTR(compass_enable, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_COMPASS_ENABLE); +static IIO_DEVICE_ATTR(compass_rate, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_COMPASS_RATE); + +static IIO_DEVICE_ATTR(pressure_enable, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_PRESSURE_ENABLE); +static IIO_DEVICE_ATTR(pressure_rate, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_PRESSURE_RATE); + +static IIO_DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_POWER_STATE); +static IIO_DEVICE_ATTR(firmware_loaded, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_FIRMWARE_LOADED); +static IIO_DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_SAMPLING_FREQ); + +/* show method only sysfs but with power on/off */ +static IIO_DEVICE_ATTR(self_test, S_IRUGO, inv_attr_show, NULL, +	ATTR_SELF_TEST); + +/* show method only sysfs */ +static IIO_DEVICE_ATTR(in_accel_x_calibbias, S_IRUGO, inv_attr_show, +	NULL, ATTR_ACCEL_X_CALIBBIAS); +static IIO_DEVICE_ATTR(in_accel_y_calibbias, S_IRUGO, inv_attr_show, +	NULL, ATTR_ACCEL_Y_CALIBBIAS); +static IIO_DEVICE_ATTR(in_accel_z_calibbias, S_IRUGO, inv_attr_show, +	NULL, ATTR_ACCEL_Z_CALIBBIAS); + +static IIO_DEVICE_ATTR(in_anglvel_x_calibbias, S_IRUGO, +		inv_attr_show, NULL, ATTR_GYRO_X_CALIBBIAS); +static IIO_DEVICE_ATTR(in_anglvel_y_calibbias, S_IRUGO, +		inv_attr_show, NULL, ATTR_GYRO_Y_CALIBBIAS); +static IIO_DEVICE_ATTR(in_anglvel_z_calibbias, S_IRUGO, +		inv_attr_show, NULL, ATTR_GYRO_Z_CALIBBIAS); + +static IIO_DEVICE_ATTR(in_anglvel_self_test_scale, S_IRUGO, +		inv_attr_show, NULL, ATTR_SELF_TEST_GYRO_SCALE); +static IIO_DEVICE_ATTR(in_accel_self_test_scale, S_IRUGO, +		inv_attr_show, NULL, ATTR_SELF_TEST_ACCEL_SCALE); + +static IIO_DEVICE_ATTR(gyro_matrix, S_IRUGO, inv_attr_show, NULL, +	ATTR_GYRO_MATRIX); +static IIO_DEVICE_ATTR(accel_matrix, S_IRUGO, inv_attr_show, NULL, +	ATTR_ACCEL_MATRIX); +#ifdef CONFIG_INV_TESTING /* read/write in test mode */ +static IIO_DEVICE_ATTR(compass_matrix, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_COMPASS_MATRIX); +static IIO_DEVICE_ATTR(compass_sens, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_COMPASS_SENS); +#else +static IIO_DEVICE_ATTR(compass_matrix, S_IRUGO, inv_attr_show, NULL, +	ATTR_COMPASS_MATRIX); +#endif +static IIO_DEVICE_ATTR(secondary_name, S_IRUGO, inv_attr_show, NULL, +	ATTR_SECONDARY_NAME); + +#ifdef CONFIG_INV_TESTING +static IIO_DEVICE_ATTR(reg_write, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_reg_write_store, ATTR_REG_WRITE); +/* smd debug related sysfs */ +static IIO_DEVICE_ATTR(debug_smd_enable_testp1, S_IWUSR, NULL, +	inv_dmp_attr_store, ATTR_DEBUG_SMD_ENABLE_TESTP1); +static IIO_DEVICE_ATTR(debug_smd_enable_testp2, S_IWUSR, NULL, +	inv_dmp_attr_store, ATTR_DEBUG_SMD_ENABLE_TESTP2); +static IIO_DEVICE_ATTR(debug_smd_exe_state, S_IRUGO, inv_attr_show, +	NULL, ATTR_DEBUG_SMD_EXE_STATE); +static IIO_DEVICE_ATTR(debug_smd_delay_cntr, S_IRUGO, inv_attr_show, +	NULL, ATTR_DEBUG_SMD_DELAY_CNTR); +static DEVICE_ATTR(test_suspend_resume, S_IRUGO | S_IWUSR, +		inv_test_suspend_resume_show, inv_test_suspend_resume_store); + +static IIO_DEVICE_ATTR(test_gyro_counter, S_IRUGO | S_IWUSR, inv_test_show, +	inv_test_store, ATTR_DEBUG_GYRO_COUNTER); +static IIO_DEVICE_ATTR(test_accel_counter, S_IRUGO | S_IWUSR, inv_test_show, +	inv_test_store, ATTR_DEBUG_ACCEL_COUNTER); +static IIO_DEVICE_ATTR(test_compass_counter, S_IRUGO | S_IWUSR, inv_test_show, +	inv_test_store, ATTR_DEBUG_COMPASS_COUNTER); +static IIO_DEVICE_ATTR(test_pressure_counter, S_IRUGO | S_IWUSR, inv_test_show, +	inv_test_store, ATTR_DEBUG_PRESSURE_COUNTER); +static IIO_DEVICE_ATTR(test_LPQ_counter, S_IRUGO | S_IWUSR, inv_test_show, +	inv_test_store, ATTR_DEBUG_LPQ_COUNTER); +static IIO_DEVICE_ATTR(test_SIXQ_counter, S_IRUGO | S_IWUSR, inv_test_show, +	inv_test_store, ATTR_DEBUG_SIXQ_COUNTER); +static IIO_DEVICE_ATTR(test_PEDQ_counter, S_IRUGO | S_IWUSR, inv_test_show, +	inv_test_store, ATTR_DEBUG_PEDQ_COUNTER); +#endif + +static const struct attribute *inv_gyro_attributes[] = { +	&iio_const_attr_sampling_frequency_available.dev_attr.attr, +	&dev_attr_reg_dump.attr, +	&dev_attr_temperature.attr, +	&dev_attr_master_enable.attr, +	&iio_dev_attr_in_anglvel_scale.dev_attr.attr, +	&iio_dev_attr_in_anglvel_x_calibbias.dev_attr.attr, +	&iio_dev_attr_in_anglvel_y_calibbias.dev_attr.attr, +	&iio_dev_attr_in_anglvel_z_calibbias.dev_attr.attr, +	&iio_dev_attr_in_anglvel_x_offset.dev_attr.attr, +	&iio_dev_attr_in_anglvel_y_offset.dev_attr.attr, +	&iio_dev_attr_in_anglvel_z_offset.dev_attr.attr, +	&iio_dev_attr_in_anglvel_self_test_scale.dev_attr.attr, +	&iio_dev_attr_self_test_samples.dev_attr.attr, +	&iio_dev_attr_self_test_threshold.dev_attr.attr, +	&iio_dev_attr_gyro_enable.dev_attr.attr, +	&iio_dev_attr_gyro_fifo_enable.dev_attr.attr, +	&iio_dev_attr_gyro_rate.dev_attr.attr, +	&iio_dev_attr_power_state.dev_attr.attr, +	&iio_dev_attr_sampling_frequency.dev_attr.attr, +	&iio_dev_attr_self_test.dev_attr.attr, +	&iio_dev_attr_gyro_matrix.dev_attr.attr, +	&iio_dev_attr_secondary_name.dev_attr.attr, +#ifdef CONFIG_INV_TESTING +	&iio_dev_attr_reg_write.dev_attr.attr, +	&iio_dev_attr_debug_smd_enable_testp1.dev_attr.attr, +	&iio_dev_attr_debug_smd_enable_testp2.dev_attr.attr, +	&iio_dev_attr_debug_smd_exe_state.dev_attr.attr, +	&iio_dev_attr_debug_smd_delay_cntr.dev_attr.attr, +	&dev_attr_test_suspend_resume.attr, +	&iio_dev_attr_test_gyro_counter.dev_attr.attr, +	&iio_dev_attr_test_accel_counter.dev_attr.attr, +	&iio_dev_attr_test_compass_counter.dev_attr.attr, +	&iio_dev_attr_test_pressure_counter.dev_attr.attr, +	&iio_dev_attr_test_LPQ_counter.dev_attr.attr, +	&iio_dev_attr_test_SIXQ_counter.dev_attr.attr, +	&iio_dev_attr_test_PEDQ_counter.dev_attr.attr, +#endif +}; + +static const struct attribute *inv_mpu6xxx_attributes[] = { +	&dev_attr_event_accel_motion.attr, +	&dev_attr_event_smd.attr, +	&dev_attr_event_pedometer.attr, +	&dev_attr_flush_batch.attr, +	&iio_dev_attr_in_accel_scale.dev_attr.attr, +	&iio_dev_attr_in_accel_x_calibbias.dev_attr.attr, +	&iio_dev_attr_in_accel_y_calibbias.dev_attr.attr, +	&iio_dev_attr_in_accel_z_calibbias.dev_attr.attr, +	&iio_dev_attr_in_accel_self_test_scale.dev_attr.attr, +	&iio_dev_attr_in_accel_x_offset.dev_attr.attr, +	&iio_dev_attr_in_accel_y_offset.dev_attr.attr, +	&iio_dev_attr_in_accel_z_offset.dev_attr.attr, +	&iio_dev_attr_in_accel_x_dmp_bias.dev_attr.attr, +	&iio_dev_attr_in_accel_y_dmp_bias.dev_attr.attr, +	&iio_dev_attr_in_accel_z_dmp_bias.dev_attr.attr, +	&iio_dev_attr_in_anglvel_x_dmp_bias.dev_attr.attr, +	&iio_dev_attr_in_anglvel_y_dmp_bias.dev_attr.attr, +	&iio_dev_attr_in_anglvel_z_dmp_bias.dev_attr.attr, +	&iio_dev_attr_pedometer_int_on.dev_attr.attr, +	&iio_dev_attr_pedometer_on.dev_attr.attr, +	&iio_dev_attr_pedometer_steps.dev_attr.attr, +	&iio_dev_attr_pedometer_time.dev_attr.attr, +	&iio_dev_attr_pedometer_counter.dev_attr.attr, +	&iio_dev_attr_pedometer_step_thresh.dev_attr.attr, +	&iio_dev_attr_pedometer_int_thresh.dev_attr.attr, +	&iio_dev_attr_smd_enable.dev_attr.attr, +	&iio_dev_attr_smd_threshold.dev_attr.attr, +	&iio_dev_attr_smd_delay_threshold.dev_attr.attr, +	&iio_dev_attr_smd_delay_threshold2.dev_attr.attr, +	&iio_dev_attr_dmp_on.dev_attr.attr, +	&iio_dev_attr_dmp_int_on.dev_attr.attr, +	&iio_dev_attr_dmp_event_int_on.dev_attr.attr, +	&iio_dev_attr_step_indicator_on.dev_attr.attr, +	&iio_dev_attr_batchmode_timeout.dev_attr.attr, +	&iio_dev_attr_batchmode_wake_fifo_full_on.dev_attr.attr, +	&iio_dev_attr_six_axes_q_on.dev_attr.attr, +	&iio_dev_attr_six_axes_q_rate.dev_attr.attr, +	&iio_dev_attr_three_axes_q_on.dev_attr.attr, +	&iio_dev_attr_three_axes_q_rate.dev_attr.attr, +	&iio_dev_attr_ped_q_on.dev_attr.attr, +	&iio_dev_attr_ped_q_rate.dev_attr.attr, +	&iio_dev_attr_step_detector_on.dev_attr.attr, +	&iio_dev_attr_accel_enable.dev_attr.attr, +	&iio_dev_attr_accel_fifo_enable.dev_attr.attr, +	&iio_dev_attr_accel_rate.dev_attr.attr, +	&iio_dev_attr_firmware_loaded.dev_attr.attr, +	&iio_dev_attr_accel_matrix.dev_attr.attr, +}; + +static const struct attribute *inv_mpu6500_attributes[] = { +	&iio_dev_attr_motion_lpa_on.dev_attr.attr, +	&iio_dev_attr_motion_lpa_freq.dev_attr.attr, +	&iio_dev_attr_motion_lpa_threshold.dev_attr.attr, +}; + +static const struct attribute *inv_tap_attributes[] = { +	&dev_attr_event_tap.attr, +	&iio_dev_attr_tap_on.dev_attr.attr, +	&iio_dev_attr_tap_threshold.dev_attr.attr, +	&iio_dev_attr_tap_min_count.dev_attr.attr, +	&iio_dev_attr_tap_time.dev_attr.attr, +}; + +static const struct attribute *inv_display_orient_attributes[] = { +	&dev_attr_event_display_orientation.attr, +	&iio_dev_attr_display_orientation_on.dev_attr.attr, +}; + +static const struct attribute *inv_compass_attributes[] = { +	&iio_dev_attr_in_magn_scale.dev_attr.attr, +	&iio_dev_attr_compass_enable.dev_attr.attr, +	&iio_dev_attr_compass_rate.dev_attr.attr, +	&iio_dev_attr_compass_matrix.dev_attr.attr, +}; + +static const struct attribute *inv_akxxxx_attributes[] = { +#ifdef CONFIG_INV_TESTING +	&iio_dev_attr_compass_sens.dev_attr.attr, +#endif +}; + +static const struct attribute *inv_pressure_attributes[] = { +	&iio_dev_attr_pressure_enable.dev_attr.attr, +	&iio_dev_attr_pressure_rate.dev_attr.attr, +}; + +static const struct attribute *inv_mpu3050_attributes[] = { +	&iio_dev_attr_in_accel_scale.dev_attr.attr, +	&iio_dev_attr_accel_enable.dev_attr.attr, +	&iio_dev_attr_accel_fifo_enable.dev_attr.attr, +	&iio_dev_attr_accel_matrix.dev_attr.attr, +}; + +static struct attribute *inv_attributes[ +	ARRAY_SIZE(inv_gyro_attributes) + +	ARRAY_SIZE(inv_mpu6xxx_attributes) + +	ARRAY_SIZE(inv_mpu6500_attributes) + +	ARRAY_SIZE(inv_compass_attributes) + +	ARRAY_SIZE(inv_akxxxx_attributes) + +	ARRAY_SIZE(inv_pressure_attributes) + +	ARRAY_SIZE(inv_tap_attributes) + +	ARRAY_SIZE(inv_display_orient_attributes) + +	1 +]; + +static const struct attribute_group inv_attribute_group = { +	.name = "mpu", +	.attrs = inv_attributes +}; + +static const struct iio_info mpu_info = { +	.driver_module = THIS_MODULE, +	.attrs = &inv_attribute_group, +}; + +static void inv_setup_func_ptr(struct inv_mpu_state *st) +{ +	if (st->chip_type == INV_MPU3050) { +		st->set_power_state    = set_power_mpu3050; +		st->switch_gyro_engine = inv_switch_3050_gyro_engine; +		st->switch_accel_engine = inv_switch_3050_accel_engine; +		st->init_config        = inv_init_config_mpu3050; +		st->setup_reg          = inv_setup_reg_mpu3050; +	} else { +		st->set_power_state    = set_power_itg; +		st->switch_gyro_engine = inv_switch_gyro_engine; +		st->switch_accel_engine = inv_switch_accel_engine; +		st->init_config        = inv_init_config; +		st->setup_reg          = inv_setup_reg; +	} +} + +static int inv_detect_6xxx(struct inv_mpu_state *st) +{ +	int result; +	u8 d; + +	result = inv_i2c_read(st, REG_WHOAMI, 1, &d); +	if (result) +		return result; +	if ((d == MPU6500_ID) || (d == MPU6515_ID)) { +		st->chip_type = INV_MPU6500; +		strcpy(st->name, "mpu6500"); +	} else { +		strcpy(st->name, "mpu6050"); +	} + +	return 0; +} + +static int inv_setup_vddio(struct inv_mpu_state *st) +{ +	int result; +	u8 data[1]; + +	if (INV_MPU6050 == st->chip_type) { +		result = inv_i2c_read(st, REG_YGOFFS_TC, 1, data); +		if (result) +			return result; +		data[0] &= ~BIT_I2C_MST_VDDIO; +		if (st->plat_data.level_shifter) +			data[0] |= BIT_I2C_MST_VDDIO; +		/*set up VDDIO register */ +		result = inv_i2c_single_write(st, REG_YGOFFS_TC, data[0]); +		if (result) +			return result; +	} + +	return 0; +} + +/* + *  inv_check_chip_type() - check and setup chip type. + */ +static int inv_check_chip_type(struct inv_mpu_state *st, +		const struct i2c_device_id *id, bool reset_needed) +{ +	struct inv_reg_map_s *reg; +	int result; +	int t_ind; +	struct inv_chip_config_s *conf; +	struct mpu_platform_data *plat; + +	conf = &st->chip_config; +	plat = &st->plat_data; +	if (!strcmp(id->name, "itg3500")) { +		st->chip_type = INV_ITG3500; +	} else if (!strcmp(id->name, "mpu3050")) { +		st->chip_type = INV_MPU3050; +	} else if (!strcmp(id->name, "mpu6050")) { +		st->chip_type = INV_MPU6050; +	} else if (!strcmp(id->name, "mpu9150")) { +		st->chip_type = INV_MPU6050; +		plat->sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS; +		plat->sec_slave_id = COMPASS_ID_AK8975; +	} else if (!strcmp(id->name, "mpu6500")) { +		st->chip_type = INV_MPU6500; +	} else if (!strcmp(id->name, "mpu9250")) { +		st->chip_type = INV_MPU6500; +		plat->sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS; +		plat->sec_slave_id = COMPASS_ID_AK8963; +	} else if (!strcmp(id->name, "mpu6xxx")) { +		st->chip_type = INV_MPU6050; +	} else if (!strcmp(id->name, "mpu9350")) { +		st->chip_type = INV_MPU6500; +		plat->sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS; +		plat->sec_slave_id = COMPASS_ID_MLX90399; +		/* block use of MPU9350 in this build +		   since it's not production ready */ +		pr_err("MPU9350 support is not officially available yet.\n"); +		return -EPERM; +	} else if (!strcmp(id->name, "mpu6515")) { +		st->chip_type = INV_MPU6500; +	} else { +		return -EPERM; +	} +	inv_setup_func_ptr(st); +	st->hw  = &hw_info[st->chip_type]; +	reg = &st->reg; +	st->setup_reg(reg); + +	if (reset_needed) { +		/* reset to make sure previous state are not there */ +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, BIT_H_RESET); +		if (result) +			return result; +		msleep(POWER_UP_TIME); +	} +	/* toggle power state */ +	result = st->set_power_state(st, false); +	if (result) +		return result; + +	result = st->set_power_state(st, true); +	if (result) +		return result; + +	if (!strcmp(id->name, "mpu6xxx")) { +		/* for MPU6500, reading register need more time */ +		msleep(POWER_UP_TIME); +		result = inv_detect_6xxx(st); +		if (result) +			return result; +	} + +	if ((plat->sec_slave_type != SECONDARY_SLAVE_TYPE_NONE) || +		(plat->aux_slave_type != SECONDARY_SLAVE_TYPE_NONE)) { +		result = inv_setup_vddio(st); +		if (result) +			return result; +	} + +	switch (st->chip_type) { +	case INV_ITG3500: +		break; +	case INV_MPU6050: +	case INV_MPU6500: +		if (SECONDARY_SLAVE_TYPE_COMPASS == plat->sec_slave_type) +			conf->has_compass = 1; +		else +			conf->has_compass = 0; +		if (SECONDARY_SLAVE_TYPE_PRESSURE == plat->aux_slave_type) +			conf->has_pressure = 1; +		else +			conf->has_pressure = 0; + +		break; +	case INV_MPU3050: +		if (SECONDARY_SLAVE_TYPE_ACCEL == plat->sec_slave_type) { +			if (ACCEL_ID_BMA250 == plat->sec_slave_id) +				inv_register_mpu3050_slave(st); +		} +		break; +	default: +		result = st->set_power_state(st, false); +		return -ENODEV; +	} +	if (conf->has_compass && conf->has_pressure && +			(COMPASS_ID_MLX90399 == plat->sec_slave_id)) { +		pr_err("MLX90399 can't share slaves with others\n"); +		return -EINVAL; +	} +	switch (st->chip_type) { +	case INV_MPU6050: +		result = inv_get_silicon_rev_mpu6050(st); +		break; +	case INV_MPU6500: +		result = inv_get_silicon_rev_mpu6500(st); +		break; +	default: +		result = 0; +		break; +	} +	if (result) { +		pr_err("read silicon rev error\n"); +		st->set_power_state(st, false); +		return result; +	} + +	/* turn off the gyro engine after OTP reading */ +	result = st->switch_gyro_engine(st, false); +	if (result) +		return result; +	result = st->switch_accel_engine(st, false); +	if (result) +		return result; + +	if (conf->has_compass) { +		result = inv_mpu_setup_compass_slave(st); +		if (result) { +			pr_err("compass setup failed\n"); +			st->set_power_state(st, false); +			return result; +		} +	} +	if (conf->has_pressure) { +		result = inv_mpu_setup_pressure_slave(st); +		if (result) { +			pr_err("pressure setup failed\n"); +			st->set_power_state(st, false); +			return result; +		} +	} + +	t_ind = 0; +	memcpy(&inv_attributes[t_ind], inv_gyro_attributes, +		sizeof(inv_gyro_attributes)); +	t_ind += ARRAY_SIZE(inv_gyro_attributes); + +	if (INV_MPU3050 == st->chip_type && st->slave_accel != NULL) { +		memcpy(&inv_attributes[t_ind], inv_mpu3050_attributes, +		       sizeof(inv_mpu3050_attributes)); +		t_ind += ARRAY_SIZE(inv_mpu3050_attributes); +		inv_attributes[t_ind] = NULL; +		return 0; +	} + +	/* all MPU6xxx based parts */ +	if ((INV_MPU6050 == st->chip_type) || (INV_MPU6500 == st->chip_type)) { +		memcpy(&inv_attributes[t_ind], inv_mpu6xxx_attributes, +		       sizeof(inv_mpu6xxx_attributes)); +		t_ind += ARRAY_SIZE(inv_mpu6xxx_attributes); +	} + +	/* MPU6500 only */ +	if (INV_MPU6500 == st->chip_type) { +		memcpy(&inv_attributes[t_ind], inv_mpu6500_attributes, +		       sizeof(inv_mpu6500_attributes)); +		t_ind += ARRAY_SIZE(inv_mpu6500_attributes); +	} + +	if (conf->has_compass) { +		memcpy(&inv_attributes[t_ind], inv_compass_attributes, +		       sizeof(inv_compass_attributes)); +		t_ind += ARRAY_SIZE(inv_compass_attributes); + +		/* AKM only */ +		if (st->plat_data.sec_slave_id == COMPASS_ID_AK8975 || +		    st->plat_data.sec_slave_id == COMPASS_ID_AK8972 || +		    st->plat_data.sec_slave_id == COMPASS_ID_AK09911 || +		    st->plat_data.sec_slave_id == COMPASS_ID_AK8963) { +			memcpy(&inv_attributes[t_ind], inv_akxxxx_attributes, +			       sizeof(inv_akxxxx_attributes)); +			t_ind += ARRAY_SIZE(inv_akxxxx_attributes); +		} +	} + +	if (conf->has_pressure) { +		memcpy(&inv_attributes[t_ind], inv_pressure_attributes, +		       sizeof(inv_pressure_attributes)); +		t_ind += ARRAY_SIZE(inv_pressure_attributes); +	} + +	inv_attributes[t_ind] = NULL; + +	return 0; +} + +/* + *  inv_create_dmp_sysfs() - create binary sysfs dmp entry. + */ +static const struct bin_attribute dmp_firmware = { +	.attr = { +		.name = "dmp_firmware", +		.mode = S_IRUGO | S_IWUSR +	}, +	.size = DMP_IMAGE_SIZE + 32, +	.read = inv_dmp_firmware_read, +	.write = inv_dmp_firmware_write, +}; + +static const struct bin_attribute six_q_value = { +	.attr = { +		.name = "six_axes_q_value", +		.mode = S_IWUSR +	}, +	.size = QUATERNION_BYTES, +	.read = NULL, +	.write = inv_six_q_write, +}; + +static int inv_create_dmp_sysfs(struct iio_dev *ind) +{ +	int result; + +	result = sysfs_create_bin_file(&ind->dev.kobj, &dmp_firmware); +	if (result) +		return result; +	result = sysfs_create_bin_file(&ind->dev.kobj, &six_q_value); + +	return result; +} + +/* + *  inv_mpu_probe() - probe function. + */ +static int inv_mpu_probe(struct i2c_client *client, +	const struct i2c_device_id *id) +{ +	struct inv_mpu_state *st; +	struct iio_dev *indio_dev; +	int result; + +	/* +	 * If we're not coming from a power-off condition, we need to +	 * reset the chip as we may have gotten here via a watchdog +	 * reboot, in which case the status of the chip is unknown +	 * (i.e. chip is not reset by hardware on a watchdog reboot). +	 */ +	bool reset_needed = true; + +#ifdef CONFIG_DTS_INV_MPU_IIO +	enable_irq_wake(client->irq); +#endif + +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { +		result = -ENOSYS; +		pr_err("I2c function error\n"); +		goto out_no_free; +	} +#ifdef CONFIG_INV_KERNEL_3_10 +	indio_dev = iio_device_alloc(sizeof(*st)); +#else +	indio_dev = iio_allocate_device(sizeof(*st)); +#endif +	if (indio_dev == NULL) { +		pr_err("memory allocation failed\n"); +		result =  -ENOMEM; +		goto out_no_free; +	} +	st = iio_priv(indio_dev); +	st->client = client; +	st->sl_handle = client->adapter; +	st->i2c_addr = client->addr; +#ifdef CONFIG_DTS_INV_MPU_IIO +	result = invensense_mpu_parse_dt(&client->dev, &st->plat_data); +	if (result) +		goto out_free; + +	/*Power on device.*/ +	if (st->plat_data.power_on) { +		result = st->plat_data.power_on(&st->plat_data); +		if (result < 0) { +			dev_err(&client->dev, +					"power_on failed: %d\n", result); +			return result; +		} +		msleep(POWER_UP_TIME); + +		/* +		 * We don't need subsequent reset of chip as it's coming +		 * from a power-off condition +		 */ +		reset_needed = false; +	} +#else +	/* power is turned on inside check chip type */ +	st->plat_data = +	*(struct mpu_platform_data *)dev_get_platdata(&client->dev); +#endif +	result = inv_check_chip_type(st, id, reset_needed); +	if (result) +		goto out_free; + +	result = st->init_config(indio_dev); +	if (result) { +		dev_err(&client->adapter->dev, +			"Could not initialize device.\n"); +		goto out_free; +	} +	/* Make state variables available to all _show and _store functions. */ +	i2c_set_clientdata(client, indio_dev); +	indio_dev->dev.parent = &client->dev; +	if (!strcmp(id->name, "mpu6xxx")) +		indio_dev->name = st->name; +	else +		indio_dev->name = id->name; +	indio_dev->channels = inv_mpu_channels; +	indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels); + +	indio_dev->info = &mpu_info; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->currentmode = INDIO_DIRECT_MODE; + +	result = inv_mpu_configure_ring(indio_dev); +	if (result) { +		pr_err("configure ring buffer fail\n"); +		goto out_free; +	} +	result = iio_buffer_register(indio_dev, indio_dev->channels, +					indio_dev->num_channels); +	if (result) { +		pr_err("ring buffer register fail\n"); +		goto out_unreg_ring; +	} +	st->irq = client->irq; +	result = inv_mpu_probe_trigger(indio_dev); +	if (result) { +		pr_err("trigger probe fail\n"); +		goto out_remove_ring; +	} + +	/* Tell the i2c counter, we have an IRQ */ +	INV_I2C_SETIRQ(IRQ_MPU, client->irq); + +	result = iio_device_register(indio_dev); +	if (result) { +		pr_err("IIO device register fail\n"); +		goto out_remove_trigger; +	} + +	if (INV_MPU6050 == st->chip_type || +		INV_MPU6500 == st->chip_type) { +		result = inv_create_dmp_sysfs(indio_dev); +		if (result) { +			pr_err("create dmp sysfs failed\n"); +			goto out_unreg_iio; +		} +	} +	INIT_KFIFO(st->timestamps); +	spin_lock_init(&st->time_stamp_lock); +	mutex_init(&st->suspend_resume_lock); +	result = st->set_power_state(st, false); +	if (result) { +		dev_err(&client->adapter->dev, +			"%s could not be turned off.\n", st->hw->name); +		goto out_unreg_iio; +	} +	inv_init_sensor_struct(st); +	dev_info(&client->dev, "%s is ready to go!\n", +					indio_dev->name); + +	return 0; +out_unreg_iio: +	iio_device_unregister(indio_dev); +out_remove_trigger: +	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) +		inv_mpu_remove_trigger(indio_dev); +out_remove_ring: +	iio_buffer_unregister(indio_dev); +out_unreg_ring: +	inv_mpu_unconfigure_ring(indio_dev); +out_free: +#ifdef CONFIG_INV_KERNEL_3_10 +	iio_device_free(indio_dev); +#else +	iio_free_device(indio_dev); +#endif +out_no_free: +	dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); +	return -EIO; +} + +static void inv_mpu_shutdown(struct i2c_client *client) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(client); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct inv_reg_map_s *reg; +	int result; + +	reg = &st->reg; +	mutex_lock(&indio_dev->mlock); +	dev_dbg(&client->adapter->dev, "Shutting down %s...\n", st->hw->name); + +	/* reset to make sure previous state are not there */ +	result = inv_i2c_single_write(st, reg->pwr_mgmt_1, BIT_H_RESET); +	if (result) +		dev_err(&client->adapter->dev, "Failed to reset %s\n", +			st->hw->name); +	msleep(POWER_UP_TIME); +	/* turn off power to ensure gyro engine is off */ +	result = st->set_power_state(st, false); +	if (result) +		dev_err(&client->adapter->dev, "Failed to turn off %s\n", +			st->hw->name); +	mutex_unlock(&indio_dev->mlock); +} + +/* + *  inv_mpu_remove() - remove function. + */ +static int inv_mpu_remove(struct i2c_client *client) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(client); +	struct inv_mpu_state *st = iio_priv(indio_dev); + +	kfifo_free(&st->timestamps); +	iio_device_unregister(indio_dev); +	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) +		inv_mpu_remove_trigger(indio_dev); +	iio_buffer_unregister(indio_dev); +	inv_mpu_unconfigure_ring(indio_dev); +#ifdef CONFIG_INV_KERNEL_3_10 +	iio_device_free(indio_dev); +#else +	iio_free_device(indio_dev); +#endif +	dev_info(&client->adapter->dev, "inv-mpu-iio module removed.\n"); + +	return 0; +} + +static int inv_setup_suspend_batchmode(struct iio_dev *indio_dev, bool suspend) +{ +	struct inv_mpu_state *st = iio_priv(indio_dev); +	int result; +	int counter; + +	if (st->chip_config.dmp_on && +		st->chip_config.enable && +		(!st->chip_config.dmp_event_int_on)) { +		/* turn off data interrupt in suspend mode;turn on resume */ +		result = inv_set_interrupt_on_gesture_event(st, suspend); +		if (result) +			return result; +		if (suspend) +			counter = INT_MAX; +		else +			counter = st->batch.counter; +		result = write_be32_key_to_mem(st, counter, KEY_BM_BATCH_THLD); +		if (result) +			return result; +	} + +	return 0; +} + +#ifdef CONFIG_PM +static void inv_disable_nonwake_sensors(struct inv_mpu_state *st) +{ +	int err = 0; +	if (st->chip_config.gyro_enable) { +		err = inv_switch_gyro_engine(st, false); +		if (err) +			pr_err("%s: ERROR %d disabling gyro\n", __func__, err); +	} + +	/* don't disable accel if pedometer or significant motion is enabled */ +	if (!st->ped.on && !st->chip_config.smd_enable && +					st->chip_config.accel_enable) { +		err = inv_switch_accel_engine(st, false); +		if (err) +			pr_err("%s: ERROR %d disabling accelerometer\n", +							__func__, err); +	} + +	if (st->sensor[SENSOR_COMPASS].on) { +		err = st->slave_compass->suspend(st); +		if (err) +			pr_err("%s: ERROR %d disabling compass\n", +							__func__, err); +	} +} + +static void inv_enable_nonwake_sensors(struct inv_mpu_state *st) +{ +	int err = 0; +	if (st->chip_config.gyro_enable) { +		err = inv_switch_gyro_engine(st, true); +		if (err) +			pr_err("%s: ERROR %d restoring gyro state\n", +							__func__, err); +	} + +	if (!st->ped.on && !st->chip_config.smd_enable && +					st->chip_config.accel_enable) { +		err = inv_switch_accel_engine(st, true); +		if (err) +			pr_err("%s: ERROR %d restoring accelerometer state\n", +							__func__, err); +	} + +	if (st->sensor[SENSOR_COMPASS].on) { +		err = st->slave_compass->resume(st); +		if (err) +			pr_err("%s: ERROR %d restoring compass state\n", +							__func__, err); +	} +} + +/* + * inv_mpu_resume(): resume method for this driver. + *    This method can be modified according to the request of different + *    customers. It basically undo everything suspend_noirq is doing + *    and recover the chip to what it was before suspend. + */ +static int inv_mpu_resume(struct device *dev) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	int result; + +	/* add code according to different request Start */ +	pr_debug("%s inv_mpu_resume\n", st->hw->name); +	mutex_lock(&indio_dev->mlock); +	st->suspend_state = false; + +	result = 0; +	if (st->chip_config.dmp_on && st->chip_config.enable) { +		result = st->set_power_state(st, true); +		enable_irq(st->client->irq); +		result |= inv_read_time_and_ticks(st, true); +		if (st->ped.int_on) +			result |= inv_enable_pedometer_interrupt(st, true); +		if (st->chip_config.display_orient_on) +			result |= inv_set_display_orient_interrupt_dmp(st, +								true); +		result |= inv_setup_suspend_batchmode(indio_dev, false); + +		/* restore enable state all non-wakeup sensors */ +		inv_enable_nonwake_sensors(st); + +	} else if (st->chip_config.enable) { +		result = st->set_power_state(st, true); +		enable_irq(st->client->irq); +	} +	mutex_unlock(&indio_dev->mlock); +	/* add code according to different request End */ + +	return result; +} + +/* + * inv_mpu_suspend(): suspend method for this driver. + *    This method can be modified according to the request of different + *    customers. If customer want some events, such as SMD to wake up the CPU, + *    then data interrupt should be disabled in this interrupt to avoid + *    unnecessary interrupts. If customer want pedometer running while CPU is + *    asleep, then pedometer should be turned on while pedometer interrupt + *    should be turned off. + */ +static int inv_mpu_suspend(struct device *dev) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	int result; + +	/* add code according to different request Start */ +	pr_debug("%s inv_mpu_suspend\n", st->hw->name); + +	result = 0; +	if (st->chip_config.dmp_on && st->chip_config.enable) { +		/* turn off pedometer interrupt during suspend */ +		if (st->ped.int_on) +			result |= inv_enable_pedometer_interrupt(st, false); +		/* turn off orientation interrupt during suspend */ +		if (st->chip_config.display_orient_on) +			result |= inv_set_display_orient_interrupt_dmp(st, +								false); +		/* setup batch mode related during suspend */ +		result = inv_setup_suspend_batchmode(indio_dev, true); + +		/* disable all non-wakeup sensors */ +		inv_disable_nonwake_sensors(st); + +		/* disable irq's to assure inv_read_fifo() runs if pending */ +		disable_irq(st->client->irq); + +		/* only in DMP non-batch data mode, turn off the power */ +		if ((!st->batch.on) && (!st->chip_config.smd_enable) && +					(!st->ped.on)) +			result |= st->set_power_state(st, false); +	} else if (st->chip_config.enable) { +		/* disable irq's to assure inv_read_fifo() runs if pending */ +		disable_irq(st->client->irq); + +		/* in non DMP case, just turn off the power */ +		result |= st->set_power_state(st, false); +	} +	st->suspend_state = true; +	/* add code according to different request End */ + +	return 0; +} + +static const struct dev_pm_ops inv_mpu_pmops = { +	.suspend       = inv_mpu_suspend, +	.resume        = inv_mpu_resume, +}; +#define INV_MPU_PMOPS (&inv_mpu_pmops) +#else +#define INV_MPU_PMOPS NULL +#endif /* CONFIG_PM */ + +static const u16 normal_i2c[] = { I2C_CLIENT_END }; +/* device id table is used to identify what device can be + * supported by this driver + */ +static const struct i2c_device_id inv_mpu_id[] = { +	{"itg3500", INV_ITG3500}, +	{"mpu3050", INV_MPU3050}, +	{"mpu6050", INV_MPU6050}, +	{"mpu9150", INV_MPU9150}, +	{"mpu6500", INV_MPU6500}, +	{"mpu9250", INV_MPU9250}, +	{"mpu6xxx", INV_MPU6XXX}, +	{"mpu9350", INV_MPU9350}, +	{"mpu6515", INV_MPU6515}, +	{} +}; + +MODULE_DEVICE_TABLE(i2c, inv_mpu_id); + +static struct i2c_driver inv_mpu_driver = { +	.class = I2C_CLASS_HWMON, +	.probe		=	inv_mpu_probe, +	.remove		=	inv_mpu_remove, +	.shutdown	=	inv_mpu_shutdown, +	.id_table	=	inv_mpu_id, +	.driver = { +		.owner	=	THIS_MODULE, +		.name	=	"inv-mpu-iio", +		.pm     =       INV_MPU_PMOPS, +	}, +	.address_list = normal_i2c, +}; + +static int __init inv_mpu_init(void) +{ +	int result = i2c_add_driver(&inv_mpu_driver); +	if (result) { +		pr_err("failed\n"); +		return result; +	} +	return 0; +} + +static void __exit inv_mpu_exit(void) +{ +	i2c_del_driver(&inv_mpu_driver); +} + +module_init(inv_mpu_init); +module_exit(inv_mpu_exit); + +MODULE_AUTHOR("Invensense Corporation"); +MODULE_DESCRIPTION("Invensense device driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("inv-mpu-iio"); + diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_dts.c b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_dts.c new file mode 100755 index 00000000000..276d5bcfea7 --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_dts.c @@ -0,0 +1,270 @@ +#include <linux/err.h> +#include <linux/of_gpio.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regulator/consumer.h> + +#include <linux/i2c.h> +#include <linux/mpu.h> +#include "inv_mpu_dts.h" + +int inv_mpu_power_on(struct mpu_platform_data *pdata) +{ +	int err ; + +	err = regulator_enable(pdata->vdd_ana); +	err = regulator_enable(pdata->vdd_i2c); +	pr_debug(KERN_INFO "inv_mpu_power_on call"); + +	return err ; + +} + +int inv_mpu_power_off(struct mpu_platform_data *pdata) +{ +	int err ; + +	err = regulator_disable(pdata->vdd_ana); +	err = regulator_disable(pdata->vdd_i2c); +	pr_debug(KERN_INFO "inv_mpu_power_off call"); + +	return err; +} + +int inv_parse_orientation_matrix(struct device *dev, s8 *orient) +{ +	int rc, i; +	struct device_node *np = dev->of_node; +	u32 temp_val, temp_val2; + +	for (i = 0; i < 9; i++) +		orient[i] = 0; + +	/* parsing axis x orientation matrix*/ +	rc = of_property_read_u32(np, "axis_map_x", &temp_val); +	if (rc) { +		dev_err(dev, "Unable to read axis_map_x\n"); +		return rc; +	} +	rc = of_property_read_u32(np, "negate_x", &temp_val2); +	if (rc) { +		dev_err(dev, "Unable to read negate_x\n"); +		return rc; +	} +	if (temp_val2) +		orient[temp_val] = -1; +	else +		orient[temp_val] = 1; + +	/* parsing axis y orientation matrix*/ +	rc = of_property_read_u32(np, "axis_map_y", &temp_val); +	if (rc) { +		dev_err(dev, "Unable to read axis_map_y\n"); +		return rc; +	} +	rc = of_property_read_u32(np, "negate_y", &temp_val2); +	if (rc) { +		dev_err(dev, "Unable to read negate_y\n"); +		return rc; +	} +	if (temp_val2) +		orient[3 + temp_val] = -1; +	else +		orient[3 + temp_val] = 1; + +	/* parsing axis z orientation matrix*/ +	rc = of_property_read_u32(np, "axis_map_z", &temp_val); +	if (rc) { +		dev_err(dev, "Unable to read axis_map_z\n"); +		return rc; +	} +	rc = of_property_read_u32(np, "negate_z", &temp_val2); +	if (rc) { +		dev_err(dev, "Unable to read negate_z\n"); +		return rc; +	} +	if (temp_val2) +		orient[6 + temp_val] = -1; +	else +		orient[6 + temp_val] = 1; + +	return 0; +} + +int inv_parse_secondary_orientation_matrix(struct device *dev, +							s8 *orient) +{ +	int rc, i; +	struct device_node *np = dev->of_node; +	u32 temp_val, temp_val2; + +	for (i = 0; i < 9; i++) +		orient[i] = 0; + +	/* parsing axis x orientation matrix*/ +	rc = of_property_read_u32(np, "inven,secondary_axis_map_x", &temp_val); +	if (rc) { +		dev_err(dev, "Unable to read secondary axis_map_x\n"); +		return rc; +	} +	rc = of_property_read_u32(np, "inven,secondary_negate_x", &temp_val2); +	if (rc) { +		dev_err(dev, "Unable to read secondary negate_x\n"); +		return rc; +	} +	if (temp_val2) +		orient[temp_val] = -1; +	else +		orient[temp_val] = 1; + +	/* parsing axis y orientation matrix*/ +	rc = of_property_read_u32(np, "inven,secondary_axis_map_y", &temp_val); +	if (rc) { +		dev_err(dev, "Unable to read secondary axis_map_y\n"); +		return rc; +	} +	rc = of_property_read_u32(np, "inven,secondary_negate_y", &temp_val2); +	if (rc) { +		dev_err(dev, "Unable to read secondary negate_y\n"); +		return rc; +	} +	if (temp_val2) +		orient[3 + temp_val] = -1; +	else +		orient[3 + temp_val] = 1; + +	/* parsing axis z orientation matrix*/ +	rc = of_property_read_u32(np, "inven,secondary_axis_map_z", &temp_val); +	if (rc) { +		dev_err(dev, "Unable to read secondary axis_map_z\n"); +		return rc; +	} +	rc = of_property_read_u32(np, "inven,secondary_negate_z", &temp_val2); +	if (rc) { +		dev_err(dev, "Unable to read secondary negate_z\n"); +		return rc; +	} +	if (temp_val2) +		orient[6 + temp_val] = -1; +	else +		orient[6 + temp_val] = 1; + +	return 0; +} + +int inv_parse_secondary(struct device *dev, struct mpu_platform_data *pdata) +{ +	int rc; +	struct device_node *np = dev->of_node; +	u32 temp_val; +	const char *name; + +	if (of_property_read_string(np, "inven,secondary_type", &name)) { +		dev_err(dev, "Missing secondary type.\n"); +		return -EINVAL; +	} +	if (!strcmp(name, "compass")) { +		pdata->sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS; +	} else if (!strcmp(name, "none")) { +		pdata->sec_slave_type = SECONDARY_SLAVE_TYPE_NONE; +		return 0; +	} else { +		return -EINVAL; +	} + +	if (of_property_read_string(np, "inven,secondary_name", &name)) { +		dev_err(dev, "Missing secondary name.\n"); +		return -EINVAL; +	} +	if (!strcmp(name, "ak8963")) +		pdata->sec_slave_id = COMPASS_ID_AK8963; +	else if (!strcmp(name, "ak8975")) +		pdata->sec_slave_id = COMPASS_ID_AK8975; +	else if (!strcmp(name, "ak8972")) +		pdata->sec_slave_id = COMPASS_ID_AK8972; +	else +		return -EINVAL; +	rc = of_property_read_u32(np, "inven,secondary_reg", &temp_val); +	if (rc) { +		dev_err(dev, "Unable to read secondary register\n"); +		return rc; +	} +	pdata->secondary_i2c_addr = temp_val; +	rc = inv_parse_secondary_orientation_matrix(dev, +						pdata->secondary_orientation); + +	return rc; +} + +int inv_parse_aux(struct device *dev, struct mpu_platform_data *pdata) +{ +	int rc; +	struct device_node *np = dev->of_node; +	u32 temp_val; +	const char *name; + +	if (of_property_read_string(np, "inven,aux_type", &name)) { +		dev_err(dev, "Missing aux type.\n"); +		return -EINVAL; +	} +	if (!strcmp(name, "pressure")) { +		pdata->aux_slave_type = SECONDARY_SLAVE_TYPE_PRESSURE; +	} else if (!strcmp(name, "none")) { +		pdata->aux_slave_type = SECONDARY_SLAVE_TYPE_NONE; +		return 0; +	} else { +		return -EINVAL; +	} + +	if (of_property_read_string(np, "inven,aux_name", &name)) { +		dev_err(dev, "Missing aux name.\n"); +		return -EINVAL; +	} +	if (!strcmp(name, "bmp280")) +		pdata->aux_slave_id = PRESSURE_ID_BMP280; +	else +		return -EINVAL; + +	rc = of_property_read_u32(np, "inven,aux_reg", &temp_val); +	if (rc) { +		dev_err(dev, "Unable to read aux register\n"); +		return rc; +	} +	pdata->aux_i2c_addr = temp_val; + +	return 0; +} + +int invensense_mpu_parse_dt(struct device *dev, struct mpu_platform_data *pdata) +{ +	int rc; +	pr_debug("Invensense MPU parse_dt started.\n"); + +	rc = inv_parse_orientation_matrix(dev, pdata->orientation); +	if (rc) +		return rc; + +	rc = inv_parse_secondary(dev, pdata); +	if (rc) +		return rc; + +	inv_parse_aux(dev, pdata); + +	pdata->vdd_ana = regulator_get(dev, "inven,vdd_ana"); +	if (IS_ERR(pdata->vdd_ana)) { +		rc = PTR_ERR(pdata->vdd_ana); +		dev_err(dev, "Regulator get failed vdd_ana-supply rc=%d\n", rc); +		return rc; +	} +	pdata->vdd_i2c = regulator_get(dev, "inven,vcc_i2c"); +	if (IS_ERR(pdata->vdd_i2c)) { +		rc = PTR_ERR(pdata->vdd_i2c); +		dev_err(dev, "Regulator get failed vcc-i2c-supply rc=%d\n", rc); +		return rc; +	} +	pdata->power_on = inv_mpu_power_on; +	pdata->power_off = inv_mpu_power_off; +	pr_debug("Invensense MPU parse_dt complete.\n"); +	return rc; +} + diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_dts.h b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_dts.h new file mode 100755 index 00000000000..151ac74ea05 --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_dts.h @@ -0,0 +1,30 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ + +#ifndef _INV_MPU_DTS_H_ +#define _INV_MPU_DTS_H_ + +#include <linux/i2c.h> +#include <linux/mpu.h> + +int inv_mpu_power_on(struct mpu_platform_data *pdata); +int inv_mpu_power_off(struct mpu_platform_data *pdata); +int inv_parse_orientation_matrix(struct device *dev, s8 *orient); +int inv_parse_secondary_orientation_matrix(struct device *dev, +							s8 *orient); +int inv_parse_secondary(struct device *dev, struct mpu_platform_data *pdata); +int inv_parse_aux(struct device *dev, struct mpu_platform_data *pdata); +int invensense_mpu_parse_dt(struct device *dev, +			    struct mpu_platform_data *pdata); + +#endif  /* #ifndef _INV_MPU_DTS_H_ */ diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_iio.h b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_iio.h new file mode 100644 index 00000000000..701d4db78ed --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_iio.h @@ -0,0 +1,1078 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ + +#ifndef _INV_MPU_IIO_H_ +#define _INV_MPU_IIO_H_ + +#include <linux/i2c.h> +#include <linux/kfifo.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> +#include <linux/mpu.h> + +#include "iio.h" +#include "buffer.h" + +#include "dmpKey.h" + +/*register and associated bit definition*/ +#define REG_3050_FIFO_EN         0x12 +#define BITS_3050_ACCEL_OUT      0x0E + +#define REG_3050_AUX_VDDIO       0x13 +#define BIT_3050_VDDIO           0x04 + +#define REG_3050_SLAVE_ADDR      0x14 +#define REG_3050_SAMPLE_RATE_DIV 0x15 +#define REG_3050_LPF             0x16 +#define REG_3050_INT_ENABLE      0x17 +#define REG_3050_AUX_BST_ADDR    0x18 +#define REG_3050_INT_STATUS      0x1A +#define REG_3050_TEMPERATURE     0x1B +#define REG_3050_RAW_GYRO        0x1D +#define REG_3050_AUX_XOUT_H      0x23 +#define REG_3050_FIFO_COUNT_H    0x3A +#define REG_3050_FIFO_R_W        0x3C + +#define REG_3050_USER_CTRL       0x3D +#define BIT_3050_AUX_IF_EN       0x20 +#define BIT_3050_FIFO_RST        0x02 + +#define REG_3050_PWR_MGMT_1      0x3E +#define BITS_3050_POWER1         0x30 +#define BITS_3050_POWER2         0x10 +#define BITS_3050_GYRO_STANDBY   0x38 + +#define REG_3500_OTP            0x0 + +#define REG_YGOFFS_TC           0x1 +#define BIT_I2C_MST_VDDIO       0x80 + +#define REG_XA_OFFS_H           0x6 +#define REG_XA_OFFS_L_TC        0x7 +#define REG_PRODUCT_ID          0xC +#define REG_ST_GCT_X            0xD +#define REG_XG_OFFS_USRH        0x13 +#define REG_SAMPLE_RATE_DIV     0x19 +#define REG_CONFIG              0x1A + +#define REG_GYRO_CONFIG         0x1B +#define BITS_SELF_TEST_EN       0xE0 + +#define REG_ACCEL_CONFIG        0x1C +#define REG_ACCEL_MOT_THR       0x1F +#define REG_ACCEL_MOT_DUR       0x20 + +#define REG_FIFO_EN             0x23 +#define BIT_ACCEL_OUT           0x08 +#define BITS_GYRO_OUT           0x70 + +#define REG_I2C_MST_CTRL        0x24 +#define BIT_WAIT_FOR_ES         0x40 + +#define REG_I2C_SLV0_ADDR       0x25 +#define REG_I2C_SLV0_REG        0x26 +#define REG_I2C_SLV0_CTRL       0x27 +#define REG_I2C_SLV1_ADDR       0x28 +#define REG_I2C_SLV1_REG        0x29 +#define REG_I2C_SLV1_CTRL       0x2A +#define REG_I2C_SLV2_ADDR       0x2B +#define REG_I2C_SLV2_REG        0x2C +#define REG_I2C_SLV2_CTRL       0x2D +#define REG_I2C_SLV3_ADDR       0x2E +#define REG_I2C_SLV3_REG        0x2F +#define REG_I2C_SLV3_CTRL       0x30 +#define REG_I2C_SLV4_CTRL       0x34 + +#define INV_MPU_BIT_SLV_EN      0x80 +#define INV_MPU_BIT_BYTE_SW     0x40 +#define INV_MPU_BIT_REG_DIS     0x20 +#define INV_MPU_BIT_GRP         0x10 +#define INV_MPU_BIT_I2C_READ    0x80 + +#define REG_INT_PIN_CFG         0x37 +#define BIT_BYPASS_EN           0x2 + +#define REG_INT_ENABLE          0x38 +#define BIT_DATA_RDY_EN         0x01 +#define BIT_DMP_INT_EN          0x02 +#define BIT_ZMOT_EN             0x20 +#define BIT_MOT_EN              0x40 +#define BIT_6500_WOM_EN         0x40 + +#define REG_DMP_INT_STATUS      0x39 +#define BIT_DMP_INT_CI          0x01 + +#define REG_INT_STATUS          0x3A +#define BIT_MOT_INT             0x40 +#define BIT_ZMOT_INT            0x20 + +#define REG_RAW_ACCEL           0x3B +#define REG_TEMPERATURE         0x41 +#define REG_EXT_SENS_DATA_00    0x49 +#define REG_EXT_SENS_DATA_08    0x51 +#define REG_EXT_SENS_DATA_09    0x52 + +#define REG_ACCEL_INTEL_STATUS  0x61 + +#define INV_MPU_REG_I2C_SLV0_DO         0x63 +#define INV_MPU_REG_I2C_SLV1_DO         0x64 +#define INV_MPU_REG_I2C_SLV2_DO         0x65 +#define INV_MPU_REG_I2C_SLV3_DO         0x66 + +#define REG_I2C_MST_DELAY_CTRL  0x67 +#define BIT_SLV0_DLY_EN                 0x01 +#define BIT_SLV1_DLY_EN                 0x02 +#define BIT_SLV2_DLY_EN                 0x04 +#define BIT_SLV3_DLY_EN                 0x08 + +#define REG_USER_CTRL           0x6A +#define BIT_FIFO_RST                    0x04 +#define BIT_DMP_RST                     0x08 +#define BIT_I2C_MST_EN                  0x20 +#define BIT_FIFO_EN                     0x40 +#define BIT_DMP_EN                      0x80 + +#define REG_PWR_MGMT_1          0x6B +#define BIT_H_RESET                     0x80 +#define BIT_SLEEP                       0x40 +#define BIT_CYCLE                       0x20 +#define BIT_CLK_MASK                    0x7 + +#define REG_PWR_MGMT_2          0x6C +#define BIT_PWR_ACCEL_STBY              0x38 +#define BIT_PWR_GYRO_STBY               0x07 +#define BIT_LPA_FREQ                    0xC0 + +#define REG_BANK_SEL            0x6D +#define REG_MEM_START_ADDR      0x6E +#define REG_MEM_RW              0x6F +#define REG_PRGM_STRT_ADDRH     0x70 +#define REG_FIFO_COUNT_H        0x72 +#define REG_FIFO_R_W            0x74 +#define REG_WHOAMI              0x75 + +#define REG_6500_XG_ST_DATA     0x0 +#define REG_6500_XA_ST_DATA     0xD +#define REG_6500_XA_OFFS_H      0x77 +#define REG_6500_YA_OFFS_H      0x7A +#define REG_6500_ZA_OFFS_H      0x7D +#define REG_6500_ACCEL_CONFIG2  0x1D +#define BIT_ACCEL_FCHOCIE_B              0x08 +#define BIT_FIFO_SIZE_1K                 0x40 + +#define REG_6500_LP_ACCEL_ODR   0x1E +#define REG_6500_ACCEL_WOM_THR  0x1F + +#define REG_6500_ACCEL_INTEL_CTRL 0x69 +#define BIT_ACCEL_INTEL_ENABLE          0x80 +#define BIT_ACCEL_INTEL_MODE            0x40 + +/* data definitions */ +#define DMP_START_ADDR           0x400 +#define DMP_MASK_TAP             0x3f +#define DMP_MASK_DIS_ORIEN       0xC0 +#define DMP_DIS_ORIEN_SHIFT      6 +/* this is derived from 1000 divided by 50, which is the pedometer +   running frequency */ +#define MS_PER_PED_TICKS         20 + + +#define BYTES_FOR_DMP            8 +#define BYTES_FOR_EVENTS         4 +#define QUATERNION_BYTES         16 +#define BYTES_PER_SENSOR         6 +#define MPU3050_FOOTER_SIZE      2 +#define FIFO_COUNT_BYTE          2 +#define FIFO_THRESHOLD           800 +#define FIFO_SIZE                992 +#define HARDWARE_FIFO_SIZE       1024 +#define MAX_READ_SIZE            64 +#define POWER_UP_TIME            100 +#define SENSOR_UP_TIME           30 +#define REG_UP_TIME              2 +#define INV_MPU_SAMPLE_RATE_CHANGE_STABLE 50 +#define MPU_MEM_BANK_SIZE        256 +#define SELF_TEST_GYRO_FULL_SCALE 250 +#define SELF_TEST_ACCEL_FULL_SCALE 8 +#define SELF_TEST_ACCEL_6500_SCALE 2 + +/* data header defines */ +#define PRESSURE_HDR             0x8000 +#define ACCEL_HDR                0x4000 +#define GYRO_HDR                 0x2000 +#define COMPASS_HDR              0x1000 +#define COMPASS_HDR_2            0x1800 +#define LPQUAT_HDR               0x0800 +#define SIXQUAT_HDR              0x0400 +#define PEDQUAT_HDR              0x0200 +#define STEP_DETECTOR_HDR        0x0100 +#define STEP_INDICATOR_MASK      0xf + +#define MAX_BYTES_PER_SAMPLE     80 +#define MAX_HW_FIFO_BYTES        (BYTES_PER_SENSOR * 2) +#define IIO_BUFFER_BYTES         8 +#define HEADERED_NORMAL_BYTES    8 +#define HEADERED_Q_BYTES         16 + +#define MPU6XXX_MAX_MOTION_THRESH (255*4) +#define MPU6050_MOTION_THRESH_SHIFT 5 +#define MPU6500_MOTION_THRESH_SHIFT 2 +#define MPU6050_MOTION_DUR_DEFAULT  1 +#define MPU6050_ID               0x68 +#define MPU6050_MAX_MOTION_DUR   255 +#define MPU_TEMP_SHIFT           16 +#define LPA_FREQ_SHIFT           6 +#define COMPASS_RATE_SCALE       10 +#define MAX_GYRO_FS_PARAM        3 +#define MAX_ACCEL_FS_PARAM        3 +#define MAX_LPA_FREQ_PARAM       3 +#define MPU_MAX_A_OFFSET_VALUE     16383 +#define MPU_MIN_A_OFFSET_VALUE     -16384 +#define MPU_MAX_G_OFFSET_VALUE     32767 +#define MPU_MIN_G_OFFSET_VALUE     -32767 +#define MPU6XXX_MAX_MPU_MEM      (256 * 12) + +#define INIT_MOT_DUR             128 +#define INIT_MOT_THR             128 +#define INIT_ZMOT_DUR            128 +#define INIT_ZMOT_THR            128 +#define INIT_ST_SAMPLES          200 +#define INIT_ST_MPU6050_SAMPLES  600 +#define INIT_ST_THRESHOLD        50 +#define INIT_PED_INT_THRESH      2 +#define INIT_PED_THRESH          9 +#define ST_THRESHOLD_MULTIPLIER  10 +#define ST_MAX_SAMPLES           500 +#define ST_MAX_THRESHOLD         100 +#define DMP_INTERVAL_INIT       (5 * NSEC_PER_MSEC) +#define DMP_INTERVAL_MIN_ADJ    (50 * NSEC_PER_USEC) + +/*---- MPU6500 ----*/ +#define MPU6500_ID               0x70      /* unique WHOAMI */ +#define MPU6515_ID               0x74      /* unique WHOAMI */ +#define MPU6500_PRODUCT_REVISION 1 +#define MPU6500_MEM_REV_ADDR     0x16 +#define INV_MPU_REV_MASK         0x0F +#define MPU6500_REV              2 +#define MPU_DMP_LOAD_START       0x20 + +/*---- MPU9250 ----*/ +#define MPU9250_ID               0x71      /* unique WHOAMI */ + +/* ----MPU9350 ----*/ +#define MPU9350_ID               0x72 + +#define THREE_AXIS               3 +#define GYRO_CONFIG_FSR_SHIFT    3 +#define ACCEL_CONFIG_FSR_SHIFT    3 +#define GYRO_DPS_SCALE           250 +#define MEM_ADDR_PROD_REV        0x6 +#define SOFT_PROD_VER_BYTES      5 +#define CRC_FIRMWARE_SEED        0 +#define SELF_TEST_SUCCESS        1 +#define MS_PER_DMP_TICK          20 +#define DMP_IMAGE_SIZE           2943 + +/* init parameters */ +#define INIT_FIFO_RATE           50 +#define INIT_DMP_OUTPUT_RATE     25 +#define INIT_DUR_TIME           (NSEC_PER_SEC / INIT_FIFO_RATE) +#define INIT_TAP_THRESHOLD       100 +#define INIT_TAP_TIME            100 +#define INIT_TAP_MIN_COUNT       2 +#define INIT_SAMPLE_DIVIDER      4 +#define MPU_INIT_SMD_DELAY_THLD  3 +#define MPU_INIT_SMD_DELAY2_THLD 1 +#define MPU_INIT_SMD_THLD        1500 +#define MPU_DEFAULT_DMP_FREQ     200 +#define MPL_PROD_KEY(ver, rev)  (ver * 100 + rev) +#define NUM_OF_PROD_REVS (ARRAY_SIZE(prod_rev_map)) +/*---- MPU6050 Silicon Revisions ----*/ +#define MPU_SILICON_REV_A2                    1       /* MPU6050A2 Device */ +#define MPU_SILICON_REV_B1                    2       /* MPU6050B1 Device */ + +#define BIT_PRFTCH_EN                         0x40 +#define BIT_CFG_USER_BANK                     0x20 +#define BITS_MEM_SEL                          0x1f + +#define TIME_STAMP_TOR                        5 +#define MAX_CATCH_UP                          5 +#define DMP_TICK_DUR                          5 +#define DEFAULT_ACCEL_TRIM                    16384 +#define DEFAULT_GYRO_TRIM                     131 +#define MAX_FIFO_RATE                         1000 +#define MAX_DMP_OUTPUT_RATE                   200 +#define MIN_FIFO_RATE                         4 +#define ONE_K_HZ                              1000 +#define NS_PER_MS_SHIFT                       20 +#define END_MARKER                            0x0010 +#define EMPTY_MARKER                          0x0020 + +/*tap related defines */ +#define INV_TAP                               0x08 +#define INV_NUM_TAP_AXES                      3 + +#define INV_TAP_AXIS_X_POS                    0x20 +#define INV_TAP_AXIS_X_NEG                    0x10 +#define INV_TAP_AXIS_Y_POS                    0x08 +#define INV_TAP_AXIS_Y_NEG                    0x04 +#define INV_TAP_AXIS_Z_POS                    0x02 +#define INV_TAP_AXIS_Z_NEG                    0x01 +#define INV_TAP_ALL_DIRECTIONS                0x3f + +#define INV_TAP_AXIS_X                        0x1 +#define INV_TAP_AXIS_Y                        0x2 +#define INV_TAP_AXIS_Z                        0x4 + +#define INV_TAP_AXIS_ALL               \ +		(INV_TAP_AXIS_X            |   \ +		INV_TAP_AXIS_Y             |   \ +		INV_TAP_AXIS_Z) + +#define INT_SRC_TAP             0x01 +#define INT_SRC_DISPLAY_ORIENT  0x08 +#define INT_SRC_SHAKE           0x10 + +#define INV_X_AXIS_INDEX                  0x00 +#define INV_Y_AXIS_INDEX                  0x01 +#define INV_Z_AXIS_INDEX                  0x02 + +#define INV_ELEMENT_1                     0x0001 +#define INV_ELEMENT_2                     0x0002 +#define INV_ELEMENT_3                     0x0004 +#define INV_ELEMENT_4                     0x0008 +#define INV_ELEMENT_5                     0x0010 +#define INV_ELEMENT_6                     0x0020 +#define INV_ELEMENT_7                     0x0040 +#define INV_ELEMENT_8                     0x0080 +#define INV_ALL                           0xFFFF +#define INV_ELEMENT_MASK                  0x00FF +#define INV_GYRO_ACC_MASK                 0x007E +#define INV_ACCEL_MASK                    0x70 +#define INV_GYRO_MASK                     0xE + +struct inv_mpu_state; + +/** + *  struct inv_reg_map_s - Notable slave registers. + *  @sample_rate_div:	Divider applied to gyro output rate. + *  @lpf:		Configures internal LPF. + *  @bank_sel:		Selects between memory banks. + *  @user_ctrl:		Enables/resets the FIFO. + *  @fifo_en:		Determines which data will appear in FIFO. + *  @gyro_config:	gyro config register. + *  @accel_config:	accel config register + *  @fifo_count_h:	Upper byte of FIFO count. + *  @fifo_r_w:		FIFO register. + *  @raw_accel		Address of first accel register. + *  @temperature	temperature register + *  @int_enable:	Interrupt enable register. + *  @int_status:	Interrupt flags. + *  @pwr_mgmt_1:	Controls chip's power state and clock source. + *  @pwr_mgmt_2:	Controls power state of individual sensors. + *  @mem_start_addr:	Address of first memory read. + *  @mem_r_w:		Access to memory. + *  @prgm_strt_addrh	firmware program start address register + */ +struct inv_reg_map_s { +	u8 sample_rate_div; +	u8 lpf; +	u8 bank_sel; +	u8 user_ctrl; +	u8 fifo_en; +	u8 gyro_config; +	u8 accel_config; +	u8 fifo_count_h; +	u8 fifo_r_w; +	u8 raw_accel; +	u8 temperature; +	u8 int_enable; +	u8 int_status; +	u8 pwr_mgmt_1; +	u8 pwr_mgmt_2; +	u8 mem_start_addr; +	u8 mem_r_w; +	u8 prgm_strt_addrh; +}; + +/* device enum */ +enum inv_devices { +	INV_ITG3500, +	INV_MPU3050, +	INV_MPU6050, +	INV_MPU9150, +	INV_MPU6500, +	INV_MPU9250, +	INV_MPU6XXX, +	INV_MPU9350, +	INV_MPU6515, +	INV_NUM_PARTS +}; + +/** + *  struct inv_hw_s - Other important hardware information. + *  @num_reg:	Number of registers on device. + *  @name:      name of the chip + */ +struct inv_hw_s { +	u8 num_reg; +	u8 *name; +}; + +/* enum for sensor */ +enum INV_SENSORS { +	SENSOR_GYRO = 0, +	SENSOR_ACCEL, +	SENSOR_COMPASS, +	SENSOR_PRESSURE, +	SENSOR_STEP, +	SENSOR_PEDQ, +	SENSOR_SIXQ, +	SENSOR_LPQ, +	SENSOR_NUM_MAX, +	SENSOR_INVALID, +}; + +/** + *  struct inv_sensor - information for each sensor. + *  @ts: this sensors timestamp. + *  @dur: duration between samples in ns. + *  @rate:  sensor data rate. + *  @counter: dmp tick counter corresponding to rate. + *  @on:    sensor on/off. + *  @sample_size: number of bytes for the sensor. + *  @send_data: function pointer to send data or not. + *  @set_rate: funcition pointer to set data rate. + */ +struct inv_sensor { +	u64 ts; +	int dur; +	int rate; +	int counter; +	bool on; +	u8 sample_size; +	int (*send_data)(struct inv_mpu_state *st, bool on); +	int (*set_rate)(struct inv_mpu_state *st); +}; + +/** + *  struct inv_batch - information batchmode. + *  @on: derived variable for batch mode. + *  @overflow_on: overflow mode for batchmode. + *  @wake_fifo_on: overflow for suspend mode. + *  @counter: counter for batch mode. + *  @timeout: nominal timeout value for batchmode in milliseconds. + *  @min_rate: minimum sensor rate that is turned on. + */ +struct inv_batch { +	bool on; +	bool overflow_on; +	bool wake_fifo_on; +	u32 counter; +	u32 timeout; +	u32 min_rate; +}; + +/** + *  struct inv_chip_config_s - Cached chip configuration data. + *  @fsr:		Full scale range. + *  @lpf:		Digital low pass filter frequency. + *  @accel_fs:		accel full scale range. + *  @has_footer:	MPU3050 specific work around. + *  @has_compass:	has compass or not. + *  @has_pressure:      has pressure sensor or not. + *  @enable:		master enable to enable output + *  @accel_enable:	enable accel functionality + *  @gyro_enable:	enable gyro functionality + *  @is_asleep:		1 if chip is powered down. + *  @dmp_on:		dmp is on/off. + *  @dmp_int_on:        dmp interrupt on/off. + *  @step_indicator_on: step indicate bit added to the sensor or not. + *  @dmp_event_int_on:  dmp event interrupt on/off. + *  @firmware_loaded:	flag indicate firmware loaded or not. + *  @lpa_mod:		low power mode. + *  @display_orient_on:	display orientation on/off. + *  @normal_compass_measure: discard first compass data after reset. + *  @normal_pressure_measure: discard first pressure data after reset. + *  @smd_enable: disable/enable SMD function. + *  @adjust_time: flag to indicate whether adjust chip clock or not. + *  @smd_triggered: smd is triggered. + *  @lpa_freq:		low power frequency + *  @prog_start_addr:	firmware program start address. + *  @fifo_rate:		current FIFO update rate. + *  @bytes_per_datum: number of bytes for 1 sample data. + */ +struct inv_chip_config_s { +	u32 fsr:2; +	u32 lpf:3; +	u32 accel_fs:2; +	u32 has_footer:1; +	u32 has_compass:1; +	u32 has_pressure:1; +	u32 enable:1; +	u32 accel_enable:1; +	u32 gyro_enable:1; +	u32 is_asleep:1; +	u32 dmp_on:1; +	u32 dmp_int_on:1; +	u32 dmp_event_int_on:1; +	u32 step_indicator_on:1; +	u32 firmware_loaded:1; +	u32 lpa_mode:1; +	u32 display_orient_on:1; +	u32 normal_compass_measure:1; +	u32 normal_pressure_measure:1; +	u32 smd_enable:1; +	u32 adjust_time:1; +	u32 smd_triggered:1; +	u16 lpa_freq; +	u16 prog_start_addr; +	u16 fifo_rate; +	u16 bytes_per_datum; +}; + +/** + *  struct inv_chip_info_s - Chip related information. + *  @product_id:	Product id. + *  @product_revision:	Product revision. + *  @silicon_revision:	Silicon revision. + *  @software_revision:	software revision. + *  @multi:		accel specific multiplier. + *  @compass_sens:	compass sensitivity. + *  @gyro_sens_trim:	Gyro sensitivity trim factor. + *  @accel_sens_trim:    accel sensitivity trim factor. + */ +struct inv_chip_info_s { +	u8 product_id; +	u8 product_revision; +	u8 silicon_revision; +	u8 software_revision; +	u8 multi; +	u8 compass_sens[3]; +	u32 gyro_sens_trim; +	u32 accel_sens_trim; +}; + +/** + *  struct inv_tap structure to store tap data. + *  @min_count:  minimum taps counted. + *  @thresh:    tap threshold. + *  @time:	tap time. + *  @on: tap on/off. + */ +struct inv_tap { +	u16 min_count; +	u16 thresh; +	u16 time; +	bool on; +}; + +/** + *  struct accel_mot_int_s structure to store motion interrupt data + *  @mot_thr:    motion threshold. + *  @mot_dur:    motion duration. + *  @mot_on:     flag to indicate motion detection on; + */ +struct accel_mot_int { +	u16 mot_thr; +	u32 mot_dur; +	u8 mot_on:1; +}; + +/** + * struct self_test_setting - self test settables from sysfs + * samples: number of samples used in self test. + * threshold: threshold fail/pass criterion in self test. + *            This value is in the percentage multiplied by 100. + *            So 14% would be 14. + */ +struct self_test_setting { +	u16 samples; +	u16 threshold; +}; + +/** + * struct inv_smd significant motion detection structure. + * @threshold: accel threshold for motion detection. + * @delay: delay time to confirm 2nd motion. + * @delay2: delay window parameter. + */ +struct inv_smd { +	u32 threshold; +	u32 delay; +	u32 delay2; +}; + +/** + * struct inv_ped pedometer related data structure. + * @step: steps taken. + * @time: time taken during the period. + * @last_step_time: last time the step is taken. + * @step_thresh: step threshold to show steps. + * @int_thresh: step threshold to generate interrupt. + * @int_on:   pedometer interrupt enable/disable. + * @on:  pedometer on/off. + */ +struct inv_ped { +	u64 step; +	u64 time; +	u64 last_step_time; +	u16 step_thresh; +	u16 int_thresh; +	bool int_on; +	bool on; +}; + +struct inv_mpu_slave; +/** + *  struct inv_mpu_state - Driver state variables. + *  @chip_config:	Cached attribute information. + *  @chip_info:		Chip information from read-only registers. + *  @trig;              iio trigger. + *  @tap:               tap data structure. + *  @smd:               SMD data structure. + *  @ped:               pedometer data structure. + *  @batch:             batchmode data structure. + *  @reg:		Map of important registers. + *  @self_test:         self test settings. + *  @hw:		Other hardware-specific information. + *  @chip_type:		chip type. + *  @time_stamp_lock:	spin lock to time stamp. + *  @suspend_resume_lock: mutex lock for suspend/resume. + *  @client:		i2c client handle. + *  @plat_data:		platform data. + *  @slave_accel:       mpu slave handle for accelerometer(MPU3050 only). + *  @slave_compass:	mpu slave handle for magnetometer. + *  @slave_pressure:	mpu slave handle for pressure sensor. + *  (*set_power_state)(struct inv_mpu_state *, int on): function ptr + *  (*switch_gyro_engine)(struct inv_mpu_state *, int on): function ptr + *  (*switch_accel_engine)(struct inv_mpu_state *, int on): function ptr + *  (*init_config)(struct iio_dev *indio_dev): function ptr + *  (*setup_reg)(struct inv_reg_map_s *reg): function ptr + *  @timestamps:        kfifo queue to store time stamp. + *  @irq:               irq number store. + *  @accel_bias:        accel bias store. + *  @gyro_bias:         gyro bias store. + *  @input_gyro_offset[3]: gyro offset from sysfs. + *  @input_accel_offset[3]: accel offset from sysfs. + *  @input_accel_dmp_bias[3]: accel bias for dmp. + *  @input_gyro_dmp_bias[3];: gyro bias for dmp. + *  @rom_gyro_bias[3]: gyro bias from sysfs. + *  @rom_accel_bias[3]: accel bias from sysfs. + *  @fifo_data[6]: fifo data storage. + *  @i2c_addr:          i2c address. + *  @sample_divider:    sample divider for dmp. + *  @fifo_divider:      fifo divider for dmp. + *  @display_orient_data:display orient data. + *  @tap_data:          tap data. + *  @bytes_per_sec: bytes per seconds when in DMP mode. + *  @left_over[HEADERED_Q_BYTES]: left over bytes storage. + *  @left_over_size: left over size. + *  @sensor{SENSOR_NUM_MAX]: sensor individual properties. + *  @fifo_count: current fifo_count; + *  @dmp_counter: dmp_counter; + *  @dmp_ticks: DMP ticks past since the previous read. + *  @sl_handle:         Handle to I2C port. + *  @irq_dur_ns:        duration between each irq. + *  @ts_counter:        time stamp counter. + *  @suspend_state:     state indicator suspend. + *  @dmp_interval:      dmp interval. nomial value is 5 ms. + *  @dmp_interval_accum: dmp interval accumlater. + *  @diff_accumulater:  accumlator for the difference of nominal and actual. + *  @last_ts:           last time stamp. + *  @step_detector_base_ts: base time stamp for step detector calculation. + *  @prev_ts:           previous time stamp. + *  @pedometer_step:    pedometer steps stored in driver. + *  @pedometer_time:    pedometer time stored in driver. + *  @last_run_time: last time the post ISR runs. + *  @name: name for distiguish MPU6050 and MPU6500 in MPU6XXX. + *  @secondary_name: name for the slave device in the secondary I2C. + */ +struct inv_mpu_state { +#define TIMESTAMP_FIFO_SIZE 64 +	struct inv_chip_config_s chip_config; +	struct inv_chip_info_s chip_info; +	struct iio_trigger  *trig; +	struct inv_tap tap; +	struct inv_smd smd; +	struct inv_ped ped; +	struct inv_batch batch; +	struct inv_reg_map_s reg; +	struct self_test_setting self_test; +	const struct inv_hw_s *hw; +	enum   inv_devices chip_type; +	spinlock_t time_stamp_lock; +	struct mutex suspend_resume_lock; +	struct i2c_client *client; +	struct mpu_platform_data plat_data; +	struct inv_mpu_slave *slave_accel; +	struct inv_mpu_slave *slave_compass; +	struct inv_mpu_slave *slave_pressure; +	struct accel_mot_int mot_int; +	int (*set_power_state)(struct inv_mpu_state *, bool on); +	int (*switch_gyro_engine)(struct inv_mpu_state *, bool on); +	int (*switch_accel_engine)(struct inv_mpu_state *, bool on); +	int (*init_config)(struct iio_dev *indio_dev); +	void (*setup_reg)(struct inv_reg_map_s *reg); +	DECLARE_KFIFO(timestamps, u64, TIMESTAMP_FIFO_SIZE); +	short irq; +	int accel_bias[3]; +	int gyro_bias[3]; +	s16 input_gyro_offset[3]; +	s16 input_accel_offset[3]; +	int input_accel_dmp_bias[3]; +	int input_gyro_dmp_bias[3]; +	s16 rom_gyro_offset[3]; +	s16 rom_accel_offset[3]; +	u8 fifo_data[6]; +	u8 i2c_addr; +	u8 sample_divider; +	u8 fifo_divider; +	u8 display_orient_data; +	u8 tap_data; +	u16 bytes_per_sec; +	u8 left_over[HEADERED_Q_BYTES]; +	u32 left_over_size; +	struct inv_sensor sensor[SENSOR_NUM_MAX]; +	u32 fifo_count; +	u32 dmp_counter; +	u32 dmp_ticks; +	void *sl_handle; +	u32 irq_dur_ns; +	u32 ts_counter; +	bool suspend_state; +	u32 dmp_interval; +	s32 dmp_interval_accum; +	s64 diff_accumulater; +	u64 last_ts; +	u64 step_detector_base_ts; +	u64 prev_ts; +	u64 last_run_time; +	u8 name[20]; +	u8 secondary_name[20]; +}; + +/* produces an unique identifier for each device based on the +   combination of product version and product revision */ +struct prod_rev_map_t { +	u16 mpl_product_key; +	u8 silicon_rev; +	u16 gyro_trim; +	u16 accel_trim; +}; + +/** + *  struct inv_mpu_slave - MPU slave structure. + *  @st_upper:  compass self test upper limit. + *  @st_lower:  compass self test lower limit. + *  @scale: compass scale. + *  @rate_scale: decide how fast a compass can read. + *  @min_read_time: minimum time between each reading. + *  @self_test: self test method of the slave. + *  @set_scale: set scale of slave + *  @get_scale: read scale back of the slave. + *  @suspend:		suspend operation. + *  @resume:		resume operation. + *  @setup:		setup chip. initialization. + *  @combine_data:	combine raw data into meaningful data. + *  @read_data:        read external sensor and output + *  @get_mode:		get current chip mode. + *  @set_lpf:            set low pass filter. + *  @set_fs:             set full scale + *  @prev_ts: last time it is read. + */ +struct inv_mpu_slave { +	const short *st_upper; +	const short *st_lower; +	int scale; +	int rate_scale; +	int min_read_time; +	int (*self_test)(struct inv_mpu_state *); +	int (*set_scale)(struct inv_mpu_state *, int scale); +	int (*get_scale)(struct inv_mpu_state *, int *val); +	int (*suspend)(struct inv_mpu_state *); +	int (*resume)(struct inv_mpu_state *); +	int (*setup)(struct inv_mpu_state *); +	int (*combine_data)(u8 *in, short *out); +	int (*read_data)(struct inv_mpu_state *, short *out); +	int (*get_mode)(void); +	int (*set_lpf)(struct inv_mpu_state *, int rate); +	int (*set_fs)(struct inv_mpu_state *, int fs); +	u64 prev_ts; +}; + +/* scan element definition */ +enum inv_mpu_scan { +	INV_MPU_SCAN_QUAT_R = 0, +	INV_MPU_SCAN_QUAT_X, +	INV_MPU_SCAN_QUAT_Y, +	INV_MPU_SCAN_QUAT_Z, +	INV_MPU_SCAN_ACCEL_X, +	INV_MPU_SCAN_ACCEL_Y, +	INV_MPU_SCAN_ACCEL_Z, +	INV_MPU_SCAN_GYRO_X, +	INV_MPU_SCAN_GYRO_Y, +	INV_MPU_SCAN_GYRO_Z, +	INV_MPU_SCAN_MAGN_X, +	INV_MPU_SCAN_MAGN_Y, +	INV_MPU_SCAN_MAGN_Z, +	INV_MPU_SCAN_TIMESTAMP, +}; + +enum inv_filter_e { +	INV_FILTER_256HZ_NOLPF2 = 0, +	INV_FILTER_188HZ, +	INV_FILTER_98HZ, +	INV_FILTER_42HZ, +	INV_FILTER_20HZ, +	INV_FILTER_10HZ, +	INV_FILTER_5HZ, +	INV_FILTER_2100HZ_NOLPF, +	NUM_FILTER +}; + +enum inv_slave_mode { +	INV_MODE_SUSPEND, +	INV_MODE_NORMAL, +}; + +/*==== MPU6050B1 MEMORY ====*/ +enum MPU_MEMORY_BANKS { +	MEM_RAM_BANK_0 = 0, +	MEM_RAM_BANK_1, +	MEM_RAM_BANK_2, +	MEM_RAM_BANK_3, +	MEM_RAM_BANK_4, +	MEM_RAM_BANK_5, +	MEM_RAM_BANK_6, +	MEM_RAM_BANK_7, +	MEM_RAM_BANK_8, +	MEM_RAM_BANK_9, +	MEM_RAM_BANK_10, +	MEM_RAM_BANK_11, +	MPU_MEM_NUM_RAM_BANKS, +	MPU_MEM_OTP_BANK_0 = 16 +}; + +/* IIO attribute address */ +enum MPU_IIO_ATTR_ADDR { +	ATTR_DMP_GYRO_X_DMP_BIAS, +	ATTR_DMP_GYRO_Y_DMP_BIAS, +	ATTR_DMP_GYRO_Z_DMP_BIAS, +	ATTR_DMP_ACCEL_X_DMP_BIAS, +	ATTR_DMP_ACCEL_Y_DMP_BIAS, +	ATTR_DMP_ACCEL_Z_DMP_BIAS, +	ATTR_DMP_PED_INT_ON, +	ATTR_DMP_PED_STEP_THRESH, +	ATTR_DMP_PED_INT_THRESH, +	ATTR_DMP_PED_ON, +	ATTR_DMP_SMD_ENABLE, +	ATTR_DMP_SMD_THLD, +	ATTR_DMP_SMD_DELAY_THLD, +	ATTR_DMP_SMD_DELAY_THLD2, +	ATTR_DMP_PEDOMETER_STEPS, +	ATTR_DMP_PEDOMETER_TIME, +	ATTR_DMP_PEDOMETER_COUNTER, +	ATTR_DMP_TAP_ON, +	ATTR_DMP_TAP_THRESHOLD, +	ATTR_DMP_TAP_MIN_COUNT, +	ATTR_DMP_TAP_TIME, +	ATTR_DMP_DISPLAY_ORIENTATION_ON, +/* *****above this line, are DMP features, power needs on/off */ +/* *****below this line, are DMP features, no power needed */ +	ATTR_DMP_ON, +	ATTR_DMP_INT_ON, +	ATTR_DMP_EVENT_INT_ON, +	ATTR_DMP_STEP_INDICATOR_ON, +	ATTR_DMP_BATCHMODE_TIMEOUT, +	ATTR_DMP_BATCHMODE_WAKE_FIFO_FULL, +	ATTR_DMP_SIX_Q_ON, +	ATTR_DMP_SIX_Q_RATE, +	ATTR_DMP_LPQ_ON, +	ATTR_DMP_LPQ_RATE, +	ATTR_DMP_PED_Q_ON, +	ATTR_DMP_PED_Q_RATE, +	ATTR_DMP_STEP_DETECTOR_ON, +	ATTR_DMP_STEP_DETECTOR_RATE, +/*  *****above this line, it is all DMP related features */ +/*  *****below this line, it is all non-DMP related features */ +	ATTR_GYRO_SCALE, +	ATTR_ACCEL_SCALE, +	ATTR_COMPASS_SCALE, +	ATTR_GYRO_X_OFFSET, +	ATTR_GYRO_Y_OFFSET, +	ATTR_GYRO_Z_OFFSET, +	ATTR_ACCEL_X_OFFSET, +	ATTR_ACCEL_Y_OFFSET, +	ATTR_ACCEL_Z_OFFSET, +	ATTR_MOTION_LPA_ON, +	ATTR_MOTION_LPA_FREQ, +	ATTR_MOTION_LPA_THRESHOLD, +/*  *****above this line, it is non-DMP, power needs on/off */ +/*  *****below this line, it is non-DMP, no needs to on/off power */ +	ATTR_SELF_TEST_SAMPLES, +	ATTR_SELF_TEST_THRESHOLD, +	ATTR_GYRO_ENABLE, +	ATTR_GYRO_FIFO_ENABLE, +	ATTR_GYRO_RATE, +	ATTR_ACCEL_ENABLE, +	ATTR_ACCEL_FIFO_ENABLE, +	ATTR_ACCEL_RATE, +	ATTR_COMPASS_ENABLE, +	ATTR_COMPASS_RATE, +	ATTR_PRESSURE_ENABLE, +	ATTR_PRESSURE_RATE, +	ATTR_POWER_STATE, /* this is fake sysfs for compatibility */ +	ATTR_FIRMWARE_LOADED, +	ATTR_SAMPLING_FREQ, +/*  *****below this line, it is attributes only has show methods */ +	ATTR_SELF_TEST, /* this has show-only methods needs power on/off */ +	ATTR_GYRO_X_CALIBBIAS, +	ATTR_GYRO_Y_CALIBBIAS, +	ATTR_GYRO_Z_CALIBBIAS, +	ATTR_ACCEL_X_CALIBBIAS, +	ATTR_ACCEL_Y_CALIBBIAS, +	ATTR_ACCEL_Z_CALIBBIAS, +	ATTR_SELF_TEST_GYRO_SCALE, +	ATTR_SELF_TEST_ACCEL_SCALE, +	ATTR_GYRO_MATRIX, +	ATTR_ACCEL_MATRIX, +	ATTR_COMPASS_MATRIX, +	ATTR_SECONDARY_NAME, +#ifdef CONFIG_INV_TESTING +	ATTR_COMPASS_SENS, +	ATTR_I2C_COUNTERS, +	ATTR_REG_WRITE, +	ATTR_DEBUG_SMD_ENABLE_TESTP1, +	ATTR_DEBUG_SMD_ENABLE_TESTP2, +	ATTR_DEBUG_SMD_EXE_STATE, +	ATTR_DEBUG_SMD_DELAY_CNTR, +	ATTR_DEBUG_GYRO_COUNTER, +	ATTR_DEBUG_ACCEL_COUNTER, +	ATTR_DEBUG_COMPASS_COUNTER, +	ATTR_DEBUG_PRESSURE_COUNTER, +	ATTR_DEBUG_LPQ_COUNTER, +	ATTR_DEBUG_SIXQ_COUNTER, +	ATTR_DEBUG_PEDQ_COUNTER, +#endif +}; + +enum inv_accel_fs_e { +	INV_FS_02G = 0, +	INV_FS_04G, +	INV_FS_08G, +	INV_FS_16G, +	NUM_ACCEL_FSR +}; + +enum inv_fsr_e { +	INV_FSR_250DPS = 0, +	INV_FSR_500DPS, +	INV_FSR_1000DPS, +	INV_FSR_2000DPS, +	NUM_FSR +}; + +enum inv_clock_sel_e { +	INV_CLK_INTERNAL = 0, +	INV_CLK_PLL, +	NUM_CLK +}; + +ssize_t inv_dmp_firmware_write(struct file *fp, struct kobject *kobj, +	struct bin_attribute *attr, char *buf, loff_t pos, size_t size); +ssize_t inv_dmp_firmware_read(struct file *filp, +				struct kobject *kobj, +				struct bin_attribute *bin_attr, +				char *buf, loff_t off, size_t count); +ssize_t inv_six_q_write(struct file *fp, struct kobject *kobj, +	struct bin_attribute *attr, char *buf, loff_t pos, size_t size); + +int inv_mpu_configure_ring(struct iio_dev *indio_dev); +int inv_mpu_probe_trigger(struct iio_dev *indio_dev); +void inv_mpu_unconfigure_ring(struct iio_dev *indio_dev); +void inv_mpu_remove_trigger(struct iio_dev *indio_dev); +int inv_init_config_mpu3050(struct iio_dev *indio_dev); +int inv_get_silicon_rev_mpu6050(struct inv_mpu_state *st); +int inv_get_silicon_rev_mpu6500(struct inv_mpu_state *st); +int set_3050_bypass(struct inv_mpu_state *st, bool enable); +int inv_register_mpu3050_slave(struct inv_mpu_state *st); +void inv_setup_reg_mpu3050(struct inv_reg_map_s *reg); +int inv_switch_3050_gyro_engine(struct inv_mpu_state *st, bool en); +int inv_switch_3050_accel_engine(struct inv_mpu_state *st, bool en); +int set_power_mpu3050(struct inv_mpu_state *st, bool power_on); +int inv_set_interrupt_on_gesture_event(struct inv_mpu_state *st, bool on); +int inv_set_display_orient_interrupt_dmp(struct inv_mpu_state *st, bool on); +u16 inv_dmp_get_address(u16 key); +int inv_q30_mult(int a, int b); +int inv_set_tap_threshold_dmp(struct inv_mpu_state *st, u16 threshold); +int inv_write_2bytes(struct inv_mpu_state *st, int k, int data); +int inv_set_min_taps_dmp(struct inv_mpu_state *st, u16 min_taps); +int  inv_set_tap_time_dmp(struct inv_mpu_state *st, u16 time); +int inv_enable_tap_dmp(struct inv_mpu_state *st, bool on); +int inv_i2c_read_base(struct inv_mpu_state *st, u16 i2c_addr, +	u8 reg, u16 length, u8 *data); +int inv_i2c_single_write_base(struct inv_mpu_state *st, +	u16 i2c_addr, u8 reg, u8 data); +int inv_hw_self_test(struct inv_mpu_state *st); +s64 get_time_ns(void); +int write_be32_key_to_mem(struct inv_mpu_state *st, +					u32 data, int key); +int inv_set_accel_bias_dmp(struct inv_mpu_state *st); +int inv_mpu_setup_compass_slave(struct inv_mpu_state *st); +int inv_mpu_setup_pressure_slave(struct inv_mpu_state *st); +int mpu_memory_write(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr, +		     u32 len, u8 const *data); +int mpu_memory_read(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr, +		    u32 len, u8 *data); +int mpu_memory_write_unaligned(struct inv_mpu_state *st, u16 key, int len, +							u8 const *d); +int inv_quaternion_on(struct inv_mpu_state *st, +				 struct iio_buffer *ring, bool en); +int inv_enable_pedometer_interrupt(struct inv_mpu_state *st, bool en); +int inv_read_pedometer_counter(struct inv_mpu_state *st); +int inv_enable_pedometer(struct inv_mpu_state *st, bool en); +int inv_get_pedometer_steps(struct inv_mpu_state *st, u32 *steps); +int inv_get_pedometer_time(struct inv_mpu_state *st, u32 *time); +int inv_reset_fifo(struct iio_dev *indio_dev); +int inv_reset_offset_reg(struct inv_mpu_state *st, bool en); +int inv_batchmode_setup(struct inv_mpu_state *st); +void inv_init_sensor_struct(struct inv_mpu_state *st); +int inv_read_time_and_ticks(struct inv_mpu_state *st, bool resume); +int inv_flush_batch_data(struct iio_dev *indio_dev, bool *has_data); +int set_inv_enable(struct iio_dev *indio_dev, bool enable); +/* used to print i2c data using pr_debug */ +char *wr_pr_debug_begin(u8 const *data, u32 len, char *string); +char *wr_pr_debug_end(char *string); + +#define mem_w(a, b, c) \ +	mpu_memory_write(st, st->i2c_addr, a, b, c) +#define mem_w_key(key, b, c) mpu_memory_write_unaligned(st, key, b, c) +#define inv_i2c_read(st, reg, len, data) \ +	inv_i2c_read_base(st, st->i2c_addr, reg, len, data) +#define inv_i2c_single_write(st, reg, data) \ +	inv_i2c_single_write_base(st, st->i2c_addr, reg, data) +#define inv_secondary_read(reg, len, data) \ +	inv_i2c_read_base(st, st->plat_data.secondary_i2c_addr, reg, len, data) +#define inv_secondary_write(reg, data) \ +	inv_i2c_single_write_base(st, st->plat_data.secondary_i2c_addr, \ +		reg, data) +#define inv_aux_read(reg, len, data) \ +	inv_i2c_read_base(st, st->plat_data.aux_i2c_addr, reg, len, data) +#define inv_aux_write(reg, data) \ +	inv_i2c_single_write_base(st, st->plat_data.aux_i2c_addr, \ +		reg, data) + +#endif  /* #ifndef _INV_MPU_IIO_H_ */ + diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_misc.c b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_misc.c new file mode 100644 index 00000000000..73b883679c9 --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_misc.c @@ -0,0 +1,2041 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/crc32.h> + +#include "inv_mpu_iio.h" +#include "inv_test/inv_counters.h" + +/* DMP defines */ +#define DMP_ORIENTATION_TIME            500 +#define DMP_ORIENTATION_ANGLE           60 +#define DMP_DEFAULT_FIFO_RATE           200 +#define DMP_TAP_SCALE                   (767603923 / 5) +#define DMP_MULTI_SHIFT                 30 +#define DMP_MULTI_TAP_TIME              500 +#define DMP_SHAKE_REJECT_THRESH         100 +#define DMP_SHAKE_REJECT_TIME           10 +#define DMP_SHAKE_REJECT_TIMEOUT        10 +#define DMP_ANGLE_SCALE                 15 +#define DMP_PRECISION                   1000 +#define DMP_MAX_DIVIDER                 4 +#define DMP_MAX_MIN_TAPS                4 +#define DMP_IMAGE_CRC_VALUE             0xa7e2110d + +/*--- Test parameters defaults --- */ +#define DEF_OLDEST_SUPP_PROD_REV        8 +#define DEF_OLDEST_SUPP_SW_REV          2 + +/* sample rate */ +#define DEF_SELFTEST_SAMPLE_RATE        0 +/* full scale setting dps */ +#define DEF_SELFTEST_GYRO_FS            (0 << 3) +#define DEF_SELFTEST_ACCEL_FS           (2 << 3) +#define DEF_SELFTEST_GYRO_SENS          (32768 / 250) +/* wait time before collecting data */ +#define DEF_GYRO_WAIT_TIME              10 +#define DEF_ST_STABLE_TIME              20 +#define DEF_ST_6500_STABLE_TIME         20 +#define DEF_GYRO_SCALE                  131 +#define DEF_ST_PRECISION                1000 +#define DEF_ST_ACCEL_FS_MG              8000UL +#define DEF_ST_SCALE                    (1L << 15) +#define DEF_ST_TRY_TIMES                2 +#define DEF_ST_COMPASS_RESULT_SHIFT     2 +#define DEF_ST_ACCEL_RESULT_SHIFT       1 +#define DEF_ST_OTP0_THRESH              60 +#define DEF_ST_ABS_THRESH               20 +#define DEF_ST_TOR                      2 + +#define X                               0 +#define Y                               1 +#define Z                               2 +/*---- MPU6050 notable product revisions ----*/ +#define MPU_PRODUCT_KEY_B1_E1_5         105 +#define MPU_PRODUCT_KEY_B2_F1           431 +/* accelerometer Hw self test min and max bias shift (mg) */ +#define DEF_ACCEL_ST_SHIFT_MIN          300 +#define DEF_ACCEL_ST_SHIFT_MAX          950 + +#define DEF_ACCEL_ST_SHIFT_DELTA        500 +#define DEF_GYRO_CT_SHIFT_DELTA         500 +/* gyroscope Coriolis self test min and max bias shift (dps) */ +#define DEF_GYRO_CT_SHIFT_MIN           10 +#define DEF_GYRO_CT_SHIFT_MAX           105 + +/*---- MPU6500 Self Test Pass/Fail Criteria ----*/ +/* Gyro Offset Max Value (dps) */ +#define DEF_GYRO_OFFSET_MAX             20 +/* Gyro Self Test Absolute Limits ST_AL (dps) */ +#define DEF_GYRO_ST_AL                  60 +/* Accel Self Test Absolute Limits ST_AL (mg) */ +#define DEF_ACCEL_ST_AL_MIN             225 +#define DEF_ACCEL_ST_AL_MAX             675 +#define DEF_6500_ACCEL_ST_SHIFT_DELTA   500 +#define DEF_6500_GYRO_CT_SHIFT_DELTA    500 +#define DEF_ST_MPU6500_ACCEL_LPF        2 +#define DEF_ST_6500_ACCEL_FS_MG         2000UL +#define DEF_SELFTEST_6500_ACCEL_FS      (0 << 3) + +/* Note: The ST_AL values are only used when ST_OTP = 0, + * i.e no factory self test values for reference + */ + +/* NOTE: product entries are in chronological order */ +static const struct prod_rev_map_t prod_rev_map[] = { +	/* prod_ver = 0 */ +	{MPL_PROD_KEY(0,   1), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,   2), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,   3), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,   4), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,   5), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,   6), MPU_SILICON_REV_A2, 131, 16384}, +	/* prod_ver = 1 */ +	{MPL_PROD_KEY(0,   7), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,   8), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,   9), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,  10), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,  11), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,  12), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,  13), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,  14), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,  15), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,  27), MPU_SILICON_REV_A2, 131, 16384}, +	/* prod_ver = 1 */ +	{MPL_PROD_KEY(1,  16), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,  17), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,  18), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,  19), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,  20), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,  28), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,   1), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,   2), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,   3), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,   4), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,   5), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,   6), MPU_SILICON_REV_B1, 131, 16384}, +	/* prod_ver = 2 */ +	{MPL_PROD_KEY(2,   7), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(2,   8), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(2,   9), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(2,  10), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(2,  11), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(2,  12), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(2,  29), MPU_SILICON_REV_B1, 131, 16384}, +	/* prod_ver = 3 */ +	{MPL_PROD_KEY(3,  30), MPU_SILICON_REV_B1, 131, 16384}, +	/* prod_ver = 4 */ +	{MPL_PROD_KEY(4,  31), MPU_SILICON_REV_B1, 131,  8192}, +	{MPL_PROD_KEY(4,   1), MPU_SILICON_REV_B1, 131,  8192}, +	{MPL_PROD_KEY(4,   3), MPU_SILICON_REV_B1, 131,  8192}, +	/* prod_ver = 5 */ +	{MPL_PROD_KEY(5,   3), MPU_SILICON_REV_B1, 131, 16384}, +	/* prod_ver = 6 */ +	{MPL_PROD_KEY(6,  19), MPU_SILICON_REV_B1, 131, 16384}, +	/* prod_ver = 7 */ +	{MPL_PROD_KEY(7,  19), MPU_SILICON_REV_B1, 131, 16384}, +	/* prod_ver = 8 */ +	{MPL_PROD_KEY(8,  19), MPU_SILICON_REV_B1, 131, 16384}, +	/* prod_ver = 9 */ +	{MPL_PROD_KEY(9,  19), MPU_SILICON_REV_B1, 131, 16384}, +	/* prod_ver = 10 */ +	{MPL_PROD_KEY(10, 19), MPU_SILICON_REV_B1, 131, 16384} +}; + +/* +*   List of product software revisions +* +*   NOTE : +*   software revision 0 falls back to the old detection method +*   based off the product version and product revision per the +*   table above +*/ +static const struct prod_rev_map_t sw_rev_map[] = { +	{0,		     0,   0,     0}, +	{1, MPU_SILICON_REV_B1, 131,  8192},	/* rev C */ +	{2, MPU_SILICON_REV_B1, 131, 16384}	/* rev D */ +}; + +static const u16 mpu_6500_st_tb[256] = { +	2620, 2646, 2672, 2699, 2726, 2753, 2781, 2808, +	2837, 2865, 2894, 2923, 2952, 2981, 3011, 3041, +	3072, 3102, 3133, 3165, 3196, 3228, 3261, 3293, +	3326, 3359, 3393, 3427, 3461, 3496, 3531, 3566, +	3602, 3638, 3674, 3711, 3748, 3786, 3823, 3862, +	3900, 3939, 3979, 4019, 4059, 4099, 4140, 4182, +	4224, 4266, 4308, 4352, 4395, 4439, 4483, 4528, +	4574, 4619, 4665, 4712, 4759, 4807, 4855, 4903, +	4953, 5002, 5052, 5103, 5154, 5205, 5257, 5310, +	5363, 5417, 5471, 5525, 5581, 5636, 5693, 5750, +	5807, 5865, 5924, 5983, 6043, 6104, 6165, 6226, +	6289, 6351, 6415, 6479, 6544, 6609, 6675, 6742, +	6810, 6878, 6946, 7016, 7086, 7157, 7229, 7301, +	7374, 7448, 7522, 7597, 7673, 7750, 7828, 7906, +	7985, 8065, 8145, 8227, 8309, 8392, 8476, 8561, +	8647, 8733, 8820, 8909, 8998, 9088, 9178, 9270, +	9363, 9457, 9551, 9647, 9743, 9841, 9939, 10038, +	10139, 10240, 10343, 10446, 10550, 10656, 10763, 10870, +	10979, 11089, 11200, 11312, 11425, 11539, 11654, 11771, +	11889, 12008, 12128, 12249, 12371, 12495, 12620, 12746, +	12874, 13002, 13132, 13264, 13396, 13530, 13666, 13802, +	13940, 14080, 14221, 14363, 14506, 14652, 14798, 14946, +	15096, 15247, 15399, 15553, 15709, 15866, 16024, 16184, +	16346, 16510, 16675, 16842, 17010, 17180, 17352, 17526, +	17701, 17878, 18057, 18237, 18420, 18604, 18790, 18978, +	19167, 19359, 19553, 19748, 19946, 20145, 20347, 20550, +	20756, 20963, 21173, 21385, 21598, 21814, 22033, 22253, +	22475, 22700, 22927, 23156, 23388, 23622, 23858, 24097, +	24338, 24581, 24827, 25075, 25326, 25579, 25835, 26093, +	26354, 26618, 26884, 27153, 27424, 27699, 27976, 28255, +	28538, 28823, 29112, 29403, 29697, 29994, 30294, 30597, +	30903, 31212, 31524, 31839, 32157, 32479, 32804 +}; + +static const int accel_st_tb[31] = { +	340, 351, 363, 375, 388, 401, 414, 428, +	443, 458, 473, 489, 506, 523, 541, 559, +	578, 597, 617, 638, 660, 682, 705, 729, +	753, 779, 805, 832, 860, 889, 919 +}; + +static const int gyro_6050_st_tb[31] = { +	3275, 3425, 3583, 3748, 3920, 4100, 4289, 4486, +	4693, 4909, 5134, 5371, 5618, 5876, 6146, 6429, +	6725, 7034, 7358, 7696, 8050, 8421, 8808, 9213, +	9637, 10080, 10544, 11029, 11537, 12067, 12622 +}; + +static const int gyro_3500_st_tb[255] = { +	2620, 2646, 2672, 2699, 2726, 2753, 2781, 2808, +	2837, 2865, 2894, 2923, 2952, 2981, 3011, 3041, +	3072, 3102, 3133, 3165, 3196, 3228, 3261, 3293, +	3326, 3359, 3393, 3427, 3461, 3496, 3531, 3566, +	3602, 3638, 3674, 3711, 3748, 3786, 3823, 3862, +	3900, 3939, 3979, 4019, 4059, 4099, 4140, 4182, +	4224, 4266, 4308, 4352, 4395, 4439, 4483, 4528, +	4574, 4619, 4665, 4712, 4759, 4807, 4855, 4903, +	4953, 5002, 5052, 5103, 5154, 5205, 5257, 5310, +	5363, 5417, 5471, 5525, 5581, 5636, 5693, 5750, +	5807, 5865, 5924, 5983, 6043, 6104, 6165, 6226, +	6289, 6351, 6415, 6479, 6544, 6609, 6675, 6742, +	6810, 6878, 6946, 7016, 7086, 7157, 7229, 7301, +	7374, 7448, 7522, 7597, 7673, 7750, 7828, 7906, +	7985, 8065, 8145, 8227, 8309, 8392, 8476, 8561, +	8647, 8733, 8820, 8909, 8998, 9088, 9178, 9270, +	9363, 9457, 9551, 9647, 9743, 9841, 9939, 10038, +	10139, 10240, 10343, 10446, 10550, 10656, 10763, 10870, +	10979, 11089, 11200, 11312, 11425, 11539, 11654, 11771, +	11889, 12008, 12128, 12249, 12371, 12495, 12620, 12746, +	12874, 13002, 13132, 13264, 13396, 13530, 13666, 13802, +	13940, 14080, 14221, 14363, 14506, 14652, 14798, 14946, +	15096, 15247, 15399, 15553, 15709, 15866, 16024, 16184, +	16346, 16510, 16675, 16842, 17010, 17180, 17352, 17526, +	17701, 17878, 18057, 18237, 18420, 18604, 18790, 18978, +	19167, 19359, 19553, 19748, 19946, 20145, 20347, 20550, +	20756, 20963, 21173, 21385, 21598, 21814, 22033, 22253, +	22475, 22700, 22927, 23156, 23388, 23622, 23858, 24097, +	24338, 24581, 24827, 25075, 25326, 25579, 25835, 26093, +	26354, 26618, 26884, 27153, 27424, 27699, 27976, 28255, +	28538, 28823, 29112, 29403, 29697, 29994, 30294, 30597, +	30903, 31212, 31524, 31839, 32157, 32479, 32804 +}; + +char *wr_pr_debug_begin(u8 const *data, u32 len, char *string) +{ +	int ii; +	string = kmalloc(len * 2 + 1, GFP_KERNEL); +	for (ii = 0; ii < len; ii++) +		sprintf(&string[ii * 2], "%02X", data[ii]); +	string[len * 2] = 0; +	return string; +} + +char *wr_pr_debug_end(char *string) +{ +	kfree(string); +	return ""; +} + +int mpu_memory_write(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr, +		     u32 len, u8 const *data) +{ +	u8 bank[2]; +	u8 addr[2]; +	u8 buf[513]; + +	struct i2c_msg msgs[3]; +	int res; + +	if (!data || !st) +		return -EINVAL; + +	if (len >= (sizeof(buf) - 1)) +		return -ENOMEM; + +	bank[0] = REG_BANK_SEL; +	bank[1] = mem_addr >> 8; + +	addr[0] = REG_MEM_START_ADDR; +	addr[1] = mem_addr & 0xFF; + +	buf[0] = REG_MEM_RW; +	memcpy(buf + 1, data, len); + +	/* write message */ +	msgs[0].addr = mpu_addr; +	msgs[0].flags = 0; +	msgs[0].buf = bank; +	msgs[0].len = sizeof(bank); + +	msgs[1].addr = mpu_addr; +	msgs[1].flags = 0; +	msgs[1].buf = addr; +	msgs[1].len = sizeof(addr); + +	msgs[2].addr = mpu_addr; +	msgs[2].flags = 0; +	msgs[2].buf = (u8 *)buf; +	msgs[2].len = len + 1; + +	INV_I2C_INC_MPUWRITE(3 + 3 + (2 + len)); +#if CONFIG_DYNAMIC_DEBUG +	{ +		char *write = 0; +		pr_debug("%s WM%02X%02X%02X%s%s - %d\n", st->hw->name, +			 mpu_addr, bank[1], addr[1], +			 wr_pr_debug_begin(data, len, write), +			 wr_pr_debug_end(write), +			 len); +	} +#endif + +	res = i2c_transfer(st->sl_handle, msgs, 3); +	if (res != 3) { +		if (res >= 0) +			res = -EIO; +		return res; +	} else { +		return 0; +	} +} + +int mpu_memory_read(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr, +		    u32 len, u8 *data) +{ +	u8 bank[2]; +	u8 addr[2]; +	u8 buf; + +	struct i2c_msg msgs[4]; +	int res; + +	if (!data || !st) +		return -EINVAL; + +	bank[0] = REG_BANK_SEL; +	bank[1] = mem_addr >> 8; + +	addr[0] = REG_MEM_START_ADDR; +	addr[1] = mem_addr & 0xFF; + +	buf = REG_MEM_RW; + +	/* write message */ +	msgs[0].addr = mpu_addr; +	msgs[0].flags = 0; +	msgs[0].buf = bank; +	msgs[0].len = sizeof(bank); + +	msgs[1].addr = mpu_addr; +	msgs[1].flags = 0; +	msgs[1].buf = addr; +	msgs[1].len = sizeof(addr); + +	msgs[2].addr = mpu_addr; +	msgs[2].flags = 0; +	msgs[2].buf = &buf; +	msgs[2].len = 1; + +	msgs[3].addr = mpu_addr; +	msgs[3].flags = I2C_M_RD; +	msgs[3].buf = data; +	msgs[3].len = len; + +	res = i2c_transfer(st->sl_handle, msgs, 4); +	if (res != 4) { +		if (res >= 0) +			res = -EIO; +	} else +		res = 0; + +	INV_I2C_INC_MPUWRITE(3 + 3 + 3); +	INV_I2C_INC_MPUREAD(len); +#if CONFIG_DYNAMIC_DEBUG +	{ +		char *read = 0; +		pr_debug("%s RM%02X%02X%02X%02X - %s%s\n", st->hw->name, +			 mpu_addr, bank[1], addr[1], len, +			 wr_pr_debug_begin(data, len, read), +			 wr_pr_debug_end(read)); +	} +#endif + +	return res; +} + +int mpu_memory_write_unaligned(struct inv_mpu_state *st, u16 key, int len, +								u8 const *d) +{ +	u32 addr; +	int start, end; +	int len1, len2; +	int result = 0; + +	if (len > MPU_MEM_BANK_SIZE) +		return -EINVAL; +	addr = inv_dmp_get_address(key); +	if (addr > MPU6XXX_MAX_MPU_MEM) +		return -EINVAL; + +	start = (addr >> 8); +	end   = ((addr + len - 1) >> 8); +	if (start == end) { +		result = mpu_memory_write(st, st->i2c_addr, addr, len, d); +	} else { +		end <<= 8; +		len1 = end - addr; +		len2 = len - len1; +		result = mpu_memory_write(st, st->i2c_addr, addr, len1, d); +		result |= mpu_memory_write(st, st->i2c_addr, end, len2, +								d + len1); +	} + +	return result; +} + +/** + *  index_of_key()- Inverse lookup of the index of an MPL product key . + *  @key: the MPL product indentifier also referred to as 'key'. + */ +static short index_of_key(u16 key) +{ +	int i; +	for (i = 0; i < NUM_OF_PROD_REVS; i++) +		if (prod_rev_map[i].mpl_product_key == key) +			return (short)i; +	return -EINVAL; +} + +int inv_get_silicon_rev_mpu6500(struct inv_mpu_state *st) +{ +	struct inv_chip_info_s *chip_info = &st->chip_info; +	int result; +	u8 whoami, sw_rev; + +	result = inv_i2c_read(st, REG_WHOAMI, 1, &whoami); +	if (result) +		return result; +	if (whoami != MPU6500_ID && whoami != MPU9250_ID && +			whoami != MPU9350_ID && whoami != MPU6515_ID) +		return -EINVAL; + +	/*memory read need more time after power up */ +	msleep(POWER_UP_TIME); +	result = mpu_memory_read(st, st->i2c_addr, +			MPU6500_MEM_REV_ADDR, 1, &sw_rev); +	sw_rev &= INV_MPU_REV_MASK; +	if (result) +		return result; +	if (sw_rev != 0) +		return -EINVAL; +	/* these values are place holders and not real values */ +	chip_info->product_id = MPU6500_PRODUCT_REVISION; +	chip_info->product_revision = MPU6500_PRODUCT_REVISION; +	chip_info->silicon_revision = MPU6500_PRODUCT_REVISION; +	chip_info->software_revision = sw_rev; +	chip_info->gyro_sens_trim = DEFAULT_GYRO_TRIM; +	chip_info->accel_sens_trim = DEFAULT_ACCEL_TRIM; +	chip_info->multi = 1; + +	return 0; +} + +int inv_get_silicon_rev_mpu6050(struct inv_mpu_state *st) +{ +	int result; +	struct inv_reg_map_s *reg; +	u8 prod_ver = 0x00, prod_rev = 0x00; +	struct prod_rev_map_t *p_rev; +	u8 bank = +	    (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0); +	u16 mem_addr = ((bank << 8) | MEM_ADDR_PROD_REV); +	u16 key; +	u8 regs[5]; +	u16 sw_rev; +	short index; +	struct inv_chip_info_s *chip_info = &st->chip_info; +	reg = &st->reg; + +	result = inv_i2c_read(st, REG_PRODUCT_ID, 1, &prod_ver); +	if (result) +		return result; +	prod_ver &= 0xf; +	/*memory read need more time after power up */ +	msleep(POWER_UP_TIME); +	result = mpu_memory_read(st, st->i2c_addr, mem_addr, 1, &prod_rev); +	if (result) +		return result; +	prod_rev >>= 2; +	/* clean the prefetch and cfg user bank bits */ +	result = inv_i2c_single_write(st, reg->bank_sel, 0); +	if (result) +		return result; +	/* get the software-product version, read from XA_OFFS_L */ +	result = inv_i2c_read(st, REG_XA_OFFS_L_TC, +				SOFT_PROD_VER_BYTES, regs); +	if (result) +		return result; + +	sw_rev = (regs[4] & 0x01) << 2 |	/* 0x0b, bit 0 */ +		 (regs[2] & 0x01) << 1 |	/* 0x09, bit 0 */ +		 (regs[0] & 0x01);		/* 0x07, bit 0 */ +	/* if 0, use the product key to determine the type of part */ +	if (sw_rev == 0) { +		key = MPL_PROD_KEY(prod_ver, prod_rev); +		if (key == 0) +			return -EINVAL; +		index = index_of_key(key); +		if (index < 0 || index >= NUM_OF_PROD_REVS) +			return -EINVAL; +		/* check MPL is compiled for this device */ +		if (prod_rev_map[index].silicon_rev != MPU_SILICON_REV_B1) +			return -EINVAL; +		p_rev = (struct prod_rev_map_t *)&prod_rev_map[index]; +	/* if valid, use the software product key */ +	} else if (sw_rev < ARRAY_SIZE(sw_rev_map)) { +		p_rev = (struct prod_rev_map_t *)&sw_rev_map[sw_rev]; +	} else { +		return -EINVAL; +	} +	chip_info->product_id = prod_ver; +	chip_info->product_revision = prod_rev; +	chip_info->silicon_revision = p_rev->silicon_rev; +	chip_info->software_revision = sw_rev; +	chip_info->gyro_sens_trim = p_rev->gyro_trim; +	chip_info->accel_sens_trim = p_rev->accel_trim; +	if (chip_info->accel_sens_trim == 0) +		chip_info->accel_sens_trim = DEFAULT_ACCEL_TRIM; +	chip_info->multi = DEFAULT_ACCEL_TRIM / chip_info->accel_sens_trim; +	if (chip_info->multi != 1) +		pr_info("multi is %d\n", chip_info->multi); +	return result; +} + +/** + *  read_accel_hw_self_test_prod_shift()- read the accelerometer hardware + *                                         self-test bias shift calculated + *                                         during final production test and + *                                         stored in chip non-volatile memory. + *  @st:  main data structure. + *  @st_prod:   A pointer to an array of 3 elements to hold the values + *              for production hardware self-test bias shifts returned to the + *              user. + *  @accel_sens: accel sensitivity. + */ +static int read_accel_hw_self_test_prod_shift(struct inv_mpu_state *st, +					int *st_prod, int *accel_sens) +{ +	u8 regs[4]; +	u8 shift_code[3]; +	int result, i; + +	for (i = 0; i < 3; i++) +		st_prod[i] = 0; + +	result = inv_i2c_read(st, REG_ST_GCT_X, ARRAY_SIZE(regs), regs); +	if (result) +		return result; +	if ((0 == regs[0])  && (0 == regs[1]) && +	    (0 == regs[2]) && (0 == regs[3])) +		return -EINVAL; +	shift_code[X] = ((regs[0] & 0xE0) >> 3) | ((regs[3] & 0x30) >> 4); +	shift_code[Y] = ((regs[1] & 0xE0) >> 3) | ((regs[3] & 0x0C) >> 2); +	shift_code[Z] = ((regs[2] & 0xE0) >> 3) |  (regs[3] & 0x03); +	for (i = 0; i < 3; i++) +		if (shift_code[i] != 0) +			st_prod[i] = accel_sens[i] * +					accel_st_tb[shift_code[i] - 1]; + +	return 0; +} + +/** +* inv_check_accel_self_test()- check accel self test. this function returns +*                              zero as success. A non-zero return value +*                              indicates failure in self test. +*  @*st: main data structure. +*  @*reg_avg: average value of normal test. +*  @*st_avg:  average value of self test +*/ +static int inv_check_accel_self_test(struct inv_mpu_state *st, +						int *reg_avg, int *st_avg){ +	int gravity, j, ret_val; +	int tmp; +	int st_shift_prod[THREE_AXIS], st_shift_cust[THREE_AXIS]; +	int st_shift_ratio[THREE_AXIS]; +	int accel_sens[THREE_AXIS]; + +	if (st->chip_info.software_revision < DEF_OLDEST_SUPP_SW_REV && +	    st->chip_info.product_revision < DEF_OLDEST_SUPP_PROD_REV) +		return 0; +	ret_val = 0; +	tmp = DEF_ST_SCALE * DEF_ST_PRECISION / DEF_ST_ACCEL_FS_MG; +	for (j = 0; j < 3; j++) +		accel_sens[j] = tmp; + +	if (MPL_PROD_KEY(st->chip_info.product_id, +			 st->chip_info.product_revision) == +	    MPU_PRODUCT_KEY_B1_E1_5) { +		/* half sensitivity Z accelerometer parts */ +		accel_sens[Z] /= 2; +	} else { +		/* half sensitivity X, Y, Z accelerometer parts */ +		accel_sens[X] /= st->chip_info.multi; +		accel_sens[Y] /= st->chip_info.multi; +		accel_sens[Z] /= st->chip_info.multi; +	} +	gravity = accel_sens[Z]; +	ret_val = read_accel_hw_self_test_prod_shift(st, st_shift_prod, +							accel_sens); +	if (ret_val) +		return ret_val; + +	for (j = 0; j < 3; j++) { +		st_shift_cust[j] = abs(reg_avg[j] - st_avg[j]); +		if (st_shift_prod[j]) { +			tmp = st_shift_prod[j] / DEF_ST_PRECISION; +			st_shift_ratio[j] = abs(st_shift_cust[j] / tmp +				- DEF_ST_PRECISION); +			if (st_shift_ratio[j] > DEF_ACCEL_ST_SHIFT_DELTA) +				ret_val = 1; +		} else { +			if (st_shift_cust[j] < +				DEF_ACCEL_ST_SHIFT_MIN * gravity) +				ret_val = 1; +			if (st_shift_cust[j] > +				DEF_ACCEL_ST_SHIFT_MAX * gravity) +				ret_val = 1; +		} +	} + +	return ret_val; +} + +/** +* inv_check_3500_gyro_self_test() check gyro self test. this function returns +*                                 zero as success. A non-zero return value +*                                 indicates failure in self test. +*  @*st: main data structure. +*  @*reg_avg: average value of normal test. +*  @*st_avg:  average value of self test +*/ + +static int inv_check_3500_gyro_self_test(struct inv_mpu_state *st, +						int *reg_avg, int *st_avg){ +	int result; +	int gst[3], ret_val; +	int gst_otp[3], i; +	u8 st_code[THREE_AXIS]; +	ret_val = 0; + +	for (i = 0; i < 3; i++) +		gst[i] = st_avg[i] - reg_avg[i]; +	result = inv_i2c_read(st, REG_3500_OTP, THREE_AXIS, st_code); +	if (result) +		return result; +	gst_otp[0] = 0; +	gst_otp[1] = 0; +	gst_otp[2] = 0; +	for (i = 0; i < 3; i++) { +		if (st_code[i] != 0) +			gst_otp[i] = gyro_3500_st_tb[st_code[i] - 1]; +	} +	/* check self test value passing criterion. Using the DEF_ST_TOR +	 * for certain degree of tolerance */ +	for (i = 0; i < 3; i++) { +		if (gst_otp[i] == 0) { +			if (abs(gst[i]) * DEF_ST_TOR < DEF_ST_OTP0_THRESH * +							DEF_ST_PRECISION * +							DEF_GYRO_SCALE) +				ret_val |= (1 << i); +		} else { +			if (abs(gst[i]/gst_otp[i] - DEF_ST_PRECISION) > +					DEF_GYRO_CT_SHIFT_DELTA) +				ret_val |= (1 << i); +		} +	} +	/* check for absolute value passing criterion. Using DEF_ST_TOR +	 * for certain degree of tolerance */ +	for (i = 0; i < 3; i++) { +		if (abs(reg_avg[i]) > DEF_ST_TOR * DEF_ST_ABS_THRESH * +		    DEF_ST_PRECISION * DEF_GYRO_SCALE) +			ret_val |= (1 << i); +	} + +	return ret_val; +} + +/** +* inv_check_6050_gyro_self_test() - check 6050 gyro self test. this function +*                                   returns zero as success. A non-zero return +*                                   value indicates failure in self test. +*  @*st: main data structure. +*  @*reg_avg: average value of normal test. +*  @*st_avg:  average value of self test +*/ +static int inv_check_6050_gyro_self_test(struct inv_mpu_state *st, +						int *reg_avg, int *st_avg){ +	int result; +	int ret_val; +	int st_shift_prod[3], st_shift_cust[3], st_shift_ratio[3], i; +	u8 regs[3]; + +	if (st->chip_info.software_revision < DEF_OLDEST_SUPP_SW_REV && +	    st->chip_info.product_revision < DEF_OLDEST_SUPP_PROD_REV) +		return 0; + +	ret_val = 0; +	result = inv_i2c_read(st, REG_ST_GCT_X, 3, regs); +	if (result) +		return result; +	regs[X] &= 0x1f; +	regs[Y] &= 0x1f; +	regs[Z] &= 0x1f; +	for (i = 0; i < 3; i++) { +		if (regs[i] != 0) +			st_shift_prod[i] = gyro_6050_st_tb[regs[i] - 1]; +		else +			st_shift_prod[i] = 0; +	} +	st_shift_prod[1] = -st_shift_prod[1]; + +	for (i = 0; i < 3; i++) { +		st_shift_cust[i] =  st_avg[i] - reg_avg[i]; +		if (st_shift_prod[i]) { +			st_shift_ratio[i] = abs(st_shift_cust[i] / +				st_shift_prod[i] - DEF_ST_PRECISION); +			if (st_shift_ratio[i] > DEF_GYRO_CT_SHIFT_DELTA) +				ret_val = 1; +		} else { +			if (st_shift_cust[i] < DEF_ST_PRECISION * +				DEF_GYRO_CT_SHIFT_MIN * DEF_SELFTEST_GYRO_SENS) +				ret_val = 1; +			if (st_shift_cust[i] > DEF_ST_PRECISION * +				DEF_GYRO_CT_SHIFT_MAX * DEF_SELFTEST_GYRO_SENS) +				ret_val = 1; +		} +	} +	/* check for absolute value passing criterion. Using DEF_ST_TOR +	 * for certain degree of tolerance */ +	for (i = 0; i < 3; i++) +		if (abs(reg_avg[i]) > DEF_ST_TOR * DEF_ST_ABS_THRESH * +		    DEF_ST_PRECISION * DEF_GYRO_SCALE) +			ret_val = 1; + +	return ret_val; +} + +/** +* inv_check_6500_gyro_self_test() - check 6500 gyro self test. this function +*                                   returns zero as success. A non-zero return +*                                   value indicates failure in self test. +*  @*st: main data structure. +*  @*reg_avg: average value of normal test. +*  @*st_avg:  average value of self test +*/ +static int inv_check_6500_gyro_self_test(struct inv_mpu_state *st, +						int *reg_avg, int *st_avg) { +	u8 regs[3]; +	int ret_val, result; +	int otp_value_zero = 0; +	int st_shift_prod[3], st_shift_cust[3], i; + +	ret_val = 0; +	result = inv_i2c_read(st, REG_6500_XG_ST_DATA, 3, regs); +	if (result) +		return result; +	pr_debug("%s self_test gyro shift_code - %02x %02x %02x\n", +		 st->hw->name, regs[0], regs[1], regs[2]); + +	for (i = 0; i < 3; i++) { +		if (regs[i] != 0) { +			st_shift_prod[i] = mpu_6500_st_tb[regs[i] - 1]; +		} else { +			st_shift_prod[i] = 0; +			otp_value_zero = 1; +		} +	} +	pr_debug("%s self_test gyro st_shift_prod - %+d %+d %+d\n", +		 st->hw->name, st_shift_prod[0], st_shift_prod[1], +		 st_shift_prod[2]); + +	for (i = 0; i < 3; i++) { +		st_shift_cust[i] = st_avg[i] - reg_avg[i]; +		if (!otp_value_zero) { +			/* Self Test Pass/Fail Criteria A */ +			if (st_shift_cust[i] < DEF_6500_GYRO_CT_SHIFT_DELTA +						* st_shift_prod[i]) +					ret_val = 1; +		} else { +			/* Self Test Pass/Fail Criteria B */ +			if (st_shift_cust[i] < DEF_GYRO_ST_AL * +						DEF_SELFTEST_GYRO_SENS * +						DEF_ST_PRECISION) +				ret_val = 1; +		} +	} +	pr_debug("%s self_test gyro st_shift_cust - %+d %+d %+d\n", +		 st->hw->name, st_shift_cust[0], st_shift_cust[1], +		 st_shift_cust[2]); + +	if (ret_val == 0) { +		/* Self Test Pass/Fail Criteria C */ +		for (i = 0; i < 3; i++) +			if (abs(reg_avg[i]) > DEF_GYRO_OFFSET_MAX * +						DEF_SELFTEST_GYRO_SENS * +						DEF_ST_PRECISION) +				ret_val = 1; +	} + +	return ret_val; +} + +/** +* inv_check_6500_accel_self_test() - check 6500 accel self test. this function +*                                   returns zero as success. A non-zero return +*                                   value indicates failure in self test. +*  @*st: main data structure. +*  @*reg_avg: average value of normal test. +*  @*st_avg:  average value of self test +*/ +static int inv_check_6500_accel_self_test(struct inv_mpu_state *st, +						int *reg_avg, int *st_avg) { +	int ret_val, result; +	int st_shift_prod[3], st_shift_cust[3], st_shift_ratio[3], i; +	u8 regs[3]; +	int otp_value_zero = 0; + +#define ACCEL_ST_AL_MIN ((DEF_ACCEL_ST_AL_MIN * DEF_ST_SCALE \ +				 / DEF_ST_6500_ACCEL_FS_MG) * DEF_ST_PRECISION) +#define ACCEL_ST_AL_MAX ((DEF_ACCEL_ST_AL_MAX * DEF_ST_SCALE \ +				 / DEF_ST_6500_ACCEL_FS_MG) * DEF_ST_PRECISION) + +	ret_val = 0; +	result = inv_i2c_read(st, REG_6500_XA_ST_DATA, 3, regs); +	if (result) +		return result; +	pr_debug("%s self_test accel shift_code - %02x %02x %02x\n", +		 st->hw->name, regs[0], regs[1], regs[2]); + +	for (i = 0; i < 3; i++) { +		if (regs[i] != 0) { +			st_shift_prod[i] = mpu_6500_st_tb[regs[i] - 1]; +		} else { +			st_shift_prod[i] = 0; +			otp_value_zero = 1; +		} +	} +	pr_debug("%s self_test accel st_shift_prod - %+d %+d %+d\n", +		 st->hw->name, st_shift_prod[0], st_shift_prod[1], +		 st_shift_prod[2]); + +	if (!otp_value_zero) { +		/* Self Test Pass/Fail Criteria A */ +		for (i = 0; i < 3; i++) { +			st_shift_cust[i] = st_avg[i] - reg_avg[i]; +			st_shift_ratio[i] = abs(st_shift_cust[i] / +					st_shift_prod[i] - DEF_ST_PRECISION); +			if (st_shift_ratio[i] > DEF_6500_ACCEL_ST_SHIFT_DELTA) +				ret_val = 1; +		} +	} else { +		/* Self Test Pass/Fail Criteria B */ +		for (i = 0; i < 3; i++) { +			st_shift_cust[i] = abs(st_avg[i] - reg_avg[i]); +			if (st_shift_cust[i] < ACCEL_ST_AL_MIN || +					st_shift_cust[i] > ACCEL_ST_AL_MAX) +				ret_val = 1; +		} +	} +	pr_debug("%s self_test accel st_shift_cust - %+d %+d %+d\n", +		 st->hw->name, st_shift_cust[0], st_shift_cust[1], +		 st_shift_cust[2]); + +	return ret_val; +} + +/* + *  inv_do_test() - do the actual test of self testing + */ +static int inv_do_test(struct inv_mpu_state *st, int self_test_flag, +		int *gyro_result, int *accel_result) +{ +	struct inv_reg_map_s *reg; +	int result, i, j, packet_size; +	u8 data[BYTES_PER_SENSOR * 2], d; +	bool has_accel; +	int fifo_count, packet_count, ind, s; + +	reg = &st->reg; +	has_accel = (st->chip_type != INV_ITG3500); +	if (has_accel) +		packet_size = BYTES_PER_SENSOR * 2; +	else +		packet_size = BYTES_PER_SENSOR; + +	result = inv_i2c_single_write(st, reg->int_enable, 0); +	if (result) +		return result; +	/* disable the sensor output to FIFO */ +	result = inv_i2c_single_write(st, reg->fifo_en, 0); +	if (result) +		return result; +	/* disable fifo reading */ +	result = inv_i2c_single_write(st, reg->user_ctrl, 0); +	if (result) +		return result; +	/* clear FIFO */ +	result = inv_i2c_single_write(st, reg->user_ctrl, BIT_FIFO_RST); +	if (result) +		return result; +	/* setup parameters */ +	result = inv_i2c_single_write(st, reg->lpf, INV_FILTER_98HZ); +	if (result) +		return result; + +	if (INV_MPU6500 == st->chip_type) { +		/* config accel LPF register for MPU6500 */ +		result = inv_i2c_single_write(st, REG_6500_ACCEL_CONFIG2, +						DEF_ST_MPU6500_ACCEL_LPF | +						BIT_FIFO_SIZE_1K); +		if (result) +			return result; +	} + +	result = inv_i2c_single_write(st, reg->sample_rate_div, +			DEF_SELFTEST_SAMPLE_RATE); +	if (result) +		return result; +	/* wait for the sampling rate change to stabilize */ +	mdelay(INV_MPU_SAMPLE_RATE_CHANGE_STABLE); +	result = inv_i2c_single_write(st, reg->gyro_config, +		self_test_flag | DEF_SELFTEST_GYRO_FS); +	if (result) +		return result; +	if (has_accel) { +		if (INV_MPU6500 == st->chip_type) +			d = DEF_SELFTEST_6500_ACCEL_FS; +		else +			d = DEF_SELFTEST_ACCEL_FS; +		d |= self_test_flag; +		result = inv_i2c_single_write(st, reg->accel_config, d); +		if (result) +			return result; +	} +	/* wait for the output to get stable */ +	if (self_test_flag) { +		if (INV_MPU6500 == st->chip_type) +			msleep(DEF_ST_6500_STABLE_TIME); +		else +			msleep(DEF_ST_STABLE_TIME); +	} + +	/* enable FIFO reading */ +	result = inv_i2c_single_write(st, reg->user_ctrl, BIT_FIFO_EN); +	if (result) +		return result; +	/* enable sensor output to FIFO */ +	if (has_accel) +		d = BITS_GYRO_OUT | BIT_ACCEL_OUT; +	else +		d = BITS_GYRO_OUT; +	for (i = 0; i < THREE_AXIS; i++) { +		gyro_result[i] = 0; +		accel_result[i] = 0; +	} +	s = 0; +	while (s < st->self_test.samples) { +		result = inv_i2c_single_write(st, reg->fifo_en, d); +		if (result) +			return result; +		mdelay(DEF_GYRO_WAIT_TIME); +		result = inv_i2c_single_write(st, reg->fifo_en, 0); +		if (result) +			return result; + +		result = inv_i2c_read(st, reg->fifo_count_h, +					FIFO_COUNT_BYTE, data); +		if (result) +			return result; +		fifo_count = be16_to_cpup((__be16 *)(&data[0])); +		pr_debug("%s self_test fifo_count - %d\n", +			 st->hw->name, fifo_count); +		packet_count = fifo_count / packet_size; +		i = 0; +		while ((i < packet_count) && (s < st->self_test.samples)) { +			short vals[3]; +			result = inv_i2c_read(st, reg->fifo_r_w, +				packet_size, data); +			if (result) +				return result; +			ind = 0; +			if (has_accel) { +				for (j = 0; j < THREE_AXIS; j++) { +					vals[j] = (short)be16_to_cpup( +					    (__be16 *)(&data[ind + 2 * j])); +					accel_result[j] += vals[j]; +				} +				ind += BYTES_PER_SENSOR; +				pr_debug( +				    "%s self_test accel data - %d %+d %+d %+d", +				    st->hw->name, s, vals[0], vals[1], vals[2]); +			} + +			for (j = 0; j < THREE_AXIS; j++) { +				vals[j] = (short)be16_to_cpup( +					(__be16 *)(&data[ind + 2 * j])); +				gyro_result[j] += vals[j]; +			} +			pr_debug("%s self_test gyro data - %d %+d %+d %+d", +				 st->hw->name, s, vals[0], vals[1], vals[2]); + +			s++; +			i++; +		} +	} + +	if (has_accel) { +		for (j = 0; j < THREE_AXIS; j++) { +			accel_result[j] = accel_result[j] / s; +			accel_result[j] *= DEF_ST_PRECISION; +		} +	} +	for (j = 0; j < THREE_AXIS; j++) { +		gyro_result[j] = gyro_result[j] / s; +		gyro_result[j] *= DEF_ST_PRECISION; +	} + +	return 0; +} + +/* + *  inv_recover_setting() recover the old settings after everything is done + */ +static void inv_recover_setting(struct inv_mpu_state *st) +{ +	struct inv_reg_map_s *reg; +	int data; + +	reg = &st->reg; +	inv_i2c_single_write(st, reg->gyro_config, +			     st->chip_config.fsr << GYRO_CONFIG_FSR_SHIFT); +	inv_i2c_single_write(st, reg->lpf, st->chip_config.lpf); +	data = ONE_K_HZ/st->chip_config.fifo_rate - 1; +	inv_i2c_single_write(st, reg->sample_rate_div, data); +	/* wait for the sampling rate change to stabilize */ +	mdelay(INV_MPU_SAMPLE_RATE_CHANGE_STABLE); +	if (INV_ITG3500 != st->chip_type) { +		inv_i2c_single_write(st, reg->accel_config, +				     (st->chip_config.accel_fs << +				     ACCEL_CONFIG_FSR_SHIFT)); +	} +	inv_reset_offset_reg(st, false); +	st->switch_gyro_engine(st, false); +	st->switch_accel_engine(st, false); +	st->set_power_state(st, false); +} + + +static int inv_power_up_self_test(struct inv_mpu_state *st) +{ +	int result; + +	result = st->set_power_state(st, true); +	if (result) +		return result; +	result = st->switch_accel_engine(st, true); +	if (result) +		return result; +	result = st->switch_gyro_engine(st, true); +	if (result) +		return result; + +	return 0; +} + +/* + *  inv_hw_self_test() - main function to do hardware self test + */ +int inv_hw_self_test(struct inv_mpu_state *st) +{ +	int result; +	int gyro_bias_st[THREE_AXIS], gyro_bias_regular[THREE_AXIS]; +	int accel_bias_st[THREE_AXIS], accel_bias_regular[THREE_AXIS]; +	int test_times, i; +	char compass_result, accel_result, gyro_result; + +	result = inv_power_up_self_test(st); +	if (result) +		return result; +	result = inv_reset_offset_reg(st, true); +	if (result) +		return result; +	compass_result = 0; +	accel_result = 0; +	gyro_result = 0; +	test_times = DEF_ST_TRY_TIMES; +	while (test_times > 0) { +		result = inv_do_test(st, 0, gyro_bias_regular, +			accel_bias_regular); +		if (result == -EAGAIN) +			test_times--; +		else +			test_times = 0; +	} +	if (result) +		goto test_fail; +	pr_debug("%s self_test accel bias_regular - %+d %+d %+d\n", +		 st->hw->name, accel_bias_regular[0], +		 accel_bias_regular[1], accel_bias_regular[2]); +	pr_debug("%s self_test gyro bias_regular - %+d %+d %+d\n", +		 st->hw->name, gyro_bias_regular[0], gyro_bias_regular[1], +		 gyro_bias_regular[2]); + +	for (i = 0; i < 3; i++) { +		st->gyro_bias[i] = gyro_bias_regular[i]; +		st->accel_bias[i] = accel_bias_regular[i]; +	} + +	test_times = DEF_ST_TRY_TIMES; +	while (test_times > 0) { +		result = inv_do_test(st, BITS_SELF_TEST_EN, gyro_bias_st, +					accel_bias_st); +		if (result == -EAGAIN) +			test_times--; +		else +			break; +	} +	if (result) +		goto test_fail; +	pr_debug("%s self_test accel bias_st - %+d %+d %+d\n", +		 st->hw->name, accel_bias_st[0], accel_bias_st[1], +		 accel_bias_st[2]); +	pr_debug("%s self_test gyro bias_st - %+d %+d %+d\n", +		 st->hw->name, gyro_bias_st[0], gyro_bias_st[1], +		 gyro_bias_st[2]); + +	if (st->chip_type == INV_ITG3500) { +		gyro_result = !inv_check_3500_gyro_self_test(st, +			gyro_bias_regular, gyro_bias_st); +	} else { +		if (st->chip_config.has_compass) +			compass_result = !st->slave_compass->self_test(st); + +		 if (INV_MPU6050 == st->chip_type) { +			accel_result = !inv_check_accel_self_test(st, +				accel_bias_regular, accel_bias_st); +			gyro_result = !inv_check_6050_gyro_self_test(st, +				gyro_bias_regular, gyro_bias_st); +		} else if (INV_MPU6500 == st->chip_type) { +			accel_result = !inv_check_6500_accel_self_test(st, +				accel_bias_regular, accel_bias_st); +			gyro_result = !inv_check_6500_gyro_self_test(st, +				gyro_bias_regular, gyro_bias_st); +		} +	} + +test_fail: +	inv_recover_setting(st); + +	return (compass_result << DEF_ST_COMPASS_RESULT_SHIFT) | +		(accel_result << DEF_ST_ACCEL_RESULT_SHIFT) | gyro_result; +} + +static int inv_load_firmware(struct inv_mpu_state *st, +	u8 *data, int size) +{ +	int bank, write_size; +	int result; +	u16 memaddr; + +	/* first bank start at MPU_DMP_LOAD_START */ +	write_size = MPU_MEM_BANK_SIZE - MPU_DMP_LOAD_START; +	memaddr = MPU_DMP_LOAD_START; +	result = mem_w(memaddr, write_size, data); +	if (result) +		return result; +	size -= write_size; +	data += write_size; + +	/* Write and verify memory */ +	for (bank = 1; size > 0; bank++, size -= write_size, +				data += write_size) { +		if (size > MPU_MEM_BANK_SIZE) +			write_size = MPU_MEM_BANK_SIZE; +		else +			write_size = size; + +		memaddr = ((bank << 8) | 0x00); + +		result = mem_w(memaddr, write_size, data); +		if (result) +			return result; +	} +	return 0; +} + +static int inv_verify_firmware(struct inv_mpu_state *st, +	u8 *data, int size) +{ +	int bank, write_size; +	int result; +	u16 memaddr; +	u8 firmware[MPU_MEM_BANK_SIZE]; + +	/* Write and verify memory */ +	write_size = MPU_MEM_BANK_SIZE - MPU_DMP_LOAD_START; +	size -= write_size; +	data += write_size; +	for (bank = 1; size > 0; bank++, +		size -= write_size, +		data += write_size) { +		if (size > MPU_MEM_BANK_SIZE) +			write_size = MPU_MEM_BANK_SIZE; +		else +			write_size = size; + +		memaddr = ((bank << 8) | 0x00); +		result = mpu_memory_read(st, +			st->i2c_addr, memaddr, write_size, firmware); +		if (result) +			return result; +		if (0 != memcmp(firmware, data, write_size)) +			return -EINVAL; +	} +	return 0; +} + +static int inv_set_step_buffer_time(struct inv_mpu_state *st) +{ +	/* Pedometer executes at 50Hz so 1.5 seconds is 20ms * 75 */ +	return inv_write_2bytes(st, KEY_D_PEDSTD_SB_TIME, 75); +} + +static int inv_set_step_threshold(struct inv_mpu_state *st) +{ +	return inv_write_2bytes(st, KEY_D_PEDSTD_SB, st->ped.step_thresh); +} + +int inv_enable_pedometer_interrupt(struct inv_mpu_state *st, bool en) +{ +	u8 reg[3]; + +	if (en) { +		reg[0] = 0xf4; +		reg[1] = 0x44; +		reg[2] = 0xf1; + +	} else { +		reg[0] = 0xf1; +		reg[1] = 0xf1; +		reg[2] = 0xf1; +	} + +	return mem_w_key(KEY_CFG_PED_INT, ARRAY_SIZE(reg), reg); +} + +int inv_read_pedometer_counter(struct inv_mpu_state *st) +{ +	int result; +	u8 d[4]; +	u32 last_step_counter, curr_counter; + +	result = mpu_memory_read(st, st->i2c_addr, +			inv_dmp_get_address(KEY_D_STPDET_TIMESTAMP), 4, d); +	if (result) +		return result; +	last_step_counter = (u32)be32_to_cpup((__be32 *)(d)); + +	result = mpu_memory_read(st, st->i2c_addr, +			inv_dmp_get_address(KEY_DMP_RUN_CNTR), 4, d); +	if (result) +		return result; +	curr_counter = (u32)be32_to_cpup((__be32 *)(d)); +	if (0 != last_step_counter) +		st->ped.last_step_time = get_time_ns() - +			((u64)(curr_counter - last_step_counter)) * +			DMP_INTERVAL_INIT; + +	return 0; +} + +int inv_enable_pedometer(struct inv_mpu_state *st, bool en) +{ +	u8 d[1]; + +	if (en) { +		inv_set_step_buffer_time(st); +		inv_set_step_threshold(st); +		d[0] = 0xf1; +	} else { +		d[0] = 0xff; +	} +	return mem_w_key(KEY_CFG_PED_ENABLE, ARRAY_SIZE(d), d); +} + +int inv_get_pedometer_steps(struct inv_mpu_state *st, u32 *steps) +{ +	u8 d[4]; +	int result; + +	result = mpu_memory_read(st, st->i2c_addr, +			inv_dmp_get_address(KEY_D_PEDSTD_STEPCTR), 4, d); +	*steps = (u32)be32_to_cpup((__be32 *)(d)); + +	return result; +} + +int inv_get_pedometer_time(struct inv_mpu_state *st, u32 *time) +{ +	u8 d[4]; +	int result; + +	result = mpu_memory_read(st, st->i2c_addr, +			inv_dmp_get_address(KEY_D_PEDSTD_TIMECTR), 4, d); +	*time = (u32)be32_to_cpup((__be32 *)(d)); + +	return result; +} + +int inv_set_display_orient_interrupt_dmp(struct inv_mpu_state *st, bool on) +{ +	int r; +	u8  rn[] = {0xf4, 0x41}; +	u8  rf[] = {0xd8, 0xd8}; + +	if (on) +		r = mem_w_key(KEY_CFG_DISPLAY_ORIENT_INT, ARRAY_SIZE(rn), rn); +	else +		r = mem_w_key(KEY_CFG_DISPLAY_ORIENT_INT, ARRAY_SIZE(rf), rf); + +	return r; +} + +static int inv_set_tap_interrupt_dmp(struct inv_mpu_state *st, u8 on) +{ +	int result; +	u16 d; + +	if (on) +		d = 192; +	else +		d = 128; + +	result = inv_write_2bytes(st, KEY_DMP_TAP_GATE, d); + +	return result; +} + +/* + * inv_set_tap_threshold_dmp(): + * Sets the tap threshold in the dmp + * Simultaneously sets secondary tap threshold to help correct the tap + * direction for soft taps. + */ +int inv_set_tap_threshold_dmp(struct inv_mpu_state *st, u16 threshold) +{ +	int result; +	int sampleDivider; +	int scaledThreshold; +	u32 dmpThreshold; +	u8 sample_div; +	const u32  accel_sens = (0x20000000 / 0x00010000); + +	if (threshold > (1 << 15)) +		return -EINVAL; +	sample_div = st->sample_divider; + +	sampleDivider = (1 + sample_div); +	/* Scale factor corresponds linearly using +	* 0  : 0 +	* 25 : 0.0250  g/ms +	* 50 : 0.0500  g/ms +	* 100: 1.0000  g/ms +	* 200: 2.0000  g/ms +	* 400: 4.0000  g/ms +	* 800: 8.0000  g/ms +	*/ +	/*multiply by 1000 to avoid floating point 1000/1000*/ +	scaledThreshold = threshold; +	/* Convert to per sample */ +	scaledThreshold *= sampleDivider; + +	/* Scale to DMP 16 bit value */ +	if (accel_sens != 0) +		dmpThreshold = (u32)(scaledThreshold * accel_sens); +	else +		return -EINVAL; +	dmpThreshold = dmpThreshold / DMP_PRECISION; +	result = inv_write_2bytes(st, KEY_DMP_TAP_THR_Z, dmpThreshold); +	if (result) +		return result; +	result = inv_write_2bytes(st, KEY_DMP_TAP_PREV_JERK_Z, +						dmpThreshold * 3 / 4); + +	return result; +} + + +/* + * inv_set_min_taps_dmp(): + * Indicates the minimum number of consecutive taps required + * before the DMP will generate an interrupt. + */ +int inv_set_min_taps_dmp(struct inv_mpu_state *st, u16 min_taps) +{ +	u8 result; + +	/* check if any spurious bit other the ones expected are set */ +	if ((min_taps > DMP_MAX_MIN_TAPS) || (min_taps < 1)) +		return -EINVAL; + +	/* DMP tap count is zero-based. So single-tap is 0. +	   Furthermore, DMP code checks for tap_count > min_taps. +	   So we have to do minus 2 here. +	   For example, if the user expects any single tap will generate an +	   interrupt, (s)he will call inv_set_min_taps_dmp(1). +	   When DMP gets a single tap, tap_count = 0. To get +	   tap_count > min_taps, we have to decrement min_taps by 2 to -1. */ +	result = inv_write_2bytes(st, KEY_DMP_TAP_MIN_TAPS, (u16)(min_taps-2)); + +	return result; +} + +/* + * inv_set_tap_time_dmp(): + * Determines how long after a tap the DMP requires before + * another tap can be registered. + */ +int  inv_set_tap_time_dmp(struct inv_mpu_state *st, u16 time) +{ +	int result; +	u16 dmpTime; +	u8 sampleDivider; + +	sampleDivider = st->sample_divider; +	sampleDivider++; + +	/* 60 ms minimum time added */ +	dmpTime = ((time) / sampleDivider); +	result = inv_write_2bytes(st, KEY_DMP_TAPW_MIN, dmpTime); + +	return result; +} + +/* + * inv_set_multiple_tap_time_dmp(): + * Determines how close together consecutive taps must occur + * to be considered double/triple taps. + */ +static int inv_set_multiple_tap_time_dmp(struct inv_mpu_state *st, u32 time) +{ +	int result; +	u16 dmpTime; +	u8 sampleDivider; + +	sampleDivider = st->sample_divider; +	sampleDivider++; + +	/* 60 ms minimum time added */ +	dmpTime = ((time) / sampleDivider); +	result = inv_write_2bytes(st, KEY_DMP_TAP_NEXT_TAP_THRES, dmpTime); + +	return result; +} + +int inv_q30_mult(int a, int b) +{ +	u64 temp; +	int result; + +	temp = (u64)a * b; +	result = (int)(temp >> DMP_MULTI_SHIFT); + +	return result; +} + +static u16 inv_row_2_scale(const s8 *row) +{ +	u16 b; + +	if (row[0] > 0) +		b = 0; +	else if (row[0] < 0) +		b = 4; +	else if (row[1] > 0) +		b = 1; +	else if (row[1] < 0) +		b = 5; +	else if (row[2] > 0) +		b = 2; +	else if (row[2] < 0) +		b = 6; +	else +		b = 7; + +	return b; +} + +/** Converts an orientation matrix made up of 0,+1,and -1 to a scalar +*	representation. +* @param[in] mtx Orientation matrix to convert to a scalar. +* @return Description of orientation matrix. The lowest 2 bits (0 and 1) +* represent the column the one is on for the +* first row, with the bit number 2 being the sign. The next 2 bits +* (3 and 4) represent +* the column the one is on for the second row with bit number 5 being +* the sign. +* The next 2 bits (6 and 7) represent the column the one is on for the +* third row with +* bit number 8 being the sign. In binary the identity matrix would therefor +* be: 010_001_000 or 0x88 in hex. +*/ +static u16 inv_orientation_matrix_to_scaler(const signed char *mtx) +{ + +	u16 scalar; +	scalar = inv_row_2_scale(mtx); +	scalar |= inv_row_2_scale(mtx + 3) << 3; +	scalar |= inv_row_2_scale(mtx + 6) << 6; + +	return scalar; +} + +static int inv_gyro_dmp_cal(struct inv_mpu_state *st) +{ +	int inv_gyro_orient; +	u8 regs[3]; +	int result; + +	u8 tmpD = DINA4C; +	u8 tmpE = DINACD; +	u8 tmpF = DINA6C; + +	inv_gyro_orient = +		inv_orientation_matrix_to_scaler(st->plat_data.orientation); + +	if ((inv_gyro_orient & 3) == 0) +		regs[0] = tmpD; +	else if ((inv_gyro_orient & 3) == 1) +		regs[0] = tmpE; +	else if ((inv_gyro_orient & 3) == 2) +		regs[0] = tmpF; +	if ((inv_gyro_orient & 0x18) == 0) +		regs[1] = tmpD; +	else if ((inv_gyro_orient & 0x18) == 0x8) +		regs[1] = tmpE; +	else if ((inv_gyro_orient & 0x18) == 0x10) +		regs[1] = tmpF; +	if ((inv_gyro_orient & 0xc0) == 0) +		regs[2] = tmpD; +	else if ((inv_gyro_orient & 0xc0) == 0x40) +		regs[2] = tmpE; +	else if ((inv_gyro_orient & 0xc0) == 0x80) +		regs[2] = tmpF; + +	result = mem_w_key(KEY_FCFG_1, ARRAY_SIZE(regs), regs); +	if (result) +		return result; + +	if (inv_gyro_orient & 4) +		regs[0] = DINA36 | 1; +	else +		regs[0] = DINA36; +	if (inv_gyro_orient & 0x20) +		regs[1] = DINA56 | 1; +	else +		regs[1] = DINA56; +	if (inv_gyro_orient & 0x100) +		regs[2] = DINA76 | 1; +	else +		regs[2] = DINA76; +	result = mem_w_key(KEY_FCFG_3, ARRAY_SIZE(regs), regs); + +	return result; +} + +static int inv_accel_dmp_cal(struct inv_mpu_state *st) +{ +	int inv_accel_orient; +	int result; +	u8 regs[3]; +	const u8 tmp[3] = { DINA0C, DINAC9, DINA2C }; +	inv_accel_orient = +		inv_orientation_matrix_to_scaler(st->plat_data.orientation); + +	regs[0] = tmp[inv_accel_orient & 3]; +	regs[1] = tmp[(inv_accel_orient >> 3) & 3]; +	regs[2] = tmp[(inv_accel_orient >> 6) & 3]; +	result = mem_w_key(KEY_FCFG_2, ARRAY_SIZE(regs), regs); +	if (result) +		return result; + +	regs[0] = DINA26; +	regs[1] = DINA46; +	regs[2] = DINA66; +	if (inv_accel_orient & 4) +		regs[0] |= 1; +	if (inv_accel_orient & 0x20) +		regs[1] |= 1; +	if (inv_accel_orient & 0x100) +		regs[2] |= 1; +	result = mem_w_key(KEY_FCFG_7, ARRAY_SIZE(regs), regs); + +	return result; +} + +int inv_set_accel_bias_dmp(struct inv_mpu_state *st) +{ +	int inv_accel_orient, result, i, accel_bias_body[3], out[3]; +	int tmp[] = {1, 1, 1}; +	int mask[] = {4, 0x20, 0x100}; +	int accel_sf = 0x20000000;/* 536870912 */ +	u8 *regs; + +	inv_accel_orient = +		inv_orientation_matrix_to_scaler(st->plat_data.orientation); + +	for (i = 0; i < 3; i++) +		if (inv_accel_orient & mask[i]) +			tmp[i] = -1; + +	for (i = 0; i < 3; i++) +		accel_bias_body[i] = +			st->input_accel_dmp_bias[(inv_accel_orient >> +			(i * 3)) & 3] * tmp[i]; +	for (i = 0; i < 3; i++) +		accel_bias_body[i] = inv_q30_mult(accel_sf, +					accel_bias_body[i]); +	for (i = 0; i < 3; i++) +		out[i] = cpu_to_be32p(&accel_bias_body[i]); +	regs = (u8 *)out; +	result = mem_w_key(KEY_D_ACCEL_BIAS, sizeof(out), regs); + +	return result; +} + +/* + * inv_set_gyro_sf_dmp(): + * The gyro threshold, in dps, above which taps will be rejected. + */ +static int inv_set_gyro_sf_dmp(struct inv_mpu_state *st) +{ +	int result; +	u8 sampleDivider; +	u32 gyro_sf; +	const u32 gyro_sens = 0x03e80000; + +	sampleDivider = st->sample_divider; +	gyro_sf = inv_q30_mult(gyro_sens, +			(int)(DMP_TAP_SCALE * (sampleDivider + 1))); +	result = write_be32_key_to_mem(st, gyro_sf, KEY_D_0_104); + +	return result; +} + +/* + * inv_set_shake_reject_thresh_dmp(): + * The gyro threshold, in dps, above which taps will be rejected. + */ +static int inv_set_shake_reject_thresh_dmp(struct inv_mpu_state *st, +						int thresh) +{ +	int result; +	u8 sampleDivider; +	int thresh_scaled; +	u32 gyro_sf; +	const u32 gyro_sens = 0x03e80000; + +	sampleDivider = st->sample_divider; +	gyro_sf = inv_q30_mult(gyro_sens, (int)(DMP_TAP_SCALE * +			(sampleDivider + 1))); +	/* We're in units of DPS, convert it back to chip units*/ +	/*split the operation to aviod overflow of integer*/ +	thresh_scaled = gyro_sens / (1L << 16); +	thresh_scaled = thresh_scaled / thresh; +	thresh_scaled = gyro_sf / thresh_scaled; +	result = write_be32_key_to_mem(st, thresh_scaled, +						KEY_DMP_TAP_SHAKE_REJECT); + +	return result; +} + +/* + * inv_set_shake_reject_time_dmp(): + * How long a gyro axis must remain above its threshold + * before taps are rejected. + */ +static int inv_set_shake_reject_time_dmp(struct inv_mpu_state *st, +						u32 time) +{ +	int result; +	u16 dmpTime; +	u8 sampleDivider; + +	sampleDivider = st->sample_divider; +	sampleDivider++; + +	/* 60 ms minimum time added */ +	dmpTime = ((time) / sampleDivider); +	result = inv_write_2bytes(st, KEY_DMP_TAP_SHAKE_COUNT_MAX, dmpTime); + +	return result; +} + +/* + * inv_set_shake_reject_timeout_dmp(): + * How long the gyros must remain below their threshold, + * after taps have been rejected, before taps can be detected again. + */ +static int inv_set_shake_reject_timeout_dmp(struct inv_mpu_state *st, +						u32 time) +{ +	int result; +	u16 dmpTime; +	u8 sampleDivider; + +	sampleDivider = st->sample_divider; +	sampleDivider++; + +	/* 60 ms minimum time added */ +	dmpTime = ((time) / sampleDivider); +	result = inv_write_2bytes(st, KEY_DMP_TAP_SHAKE_TIMEOUT_MAX, dmpTime); + +	return result; +} + +int inv_set_interrupt_on_gesture_event(struct inv_mpu_state *st, bool on) +{ +	u8 r; +	const u8 rn[] = {0xA3}; +	const u8 rf[] = {0xFE}; + +	if (on) +		r = mem_w_key(KEY_CFG_FIFO_INT, ARRAY_SIZE(rn), rn); +	else +		r = mem_w_key(KEY_CFG_FIFO_INT, ARRAY_SIZE(rf), rf); + +	return r; +} + +/* + * inv_enable_tap_dmp() -  calling this function will enable/disable tap + *                         function. + */ +int inv_enable_tap_dmp(struct inv_mpu_state *st, bool on) +{ +	int result; + +	result = inv_set_tap_interrupt_dmp(st, on); +	if (result) +		return result; +	result = inv_set_tap_threshold_dmp(st, st->tap.thresh); +	if (result) +		return result; + +	result = inv_set_min_taps_dmp(st, st->tap.min_count); +	if (result) +		return result; + +	result = inv_set_tap_time_dmp(st, st->tap.time); +	if (result) +		return result; + +	result = inv_set_multiple_tap_time_dmp(st, DMP_MULTI_TAP_TIME); +	if (result) +		return result; + +	result = inv_set_gyro_sf_dmp(st); +	if (result) +		return result; + +	result = inv_set_shake_reject_thresh_dmp(st, DMP_SHAKE_REJECT_THRESH); +	if (result) +		return result; + +	result = inv_set_shake_reject_time_dmp(st, DMP_SHAKE_REJECT_TIME); +	if (result) +		return result; + +	result = inv_set_shake_reject_timeout_dmp(st, +						  DMP_SHAKE_REJECT_TIMEOUT); +	return result; +} + +static int inv_dry_run_dmp(struct inv_mpu_state *st) +{ +	int result; +	struct inv_reg_map_s *reg; + +	reg = &st->reg; +	result = st->switch_gyro_engine(st, true); +	if (result) +		return result; +	result = inv_i2c_single_write(st, reg->user_ctrl, BIT_DMP_EN); +	if (result) +		return result; +	msleep(400); +	result = inv_i2c_single_write(st, reg->user_ctrl, 0); +	if (result) +		return result; +	result = st->switch_gyro_engine(st, false); +	if (result) +		return result; + +	return 0; +} + +static void inv_test_reset(struct inv_mpu_state *st) +{ +	int result, ii; +	u8 d[0x80]; + +	if (INV_MPU6500 != st->chip_type) +		return; + +	for (ii = 3; ii < 0x80; ii++) { +		/* don't read fifo r/w register */ +		if (ii != st->reg.fifo_r_w) +			inv_i2c_read(st, ii, 1, &d[ii]); +	} +	result = inv_i2c_single_write(st, st->reg.pwr_mgmt_1, BIT_H_RESET); +	if (result) +		return; +	msleep(POWER_UP_TIME); + +	for (ii = 3; ii < 0x80; ii++) { +		/* don't write certain registers */ +		if ((ii != st->reg.fifo_r_w) && +		    (ii != st->reg.mem_r_w) && +		    (ii != st->reg.mem_start_addr) && +		    (ii != st->reg.fifo_count_h) && +		    ii != (st->reg.fifo_count_h + 1)) +			result = inv_i2c_single_write(st, ii, d[ii]); +	} +} + +/* + * inv_dmp_firmware_write() -  calling this function will load the firmware. + *                        This is the write function of file "dmp_firmware". + */ +ssize_t inv_dmp_firmware_write(struct file *fp, struct kobject *kobj, +	struct bin_attribute *attr, +	char *buf, loff_t pos, size_t size) +{ +	u8 *firmware; +	int result; +	struct inv_reg_map_s *reg; +	struct iio_dev *indio_dev; +	struct inv_mpu_state *st; + +	indio_dev = dev_get_drvdata(container_of(kobj, struct device, kobj)); +	st = iio_priv(indio_dev); + +	if (st->chip_config.firmware_loaded) +		return -EINVAL; +	if (st->chip_config.enable) +		return -EBUSY; + +	reg = &st->reg; +	if (DMP_IMAGE_SIZE != size) { +		pr_err("wrong DMP image size - expected %d, actual %d\n", +			DMP_IMAGE_SIZE, size); +		return -EINVAL; +	} + +	firmware = kmalloc(size, GFP_KERNEL); +	if (!firmware) +		return -ENOMEM; + +	mutex_lock(&indio_dev->mlock); + +	memcpy(firmware, buf, size); +	result = crc32(CRC_FIRMWARE_SEED, firmware, size); +	if (DMP_IMAGE_CRC_VALUE != result) { +		pr_err("firmware CRC error - 0x%08x vs 0x%08x\n", +			result, DMP_IMAGE_CRC_VALUE); +		result = -EINVAL; +		goto firmware_write_fail; +	} + +	result = st->set_power_state(st, true); +	if (result) +		goto firmware_write_fail; +	inv_test_reset(st); + +	result = inv_load_firmware(st, firmware, size); +	if (result) +		goto firmware_write_fail; + +	result = inv_verify_firmware(st, firmware, size); +	if (result) +		goto firmware_write_fail; + +	result = inv_i2c_single_write(st, reg->prgm_strt_addrh, +	st->chip_config.prog_start_addr >> 8); +	if (result) +		goto firmware_write_fail; +	result = inv_i2c_single_write(st, reg->prgm_strt_addrh + 1, +	st->chip_config.prog_start_addr & 0xff); +	if (result) +		goto firmware_write_fail; + +	result = inv_gyro_dmp_cal(st); +	if (result) +		goto firmware_write_fail; +	result = inv_accel_dmp_cal(st); +	if (result) +		goto firmware_write_fail; +	result = inv_dry_run_dmp(st); +	if (result) +		goto firmware_write_fail; + +	st->chip_config.firmware_loaded = 1; + +firmware_write_fail: +	result |= st->set_power_state(st, false); +	mutex_unlock(&indio_dev->mlock); +	kfree(firmware); +	if (result) +		return result; + +	return size; +} + +ssize_t inv_dmp_firmware_read(struct file *filp, +				struct kobject *kobj, +				struct bin_attribute *bin_attr, +				char *buf, loff_t off, size_t count) +{ +	int bank, write_size, size, data, result; +	u16 memaddr; +	struct iio_dev *indio_dev; +	struct inv_mpu_state *st; + +	size = count; +	indio_dev = dev_get_drvdata(container_of(kobj, struct device, kobj)); +	st = iio_priv(indio_dev); + +	data = 0; +	mutex_lock(&indio_dev->mlock); +	if (!st->chip_config.enable) { +		result = st->set_power_state(st, true); +		if (result) { +			mutex_unlock(&indio_dev->mlock); +			return result; +		} +	} +	for (bank = 0; size > 0; bank++, size -= write_size, +					data += write_size) { +		if (size > MPU_MEM_BANK_SIZE) +			write_size = MPU_MEM_BANK_SIZE; +		else +			write_size = size; + +		memaddr = (bank << 8); +		result = mpu_memory_read(st, +			st->i2c_addr, memaddr, write_size, &buf[data]); +		if (result) { +			mutex_unlock(&indio_dev->mlock); +			return result; +		} +	} +	if (!st->chip_config.enable) +		result = st->set_power_state(st, false); +	mutex_unlock(&indio_dev->mlock); +	if (result) +		return result; + +	return count; +} + +ssize_t inv_six_q_write(struct file *fp, struct kobject *kobj, +	struct bin_attribute *attr, char *buf, loff_t pos, size_t size) +{ +	u8 q[QUATERNION_BYTES]; +	struct inv_reg_map_s *reg; +	struct iio_dev *indio_dev; +	struct inv_mpu_state *st; +	int result; + +	indio_dev = dev_get_drvdata(container_of(kobj, struct device, kobj)); +	st = iio_priv(indio_dev); + +	mutex_lock(&indio_dev->mlock); + +	if (!st->chip_config.firmware_loaded) { +		mutex_unlock(&indio_dev->mlock); +		return -EINVAL; +	} +	if (st->chip_config.enable) { +		mutex_unlock(&indio_dev->mlock); +		return -EBUSY; +	} +	reg = &st->reg; +	if (QUATERNION_BYTES != size) { +		pr_err("wrong quaternion size=%d, should=%d\n", size, +							QUATERNION_BYTES); +		mutex_unlock(&indio_dev->mlock); +		return -EINVAL; +	} + +	memcpy(q, buf, size); +	result = st->set_power_state(st, true); +	if (result) +		goto firmware_write_fail; +	result = mem_w_key(KEY_DMP_Q0, QUATERNION_BYTES, q); + +firmware_write_fail: +	result |= st->set_power_state(st, false); +	mutex_unlock(&indio_dev->mlock); +	if (result) +		return result; + +	return size; +} + diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_ring.c b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_ring.c new file mode 100644 index 00000000000..9c3d0912df4 --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_ring.c @@ -0,0 +1,1899 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> + +#include "iio.h" +#include "kfifo_buf.h" +#include "trigger_consumer.h" +#include "sysfs.h" + +#include "inv_mpu_iio.h" + +static u8 fifo_data[HARDWARE_FIFO_SIZE + HEADERED_Q_BYTES]; +static int inv_process_batchmode(struct inv_mpu_state *st); + +static int inv_push_marker_to_buffer(struct inv_mpu_state *st, u16 hdr) +{ +	struct iio_dev *indio_dev = iio_priv_to_dev(st); +	u8 buf[IIO_BUFFER_BYTES]; + +	memcpy(buf, &hdr, sizeof(hdr)); +#ifdef CONFIG_INV_KERNEL_3_10 +	iio_push_to_buffers(indio_dev, buf); +#else +	iio_push_to_buffer(indio_dev->buffer, buf, 0); +#endif +	return 0; +} + +static int inv_push_8bytes_buffer(struct inv_mpu_state *st, u16 hdr, +							u64 t, s16 *d) +{ +	struct iio_dev *indio_dev = iio_priv_to_dev(st); +	u8 buf[IIO_BUFFER_BYTES]; +	int i; + +	memcpy(buf, &hdr, sizeof(hdr)); +	for (i = 0; i < 3; i++) +		memcpy(&buf[2 + i * 2], &d[i], sizeof(d[i])); +#ifdef CONFIG_INV_KERNEL_3_10 +	iio_push_to_buffers(indio_dev, buf); +#else +	iio_push_to_buffer(indio_dev->buffer, buf, 0); +#endif +	memcpy(buf, &t, sizeof(t)); +#ifdef CONFIG_INV_KERNEL_3_10 +	iio_push_to_buffers(indio_dev, buf); +#else +	iio_push_to_buffer(indio_dev->buffer, buf, 0); +#endif +	return 0; +} + +static int inv_push_16bytes_buffer(struct inv_mpu_state *st, u16 hdr, u64 t, +									int *q) +{ +	struct iio_dev *indio_dev = iio_priv_to_dev(st); +	u8 buf[IIO_BUFFER_BYTES]; +	int i; + +	memcpy(buf, &hdr, sizeof(hdr)); +	memcpy(buf + 4, &q[0], sizeof(q[0])); +#ifdef CONFIG_INV_KERNEL_3_10 +	iio_push_to_buffers(indio_dev, buf); +#else +	iio_push_to_buffer(indio_dev->buffer, buf, 0); +#endif +	for (i = 0; i < 2; i++) +		memcpy(buf + 4 * i, &q[i + 1], sizeof(q[i])); +#ifdef CONFIG_INV_KERNEL_3_10 +	iio_push_to_buffers(indio_dev, buf); +#else +	iio_push_to_buffer(indio_dev->buffer, buf, 0); +#endif +	memcpy(buf, &t, sizeof(t)); +#ifdef CONFIG_INV_KERNEL_3_10 +	iio_push_to_buffers(indio_dev, buf); +#else +	iio_push_to_buffer(indio_dev->buffer, buf, 0); +#endif + +	return 0; +} + +static int inv_send_pressure_data(struct inv_mpu_state *st) +{ +	short sen[3]; +	struct inv_chip_config_s *conf; +	struct inv_mpu_slave *slave; +	u64 curr_ts; +	int result; + +	conf = &st->chip_config; +	slave = st->slave_pressure; +	if (!st->sensor[SENSOR_PRESSURE].on) +		return 0; +	if (conf->dmp_on && conf->dmp_event_int_on) +		return 0; +	if (!conf->normal_pressure_measure) { +		conf->normal_pressure_measure = 1; +		return 0; +	} +	curr_ts = get_time_ns(); +	if (curr_ts - slave->prev_ts > slave->min_read_time) { +		result = slave->read_data(st, sen); +		if (!result) +			inv_push_8bytes_buffer(st, PRESSURE_HDR, +						st->last_ts, sen); +		slave->prev_ts = curr_ts; +	} + +	return 0; +} + +static int inv_send_compass_data(struct inv_mpu_state *st) +{ +	short sen[3]; +	struct inv_chip_config_s *conf; +	struct inv_mpu_slave *slave; +	u64 curr_ts; +	int result; + +	conf = &st->chip_config; +	slave = st->slave_compass; +	if (!st->sensor[SENSOR_COMPASS].on) +		return 0; +	if (conf->dmp_on && conf->dmp_event_int_on) +		return 0; +	if (!conf->normal_compass_measure) { +		conf->normal_compass_measure = 1; +		return 0; +	} +	curr_ts = get_time_ns(); +	if (curr_ts - slave->prev_ts > slave->min_read_time) { +		result = slave->read_data(st, sen); +		if (result) +			inv_push_marker_to_buffer(st, COMPASS_HDR_2); +		else +			inv_push_8bytes_buffer(st, COMPASS_HDR, +						st->last_ts, sen); +		slave->prev_ts = curr_ts; +	} + +	return 0; +} + +static int inv_batchmode_calc(struct inv_mpu_state *st) +{ +	int b, timeout; +	int rate_dur; + +	rate_dur = MSEC_PER_SEC / st->batch.min_rate; +	if (st->batch.timeout < rate_dur) +		st->batch.timeout = rate_dur; +	b = st->batch.timeout * st->bytes_per_sec; +	if ((b > (FIFO_SIZE * ONE_K_HZ)) && (!st->batch.overflow_on)) +		timeout = FIFO_SIZE * ONE_K_HZ / st->bytes_per_sec; +	else +		timeout = st->batch.timeout; + +	st->batch.counter = timeout / 5; +	if (timeout) +		st->batch.on = true; + +	return 0; +} + +int inv_batchmode_setup(struct inv_mpu_state *st) +{ +	int r; + +	r = inv_write_2bytes(st, KEY_BM_NUMWORD_TOFILL, 0); +	if (r) +		return r; +	r = write_be32_key_to_mem(st, 0, KEY_BM_BATCH_CNTR); +	if (r) +		return r; + +	if (st->chip_config.dmp_on && (st->batch.timeout > 0) && +			(st->chip_config.dmp_event_int_on == 0)) { +		r = inv_batchmode_calc(st); +		if (r) +			return r; +	} + +	if (st->batch.on) { +		r = write_be32_key_to_mem(st, st->batch.counter, +						KEY_BM_BATCH_THLD); +		if (r) +			return r; +	} +	r = inv_write_2bytes(st, KEY_BM_ENABLE, st->batch.on); + +	return r; +} + +/** + *  reset_fifo_mpu3050() - Reset FIFO related registers + *  @indio_dev:	Device driver instance. + */ +static int reset_fifo_mpu3050(struct iio_dev *indio_dev) +{ +	struct inv_reg_map_s *reg; +	int result; +	u8 val, user_ctrl; +	struct inv_mpu_state  *st = iio_priv(indio_dev); +	reg = &st->reg; + +	/* disable interrupt */ +	result = inv_i2c_single_write(st, reg->int_enable, +				st->plat_data.int_config); +	if (result) +		return result; +	/* disable the sensor output to FIFO */ +	result = inv_i2c_single_write(st, reg->fifo_en, 0); +	if (result) +		goto reset_fifo_fail; +	result = inv_i2c_read(st, reg->user_ctrl, 1, &user_ctrl); +	if (result) +		goto reset_fifo_fail; +	/* disable fifo reading */ +	user_ctrl &= ~BIT_FIFO_EN; +	st->chip_config.has_footer = 0; +	/* reset fifo */ +	val = (BIT_3050_FIFO_RST | user_ctrl); +	result = inv_i2c_single_write(st, reg->user_ctrl, val); +	if (result) +		goto reset_fifo_fail; +	if (st->chip_config.dmp_on) { +		/* enable interrupt when DMP is done */ +		result = inv_i2c_single_write(st, reg->int_enable, +				st->plat_data.int_config | BIT_DMP_INT_EN); +		if (result) +			return result; + +		result = inv_i2c_single_write(st, reg->user_ctrl, +			BIT_FIFO_EN|user_ctrl); +		if (result) +			return result; +	} else { +		/* enable interrupt */ +		if (st->sensor[SENSOR_ACCEL].on || +		    st->sensor[SENSOR_GYRO].on) { +			result = inv_i2c_single_write(st, reg->int_enable, +				st->plat_data.int_config | BIT_DATA_RDY_EN); +			if (result) +				return result; +		} +		/* enable FIFO reading and I2C master interface*/ +		result = inv_i2c_single_write(st, reg->user_ctrl, +			BIT_FIFO_EN | user_ctrl); +		if (result) +			return result; +		/* enable sensor output to FIFO and FIFO footer*/ +		val = 1; +		if (st->sensor[SENSOR_ACCEL].on) +			val |= BITS_3050_ACCEL_OUT; +		if (st->sensor[SENSOR_GYRO].on) +			val |= BITS_GYRO_OUT; +		result = inv_i2c_single_write(st, reg->fifo_en, val); +		if (result) +			return result; +	} + +	return 0; +reset_fifo_fail: +	if (st->chip_config.dmp_on) +		val = BIT_DMP_INT_EN; +	else +		val = BIT_DATA_RDY_EN; +	inv_i2c_single_write(st, reg->int_enable, +			     st->plat_data.int_config | val); +	pr_err("reset fifo failed\n"); + +	return result; +} + +/* + *  inv_set_lpf() - set low pass filer based on fifo rate. + */ +static int inv_set_lpf(struct inv_mpu_state *st, int rate) +{ +	const short hz[] = {188, 98, 42, 20, 10, 5}; +	const int   d[] = {INV_FILTER_188HZ, INV_FILTER_98HZ, +			INV_FILTER_42HZ, INV_FILTER_20HZ, +			INV_FILTER_10HZ, INV_FILTER_5HZ}; +	int i, h, data, result; +	struct inv_reg_map_s *reg; + +	reg = &st->reg; +	h = (rate >> 1); +	i = 0; +	while ((h < hz[i]) && (i < ARRAY_SIZE(d) - 1)) +		i++; +	data = d[i]; +	if (INV_MPU3050 == st->chip_type) { +		if (st->slave_accel != NULL) { +			result = st->slave_accel->set_lpf(st, rate); +			if (result) +				return result; +		} +		result = inv_i2c_single_write(st, reg->lpf, data | +			(st->chip_config.fsr << GYRO_CONFIG_FSR_SHIFT)); +	} else { +		result = inv_i2c_single_write(st, reg->lpf, data); +	} +	if (result) +		return result; +	st->chip_config.lpf = data; + +	return 0; +} + +/* + *  set_fifo_rate_reg() - Set fifo rate in hardware register + */ +static int set_fifo_rate_reg(struct inv_mpu_state *st) +{ +	u8 data; +	u16 fifo_rate; +	int result; +	struct inv_reg_map_s *reg; + +	reg = &st->reg; +	fifo_rate = st->chip_config.fifo_rate; +	data = ONE_K_HZ / fifo_rate - 1; +	result = inv_i2c_single_write(st, reg->sample_rate_div, data); +	if (result) +		return result; +	result = inv_set_lpf(st, fifo_rate); + +	return result; +} + +/* + *  inv_lpa_mode() - store current low power mode settings + */ +static int inv_lpa_mode(struct inv_mpu_state *st, int lpa_mode) +{ +	unsigned long result; +	u8 d; +	struct inv_reg_map_s *reg; + +	reg = &st->reg; +	result = inv_i2c_read(st, reg->pwr_mgmt_1, 1, &d); +	if (result) +		return result; +	if (lpa_mode) +		d |= BIT_CYCLE; +	else +		d &= ~BIT_CYCLE; + +	result = inv_i2c_single_write(st, reg->pwr_mgmt_1, d); +	if (result) +		return result; +	if (INV_MPU6500 == st->chip_type) { +		d = BIT_FIFO_SIZE_1K; +		if (lpa_mode) +			d |= BIT_ACCEL_FCHOCIE_B; +		result = inv_i2c_single_write(st, REG_6500_ACCEL_CONFIG2, d); +		if (result) +			return result; +	} + +	return 0; +} + +static int inv_saturate_secondary_counter(struct inv_mpu_state *st) +{ +	int result; +	struct inv_reg_map_s *reg; + +#define COUNT_SATURATE_TIME_MS 32 +	reg = &st->reg; +	/* set sampling to 1KHz */ +	result = inv_i2c_single_write(st, reg->sample_rate_div, 0); +	if (result) +		return result; +	result = inv_i2c_single_write(st, REG_I2C_MST_DELAY_CTRL, +							BIT_SLV0_DLY_EN); +	if (result) +		return result; +	result = inv_i2c_single_write(st, REG_I2C_SLV4_CTRL, 0); +	if (result) +		return result; +	result = inv_i2c_single_write(st, reg->user_ctrl, BIT_I2C_MST_EN); +	if (result) +		return result; +	msleep(COUNT_SATURATE_TIME_MS); + +	return 0; +} +static int inv_set_master_delay(struct inv_mpu_state *st) +{ +	int d, result, rate; +	u8 delay; + +	if ((!st->sensor[SENSOR_COMPASS].on) && +		(!st->sensor[SENSOR_PRESSURE].on)) +		return 0; + +	delay = 0; +	d = 0; +	if (st->sensor[SENSOR_COMPASS].on) { +		switch (st->plat_data.sec_slave_id) { +		case COMPASS_ID_AK8975: +		case COMPASS_ID_AK8972: +		case COMPASS_ID_AK8963: +		case COMPASS_ID_AK09911: +			delay = (BIT_SLV0_DLY_EN | BIT_SLV1_DLY_EN); +			break; +		case COMPASS_ID_MLX90399: +			delay = (BIT_SLV0_DLY_EN | +				BIT_SLV1_DLY_EN | +				BIT_SLV2_DLY_EN | +				BIT_SLV3_DLY_EN); +			break; +		default: +			return -EINVAL; +		} +		d = max(d, st->slave_compass->rate_scale); +	} +	if (st->sensor[SENSOR_PRESSURE].on) { +		/* read fake data when compass is disabled for DMP read */ +		if ((!st->sensor[SENSOR_COMPASS].on) && st->chip_config.dmp_on) +			delay |= BIT_SLV0_DLY_EN; +		switch (st->plat_data.aux_slave_id) { +		case PRESSURE_ID_BMP280: +			delay |= (BIT_SLV2_DLY_EN | BIT_SLV3_DLY_EN); +			break; +		default: +			return -EINVAL; +		} +		d = max(d, st->slave_pressure->rate_scale); +	} + +	d = d * st->chip_config.fifo_rate / ONE_K_HZ; +	if (st->chip_config.dmp_on) { +		rate = 0; +		if (st->sensor[SENSOR_PRESSURE].on) +			rate = max(rate, st->sensor[SENSOR_PRESSURE].rate); +		if (st->sensor[SENSOR_COMPASS].on) +			rate = max(rate, st->sensor[SENSOR_COMPASS].rate); +		if (rate == 0) +			return -EINVAL; +		d = max(d, st->chip_config.fifo_rate / rate); +	} + +	if (d > 0) +		d -= 1; +	if (d > 0x1F) +		d = 0x1F; + +	/* I2C_MST_DLY is set to slow down secondary I2C */ +	if (0 == d) +		delay = 0; +	if (delay) { +		result = inv_saturate_secondary_counter(st); +		if (result) +			return result; +	} +	result = inv_i2c_single_write(st, REG_I2C_MST_DELAY_CTRL, delay); +	if (result) +		return result; + +	return inv_i2c_single_write(st, REG_I2C_SLV4_CTRL, d); +} + +/* + *  reset_fifo_itg() - Reset FIFO related registers. + */ +static int reset_fifo_itg(struct iio_dev *indio_dev) +{ +	struct inv_reg_map_s *reg; +	int result, i; +	u8 val, int_word; +	struct inv_mpu_state  *st = iio_priv(indio_dev); + +	reg = &st->reg; +	if (st->chip_config.lpa_mode) { +		result = inv_lpa_mode(st, 0); +		if (result) { +			pr_err("reset lpa mode failed\n"); +			return result; +		} +	} +	/* disable interrupt */ +	result = inv_i2c_single_write(st, reg->int_enable, 0); +	if (result) { +		pr_err("int_enable write failed\n"); +		return result; +	} +	/* disable the sensor output to FIFO */ +	result = inv_i2c_single_write(st, reg->fifo_en, 0); +	if (result) +		goto reset_fifo_fail; +	/* disable fifo reading */ +	result = inv_i2c_single_write(st, reg->user_ctrl, 0); +	if (result) +		goto reset_fifo_fail; +	int_word = 0; + +	/* MPU6500's BIT_6500_WOM_EN is the same as BIT_MOT_EN */ +	if (st->mot_int.mot_on) +		int_word |= BIT_MOT_EN; + +	if (st->chip_config.dmp_on) { +		val = (BIT_FIFO_RST | BIT_DMP_RST); +		result = inv_i2c_single_write(st, reg->user_ctrl, val); +		if (result) +			goto reset_fifo_fail; +		if (st->chip_config.dmp_int_on) { +			int_word |= BIT_DMP_INT_EN; +			result = inv_i2c_single_write(st, reg->int_enable, +							int_word); +			if (result) +				return result; +		} +		val = (BIT_DMP_EN | BIT_FIFO_EN); +		if ((st->sensor[SENSOR_COMPASS].on || +			st->sensor[SENSOR_PRESSURE].on) && +			(!st->chip_config.dmp_event_int_on)) +			val |= BIT_I2C_MST_EN; +		result = inv_i2c_single_write(st, reg->user_ctrl, val); +		if (result) +			goto reset_fifo_fail; +	} else { +		/* reset FIFO and possibly reset I2C*/ +		val = BIT_FIFO_RST; +		result = inv_i2c_single_write(st, reg->user_ctrl, val); +		if (result) +			goto reset_fifo_fail; +		/* enable interrupt */ +		if (st->sensor[SENSOR_ACCEL].on || +				    st->sensor[SENSOR_GYRO].on || +				    st->sensor[SENSOR_COMPASS].on || +				    st->sensor[SENSOR_PRESSURE].on) +			int_word |= BIT_DATA_RDY_EN; + +		result = inv_i2c_single_write(st, reg->int_enable, int_word); +		if (result) +			return result; +		/* enable FIFO reading and I2C master interface*/ +		val = BIT_FIFO_EN; +		if (st->sensor[SENSOR_COMPASS].on || +		    st->sensor[SENSOR_PRESSURE].on) +			val |= BIT_I2C_MST_EN; +		result = inv_i2c_single_write(st, reg->user_ctrl, val); +		if (result) +			goto reset_fifo_fail; +		/* enable sensor output to FIFO */ +		val = 0; +		if (st->sensor[SENSOR_GYRO].on) +			val |= BITS_GYRO_OUT; +		if (st->sensor[SENSOR_ACCEL].on) +			val |= BIT_ACCEL_OUT; +		result = inv_i2c_single_write(st, reg->fifo_en, val); +		if (result) +			goto reset_fifo_fail; +	} +	st->last_ts = get_time_ns(); +	st->prev_ts = st->last_ts; +	st->last_run_time = st->last_ts; +	if (st->sensor[SENSOR_COMPASS].on) +		st->slave_compass->prev_ts = st->last_ts; +	if (st->sensor[SENSOR_PRESSURE].on) +		st->slave_pressure->prev_ts = st->last_ts; + +	st->dmp_interval = DMP_INTERVAL_INIT; +	st->ts_counter = 0; +	st->diff_accumulater = 0; +	st->dmp_interval_accum = 0; +	st->step_detector_base_ts = st->last_ts; +	st->chip_config.normal_compass_measure = 0; +	st->chip_config.normal_pressure_measure = 0; +	st->left_over_size = 0; +	for (i = 0; i < SENSOR_NUM_MAX; i++) +		st->sensor[i].ts = st->last_ts; + +	result = inv_lpa_mode(st, st->chip_config.lpa_mode); +	if (result) +		goto reset_fifo_fail; + +	return 0; + +reset_fifo_fail: +	if (st->chip_config.dmp_on) +		val = BIT_DMP_INT_EN; +	else +		val = BIT_DATA_RDY_EN; +	inv_i2c_single_write(st, reg->int_enable, val); +	pr_err("reset fifo failed\n"); + +	return result; +} + +/** + *  inv_clear_kfifo() - clear time stamp fifo + *  @st:	Device driver instance. + */ +static void inv_clear_kfifo(struct inv_mpu_state *st) +{ +	unsigned long flags; + +	spin_lock_irqsave(&st->time_stamp_lock, flags); +	kfifo_reset(&st->timestamps); +	spin_unlock_irqrestore(&st->time_stamp_lock, flags); +} + +/* + *  inv_reset_fifo() - Reset FIFO related registers. + */ +int inv_reset_fifo(struct iio_dev *indio_dev) +{ +	struct inv_mpu_state *st = iio_priv(indio_dev); + +	inv_clear_kfifo(st); +	if (INV_MPU3050 == st->chip_type) +		return reset_fifo_mpu3050(indio_dev); +	else +		return reset_fifo_itg(indio_dev); +} + +static int inv_send_gyro_data(struct inv_mpu_state *st, bool on) +{ +	u8 rn[] = {0xa3, 0xa3}; +	u8 rf[] = {0xf4, 0x12}; +	int result; +	u8 *r; + +	if (on) +		r = rn; +	else +		r = rf; +	result = mem_w_key(KEY_CFG_OUT_GYRO, ARRAY_SIZE(rf), r); + +	return result; +} + +static int inv_send_accel_data(struct inv_mpu_state *st, bool on) +{ +	u8 rn[] = {0xa3, 0xa3}; +	u8 rf[] = {0xf4, 0x12}; +	int result; +	u8 *r; + +	if (on) +		r = rn; +	else +		r = rf; +	result = mem_w_key(KEY_CFG_OUT_ACCL, ARRAY_SIZE(rf), r); + +	return result; +} + +static int inv_send_three_q_data(struct inv_mpu_state *st, bool on) +{ +	u8 rn[] = {0xa3, 0xa3}; +	u8 rf[] = {0xf4, 0x13}; +	int result; +	u8 *r; + +	if (on) +		r = rn; +	else +		r = rf; +	result = mem_w_key(KEY_CFG_OUT_3QUAT, ARRAY_SIZE(rf), r); + +	return result; +} + +static int inv_send_six_q_data(struct inv_mpu_state *st, bool on) +{ +	u8 rn[] = {0xa3, 0xa3}; +	u8 rf[] = {0xf4, 0x13}; +	int result; +	u8 *r; + +	if (on) +		r = rn; +	else +		r = rf; +	result = mem_w_key(KEY_CFG_OUT_6QUAT, ARRAY_SIZE(rf), r); + +	return result; +} + +static int inv_send_ped_q_data(struct inv_mpu_state *st, bool on) +{ +	u8 rn[] = {0xa3, 0xa3}; +	u8 rf[] = {0xf4, 0x12}; +	u8 *r; +	int result; + +	if (on) +		r = rn; +	else +		r = rf; +	result = mem_w_key(KEY_CFG_OUT_PQUAT, ARRAY_SIZE(rn), r); + +	return result; +} + +static int inv_add_step_indicator(struct inv_mpu_state *st, bool on) +{ +	u8 rn[] = {0xf3, 0xf3}; +	u8 rf[] = {0xf4, 0x03}; +	int result; +	u8 *r; + +	if (on) +		r = rn; +	else +		r = rf; +	result = mem_w_key(KEY_CFG_PEDSTEP_DET, ARRAY_SIZE(rn), r); + +	return result; +} + +static int inv_send_compass_dmp_data(struct inv_mpu_state *st, bool on) +{ +	u8 rn[] = {0xa3, 0xa3}; +	u8 rf[] = {0xf4, 0x12}; +	u8 *r; +	int result; + +	if (on) +		r = rn; +	else +		r = rf; +	result = mem_w_key(KEY_CFG_OUT_CPASS, ARRAY_SIZE(rf), r); + +	return result; +} + +static int inv_send_pressure_dmp_data(struct inv_mpu_state *st, bool on) +{ +	u8 rn[] = {0xa3, 0xa3}; +	u8 rf[] = {0xf4, 0x12}; +	u8 *r; +	int result; + +	if (on) +		r = rn; +	else +		r = rf; +	result = mem_w_key(KEY_CFG_OUT_PRESS, ARRAY_SIZE(rf), r); + +	return result; +} + +static int inv_send_step_detector(struct inv_mpu_state *st, bool on) +{ +	u8 rn[] = {0xa3, 0xa3}; +	u8 rf[] = {0xf4, 0x0e}; +	u8 *r; +	int result; + +	if (on) +		r = rn; +	else +		r = rf; +	result = mem_w_key(KEY_CFG_OUT_STEPDET, ARRAY_SIZE(rf), r); + +	return result; +} + +static int inv_set_rate(struct inv_mpu_state *st, int k, int k_ct, int rate) +{ +	int v, result; + +	v = MPU_DEFAULT_DMP_FREQ / rate - 1; +	result = inv_write_2bytes(st, k, v); +	if (result) +		return result; +	result = inv_write_2bytes(st, k_ct, 0); + +	return result; +} + +static int inv_set_gyro_rate(struct inv_mpu_state *st) +{ +	int result; + +	result = inv_set_rate(st, KEY_CFG_GYRO_ODR, KEY_ODR_CNTR_GYRO, +					st->sensor[SENSOR_GYRO].rate); + +	return result; +} + +static int inv_set_accel_rate(struct inv_mpu_state *st) +{ +	int result; + +	result = inv_set_rate(st, KEY_CFG_ACCL_ODR, KEY_ODR_CNTR_ACCL, +					st->sensor[SENSOR_ACCEL].rate); + +	return result; +} + +static int inv_set_compass_rate(struct inv_mpu_state *st) +{ +	int result; + +	result = inv_set_rate(st, KEY_CFG_CPASS_ODR, KEY_ODR_CNTR_CPASS, +					st->sensor[SENSOR_COMPASS].rate); + +	return result; +} + +static int inv_set_pressure_rate(struct inv_mpu_state *st) +{ +	int result; + +	result = inv_set_rate(st, KEY_CFG_PRESS_ODR, KEY_ODR_CNTR_PRESS, +					st->sensor[SENSOR_PRESSURE].rate); + +	return result; +} + +static int inv_set_step_detector(struct inv_mpu_state *st) +{ +	return 0; +} + + +static int inv_set_lpq_rate(struct inv_mpu_state *st) +{ +	int result; + +	result = inv_set_rate(st, KEY_CFG_3QUAT_ODR, KEY_ODR_CNTR_3QUAT, +					st->sensor[SENSOR_LPQ].rate); + +	return result; +} + +static int inv_set_sixq_rate(struct inv_mpu_state *st) +{ +	int result; + +	result = inv_set_rate(st, KEY_CFG_6QUAT_ODR, KEY_ODR_CNTR_6QUAT, +					st->sensor[SENSOR_SIXQ].rate); + +	return result; +} + +static int inv_set_pedq_rate(struct inv_mpu_state *st) +{ +	int result; + +	result = inv_set_rate(st, KEY_CFG_PQUAT6_ODR, KEY_ODR_CNTR_PQUAT, +					st->sensor[SENSOR_PEDQ].rate); + +	return result; +} + + +static int inv_set_dmp_sysfs(struct inv_mpu_state *st) +{ +	int result, i, s; +	u8 d[] = {0, 0, 0, 0}; + +	result = inv_set_interrupt_on_gesture_event(st, +				st->chip_config.dmp_event_int_on); +	if (result) +		return result; + +	if (st->chip_config.dmp_event_int_on) { +		for (i = 0; i < SENSOR_NUM_MAX; i++) { +			result = st->sensor[i].send_data(st, false); +			if (result) +				return result; +		} +	} else { +		s = 0; +		st->batch.min_rate = MAX_DMP_OUTPUT_RATE; +		for (i = 0; i < SENSOR_NUM_MAX; i++) { +			result = st->sensor[i].send_data(st, st->sensor[i].on); +			if (result) +				return result; +			if (st->sensor[i].on) { +				if (0 == st->sensor[i].rate) +					return -EINVAL; +				if (st->sensor[i].rate < st->batch.min_rate) +					st->batch.min_rate = st->sensor[i].rate; +				s += st->sensor[i].rate * +						st->sensor[i].sample_size; + +				result = st->sensor[i].set_rate(st); +				if (result) +					return result; +				st->sensor[i].counter = MPU_DEFAULT_DMP_FREQ / +							st->sensor[i].rate; +			} +		} +		st->bytes_per_sec = s; +		if (st->sensor[SENSOR_STEP].on) +			result = inv_add_step_indicator(st, true); +		else +			result = inv_add_step_indicator(st, +					st->chip_config.step_indicator_on); +		if (result) +			return result; +	} +	result = inv_batchmode_setup(st); +	if (result) +		return result; + +	st->dmp_counter = 0; +	result = mem_w_key(KEY_DMP_RUN_CNTR, ARRAY_SIZE(d), d); +	if (result) +		return result; +	result = mem_w_key(KEY_D_STPDET_TIMESTAMP, ARRAY_SIZE(d), d); + +	return result; +} + +static void inv_get_data_count(struct inv_mpu_state *st) +{ +	struct inv_chip_config_s *c; +	int b, i; + +	c = &st->chip_config; +	b = 0; +	if (st->chip_config.dmp_on) { +		for (i = 0; i < SENSOR_NUM_MAX; i++) { +			if (st->sensor[i].on) +				b += st->sensor[i].sample_size; +		} +	} else { +		if (st->sensor[SENSOR_ACCEL].on) +			b += BYTES_PER_SENSOR; +		if (st->sensor[SENSOR_GYRO].on) +			b += BYTES_PER_SENSOR; +	} +	c->bytes_per_datum = b; + +	return; +} +/* + *  set_inv_enable() - main enable/disable function. + */ +int set_inv_enable(struct iio_dev *indio_dev, bool enable) +{ +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct inv_reg_map_s *reg; +	u8 data[2]; +	int result; + +	reg = &st->reg; +	if (enable) { +		if (st->chip_config.dmp_on && +				(!st->chip_config.firmware_loaded)) +			return -EINVAL; +		st->batch.on = false; + +		inv_get_data_count(st); +		result = inv_set_master_delay(st); +		if (result) +			return result; + +		result = set_fifo_rate_reg(st); +		if (result) +			return result; +		if (st->chip_config.dmp_on) { +			result = inv_set_dmp_sysfs(st); +			if (result) +				return result; +		} + +		if (st->chip_config.gyro_enable) { +			result = st->switch_gyro_engine(st, true); +			if (result) +				return result; +		} +		if (st->chip_config.accel_enable) { +			result = st->switch_accel_engine(st, true); +			if (result) +				return result; +		} +		if (st->sensor[SENSOR_COMPASS].on) { +			result = st->slave_compass->resume(st); +			if (result) +				return result; +		} +		if (st->sensor[SENSOR_PRESSURE].on) { +			result = st->slave_pressure->resume(st); +			if (result) +				return result; +		} +		result = inv_reset_fifo(indio_dev); +		if (result) +			return result; +	} else { +		if ((INV_MPU3050 != st->chip_type) +			&& st->chip_config.lpa_mode) { +			/* if the chip is in low power mode, +				register write/read could fail */ +			result = inv_lpa_mode(st, 0); +			if (result) +				return result; +		} +		result = inv_i2c_single_write(st, reg->fifo_en, 0); +		if (result) +			return result; +		if (st->chip_config.dmp_on) { +			result = inv_read_time_and_ticks(st, false); +			if (result) +				return result; +			result = inv_i2c_read(st, reg->fifo_count_h, +						FIFO_COUNT_BYTE, data); +			if (result) +				return result; +			st->fifo_count = be16_to_cpup((__be16 *)(data)); +			if (st->fifo_count) { +				result = inv_process_batchmode(st); +				if (result) +					return result; +			} +		} +		inv_push_marker_to_buffer(st, END_MARKER); +		/* disable fifo reading */ +		if (INV_MPU3050 != st->chip_type) { +			result = inv_i2c_single_write(st, reg->int_enable, 0); +			if (result) +				return result; +			result = inv_i2c_single_write(st, reg->user_ctrl, 0); +		} else { +			result = inv_i2c_single_write(st, reg->int_enable, +				st->plat_data.int_config); +		} +		if (result) +			return result; +		/* turn off the gyro/accel engine during disable phase */ +		result = st->switch_gyro_engine(st, false); +		if (result) +			return result; +		result = st->switch_accel_engine(st, false); +		if (result) +			return result; +		if (st->sensor[SENSOR_COMPASS].on) { +			result = st->slave_compass->suspend(st); +			if (result) +				return result; +		} +		if (st->sensor[SENSOR_PRESSURE].on) { +			result = st->slave_pressure->suspend(st); +			if (result) +				return result; +		} +	} +	st->chip_config.enable = enable; + +	return 0; +} + +/* + *  inv_irq_handler() - Cache a timestamp at each data ready interrupt. + */ +static irqreturn_t inv_irq_handler(int irq, void *dev_id) +{ +	struct inv_mpu_state *st = (struct inv_mpu_state *)dev_id; +	u64 ts; + +	if (!st->chip_config.dmp_on) { +		ts = get_time_ns(); +		kfifo_in_spinlocked(&st->timestamps, &ts, 1, +						&st->time_stamp_lock); +	} + +	return IRQ_WAKE_THREAD; +} + +static void inv_report_data_3050(struct iio_dev *indio_dev, s64 t, +			int has_footer, u8 *data) +{ +	struct inv_mpu_state *st = iio_priv(indio_dev); +	int ind, i; +	short s[THREE_AXIS]; + +	ind = 0; +	if (has_footer) +		ind += 2; + +	if (st->sensor[SENSOR_GYRO].on) { +		for (i = 0; i < 3; i++) +			s[i] = be16_to_cpup((__be16 *)(&data[ind + i * 2])); + +		inv_push_8bytes_buffer(st, GYRO_HDR, t, s); +		ind += BYTES_PER_SENSOR; +	} +	if (st->sensor[SENSOR_ACCEL].on) { +		st->slave_accel->combine_data(&data[ind], s); +		inv_push_8bytes_buffer(st, ACCEL_HDR, t, s); +	} +} + +/* + *  inv_read_fifo_mpu3050() - Transfer data from FIFO to ring buffer for + *                            mpu3050. + */ +irqreturn_t inv_read_fifo_mpu3050(int irq, void *dev_id) +{ + +	struct inv_mpu_state *st = (struct inv_mpu_state *)dev_id; +	struct iio_dev *indio_dev = iio_priv_to_dev(st); +	int bytes_per_datum; +	u8 data[64]; +	int result; +	short fifo_count, byte_read; +	s64 timestamp; +	struct inv_reg_map_s *reg; + +	reg = &st->reg; +	mutex_lock(&st->suspend_resume_lock); +	mutex_lock(&indio_dev->mlock); +	if (st->chip_config.dmp_on) +		bytes_per_datum = HEADERED_NORMAL_BYTES; +	else +		bytes_per_datum = (st->sensor[SENSOR_ACCEL].on + +			st->sensor[SENSOR_GYRO].on) * BYTES_PER_SENSOR; +	if (st->chip_config.has_footer) +		byte_read = bytes_per_datum + MPU3050_FOOTER_SIZE; +	else +		byte_read = bytes_per_datum; + +	fifo_count = 0; +	if (byte_read != 0) { +		result = inv_i2c_read(st, reg->fifo_count_h, +				FIFO_COUNT_BYTE, data); +		if (result) +			goto end_session; +		fifo_count = be16_to_cpup((__be16 *)(&data[0])); +		if (fifo_count < byte_read) +			goto end_session; +		if (fifo_count & 1) +			goto flush_fifo; +		if (fifo_count > FIFO_THRESHOLD) +			goto flush_fifo; +		/* Timestamp mismatch. */ +		if (kfifo_len(&st->timestamps) < +			fifo_count / byte_read) +			goto flush_fifo; +		if (kfifo_len(&st->timestamps) > +			fifo_count / byte_read + TIME_STAMP_TOR) { +			if (st->chip_config.dmp_on) { +				result = kfifo_out(&st->timestamps, +				×tamp, 1); +				if (result != 1) +					goto flush_fifo; +			} else { +				goto flush_fifo; +			} +		} +	} +	while ((bytes_per_datum != 0) && (fifo_count >= byte_read)) { +		result = inv_i2c_read(st, reg->fifo_r_w, byte_read, data); +		if (result) +			goto flush_fifo; + +		result = kfifo_out(&st->timestamps, ×tamp, 1); +		if (result != 1) +			goto flush_fifo; +		inv_report_data_3050(indio_dev, timestamp, +				     st->chip_config.has_footer, data); +		fifo_count -= byte_read; +		if (st->chip_config.has_footer == 0) { +			st->chip_config.has_footer = 1; +			byte_read = bytes_per_datum + MPU3050_FOOTER_SIZE; +		} +	} + +end_session: +	mutex_unlock(&indio_dev->mlock); +	mutex_unlock(&st->suspend_resume_lock); + +	return IRQ_HANDLED; + +flush_fifo: +	/* Flush HW and SW FIFOs. */ +	inv_reset_fifo(indio_dev); +	inv_clear_kfifo(st); +	mutex_unlock(&indio_dev->mlock); +	mutex_unlock(&st->suspend_resume_lock); + +	return IRQ_HANDLED; +} + +static int inv_report_gyro_accel(struct iio_dev *indio_dev, +					u8 *data, s64 t) +{ +	struct inv_mpu_state *st = iio_priv(indio_dev); +	short s[THREE_AXIS]; +	int ind; +	int i; + +	ind = 0; +	if (st->sensor[SENSOR_ACCEL].on) { +		for (i = 0; i < 3; i++) +			s[i] = be16_to_cpup((__be16 *)(&data[ind + i * 2])); +		inv_push_8bytes_buffer(st, ACCEL_HDR, t, s); +		ind += BYTES_PER_SENSOR; +	} + +	if (st->sensor[SENSOR_GYRO].on) { +		for (i = 0; i < 3; i++) +			s[i] = be16_to_cpup((__be16 *)(&data[ind + i * 2])); +		inv_push_8bytes_buffer(st, GYRO_HDR, t, s); +		ind += BYTES_PER_SENSOR; +	} + +	return 0; +} + +static void inv_process_motion(struct inv_mpu_state *st) +{ +	struct iio_dev *indio_dev = iio_priv_to_dev(st); +	int result; +	u8 data[1]; + +	/* motion interrupt */ +	result = inv_i2c_read(st, REG_INT_STATUS, 1, data); +	if (result) +		return; + +	if (data[0] & BIT_MOT_INT) +		sysfs_notify(&indio_dev->dev.kobj, NULL, "event_accel_motion"); +} + +static int inv_get_timestamp(struct inv_mpu_state *st, int count) +{ +	u32 *dur; +	u32 thresh; +	s32 diff, result, counter; +	u64 ts; + +	/* goal of algorithm is to estimate the true frequency of the chip */ +	if (st->chip_config.dmp_on && st->chip_config.dmp_event_int_on) +		return 0; +	dur = &st->irq_dur_ns; +	counter = 1; +	thresh = min((u32)((*dur) >> 2), (u32)(10 * NSEC_PER_MSEC)); +	while (kfifo_len(&st->timestamps) >= count) { +		result = kfifo_out(&st->timestamps, &ts, 1); +		if (result != 1) +			return -EINVAL; +		/* first time since reset fifo, just take it */ +		if (!st->ts_counter) { +			st->last_ts = ts; +			st->prev_ts = ts; +			st->ts_counter++; +			return 0; +		} +		diff = (s32)(ts - st->prev_ts); +		st->prev_ts = ts; +		if (abs(diff - (*dur)) < thresh) { +			st->diff_accumulater >>= 1; +			if (*dur > diff) +				st->diff_accumulater -= (((*dur) - diff) >> 7); +			else +				st->diff_accumulater += ((diff - (*dur)) >> 7); +			*dur += st->diff_accumulater; +		} +	} +	ts = *dur; +	ts *= counter; +	st->last_ts += ts; + +	return 0; +} + +static int inv_process_dmp_interrupt(struct inv_mpu_state *st) +{ +	int r; +	u8 d[1]; +	struct iio_dev *indio_dev = iio_priv_to_dev(st); + +#define DMP_INT_SMD             0x04 +#define DMP_INT_PED             0x08 + +	if ((!st->chip_config.smd_enable) && +			(!st->ped.int_on) && +			(!st->tap.on)) +		return 0; + +	r = inv_i2c_read(st, REG_DMP_INT_STATUS, 1, d); +	if (r) +		return r; +	if (d[0] & DMP_INT_SMD) { +		sysfs_notify(&indio_dev->dev.kobj, NULL, "event_smd"); +		st->chip_config.smd_enable = false; +		st->chip_config.smd_triggered = true; +	} +	if (d[0] & DMP_INT_PED) +		sysfs_notify(&indio_dev->dev.kobj, NULL, "event_pedometer"); + +	return 0; +} + +static int inv_get_shift2(int count) +{ +	int i; + +	if (1 == count) +		return 13; +	if (count > 2000) +		return 2; +	i = 13; +	while (count > 0) { +		count >>= 1; +		i--; +	} + +	return i; +} + +static void inv_adjust_sensor_ts(struct inv_mpu_state *st, int sensor_ind) +{ +	s64 diff; +	int i, rate_adj, s3, delta, total_count; + +	if (!st->chip_config.adjust_time) +		return; +#define MAX_DIFF 0x7fffffff +	total_count = st->dmp_ticks; +	if (0 == total_count) +		total_count = 1; + +	diff = (st->last_ts - st->prev_ts) - (u64)(st->dmp_interval) * +								total_count; +	if (diff > MAX_DIFF) +		diff = MAX_DIFF; +	if (diff < -MAX_DIFF) +		diff = -MAX_DIFF; +	s3 = 4; +	rate_adj = (int)diff; +	rate_adj /= total_count; +	delta = min(abs(rate_adj) >> inv_get_shift2(total_count), +						DMP_INTERVAL_MIN_ADJ); +	if (rate_adj < 0) +		delta = -delta; +	st->dmp_interval_accum >>= 1; +	st->dmp_interval_accum += delta; +	st->dmp_interval += st->dmp_interval_accum; +	for (i = 0; i < SENSOR_NUM_MAX; i++) +		if (st->sensor[i].on) +			st->sensor[i].dur = st->dmp_interval * +						st->sensor[i].counter; + +	st->prev_ts = st->last_ts; +} + +static void inv_reset_ts(struct inv_mpu_state *st, u64 curr_ts) +{ +	u32 dur, i; + +	dur = USEC_PER_SEC / st->bytes_per_sec; +	dur *= 1024; +	curr_ts -= ((u64)dur * NSEC_PER_USEC); +	for (i = 0; i < SENSOR_NUM_MAX; i++) +		st->sensor[i].ts = curr_ts; +} + +static void inv_push_step_indicator(struct inv_mpu_state *st, int sensor_ind, +							int steps) +{ +	int dur, i; +	s16 sen[3]; +	u64 base; +#define STEP_INDICATOR_HEADER 0x0001 + +	dur = st->sensor[sensor_ind].dur / steps; +	base = st->sensor[sensor_ind].ts; + +	for (i = 1; i < steps; i++) +		inv_push_8bytes_buffer(st, STEP_INDICATOR_HEADER, +						base + i * dur, sen); +} + +static int inv_parse_header(u16 hdr) +{ +	switch (hdr) { +	case ACCEL_HDR: +		return SENSOR_ACCEL; +	case GYRO_HDR: +		return SENSOR_GYRO; +	case PEDQUAT_HDR: +		return SENSOR_PEDQ; +	case LPQUAT_HDR: +		return SENSOR_LPQ; +	case SIXQUAT_HDR: +		return SENSOR_SIXQ; +	case COMPASS_HDR: +	case COMPASS_HDR_2: +		return SENSOR_COMPASS; +	case PRESSURE_HDR: +		return SENSOR_PRESSURE; +	case STEP_DETECTOR_HDR: +		return SENSOR_STEP; +	default: +		return SENSOR_INVALID; +	} +} +#define FEATURE_IKR_PANIC 1 + +static int inv_process_batchmode(struct inv_mpu_state *st) +{ +	int i, target_bytes, tmp, res, counter; +	int sensor_ind, q[3]; +	u8 *dptr, *d; +	u16 hdr, steps; +	s16 sen[3]; +	u64 t; +	bool done_flag; + +#if FEATURE_IKR_PANIC +	if (1024 <= st->fifo_count) { +		if (1024 < st->fifo_count) { +			pr_err("fifo_count over spec\n"); +			return 0; +		} +		inv_reset_ts(st, st->last_ts); +		st->left_over_size = 0; +	} +#else +	if (1024 == st->fifo_count) { +		inv_reset_ts(st, st->last_ts); +		st->left_over_size = 0; +	} +#endif + +	d = fifo_data; +	if (st->left_over_size > 0) { +#if FEATURE_IKR_PANIC +		if (st->left_over_size > HEADERED_Q_BYTES) { +			pr_err("left_over_size overflow 1\n"); +			st->left_over_size = HEADERED_Q_BYTES; +		} +#endif +		dptr = d + st->left_over_size; +		memcpy(d, st->left_over, st->left_over_size); +	} else { +		dptr = d; +	} +	target_bytes = st->fifo_count; +	while (target_bytes > 0) { +		if (target_bytes < MAX_READ_SIZE) +			tmp = target_bytes; +		else +			tmp = MAX_READ_SIZE; +		res = inv_i2c_read(st, st->reg.fifo_r_w, tmp, dptr); +		if (res < 0) +			return res; +		dptr += tmp; +		target_bytes -= tmp; +	} +	dptr = d; +	done_flag = false; +	target_bytes = st->fifo_count + st->left_over_size; +	counter = 0; +	while ((dptr - d <= target_bytes - HEADERED_NORMAL_BYTES) && +							(!done_flag)) { +		hdr = (u16)be16_to_cpup((__be16 *)(dptr)); +		steps = (hdr & STEP_INDICATOR_MASK); +		hdr &= (~STEP_INDICATOR_MASK); +		sensor_ind = inv_parse_header(hdr); +		/* error packet */ +		if ((sensor_ind == SENSOR_INVALID) || +				(!st->sensor[sensor_ind].on)) { +			dptr += HEADERED_NORMAL_BYTES; +			continue; +		} +		/* incomplete packet */ +		if (target_bytes - (dptr - d) < +					st->sensor[sensor_ind].sample_size) { +			done_flag = true; +			continue; +		} +		if (sensor_ind == SENSOR_STEP) { +			tmp = (int)be32_to_cpup((__be32 *)(dptr + 4)); +			t = st->step_detector_base_ts + +					(u64)tmp * 5 * NSEC_PER_MSEC; +			inv_push_8bytes_buffer(st, hdr, t, sen); +			dptr += HEADERED_NORMAL_BYTES; +			continue; +		} +		if (steps > 1) +			inv_push_step_indicator(st, sensor_ind, steps); +		st->sensor[sensor_ind].ts += (u64)st->sensor[sensor_ind].dur; +		t = st->sensor[sensor_ind].ts; +		if (sensor_ind == SENSOR_COMPASS) { +			if (!st->chip_config.normal_compass_measure) { +				st->chip_config.normal_compass_measure = 1; +			} else if (COMPASS_HDR == hdr) { +				for (i = 0; i < 6; i++) +					st->fifo_data[i] = dptr[i + 2]; +				res = st->slave_compass->read_data(st, sen); +				if (!res) +					inv_push_8bytes_buffer(st, hdr | +							(!!steps), t, sen); +			} else if (COMPASS_HDR_2 == hdr) { +				inv_push_marker_to_buffer(st, hdr); +			} +			dptr += HEADERED_NORMAL_BYTES; +			continue; +		} +		if (sensor_ind == SENSOR_PRESSURE) { +			if (!st->chip_config.normal_pressure_measure) { +				st->chip_config.normal_pressure_measure = 1; +			} else { +				for (i = 0; i < 6; i++) +					st->fifo_data[i] = dptr[i + 2]; +				res = st->slave_pressure->read_data(st, sen); +				if (!res) +					inv_push_8bytes_buffer(st, hdr | +							(!!steps), t, sen); +			} +			dptr += HEADERED_NORMAL_BYTES; +			continue; +		} +		if (st->sensor[sensor_ind].sample_size == HEADERED_Q_BYTES) { +			for (i = 0; i < 3; i++) +				q[i] = (int)be32_to_cpup((__be32 *)(dptr + 4 +							+ i * 4)); +			inv_push_16bytes_buffer(st, hdr | (!!steps), t, q); +		} else { +			for (i = 0; i < 3; i++) +				sen[i] = (short)be16_to_cpup((__be16 *)(dptr + +							2 + i * 2)); +			inv_push_8bytes_buffer(st, hdr | (!!steps), t, sen); +		} +		dptr += st->sensor[sensor_ind].sample_size; +	} +	inv_adjust_sensor_ts(st, sensor_ind); +	st->left_over_size = target_bytes - (dptr - d); + +	if (st->left_over_size) { +#if FEATURE_IKR_PANIC +		if (st->left_over_size > HEADERED_Q_BYTES) { +			pr_err("left_over_size overflow 2\n"); +			st->left_over_size = HEADERED_Q_BYTES; +		} +#endif +		memcpy(st->left_over, dptr, st->left_over_size); +	} + +	return 0; +} + +int inv_read_time_and_ticks(struct inv_mpu_state *st, bool resume) +{ +	int result; +	u32 counter; +	u8 data[4]; + +#define MIN_TICK_READING_TIME NSEC_PER_SEC +	st->last_ts = get_time_ns(); +	if ((st->last_ts - st->prev_ts < MIN_TICK_READING_TIME) && +							(!resume)) { +		st->chip_config.adjust_time = false; +		return 0; +	} +	result = mpu_memory_read(st, st->i2c_addr, +			inv_dmp_get_address(KEY_DMP_RUN_CNTR), 4, data); +	if (result) +		return result; + +	counter = be32_to_cpup((__be32 *)(data)); +	if (resume) { +		st->dmp_counter = counter; +		st->prev_ts = st->last_ts; + +		return 0; +	} +	if (counter > st->dmp_counter) +		st->dmp_ticks = counter - st->dmp_counter; +	else +		st->dmp_ticks = 0xffffffff - st->dmp_counter + counter + 1; +	st->dmp_counter = counter; +	st->chip_config.adjust_time = true; + +	return 0; +} + +/* + *  inv_read_fifo() - Transfer data from FIFO to ring buffer. + */ +irqreturn_t inv_read_fifo(int irq, void *dev_id) +{ + +	struct inv_mpu_state *st = (struct inv_mpu_state *)dev_id; +	struct iio_dev *indio_dev = iio_priv_to_dev(st); +	int result, bpm; +	u8 data[MAX_HW_FIFO_BYTES]; +	u16 fifo_count; +	struct inv_reg_map_s *reg; +	u64 pts1; + +#define DMP_MIN_RUN_TIME (37 * NSEC_PER_MSEC) +	if (st->suspend_state) +		return IRQ_HANDLED; +	mutex_lock(&indio_dev->mlock); +	if (st->chip_config.dmp_on) { +		pts1 = get_time_ns(); +		result = inv_process_dmp_interrupt(st); +		if (result || st->chip_config.dmp_event_int_on) +			goto end_session; +		if (!st->chip_config.smd_triggered) { +			if (pts1 - st->last_run_time < DMP_MIN_RUN_TIME) +				goto end_session; +			else +				st->last_run_time = pts1; +		} else { +			st->chip_config.smd_triggered = false; +		} +	} + +	if (!(iio_buffer_enabled(indio_dev)) || (!st->chip_config.enable)) +		goto end_session; + +	reg = &st->reg; +	if (!(st->sensor[SENSOR_ACCEL].on | +		st->sensor[SENSOR_GYRO].on | +		st->sensor[SENSOR_COMPASS].on | +		st->sensor[SENSOR_PRESSURE].on | +		st->chip_config.dmp_on | +		st->mot_int.mot_on)) +		goto end_session; +	if (st->chip_config.lpa_mode) { +		result = inv_i2c_read(st, reg->raw_accel, +						BYTES_PER_SENSOR, data); +		if (result) +			goto end_session; +		inv_report_gyro_accel(indio_dev, data, get_time_ns()); +		if (st->mot_int.mot_on) +			inv_process_motion(st); + +		goto end_session; +	} + +	if (st->chip_config.dmp_on) { +		result = inv_read_time_and_ticks(st, false); +		if (result) +			goto end_session; +	} +	bpm = st->chip_config.bytes_per_datum; +	fifo_count = 0; +	if (bpm) { +		result = inv_i2c_read(st, reg->fifo_count_h, FIFO_COUNT_BYTE, +									data); +		if (result) +			goto end_session; +		fifo_count = be16_to_cpup((__be16 *)(data)); +		/* fifo count can't be odd number */ +		if (fifo_count & 1) +			goto flush_fifo; +		if (fifo_count == 0) +			goto end_session; +		st->fifo_count = fifo_count; +	} + +	if (st->chip_config.dmp_on) { +		result = inv_process_batchmode(st); +	} else { +		if (fifo_count >  FIFO_THRESHOLD) +			goto flush_fifo; +		if (bpm) { +			while (fifo_count >= bpm) { +				result = inv_i2c_read(st, reg->fifo_r_w, bpm, +									data); +				if (result) +					goto flush_fifo; +				result = inv_get_timestamp(st, +							fifo_count / bpm); +				if (result) +					goto flush_fifo; +				inv_report_gyro_accel(indio_dev, data, +								st->last_ts); +				fifo_count -= bpm; +			} +		} else { +			result = inv_get_timestamp(st, 1); +			if (result) +				goto flush_fifo; +		} +		inv_send_compass_data(st); +		inv_send_pressure_data(st); +	} +end_session: +	mutex_unlock(&indio_dev->mlock); + +	return IRQ_HANDLED; +flush_fifo: +	/* Flush HW and SW FIFOs. */ +	inv_reset_fifo(indio_dev); +	inv_clear_kfifo(st); +	mutex_unlock(&indio_dev->mlock); + +	return IRQ_HANDLED; +} + +void inv_mpu_unconfigure_ring(struct iio_dev *indio_dev) +{ +	struct inv_mpu_state *st = iio_priv(indio_dev); +	free_irq(st->client->irq, st); +	iio_kfifo_free(indio_dev->buffer); +}; + +static int inv_predisable(struct iio_dev *indio_dev) +{ +	struct inv_mpu_state  *st = iio_priv(indio_dev); +	int result; + +	if (st->chip_config.enable) { +		result = set_inv_enable(indio_dev, false); +		if (result) +			return result; +		result = st->set_power_state(st, false); +		if (result) +			return result; +	} + +	return 0; +} + +static int inv_check_conflict_sysfs(struct iio_dev *indio_dev) +{ +	struct inv_mpu_state  *st = iio_priv(indio_dev); + +	if (st->chip_config.lpa_mode) { +		/* dmp cannot run with low power mode on */ +		st->chip_config.dmp_on = 0; +		st->chip_config.gyro_enable = false; +		st->sensor[SENSOR_GYRO].on = false; +		st->sensor[SENSOR_COMPASS].on = false; +	} +	if (st->sensor[SENSOR_GYRO].on && +		(!st->chip_config.gyro_enable)) { +		st->chip_config.gyro_enable = true; +	} +	if (st->sensor[SENSOR_ACCEL].on && +		(!st->chip_config.accel_enable)) { +		st->chip_config.accel_enable = true; +	} + +	return 0; +} + +static int inv_preenable(struct iio_dev *indio_dev) +{ +	int result; + +	result = inv_check_conflict_sysfs(indio_dev); +	if (result) +		return result; +	result = iio_sw_buffer_preenable(indio_dev); + +	return result; +} + +void inv_init_sensor_struct(struct inv_mpu_state *st) +{ +	int i; + +	for (i = 0; i < SENSOR_NUM_MAX; i++) { +		if (i < SENSOR_SIXQ) +			st->sensor[i].sample_size = +					HEADERED_NORMAL_BYTES; +		else +			st->sensor[i].sample_size = HEADERED_Q_BYTES; +		if (i == SENSOR_STEP) { +			st->sensor[i].rate = 1; +			st->sensor[i].dur = NSEC_PER_SEC; +		} else { +			st->sensor[i].rate = INIT_DMP_OUTPUT_RATE; +			st->sensor[i].dur  = NSEC_PER_SEC / +						INIT_DMP_OUTPUT_RATE; +		} +	} + +	st->sensor[SENSOR_ACCEL].send_data     = inv_send_accel_data; +	st->sensor[SENSOR_GYRO].send_data      = inv_send_gyro_data; +	st->sensor[SENSOR_COMPASS].send_data   = inv_send_compass_dmp_data; +	st->sensor[SENSOR_PRESSURE].send_data  = inv_send_pressure_dmp_data; +	st->sensor[SENSOR_STEP].send_data      = inv_send_step_detector; +	st->sensor[SENSOR_PEDQ].send_data      = inv_send_ped_q_data; +	st->sensor[SENSOR_SIXQ].send_data      = inv_send_six_q_data; +	st->sensor[SENSOR_LPQ].send_data       = inv_send_three_q_data; + +	st->sensor[SENSOR_ACCEL].set_rate     = inv_set_accel_rate; +	st->sensor[SENSOR_GYRO].set_rate      = inv_set_gyro_rate; +	st->sensor[SENSOR_COMPASS].set_rate   = inv_set_compass_rate; +	st->sensor[SENSOR_PRESSURE].set_rate  = inv_set_pressure_rate; +	st->sensor[SENSOR_STEP].set_rate      = inv_set_step_detector; +	st->sensor[SENSOR_PEDQ].set_rate      = inv_set_pedq_rate; +	st->sensor[SENSOR_SIXQ].set_rate      = inv_set_sixq_rate; +	st->sensor[SENSOR_LPQ].set_rate       = inv_set_lpq_rate; +} + +int inv_flush_batch_data(struct iio_dev *indio_dev, bool *has_data) +{ +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct inv_reg_map_s *reg; +	u8 data[FIFO_COUNT_BYTE]; +	int result; + +	reg = &st->reg; +	if (!(iio_buffer_enabled(indio_dev)) || (!st->chip_config.enable)) +		return -EINVAL; + +	if (st->batch.on) { +		result = inv_read_time_and_ticks(st, false); +		if (result) +			return result; +		result = inv_i2c_read(st, reg->fifo_count_h, +					FIFO_COUNT_BYTE, data); +		if (result) +			return result; +		st->fifo_count = be16_to_cpup((__be16 *)(data)); +		if (st->fifo_count) { +			result = inv_process_batchmode(st); +			if (result) +				return result; +			*has_data = !!st->fifo_count; +			inv_push_marker_to_buffer(st, END_MARKER); +			result = write_be32_key_to_mem(st, 0, +						KEY_BM_BATCH_CNTR); +			return result; +		} +	} +	inv_push_marker_to_buffer(st, EMPTY_MARKER); + +	return 0; +} + +static const struct iio_buffer_setup_ops inv_mpu_ring_setup_ops = { +	.preenable = &inv_preenable, +	.predisable = &inv_predisable, +}; + +int inv_mpu_configure_ring(struct iio_dev *indio_dev) +{ +	int ret; +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct iio_buffer *ring; + +	ring = iio_kfifo_allocate(indio_dev); +	if (!ring) +		return -ENOMEM; +	indio_dev->buffer = ring; +	/* setup ring buffer */ +	ring->scan_timestamp = true; +	indio_dev->setup_ops = &inv_mpu_ring_setup_ops; +	/*scan count double count timestamp. should subtract 1. but +	number of channels still includes timestamp*/ +	if (INV_MPU3050 == st->chip_type) +		ret = request_threaded_irq(st->client->irq, inv_irq_handler, +			inv_read_fifo_mpu3050, +			IRQF_TRIGGER_RISING | IRQF_SHARED, "inv_irq", st); +	else +		ret = request_threaded_irq(st->client->irq, inv_irq_handler, +			inv_read_fifo, +			IRQF_TRIGGER_RISING | IRQF_SHARED, "inv_irq", st); +	if (ret) +		goto error_iio_sw_rb_free; + +	indio_dev->modes |= INDIO_BUFFER_TRIGGERED; + +	return 0; +error_iio_sw_rb_free: +	iio_kfifo_free(indio_dev->buffer); + +	return ret; +} + diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_trigger.c b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_trigger.c new file mode 100755 index 00000000000..ad67c089cc3 --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_mpu_trigger.c @@ -0,0 +1,94 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +* GNU General Public License for more details. +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> + +#include "iio.h" +#include "sysfs.h" +#include "trigger.h" + +#include "inv_mpu_iio.h" + +/* + * inv_mpu_data_rdy_trigger_set_state() set data ready interrupt state + */ +static int inv_mpu_data_rdy_trigger_set_state(struct iio_trigger *trig, +						bool state) +{ +	return 0; +} + +static const struct iio_trigger_ops inv_mpu_trigger_ops = { +	.owner = THIS_MODULE, +	.set_trigger_state = &inv_mpu_data_rdy_trigger_set_state, +}; + +int inv_mpu_probe_trigger(struct iio_dev *indio_dev) +{ +	int ret; +	struct inv_mpu_state *st = iio_priv(indio_dev); + +#ifdef CONFIG_INV_KERNEL_3_10 +	st->trig = iio_trigger_alloc("%s-dev%d", +#else +	st->trig = iio_allocate_trigger("%s-dev%d", +#endif +					indio_dev->name, +					indio_dev->id); +	if (st->trig == NULL) +		return -ENOMEM; +	st->trig->dev.parent = &st->client->dev; +#ifndef CONFIG_INV_KERNEL_3_10 +	st->trig->private_data = indio_dev; +#endif +	st->trig->ops = &inv_mpu_trigger_ops; +	ret = iio_trigger_register(st->trig); + +	if (ret) { +#ifdef CONFIG_INV_KERNEL_3_10 +		iio_trigger_free(st->trig); +#else +		iio_free_trigger(st->trig); +#endif +		return -EPERM; +	} +	indio_dev->trig = st->trig; + +	return 0; +} + +void inv_mpu_remove_trigger(struct iio_dev *indio_dev) +{ +	struct inv_mpu_state *st = iio_priv(indio_dev); + +	iio_trigger_unregister(st->trig); +#ifdef CONFIG_INV_KERNEL_3_10 +	iio_trigger_free(st->trig); +#else +	iio_free_trigger(st->trig); +#endif +} + diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_slave_bma250.c b/drivers/iio/imu-aosp/inv_mpu6515/inv_slave_bma250.c new file mode 100644 index 00000000000..99ae702e67a --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_slave_bma250.c @@ -0,0 +1,315 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +* GNU General Public License for more details. +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> + +#include "inv_mpu_iio.h" +#define BMA250_CHIP_ID			3 +#define BMA250_RANGE_SET		0 +#define BMA250_BW_SET			4 + +/* range and bandwidth */ +#define BMA250_RANGE_2G                 3 +#define BMA250_RANGE_4G                 5 +#define BMA250_RANGE_8G                 8 +#define BMA250_RANGE_16G                12 +#define BMA250_RANGE_MAX                4 +#define BMA250_RANGE_MASK               0xF0 + +#define BMA250_BW_7_81HZ        0x08 +#define BMA250_BW_15_63HZ       0x09 +#define BMA250_BW_31_25HZ       0x0A +#define BMA250_BW_62_50HZ       0x0B +#define BMA250_BW_125HZ         0x0C +#define BMA250_BW_250HZ         0x0D +#define BMA250_BW_500HZ         0x0E +#define BMA250_BW_1000HZ        0x0F +#define BMA250_MAX_BW_SIZE      8 +#define BMA250_BW_REG_MASK      0xE0 + +/*      register definitions */ +#define BMA250_X_AXIS_LSB_REG                   0x02 +#define BMA250_RANGE_SEL_REG                    0x0F +#define BMA250_BW_SEL_REG                       0x10 +#define BMA250_MODE_CTRL_REG                    0x11 + +/* mode settings */ +#define BMA250_MODE_NORMAL     0 +#define BMA250_MODE_LOWPOWER   1 +#define BMA250_MODE_SUSPEND    2 +#define BMA250_MODE_MAX        3 +#define BMA250_MODE_MASK       0x3F +#define BMA250_BIT_SUSPEND     0x80 +#define BMA250_BIT_LP          0x40 + +struct bma_property { +	int range; +	int bandwidth; +	int mode; +}; + +static struct bma_property bma_static_property = { +	.range = BMA250_RANGE_SET, +	.bandwidth = BMA250_BW_SET, +	.mode = BMA250_MODE_SUSPEND +}; + +static int bma250_set_bandwidth(struct inv_mpu_state *st, u8 bw) +{ +	int res; +	u8 data; +	int bandwidth; +	switch (bw) { +	case 0: +		bandwidth = BMA250_BW_7_81HZ; +		break; +	case 1: +		bandwidth = BMA250_BW_15_63HZ; +		break; +	case 2: +		bandwidth = BMA250_BW_31_25HZ; +		break; +	case 3: +		bandwidth = BMA250_BW_62_50HZ; +		break; +	case 4: +		bandwidth = BMA250_BW_125HZ; +		break; +	case 5: +		bandwidth = BMA250_BW_250HZ; +		break; +	case 6: +		bandwidth = BMA250_BW_500HZ; +		break; +	case 7: +		bandwidth = BMA250_BW_1000HZ; +		break; +	default: +		return -EINVAL; +	} +	res = inv_secondary_read(BMA250_BW_SEL_REG, 1, &data); +	if (res) +		return res; +	data &= BMA250_BW_REG_MASK; +	data |= bandwidth; +	res = inv_secondary_write(BMA250_BW_SEL_REG, data); +	return res; +} + +static int bma250_set_range(struct inv_mpu_state *st, u8 range) +{ +	int res; +	u8 orig, data; +	switch (range) { +	case 0: +		data  = BMA250_RANGE_2G; +		break; +	case 1: +		data  = BMA250_RANGE_4G; +		break; +	case 2: +		data  = BMA250_RANGE_8G; +		break; +	case 3: +		data  = BMA250_RANGE_16G; +		break; +	default: +		return -EINVAL; +	} +	res = inv_secondary_read(BMA250_RANGE_SEL_REG, 1, &orig); +	if (res) +		return res; +	orig &= BMA250_RANGE_MASK; +	data |= orig; +	res = inv_secondary_write(BMA250_RANGE_SEL_REG, data); +	if (res) +		return res; +	bma_static_property.range = range; + +	return 0; +} + +static int setup_slave_bma250(struct inv_mpu_state *st) +{ +	int result; +	u8 data[2]; +	result = set_3050_bypass(st, true); +	if (result) +		return result; +	/*read secondary i2c ID register */ +	result = inv_secondary_read(0, 1, data); +	if (result) +		return result; +	if (BMA250_CHIP_ID != data[0]) +		return -EINVAL; +	result = set_3050_bypass(st, false); +	if (result) +		return result; +	/*AUX(accel), slave address is set inside set_3050_bypass*/ +	/* bma250 x axis LSB register address is 2 */ +	result = inv_i2c_single_write(st, REG_3050_AUX_BST_ADDR, +					BMA250_X_AXIS_LSB_REG); + +	return result; +} + +static int bma250_set_mode(struct inv_mpu_state *st, u8 mode) +{ +	int res; +	u8 data; + +	res = inv_secondary_read(BMA250_MODE_CTRL_REG, 1, &data); +	if (res) +		return res; +	data &= BMA250_MODE_MASK; +	switch (mode) { +	case BMA250_MODE_NORMAL: +		break; +	case BMA250_MODE_LOWPOWER: +		data |= BMA250_BIT_LP; +		break; +	case BMA250_MODE_SUSPEND: +		data |= BMA250_BIT_SUSPEND; +		break; +	default: +		return -EINVAL; +	} +	res = inv_secondary_write(BMA250_MODE_CTRL_REG, data); +	if (res) +		return res; +	bma_static_property.mode = mode; + +	return 0; +} + +static int suspend_slave_bma250(struct inv_mpu_state *st) +{ +	int result; +	if (bma_static_property.mode == BMA250_MODE_SUSPEND) +		return 0; +	/*set to bypass mode */ +	result = set_3050_bypass(st, true); +	if (result) +		return result; +	bma250_set_mode(st, BMA250_MODE_SUSPEND); +	/* no need to recover to non-bypass mode because we need it now */ + +	return 0; +} + +static int resume_slave_bma250(struct inv_mpu_state *st) +{ +	int result; +	if (bma_static_property.mode == BMA250_MODE_NORMAL) +		return 0; +	/*set to bypass mode */ +	result = set_3050_bypass(st, true); +	if (result) +		return result; +	result = bma250_set_mode(st, BMA250_MODE_NORMAL); +	/* recover bypass mode */ +	result |= set_3050_bypass(st, false); + +	return result ? (-EINVAL) : 0; +} + +static int combine_data_slave_bma250(u8 *in, short *out) +{ +	out[0] = le16_to_cpup((__le16 *)(&in[0])); +	out[1] = le16_to_cpup((__le16 *)(&in[2])); +	out[2] = le16_to_cpup((__le16 *)(&in[4])); + +	return 0; +} + +static int get_mode_slave_bma250(void) +{ +	switch (bma_static_property.mode) { +	case BMA250_MODE_SUSPEND: +		return INV_MODE_SUSPEND; +	case BMA250_MODE_NORMAL: +		return INV_MODE_NORMAL; +	default: +		return -EINVAL; +	} +} + +/** + *  set_lpf_bma250() - set lpf value + */ + +static int set_lpf_bma250(struct inv_mpu_state *st, int rate) +{ +	const short hz[] = {1000, 500, 250, 125, 62, 31, 15, 7}; +	const int   d[] = {7, 6, 5, 4, 3, 2, 1, 0}; +	int i, h, data, result; +	h = (rate >> 1); +	i = 0; +	while ((h < hz[i]) && (i < ARRAY_SIZE(hz) - 1)) +		i++; +	data = d[i]; + +	result = set_3050_bypass(st, true); +	if (result) +		return result; +	result = bma250_set_bandwidth(st, (u8) data); +	result |= set_3050_bypass(st, false); + +	return result ? (-EINVAL) : 0; +} +/** + *  set_fs_bma250() - set range value + */ + +static int set_fs_bma250(struct inv_mpu_state *st, int fs) +{ +	int result; +	result = set_3050_bypass(st, true); +	if (result) +		return result; +	result = bma250_set_range(st, (u8) fs); +	result |= set_3050_bypass(st, false); + +	return result ? (-EINVAL) : 0; +} + +static struct inv_mpu_slave slave_bma250 = { +	.suspend = suspend_slave_bma250, +	.resume  = resume_slave_bma250, +	.setup   = setup_slave_bma250, +	.combine_data = combine_data_slave_bma250, +	.get_mode = get_mode_slave_bma250, +	.set_lpf = set_lpf_bma250, +	.set_fs  = set_fs_bma250 +}; + +int inv_register_mpu3050_slave(struct inv_mpu_state *st) +{ +	st->slave_accel = &slave_bma250; + +	return 0; +} + diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_slave_compass.c b/drivers/iio/imu-aosp/inv_mpu6515/inv_slave_compass.c new file mode 100755 index 00000000000..68b76d3a8e0 --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_slave_compass.c @@ -0,0 +1,854 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> + +#include "inv_mpu_iio.h" +/* AKM definitions */ +#define REG_AKM_ID               0x00 +#define REG_AKM_INFO             0x01 +#define REG_AKM_STATUS           0x02 +#define REG_AKM_MEASURE_DATA     0x03 +#define REG_AKM_MODE             0x0A +#define REG_AKM_ST_CTRL          0x0C +#define REG_AKM_SENSITIVITY      0x10 +#define REG_AKM8963_CNTL1        0x0A + +/* AK09911 register definition */ +#define REG_AK09911_DMP_READ    0x3 +#define REG_AK09911_STATUS1     0x10 +#define REG_AK09911_CNTL2       0x31 +#define REG_AK09911_SENSITIVITY 0x60 + +#define DATA_AKM_ID              0x48 +#define DATA_AKM_MODE_PD	 0x00 +#define DATA_AKM_MODE_SM	 0x01 +#define DATA_AKM_MODE_ST	 0x08 +#define DATA_AKM_MODE_FR	 0x0F +#define DATA_AK09911_MODE_FR     0x1F +#define DATA_AKM_SELF_TEST       0x40 +#define DATA_AKM_DRDY            0x01 +#define DATA_AKM8963_BIT         0x10 +#define DATA_AKM_STAT_MASK       0x0C + +#define DATA_AKM8975_SCALE       (9830 * (1L << 15)) +#define DATA_AKM8972_SCALE       (19661 * (1L << 15)) +#define DATA_AKM8963_SCALE0      (19661 * (1L << 15)) +#define DATA_AKM8963_SCALE1      (4915 * (1L << 15)) +#define DATA_AK09911_SCALE       (19661 * (1L << 15)) +#define DATA_MLX_SCALE           (4915 * (1L << 15)) +#define DATA_MLX_SCALE_EMPIRICAL (26214 * (1L << 15)) + +#define DATA_AKM8963_SCALE_SHIFT      4 +#define DATA_AKM_99_BYTES_DMP  10 +#define DATA_AKM_89_BYTES_DMP  9 +#define DATA_AKM_MIN_READ_TIME            (9 * NSEC_PER_MSEC) + +#define DEF_ST_COMPASS_WAIT_MIN     (10 * 1000) +#define DEF_ST_COMPASS_WAIT_MAX     (15 * 1000) +#define DEF_ST_COMPASS_TRY_TIMES    10 +#define DEF_ST_COMPASS_8963_SHIFT   2 +#define X                           0 +#define Y                           1 +#define Z                           2 + +/* milliseconds between each access */ +#define AKM_RATE_SCALE       10 +#define MLX_RATE_SCALE       50 + +/* MLX90399 compass definition */ +#define DATA_MLX_CMD_READ_MEASURE         0x4F +#define DATA_MLX_CMD_SINGLE_MEASURE       0x3F +#define DATA_MLX_READ_DATA_BYTES          9 +#define DATA_MLX_STATUS_DATA              3 +#define DATA_MLX_MIN_READ_TIME            (95 * NSEC_PER_MSEC) + +static const short AKM8975_ST_Lower[3] = {-100, -100, -1000}; +static const short AKM8975_ST_Upper[3] = {100, 100, -300}; + +static const short AKM8972_ST_Lower[3] = {-50, -50, -500}; +static const short AKM8972_ST_Upper[3] = {50, 50, -100}; + +static const short AKM8963_ST_Lower[3] = {-200, -200, -3200}; +static const short AKM8963_ST_Upper[3] = {200, 200, -800}; + +/* + *  inv_setup_compass_akm() - Configure akm series compass. + */ +static int inv_setup_compass_akm(struct inv_mpu_state *st) +{ +	int result; +	u8 data[4]; +	u8 sens, mode, cmd; + +	/* set to bypass mode */ +	result = inv_i2c_single_write(st, REG_INT_PIN_CFG, +				st->plat_data.int_config | BIT_BYPASS_EN); +	if (result) +		return result; +	/* read secondary i2c ID register */ +	result = inv_secondary_read(REG_AKM_ID, 1, data); +	if (result) +		return result; +	if (data[0] != DATA_AKM_ID) +		return -ENXIO; +	/* set AKM to Fuse ROM access mode */ +	if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id) { +		mode = REG_AK09911_CNTL2; +		sens = REG_AK09911_SENSITIVITY; +		cmd = DATA_AK09911_MODE_FR; +	} else { +		mode = REG_AKM_MODE; +		sens = REG_AKM_SENSITIVITY; +		cmd = DATA_AKM_MODE_FR; +	} + +	result = inv_secondary_write(mode, cmd); +	if (result) +		return result; +	result = inv_secondary_read(sens, THREE_AXIS, +						st->chip_info.compass_sens); +	if (result) +		return result; +	/* revert to power down mode */ +	result = inv_secondary_write(mode, DATA_AKM_MODE_PD); +	if (result) +		return result; +	pr_debug("%s senx=%d, seny=%d, senz=%d\n", +		 st->hw->name, +		 st->chip_info.compass_sens[0], +		 st->chip_info.compass_sens[1], +		 st->chip_info.compass_sens[2]); +	/* restore to non-bypass mode */ +	result = inv_i2c_single_write(st, REG_INT_PIN_CFG, +			st->plat_data.int_config); +	if (result) +		return result; + +	/* setup master mode and master clock and ES bit */ +	result = inv_i2c_single_write(st, REG_I2C_MST_CTRL, BIT_WAIT_FOR_ES); +	if (result) +		return result; +	/* slave 1 is used for AKM mode change only */ +	result = inv_i2c_single_write(st, REG_I2C_SLV1_ADDR, +		st->plat_data.secondary_i2c_addr); +	if (result) +		return result; +	/* AKM mode register address */ +	result = inv_i2c_single_write(st, REG_I2C_SLV1_REG, mode); +	if (result) +		return result; +	/* output data for slave 1 is fixed, single measure mode */ +	st->slave_compass->scale = 1; +	if (COMPASS_ID_AK8975 == st->plat_data.sec_slave_id) { +		st->slave_compass->st_upper = AKM8975_ST_Upper; +		st->slave_compass->st_lower = AKM8975_ST_Lower; +		data[0] = DATA_AKM_MODE_SM; +	} else if (COMPASS_ID_AK8972 == st->plat_data.sec_slave_id) { +		st->slave_compass->st_upper = AKM8972_ST_Upper; +		st->slave_compass->st_lower = AKM8972_ST_Lower; +		data[0] = DATA_AKM_MODE_SM; +	} else if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id) { +		st->slave_compass->st_upper = AKM8963_ST_Upper; +		st->slave_compass->st_lower = AKM8963_ST_Lower; +		data[0] = DATA_AKM_MODE_SM | +			(st->slave_compass->scale << DATA_AKM8963_SCALE_SHIFT); +	}  else if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id) { +		st->slave_compass->st_upper = AKM8963_ST_Upper; +		st->slave_compass->st_lower = AKM8963_ST_Lower; +		data[0] = DATA_AKM_MODE_SM; +	} else { +		return -EINVAL; +	} + +	result = inv_i2c_single_write(st, INV_MPU_REG_I2C_SLV1_DO, data[0]); + +	return result; +} + +static int inv_akm_read_data(struct inv_mpu_state *st, short *o) +{ +	int result, shift; +	int i; +	u8 d[DATA_AKM_99_BYTES_DMP - 1]; +	u8 *sens; + +	sens = st->chip_info.compass_sens; +	result = 0; +	if (st->chip_config.dmp_on) { +		for (i = 0; i < 6; i++) +			d[1 + i] = st->fifo_data[i]; +	} else { +		result = inv_i2c_read(st, REG_EXT_SENS_DATA_00, +					DATA_AKM_99_BYTES_DMP - 1, d); +		if ((DATA_AKM_DRDY != d[0]) || (d[7] & 0x8) || result) +			result = -EINVAL; +	} +	if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id) +		shift = 7; +	else +		shift = 8; +	for (i = 0; i < 3; i++) { +		o[i] = (short)((d[i * 2 + 1] << 8) | d[i * 2 + 2]); +		o[i] = (short)(((int)o[i] * (sens[i] + 128)) >> shift); +	} + +	return result; +} + +static int inv_mlx_read_data(struct inv_mpu_state *st, short *o) +{ +	int result; +	int i, z; +	u8 d[DATA_MLX_READ_DATA_BYTES]; + +	result = inv_i2c_read(st, REG_EXT_SENS_DATA_00, +			     DATA_MLX_READ_DATA_BYTES, d); +	if ((!(d[0] & ~DATA_MLX_STATUS_DATA)) && (!result)) { +		for (i = 0; i < 3; i++) +			o[i] = (short)((d[i * 2 + 3] << 8) + d[i * 2 + 4]); +	} else { +		for (i = 0; i < 3; i++) +			o[i] = 0; +	} +	z = o[2]; +	/* axis sensitivity conversion. Z axis has different sensitiviy from +	   x and y */ +	z *= 26; +	z /= 15; +	o[2] = z; + +	return 0; +} + +static int inv_check_akm_self_test(struct inv_mpu_state *st) +{ +	int result; +	u8 data[6], mode; +	u8 counter, cntl; +	short x, y, z; +	u8 *sens; +	sens = st->chip_info.compass_sens; + +	/* set to bypass mode */ +	result = inv_i2c_single_write(st, REG_INT_PIN_CFG, +				st->plat_data.int_config | BIT_BYPASS_EN); +	if (result) { +		result = inv_i2c_single_write(st, REG_INT_PIN_CFG, +				st->plat_data.int_config); +		return result; +	} +	if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id) +		mode = REG_AK09911_CNTL2; +	else +		mode = REG_AKM_MODE; +	/* set to power down mode */ +	result = inv_secondary_write(mode, DATA_AKM_MODE_PD); +	if (result) +		goto AKM_fail; + +	/* write 1 to ASTC register */ +	result = inv_secondary_write(REG_AKM_ST_CTRL, DATA_AKM_SELF_TEST); +	if (result) +		goto AKM_fail; +	/* set self test mode */ +	result = inv_secondary_write(mode, DATA_AKM_MODE_ST); +	if (result) +		goto AKM_fail; +	counter = DEF_ST_COMPASS_TRY_TIMES; +	while (counter > 0) { +		usleep_range(DEF_ST_COMPASS_WAIT_MIN, DEF_ST_COMPASS_WAIT_MAX); +		result = inv_secondary_read(REG_AKM_STATUS, 1, data); +		if (result) +			goto AKM_fail; +		if ((data[0] & DATA_AKM_DRDY) == 0) +			counter--; +		else +			counter = 0; +	} +	if ((data[0] & DATA_AKM_DRDY) == 0) { +		result = -EINVAL; +		goto AKM_fail; +	} +	result = inv_secondary_read(REG_AKM_MEASURE_DATA, +					BYTES_PER_SENSOR, data); +	if (result) +		goto AKM_fail; + +	x = le16_to_cpup((__le16 *)(&data[0])); +	y = le16_to_cpup((__le16 *)(&data[2])); +	z = le16_to_cpup((__le16 *)(&data[4])); +	x = ((x * (sens[0] + 128)) >> 8); +	y = ((y * (sens[1] + 128)) >> 8); +	z = ((z * (sens[2] + 128)) >> 8); +	if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id) { +		result = inv_secondary_read(REG_AKM8963_CNTL1, 1, &cntl); +		if (result) +			goto AKM_fail; +		if (0 == (cntl & DATA_AKM8963_BIT)) { +			x <<= DEF_ST_COMPASS_8963_SHIFT; +			y <<= DEF_ST_COMPASS_8963_SHIFT; +			z <<= DEF_ST_COMPASS_8963_SHIFT; +		} +	} +	result = -EINVAL; +	if (x > st->slave_compass->st_upper[X] || +					x < st->slave_compass->st_lower[X]) +		goto AKM_fail; +	if (y > st->slave_compass->st_upper[Y] || +					y < st->slave_compass->st_lower[Y]) +		goto AKM_fail; +	if (z > st->slave_compass->st_upper[Z] || +					z < st->slave_compass->st_lower[Z]) +		goto AKM_fail; +	result = 0; +AKM_fail: +	/*write 0 to ASTC register */ +	result |= inv_secondary_write(REG_AKM_ST_CTRL, 0); +	/*set to power down mode */ +	result |= inv_secondary_write(mode, DATA_AKM_MODE_PD); +	/*restore to non-bypass mode */ +	result |= inv_i2c_single_write(st, REG_INT_PIN_CFG, +			st->plat_data.int_config); +	return result; +} + +/* + *  inv_write_akm_scale() - Configure the akm scale range. + */ +static int inv_write_akm_scale(struct inv_mpu_state *st, int data) +{ +	char d, en; +	int result; + +	if (COMPASS_ID_AK8963 != st->plat_data.sec_slave_id) +		return 0; +	en = !!data; +	if (st->slave_compass->scale == en) +		return 0; +	d = (DATA_AKM_MODE_SM | (en << DATA_AKM8963_SCALE_SHIFT)); +	result = inv_i2c_single_write(st, INV_MPU_REG_I2C_SLV1_DO, d); +	if (result) +		return result; +	st->slave_compass->scale = en; + +	return 0; +} + +/* + *  inv_read_akm_scale() - show AKM scale. + */ +static int inv_read_akm_scale(struct inv_mpu_state *st, int *scale) +{ +	if (COMPASS_ID_AK8975 == st->plat_data.sec_slave_id) +		*scale = DATA_AKM8975_SCALE; +	else if (COMPASS_ID_AK8972 == st->plat_data.sec_slave_id) +		*scale = DATA_AKM8972_SCALE; +	else if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id) +		if (st->slave_compass->scale) +			*scale = DATA_AKM8963_SCALE1; +		else +			*scale = DATA_AKM8963_SCALE0; +	else if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id) +		*scale = DATA_AK09911_SCALE; +	else +		return -EINVAL; + +	return IIO_VAL_INT; +} + +static int inv_suspend_akm(struct inv_mpu_state *st) +{ +	int result; + +	/* slave 0 is disabled */ +	result = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, 0); +	if (result) +		return result; +	/* slave 1 is disabled */ +	result = inv_i2c_single_write(st, REG_I2C_SLV1_CTRL, 0); + +	return result; +} + +static int inv_resume_akm(struct inv_mpu_state *st) +{ +	int result; +	u8 reg_addr, bytes; + +	/* slave 0 is used to read data from compass */ +	/*read mode */ +	result = inv_i2c_single_write(st, REG_I2C_SLV0_ADDR, +					INV_MPU_BIT_I2C_READ | +					st->plat_data.secondary_i2c_addr); +	if (result) +		return result; +	/* AKM status register address is 1 */ +	if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id) { +		if (st->chip_config.dmp_on) { +			reg_addr = REG_AK09911_DMP_READ; +			bytes = DATA_AKM_99_BYTES_DMP; +		} else { +			reg_addr = REG_AK09911_STATUS1; +			bytes = DATA_AKM_99_BYTES_DMP - 1; +		} +	} else { +		if (st->chip_config.dmp_on) { +			reg_addr = REG_AKM_INFO; +			bytes = DATA_AKM_89_BYTES_DMP; +		} else { +			reg_addr = REG_AKM_STATUS; +			bytes = DATA_AKM_89_BYTES_DMP - 1; +		} +	} +	result = inv_i2c_single_write(st, REG_I2C_SLV0_REG, reg_addr); +	if (result) +		return result; + +	/* slave 0 is enabled, read 10 or 8 bytes from here, swap bytes */ +	result = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, +						INV_MPU_BIT_GRP | +						INV_MPU_BIT_BYTE_SW | +						INV_MPU_BIT_SLV_EN | +						bytes); +	if (result) +		return result; +	/* slave 1 is enabled, write byte length is 1 */ +	result = inv_i2c_single_write(st, REG_I2C_SLV1_CTRL, +						INV_MPU_BIT_SLV_EN | 1); + +	return result; +} + +/* + *  inv_write_mlx_scale() - Configure the mlx90399 scale range. + */ +static int inv_write_mlx_scale(struct inv_mpu_state *st, int data) +{ +	st->slave_compass->scale = data; +	return 0; +} + +/* + *  inv_read_mlx_scale() - show mlx90399 scale. + */ +static int inv_read_mlx_scale(struct inv_mpu_state *st, int *scale) +{ +	*scale = st->slave_compass->scale; +	return IIO_VAL_INT; +} + +static int inv_i2c_read_mlx(struct inv_mpu_state *st, u16 i2c_addr, +			    u16 length, u8 *data) +{ +	struct i2c_msg msgs[1]; +	int res; + +	if (!data) +		return -EINVAL; + +	msgs[0].addr = i2c_addr; +	msgs[0].flags = I2C_M_RD; +	msgs[0].buf = data; +	msgs[0].len = length; + +	res = i2c_transfer(st->sl_handle, msgs, 1); + +	if (res < 1) { +		if (res >= 0) +			res = -EIO; +	} else +		res = 0; + +	return res; +} + +static int inv_i2c_write_mlx(struct inv_mpu_state *st, +	u16 i2c_addr, u8 data) +{ +	u8 tmp[1]; +	struct i2c_msg msg; +	int res; + +	tmp[0] = data; +	msg.addr = i2c_addr; +	msg.flags = 0;	/* write */ +	msg.buf = tmp; +	msg.len = 1; + +	res = i2c_transfer(st->sl_handle, &msg, 1); +	if (res < 1) { +		if (res == 0) +			res = -EIO; +		return res; +	} else +		return 0; +} + +static int inv_i2c_read_reg_mlx(struct inv_mpu_state *st, +	u16 i2c_addr, u8 reg, u16 *val) +{ +	u8 tmp[10]; +	struct i2c_msg msg; +	int res; + +	tmp[0] = 0x50; +	tmp[1] = (reg << 2); +	msg.addr = i2c_addr; +	msg.flags = 0;	/* write */ +	msg.buf = tmp; +	msg.len = 2; + +	res = i2c_transfer(st->sl_handle, &msg, 1); +	if (res < 1) { +		if (res == 0) +			res = -EIO; +		return res; +	} +	res = inv_i2c_read_mlx(st, i2c_addr, 10, tmp); +	if (res) +		return res; +	*val = ((tmp[1] << 8) | tmp[2]); + +	return res; +} + +static int inv_i2c_write_mlx_reg(struct inv_mpu_state *st, +	u16 i2c_addr, int reg, u16 d) +{ +	u8 tmp[10]; +	struct i2c_msg msg; +	int res; + +	/* write register command, writing volatile memory */ +	tmp[0] = 0x60; +	tmp[1] = ((d >> 8) & 0xff); +	tmp[2] = (d & 0xff); +	tmp[3] = (reg << 2); +	msg.addr = i2c_addr; +	msg.flags = 0;	/* write */ +	msg.buf = tmp; +	msg.len = 4; + +	res = i2c_transfer(st->sl_handle, &msg, 1); +	if (res < 1) { +		if (res == 0) +			res = -EIO; +		return res; +	} +	/* read status */ +	res = inv_i2c_read_mlx(st, i2c_addr, 10, tmp); + +	return res; +} + +static int inv_write_mlx_cmd(struct inv_mpu_state *st, u8 cmd) +{ +	int result; +	u8 d[10]; +	int addr; + +	addr = st->plat_data.secondary_i2c_addr; +	result = inv_i2c_write_mlx(st, addr, cmd); +	if (result) +		return result; +	/* read back status byte */ +	result = inv_i2c_read_mlx(st, addr, 10, d); + +	return result; +} + +static int inv_read_mlx_z_axis(struct inv_mpu_state *st, s16 *z) +{ +	int result; +	u8 d[10]; +	int addr; + +	addr = st->plat_data.secondary_i2c_addr; + +	/* measure z axis */ +	result = inv_write_mlx_cmd(st, 0x39); +	if (result) +		return result; +	msleep(100); +	/* read z axis */ +	result = inv_i2c_write_mlx(st, addr, 0x49); +	if (result) +		return result; +	/* read back status byte */ +	result = inv_i2c_read_mlx(st, addr, 10, d); +	if (result) +		return result; +	if ((d[0] & 0x3) == 1) +		*z = (short)((d[3] << 8) + d[4]); +	else +		return -EINVAL; + +	return 0; +} + +static int inv_write_mlx_reg(struct inv_mpu_state *st) +{ +	int result; +	int addr; +	u16 r_val; + +	addr = st->plat_data.secondary_i2c_addr; + +	/* write register 0. +	   set GAIN_SEL as 7; +	   set HALL_CONF as 0xC. */ +	result = inv_i2c_write_mlx_reg(st, addr, 0, 0x7c); +	if (result) +		return result; +	/* write register 2. +	   set resolution is zero for all axes; +	   set DIGI filter as 6. +	   set OSR as 0. +	   set OSR2 as 0. */ +	result = inv_i2c_write_mlx_reg(st, addr, 2, 0x18); +	if (result) +		return result; +	/* read register 1 */ +	result = inv_i2c_read_reg_mlx(st, addr, 1, &r_val); +	if (result) +		return result; +	/* enable temp comp */ +	r_val |= 0x400; +	result = inv_i2c_write_mlx_reg(st, addr, 1, r_val); +	/* the value should be kept in the volatile memory */ + +	return result; +} + +static int inv_check_mlx_self_test(struct inv_mpu_state *st) +{ +	int result; +	int addr; +	s16 meas_ref, meas_coil; +	u16 diff, r_val; + +	/* set to bypass mode */ +	result = inv_i2c_single_write(st, REG_INT_PIN_CFG, +				st->plat_data.int_config | BIT_BYPASS_EN); +	if (result) { +		result = inv_i2c_single_write(st, REG_INT_PIN_CFG, +				st->plat_data.int_config); +		return result; +	} + +	addr = st->plat_data.secondary_i2c_addr; + +	/* fake read to flush the previous data */ +	result = inv_read_mlx_z_axis(st, &meas_ref); + +	result = inv_read_mlx_z_axis(st, &meas_ref); +	if (result) +		return result; + +	/* read register 1 */ +	result = inv_i2c_read_reg_mlx(st, addr, 0, &r_val); +	if (result) +		return result; +	/* enable self test */ +	r_val |= 0x100; +	result = inv_i2c_write_mlx_reg(st, addr, 0, r_val); +	if (result) +		return result; +	msleep(200); +	result = inv_read_mlx_z_axis(st, &meas_coil); +	if (result) +		return result; +	result = inv_write_mlx_cmd(st, 0xD0); +	if (result) +		return result; +	result = inv_write_mlx_reg(st); +	if (result) +		return result; +	diff = abs(meas_ref - meas_coil); +	if (diff < 25 || diff > 300) +		result = 1; + +	/*restore to non-bypass mode */ +	result |= inv_i2c_single_write(st, REG_INT_PIN_CFG, +			st->plat_data.int_config); + +	return result; +} + +/* + *  inv_setup_compass_mlx() - Configure akm series compass. + */ +static int inv_setup_compass_mlx(struct inv_mpu_state *st) +{ +	int result; +	int addr; + +	addr = st->plat_data.secondary_i2c_addr; +	/* set to bypass mode */ +	result = inv_i2c_single_write(st, REG_INT_PIN_CFG, +				st->plat_data.int_config | BIT_BYPASS_EN); +	if (result) +		return result; +	result = inv_write_mlx_reg(st); +	if (result) +		return result; + +	/*restore to non-bypass mode */ +	result = inv_i2c_single_write(st, REG_INT_PIN_CFG, +						st->plat_data.int_config); +	if (result) +		return result; + +	/*setup master mode and master clock and ES bit*/ +	result = inv_i2c_single_write(st, REG_I2C_MST_CTRL, BIT_WAIT_FOR_ES); +	if (result) +		return result; + +	/* slave 0 used to write read measurement command, write mode */ +	result = inv_i2c_single_write(st, REG_I2C_SLV0_ADDR, addr); +	if (result) +		return result; +	/* ignore the register address, send out data only */ +	result = inv_i2c_single_write(st, INV_MPU_REG_I2C_SLV0_DO, +					DATA_MLX_CMD_READ_MEASURE); +	if (result) +		return result; + +	/* slave 1 used to read status bytes and data of read measurement */ +	result = inv_i2c_single_write(st, REG_I2C_SLV1_ADDR, +						INV_MPU_BIT_I2C_READ | addr); +	if (result) +		return result; +	/* slave 2 used to write single measurement command, write mode */ +	result = inv_i2c_single_write(st, REG_I2C_SLV2_ADDR, addr); +	if (result) +		return result; +	/* ignore the register address, send out data only */ +	result = inv_i2c_single_write(st, INV_MPU_REG_I2C_SLV2_DO, +					DATA_MLX_CMD_SINGLE_MEASURE); +	if (result) +		return result; +	/* slave 3 used to read status bytes and data of read measurement */ +	result = inv_i2c_single_write(st, REG_I2C_SLV3_ADDR, +					INV_MPU_BIT_I2C_READ | addr); + +	st->slave_compass->scale = DATA_MLX_SCALE; + +	return result; +} + +static int inv_suspend_mlx(struct inv_mpu_state *st) +{ +	int result; + +	result = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, 0); +	if (result) +		return result; +	result = inv_i2c_single_write(st, REG_I2C_SLV1_CTRL, 0); +	if (result) +		return result; +	result = inv_i2c_single_write(st, REG_I2C_SLV2_CTRL, 0); +	if (result) +		return result; +	result = inv_i2c_single_write(st, REG_I2C_SLV3_CTRL, 0); + +	return result; +} + +static int inv_resume_mlx(struct inv_mpu_state *st) +{ +	int result; + +	/* enable, ignore register, write 1 bytes */ +	result = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, +						INV_MPU_BIT_SLV_EN | +						INV_MPU_BIT_REG_DIS | +						1); +	if (result) +		return result; + +	/* enable, ignore register, read 9 bytes */ +	result = inv_i2c_single_write(st, REG_I2C_SLV1_CTRL, +						INV_MPU_BIT_SLV_EN | +						INV_MPU_BIT_REG_DIS | +						DATA_MLX_READ_DATA_BYTES); +	if (result) +		return result; +	/* enable, ignore register, write 1 bytes */ +	result = inv_i2c_single_write(st, REG_I2C_SLV2_CTRL, +						INV_MPU_BIT_SLV_EN | +						INV_MPU_BIT_REG_DIS | +						1); +	if (result) +		return result; + +	/* enable, ignore register, read 1 bytes */ +	result = inv_i2c_single_write(st, REG_I2C_SLV3_CTRL, +						INV_MPU_BIT_SLV_EN | +						INV_MPU_BIT_REG_DIS | +						1); + +	return result; +} + +static struct inv_mpu_slave slave_akm = { +	.suspend   = inv_suspend_akm, +	.resume    = inv_resume_akm, +	.get_scale = inv_read_akm_scale, +	.set_scale = inv_write_akm_scale, +	.self_test = inv_check_akm_self_test, +	.setup     = inv_setup_compass_akm, +	.read_data = inv_akm_read_data, +	.rate_scale = AKM_RATE_SCALE, +	.min_read_time = DATA_AKM_MIN_READ_TIME, +}; + +static struct inv_mpu_slave slave_mlx90399 = { +	.suspend   = inv_suspend_mlx, +	.resume    = inv_resume_mlx, +	.get_scale = inv_read_mlx_scale, +	.set_scale = inv_write_mlx_scale, +	.self_test = inv_check_mlx_self_test, +	.setup     = inv_setup_compass_mlx, +	.read_data = inv_mlx_read_data, +	.rate_scale = MLX_RATE_SCALE, +	.min_read_time = DATA_MLX_MIN_READ_TIME, +}; + +int inv_mpu_setup_compass_slave(struct inv_mpu_state *st) +{ +	switch (st->plat_data.sec_slave_id) { +	case COMPASS_ID_AK8975: +	case COMPASS_ID_AK8972: +	case COMPASS_ID_AK8963: +	case COMPASS_ID_AK09911: +		st->slave_compass = &slave_akm; +		break; +	case COMPASS_ID_MLX90399: +		st->slave_compass = &slave_mlx90399; +		break; +	default: +		return -EINVAL; +	} + +	return st->slave_compass->setup(st); +} + diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_slave_pressure.c b/drivers/iio/imu-aosp/inv_mpu6515/inv_slave_pressure.c new file mode 100755 index 00000000000..24d80810449 --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_slave_pressure.c @@ -0,0 +1,522 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> + +#include "inv_mpu_iio.h" + +/* Constants */ +#define SHIFT_RIGHT_4_POSITION				 4 +#define SHIFT_LEFT_2_POSITION                2 +#define SHIFT_LEFT_4_POSITION                4 +#define SHIFT_LEFT_5_POSITION                5 +#define SHIFT_LEFT_8_POSITION                8 +#define SHIFT_LEFT_12_POSITION               12 +#define SHIFT_LEFT_16_POSITION               16 + +/* Sensor Specific constants */ +#define BMP280_SLEEP_MODE                    0x00 +#define BMP280_FORCED_MODE                   0x01 +#define BMP280_NORMAL_MODE                   0x03 +#define BMP280_SOFT_RESET                    0xB6 + +#define BMP280_DELAYTIME_MS_NONE             0 +#define BMP280_DELAYTIME_MS_5                5 +#define BMP280_DELAYTIME_MS_6                6 +#define BMP280_DELAYTIME_MS_8                8 +#define BMP280_DELAYTIME_MS_12               12 +#define BMP280_DELAYTIME_MS_22               22 +#define BMP280_DELAYTIME_MS_38               38 + +#define BMP280_OVERSAMPLING_SKIPPED          0x00 +#define BMP280_OVERSAMPLING_1X               0x01 +#define BMP280_OVERSAMPLING_2X               0x02 +#define BMP280_OVERSAMPLING_4X               0x03 +#define BMP280_OVERSAMPLING_8X               0x04 +#define BMP280_OVERSAMPLING_16X              0x05 + +#define BMP280_ULTRALOWPOWER_MODE            0x00 +#define BMP280_LOWPOWER_MODE	             0x01 +#define BMP280_STANDARDRESOLUTION_MODE       0x02 +#define BMP280_HIGHRESOLUTION_MODE           0x03 +#define BMP280_ULTRAHIGHRESOLUTION_MODE      0x04 + +#define BMP280_ULTRALOWPOWER_OSRS_P          BMP280_OVERSAMPLING_1X +#define BMP280_ULTRALOWPOWER_OSRS_T          BMP280_OVERSAMPLING_1X + +#define BMP280_LOWPOWER_OSRS_P	             BMP280_OVERSAMPLING_2X +#define BMP280_LOWPOWER_OSRS_T	             BMP280_OVERSAMPLING_1X + +#define BMP280_STANDARDRESOLUTION_OSRS_P     BMP280_OVERSAMPLING_4X +#define BMP280_STANDARDRESOLUTION_OSRS_T     BMP280_OVERSAMPLING_1X + +#define BMP280_HIGHRESOLUTION_OSRS_P         BMP280_OVERSAMPLING_8X +#define BMP280_HIGHRESOLUTION_OSRS_T         BMP280_OVERSAMPLING_1X + +#define BMP280_ULTRAHIGHRESOLUTION_OSRS_P    BMP280_OVERSAMPLING_16X +#define BMP280_ULTRAHIGHRESOLUTION_OSRS_T    BMP280_OVERSAMPLING_2X + +#define BMP280_FILTERCOEFF_OFF               0x00 +#define BMP280_FILTERCOEFF_2                 0x01 +#define BMP280_FILTERCOEFF_4                 0x02 +#define BMP280_FILTERCOEFF_8                 0x03 +#define BMP280_FILTERCOEFF_16                0x04 + +/*calibration parameters */ +#define BMP280_DIG_T1_LSB_REG                0x88 +#define BMP280_DIG_T1_MSB_REG                0x89 +#define BMP280_DIG_T2_LSB_REG                0x8A +#define BMP280_DIG_T2_MSB_REG                0x8B +#define BMP280_DIG_T3_LSB_REG                0x8C +#define BMP280_DIG_T3_MSB_REG                0x8D +#define BMP280_DIG_P1_LSB_REG                0x8E +#define BMP280_DIG_P1_MSB_REG                0x8F +#define BMP280_DIG_P2_LSB_REG                0x90 +#define BMP280_DIG_P2_MSB_REG                0x91 +#define BMP280_DIG_P3_LSB_REG                0x92 +#define BMP280_DIG_P3_MSB_REG                0x93 +#define BMP280_DIG_P4_LSB_REG                0x94 +#define BMP280_DIG_P4_MSB_REG                0x95 +#define BMP280_DIG_P5_LSB_REG                0x96 +#define BMP280_DIG_P5_MSB_REG                0x97 +#define BMP280_DIG_P6_LSB_REG                0x98 +#define BMP280_DIG_P6_MSB_REG                0x99 +#define BMP280_DIG_P7_LSB_REG                0x9A +#define BMP280_DIG_P7_MSB_REG                0x9B +#define BMP280_DIG_P8_LSB_REG                0x9C +#define BMP280_DIG_P8_MSB_REG                0x9D +#define BMP280_DIG_P9_LSB_REG                0x9E +#define BMP280_DIG_P9_MSB_REG                0x9F + +#define BMP280_CHIPID_REG                    0xD0  /*Chip ID Register */ +#define BMP280_RESET_REG                     0xE0  /*Softreset Register */ +#define BMP280_STATUS_REG                    0xF3  /*Status Register */ +#define BMP280_CTRLMEAS_REG                  0xF4  /*Ctrl Measure Register */ +#define BMP280_CONFIG_REG                    0xF5  /*Configuration Register */ +#define BMP280_PRESSURE_MSB_REG              0xF7  /*Pressure MSB Register */ +#define BMP280_PRESSURE_LSB_REG              0xF8  /*Pressure LSB Register */ +#define BMP280_PRESSURE_XLSB_REG             0xF9  /*Pressure XLSB Register */ +#define BMP280_TEMPERATURE_MSB_REG           0xFA  /*Temperature MSB Reg */ +#define BMP280_TEMPERATURE_LSB_REG           0xFB  /*Temperature LSB Reg */ +#define BMP280_TEMPERATURE_XLSB_REG          0xFC  /*Temperature XLSB Reg */ + +/* Status Register */ +#define BMP280_STATUS_REG_MEASURING__POS           3 +#define BMP280_STATUS_REG_MEASURING__MSK           0x08 +#define BMP280_STATUS_REG_MEASURING__LEN           1 +#define BMP280_STATUS_REG_MEASURING__REG           BMP280_STATUS_REG + +#define BMP280_STATUS_REG_IMUPDATE__POS            0 +#define BMP280_STATUS_REG_IMUPDATE__MSK            0x01 +#define BMP280_STATUS_REG_IMUPDATE__LEN            1 +#define BMP280_STATUS_REG_IMUPDATE__REG            BMP280_STATUS_REG + +/* Control Measurement Register */ +#define BMP280_CTRLMEAS_REG_OSRST__POS             5 +#define BMP280_CTRLMEAS_REG_OSRST__MSK             0xE0 +#define BMP280_CTRLMEAS_REG_OSRST__LEN             3 +#define BMP280_CTRLMEAS_REG_OSRST__REG             BMP280_CTRLMEAS_REG + +#define BMP280_CTRLMEAS_REG_OSRSP__POS             2 +#define BMP280_CTRLMEAS_REG_OSRSP__MSK             0x1C +#define BMP280_CTRLMEAS_REG_OSRSP__LEN             3 +#define BMP280_CTRLMEAS_REG_OSRSP__REG             BMP280_CTRLMEAS_REG + +#define BMP280_CTRLMEAS_REG_MODE__POS              0 +#define BMP280_CTRLMEAS_REG_MODE__MSK              0x03 +#define BMP280_CTRLMEAS_REG_MODE__LEN              2 +#define BMP280_CTRLMEAS_REG_MODE__REG              BMP280_CTRLMEAS_REG + +/* Configuation Register */ +#define BMP280_CONFIG_REG_TSB__POS                 5 +#define BMP280_CONFIG_REG_TSB__MSK                 0xE0 +#define BMP280_CONFIG_REG_TSB__LEN                 3 +#define BMP280_CONFIG_REG_TSB__REG                 BMP280_CONFIG_REG + +#define BMP280_CONFIG_REG_FILTER__POS              2 +#define BMP280_CONFIG_REG_FILTER__MSK              0x1C +#define BMP280_CONFIG_REG_FILTER__LEN              3 +#define BMP280_CONFIG_REG_FILTER__REG              BMP280_CONFIG_REG + +#define BMP280_CONFIG_REG_SPI3WEN__POS             0 +#define BMP280_CONFIG_REG_SPI3WEN__MSK             0x01 +#define BMP280_CONFIG_REG_SPI3WEN__LEN             1 +#define BMP280_CONFIG_REG_SPI3WEN__REG             BMP280_CONFIG_REG + +/* Data Register */ +#define BMP280_PRESSURE_XLSB_REG_DATA__POS         4 +#define BMP280_PRESSURE_XLSB_REG_DATA__MSK         0xF0 +#define BMP280_PRESSURE_XLSB_REG_DATA__LEN         4 +#define BMP280_PRESSURE_XLSB_REG_DATA__REG         BMP280_PRESSURE_XLSB_REG + +#define BMP280_TEMPERATURE_XLSB_REG_DATA__POS      4 +#define BMP280_TEMPERATURE_XLSB_REG_DATA__MSK      0xF0 +#define BMP280_TEMPERATURE_XLSB_REG_DATA__LEN      4 +#define BMP280_TEMPERATURE_XLSB_REG_DATA__REG      BMP280_TEMPERATURE_XLSB_REG + +#define BMP280_RATE_SCALE  35 +#define DATA_BMP280_MIN_READ_TIME            (32 * NSEC_PER_MSEC) +#define BMP280_DATA_BYTES_9911                6 +#define FAKE_DATA_NUM_BYTES 10 + +/** this structure holds all device specific calibration parameters */ +struct bmp280_calibration_param_t { +	u32 dig_T1; +	s32 dig_T2; +	s32 dig_T3; +	u32 dig_P1; +	s32 dig_P2; +	s32 dig_P3; +	s32 dig_P4; +	s32 dig_P5; +	s32 dig_P6; +	s32 dig_P7; +	s32 dig_P8; +	s32 dig_P9; + +	s32 t_fine; +}; +/** BMP280 image registers data structure */ +struct bmp280_t { +	struct bmp280_calibration_param_t cal_param; + +	u8 chip_id; +	u8 dev_addr; + +	u8 waittime; + +	u8 osrs_t; +	u8 osrs_p; +}; +static struct bmp280_t bmp280; + +static int bmp280_get_calib_param(struct inv_mpu_state *st) +{ +	u8 d[24]; +	int r; + +	r = inv_aux_read(BMP280_DIG_T1_LSB_REG, 24, d); +	if (r) +		return r; + +	bmp280.cal_param.dig_T1 = (u16)((((u16)((u8)d[1])) << +		SHIFT_LEFT_8_POSITION) | d[0]); +	bmp280.cal_param.dig_T2 = (s16)((((s16)((s8)d[3])) << +		SHIFT_LEFT_8_POSITION) | d[2]); +	bmp280.cal_param.dig_T3 = (s16)((((s16)((s8)d[5])) << +		SHIFT_LEFT_8_POSITION) | d[4]); +	bmp280.cal_param.dig_P1 = (u16)((((u16)((u8)d[7])) << +		SHIFT_LEFT_8_POSITION) | d[6]); +	bmp280.cal_param.dig_P2 = (s16)((((s16)((s8)d[9])) << +		SHIFT_LEFT_8_POSITION) | d[8]); +	bmp280.cal_param.dig_P3 = (s16)((((s16)((s8)d[11])) << +		SHIFT_LEFT_8_POSITION) | d[10]); +	bmp280.cal_param.dig_P4 = (s16)((((s16)((s8)d[13])) << +		SHIFT_LEFT_8_POSITION) | d[12]); +	bmp280.cal_param.dig_P5 = (s16)((((s16)((s8)d[15])) << +		SHIFT_LEFT_8_POSITION) | d[14]); +	bmp280.cal_param.dig_P6 = (s16)((((s16)((s8)d[17])) << +		SHIFT_LEFT_8_POSITION) | d[16]); +	bmp280.cal_param.dig_P7 = (s16)((((s16)((s8)d[19])) << +		SHIFT_LEFT_8_POSITION) | d[18]); +	bmp280.cal_param.dig_P8 = (s16)((((s16)((s8)d[21])) << +		SHIFT_LEFT_8_POSITION) | d[20]); +	bmp280.cal_param.dig_P9 = (s16)((((s16)((s8)d[23])) << +		SHIFT_LEFT_8_POSITION) | d[22]); + +	return 0; +} + +static int inv_setup_bmp280(struct inv_mpu_state *st) +{ +	int r; +	u8 d[10]; + +	/* set to bypass mode */ +	r = inv_i2c_single_write(st, REG_INT_PIN_CFG, +				st->plat_data.int_config | BIT_BYPASS_EN); +	if (r) +		return r; +	/* issue soft reset */ +	r = inv_aux_write(BMP280_RESET_REG, BMP280_SOFT_RESET); +	if (r) +		return r; +	msleep(100); +	r = inv_aux_read(BMP280_CHIPID_REG, 1, d); +	if (r) +		return r; +	/* set pressure as ultra high resolution */ +	bmp280.osrs_t = BMP280_ULTRAHIGHRESOLUTION_OSRS_T; +	bmp280.osrs_p = BMP280_ULTRAHIGHRESOLUTION_OSRS_P; + +	/* set IIR filter as 4 */ +	r = inv_aux_write(BMP280_CONFIG_REG_FILTER__REG, +			BMP280_FILTERCOEFF_16 << SHIFT_LEFT_2_POSITION); +	if (r) +		return r; +	r = bmp280_get_calib_param(st); +	if (r) +		return r; + +	/*restore to non-bypass mode */ +	r = inv_i2c_single_write(st, REG_INT_PIN_CFG, +			st->plat_data.int_config); +	if (r) +		return r; + +	/* setup master mode and master clock and ES bit */ +	r = inv_i2c_single_write(st, REG_I2C_MST_CTRL, BIT_WAIT_FOR_ES); +	if (r) +		return r; +	/*slave 3 is used for pressure mode change only*/ +	r = inv_i2c_single_write(st, REG_I2C_SLV3_ADDR, +						st->plat_data.aux_i2c_addr); +	if (r) +		return r; +	/* pressure sensor mode register address */ +	r = inv_i2c_single_write(st, REG_I2C_SLV3_REG, BMP280_CTRLMEAS_REG); +	if (r) +		return r; +	d[0] = (bmp280.osrs_t << SHIFT_LEFT_5_POSITION) + +			(bmp280.osrs_p << SHIFT_LEFT_2_POSITION) + +						BMP280_FORCED_MODE; +	r = inv_i2c_single_write(st, INV_MPU_REG_I2C_SLV3_DO, d[0]); + +	return r; +} + +static int inv_check_bmp280_self_test(struct inv_mpu_state *st) +{ +	return 0; +} +static int inv_write_bmp280_scale(struct inv_mpu_state *st, int data) +{ +	return 0; +} +static int inv_read_bmp280_scale(struct inv_mpu_state *st, int *scale) +{ +	return 0; +} + +static int inv_resume_bmp280(struct inv_mpu_state *st) +{ +	int r; +	u8 bytes, start; + +	bytes = BMP280_DATA_BYTES_9911; +	start = BMP280_PRESSURE_MSB_REG; +	if (st->chip_config.dmp_on) { +		if (st->sensor[SENSOR_COMPASS].on) { +			if (COMPASS_ID_AK09911 != st->plat_data.sec_slave_id) { +				bytes++; +				start -= 1; +			} +		} else { +			/* if compass is disabled, read fake data for DMP */ +			/*read mode */ +			r = inv_i2c_single_write(st, REG_I2C_SLV0_ADDR, +						INV_MPU_BIT_I2C_READ | +						st->plat_data.aux_i2c_addr); +			if (r) +				return r; +			/* read calibration data as the fake data */ +			r = inv_i2c_single_write(st, REG_I2C_SLV0_REG, +						BMP280_DIG_T1_LSB_REG); +			if (r) +				return r; +			/* slave 0 is enabled, read 10 bytes from here */ +			r = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, +						INV_MPU_BIT_SLV_EN | +						FAKE_DATA_NUM_BYTES); +		} +	} +	/* slave 2 is used to read data from pressure sensor */ +	/*read mode */ +	r = inv_i2c_single_write(st, REG_I2C_SLV2_ADDR, +					INV_MPU_BIT_I2C_READ | +					st->plat_data.aux_i2c_addr); +	if (r) +		return r; +	/* start from pressure sensor  */ +	r = inv_i2c_single_write(st, REG_I2C_SLV2_REG, start); +	if (r) +		return r; + +	/* slave 2 is enabled, read 6 or 7 bytes from here */ +	r = inv_i2c_single_write(st, REG_I2C_SLV2_CTRL, +					INV_MPU_BIT_SLV_EN | bytes); +	if (r) +		return r; +	/* slave 3 is enabled, write byte length is 1 */ +	r = inv_i2c_single_write(st, REG_I2C_SLV3_CTRL, +						INV_MPU_BIT_SLV_EN | 1); + +	return r; +} + +static int inv_suspend_bmp280(struct inv_mpu_state *st) +{ +	int r; + +	if ((!st->sensor[SENSOR_COMPASS].on) && st->chip_config.dmp_on) { +		/* slave 0 is disabled */ +		r = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, 0); +		if (r) +			return r; +	} + +	/* slave 2 is disabled */ +	r = inv_i2c_single_write(st, REG_I2C_SLV2_CTRL, 0); +	if (r) +		return r; +	/* slave 3 is disabled */ +	r = inv_i2c_single_write(st, REG_I2C_SLV3_CTRL, 0); + +	return r; +} + +static s32 bmp280_compensate_T_int32(s32 adc_t) +{ +	s32 v_x1_u32r = 0; +	s32 v_x2_u32r = 0; +	s32 temperature = 0; + +	v_x1_u32r  = ((((adc_t >> 3) - ((s32) +		bmp280.cal_param.dig_T1 << 1))) * +		((s32)bmp280.cal_param.dig_T2)) >> 11; +	v_x2_u32r  = (((((adc_t >> 4) - +		((s32)bmp280.cal_param.dig_T1)) * ((adc_t >> 4) - +		((s32)bmp280.cal_param.dig_T1))) >> 12) * +		((s32)bmp280.cal_param.dig_T3)) >> 14; +	bmp280.cal_param.t_fine = v_x1_u32r + v_x2_u32r; +	temperature  = (bmp280.cal_param.t_fine * 5 + 128) >> 8; + +	return temperature; +} + + +static u32 bmp280_compensate_P_int32(s32 adc_p) +{ +	s32 v_x1_u32r = 0; +	s32 v_x2_u32r = 0; +	u32 pressure = 0; + +	v_x1_u32r = (((s32)bmp280.cal_param.t_fine) >> 1) - +		(s32)64000; +	v_x2_u32r = (((v_x1_u32r >> 2) * (v_x1_u32r >> 2)) >> 11) * +		((s32)bmp280.cal_param.dig_P6); +	v_x2_u32r = v_x2_u32r + ((v_x1_u32r * +		((s32)bmp280.cal_param.dig_P5)) << 1); +	v_x2_u32r = (v_x2_u32r >> 2) + +		(((s32)bmp280.cal_param.dig_P4) << 16); +	v_x1_u32r = (((bmp280.cal_param.dig_P3 * (((v_x1_u32r >> 2) * +		(v_x1_u32r >> 2)) >> 13)) >> 3) + +		((((s32)bmp280.cal_param.dig_P2) * +		v_x1_u32r) >> 1)) >> 18; +	v_x1_u32r = ((((32768+v_x1_u32r)) * +		((s32)bmp280.cal_param.dig_P1))	>> 15); +	/* Avoid exception caused by division by zero */ +	if (v_x1_u32r == 0) +		return 0; +	pressure = (((u32)(((s32)1048576) - adc_p) - +		(v_x2_u32r >> 12))) * 3125; +	if (pressure < 0x80000000) +		pressure = (pressure << 1) / ((u32)v_x1_u32r); +	else +		pressure = (pressure / (u32)v_x1_u32r) * 2; +	v_x1_u32r = (((s32)bmp280.cal_param.dig_P9) * +		((s32)(((pressure >> 3) * (pressure >> 3)) >> 13))) +		>> 12; +	v_x2_u32r = (((s32)(pressure >> 2)) * +		((s32)bmp280.cal_param.dig_P8)) >> 13; +	pressure = (u32)((s32)pressure + +		((v_x1_u32r + v_x2_u32r + bmp280.cal_param.dig_P7) >> 4)); + +	return pressure; +} + +static int inv_bmp280_read_data(struct inv_mpu_state *st, short *o) +{ +	int r, i; +	u8 d[BMP280_DATA_BYTES_9911], reg_addr; +	s32 upressure, utemperature; + +	if (st->chip_config.dmp_on) { +		for (i = 0; i < 6; i++) +			d[i] = st->fifo_data[i]; +	} else { +		if (st->sensor[SENSOR_COMPASS].on) { +			if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id) +				reg_addr = REG_EXT_SENS_DATA_09; +			else +				reg_addr = REG_EXT_SENS_DATA_08; +		} else { +			reg_addr = REG_EXT_SENS_DATA_00; +		} +		r = inv_i2c_read(st, reg_addr, BMP280_DATA_BYTES_9911, d); +		if (r) +			return r; +	} +	/* pressure */ +	upressure = (s32)((((s32)(d[0])) +		<< SHIFT_LEFT_12_POSITION) | (((u32)(d[1])) +		<< SHIFT_LEFT_4_POSITION) | ((u32)d[2] >> +		SHIFT_RIGHT_4_POSITION)); + +	/* Temperature */ +	utemperature = (s32)((( +		(s32) (d[3])) << SHIFT_LEFT_12_POSITION) | +		(((u32)(d[4])) << SHIFT_LEFT_4_POSITION) +		| ((u32)d[5] >> SHIFT_RIGHT_4_POSITION)); + +	bmp280_compensate_T_int32(utemperature); +	r = bmp280_compensate_P_int32(upressure); +	o[0] = 0; +	o[1] = (r >> 16); +	o[2] = (r & 0xffff); + +	return 0; +} + +static struct inv_mpu_slave slave_bmp280 = { +	.suspend   = inv_suspend_bmp280, +	.resume    = inv_resume_bmp280, +	.get_scale = inv_read_bmp280_scale, +	.set_scale = inv_write_bmp280_scale, +	.self_test = inv_check_bmp280_self_test, +	.setup     = inv_setup_bmp280, +	.read_data = inv_bmp280_read_data, +	.rate_scale = BMP280_RATE_SCALE, +	.min_read_time = DATA_BMP280_MIN_READ_TIME, +}; + +int inv_mpu_setup_pressure_slave(struct inv_mpu_state *st) +{ +	switch (st->plat_data.aux_slave_id) { +	case PRESSURE_ID_BMP280: +		st->slave_pressure = &slave_bmp280; +		break; +	default: +		return -EINVAL; +	} + +	return st->slave_pressure->setup(st); +} + diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_test/Kconfig b/drivers/iio/imu-aosp/inv_mpu6515/inv_test/Kconfig new file mode 100755 index 00000000000..86c30bd8a63 --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_test/Kconfig @@ -0,0 +1,13 @@ +# +# Kconfig for Invensense IIO testing hooks +# + +config INV_TESTING +    boolean "Invensense IIO testing hooks" +    depends on INV_MPU_IIO || INV_AMI306_IIO || INV_YAS530 || INV_HUB_IIO +    default n +    help +      This flag enables display of additional testing information from the +      Invensense IIO drivers. +      It also enables the I2C counters facility to perform IO profiling. +      Some additional sysfs entries will appear when this flag is enabled. diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_test/Makefile b/drivers/iio/imu-aosp/inv_mpu6515/inv_test/Makefile new file mode 100755 index 00000000000..4f0edd3de90 --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_test/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for Invensense IIO testing hooks. +# + +obj-$(CONFIG_INV_TESTING) += inv_counters.o + diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_test/inv_counters.c b/drivers/iio/imu-aosp/inv_mpu6515/inv_test/inv_counters.c new file mode 100755 index 00000000000..3b26ca97284 --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_test/inv_counters.c @@ -0,0 +1,154 @@ +/* + * @file inv_counters.c + * @brief Exports i2c read write counts through sysfs + * + * @version 0.1 + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/miscdevice.h> +#include <linux/err.h> +#include <linux/sysfs.h> +#include <linux/kdev_t.h> +#include <linux/string.h> +#include <linux/jiffies.h> +#include <linux/spinlock.h> +#include <linux/kernel_stat.h> + +#include "inv_counters.h" + +static int mpu_irq; +static int accel_irq; +static int compass_irq; + +struct inv_counters { +	uint32_t i2c_tempreads; +	uint32_t i2c_mpureads; +	uint32_t i2c_mpuwrites; +	uint32_t i2c_accelreads; +	uint32_t i2c_accelwrites; +	uint32_t i2c_compassreads; +	uint32_t i2c_compasswrites; +	uint32_t i2c_compassirq; +	uint32_t i2c_accelirq; +}; + +static struct inv_counters Counters; + +static ssize_t i2c_counters_show(struct class *cls, +			struct class_attribute *attr, char *buf) +{ +	return scnprintf(buf, PAGE_SIZE, +		"%ld.%03ld %u %u %u %u %u %u %u %u %u %u\n", +		jiffies / HZ, ((jiffies % HZ) * (1024 / HZ)), +		mpu_irq ? kstat_irqs(mpu_irq) : 0, +		Counters.i2c_tempreads, +		Counters.i2c_mpureads, Counters.i2c_mpuwrites, +		accel_irq ? kstat_irqs(accel_irq) : Counters.i2c_accelirq, +		Counters.i2c_accelreads, Counters.i2c_accelwrites, +		compass_irq ? kstat_irqs(compass_irq) : Counters.i2c_compassirq, +		Counters.i2c_compassreads, Counters.i2c_compasswrites); +} + +void inv_iio_counters_set_i2cirq(enum irqtype type, int irq) +{ +	switch (type) { +	case IRQ_MPU: +		mpu_irq = irq; +		break; +	case IRQ_ACCEL: +		accel_irq = irq; +		break; +	case IRQ_COMPASS: +		compass_irq = irq; +		break; +	} +} +EXPORT_SYMBOL_GPL(inv_iio_counters_set_i2cirq); + +void inv_iio_counters_tempread(int count) +{ +	Counters.i2c_tempreads += count; +} +EXPORT_SYMBOL_GPL(inv_iio_counters_tempread); + +void inv_iio_counters_mpuread(int count) +{ +	Counters.i2c_mpureads += count; +} +EXPORT_SYMBOL_GPL(inv_iio_counters_mpuread); + +void inv_iio_counters_mpuwrite(int count) +{ +	Counters.i2c_mpuwrites += count; +} +EXPORT_SYMBOL_GPL(inv_iio_counters_mpuwrite); + +void inv_iio_counters_accelread(int count) +{ +	Counters.i2c_accelreads += count; +} +EXPORT_SYMBOL_GPL(inv_iio_counters_accelread); + +void inv_iio_counters_accelwrite(int count) +{ +	Counters.i2c_accelwrites += count; +} +EXPORT_SYMBOL_GPL(inv_iio_counters_accelwrite); + +void inv_iio_counters_compassread(int count) +{ +	Counters.i2c_compassreads += count; +} +EXPORT_SYMBOL_GPL(inv_iio_counters_compassread); + +void inv_iio_counters_compasswrite(int count) +{ +	Counters.i2c_compasswrites += count; +} +EXPORT_SYMBOL_GPL(inv_iio_counters_compasswrite); + +void inv_iio_counters_compassirq(void) +{ +	Counters.i2c_compassirq++; +} +EXPORT_SYMBOL_GPL(inv_iio_counters_compassirq); + +void inv_iio_counters_accelirq(void) +{ +	Counters.i2c_accelirq++; +} +EXPORT_SYMBOL_GPL(inv_iio_counters_accelirq); + +static struct class_attribute inv_class_attr[] = { +	__ATTR(i2c_counter, S_IRUGO, i2c_counters_show, NULL), +	__ATTR_NULL +}; + +static struct class inv_counters_class = { +	.name = "inv_counters", +	.owner = THIS_MODULE, +	.class_attrs = (struct class_attribute *) &inv_class_attr +}; + +static int __init inv_counters_init(void) +{ +	memset(&Counters, 0, sizeof(Counters)); + +	return class_register(&inv_counters_class); +} + +static void __exit inv_counters_exit(void) +{ +	class_unregister(&inv_counters_class); +} + +module_init(inv_counters_init); +module_exit(inv_counters_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("GESL"); +MODULE_DESCRIPTION("inv_counters debug support"); + diff --git a/drivers/iio/imu-aosp/inv_mpu6515/inv_test/inv_counters.h b/drivers/iio/imu-aosp/inv_mpu6515/inv_test/inv_counters.h new file mode 100755 index 00000000000..d60dac9d97b --- /dev/null +++ b/drivers/iio/imu-aosp/inv_mpu6515/inv_test/inv_counters.h @@ -0,0 +1,72 @@ +/* + * @file  inv_counters.h + * @brief Debug file to keep track of various counters for the InvenSense + *        sensor drivers. + * + * @version 0.1 + */ + +#ifndef _INV_COUNTERS_H_ +#define _INV_COUNTERS_H_ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/sysfs.h> +#include <linux/string.h> +#include <linux/jiffies.h> +#include <linux/spinlock.h> + +#ifdef CONFIG_INV_TESTING + +enum irqtype { +	IRQ_MPU, +	IRQ_ACCEL, +	IRQ_COMPASS +}; + +#define INV_I2C_INC_MPUREAD(x)		inv_iio_counters_mpuread(x) +#define INV_I2C_INC_MPUWRITE(x)		inv_iio_counters_mpuwrite(x) +#define INV_I2C_INC_ACCELREAD(x)	inv_iio_counters_accelread(x) +#define INV_I2C_INC_ACCELWRITE(x)	inv_iio_counters_accelwrite(x) +#define INV_I2C_INC_COMPASSREAD(x)	inv_iio_counters_compassread(x) +#define INV_I2C_INC_COMPASSWRITE(x)	inv_iio_counters_compasswrite(x) + +#define INV_I2C_INC_TEMPREAD(x)		inv_iio_counters_tempread(x) + +#define INV_I2C_SETIRQ(type, irq)	inv_iio_counters_set_i2cirq(type, irq) +#define INV_I2C_INC_COMPASSIRQ()	inv_iio_counters_compassirq() +#define INV_I2C_INC_ACCELIRQ()		inv_iio_counters_accelirq() + +void inv_iio_counters_mpuread(int count); +void inv_iio_counters_mpuwrite(int count); +void inv_iio_counters_accelread(int count); +void inv_iio_counters_accelwrite(int count); +void inv_iio_counters_compassread(int count); +void inv_iio_counters_compasswrite(int count); + +void inv_iio_counters_tempread(int count); + +void inv_iio_counters_set_i2cirq(enum irqtype type, int irq); +void inv_iio_counters_compassirq(void); +void inv_iio_counters_accelirq(void); + +#else + +#define INV_I2C_INC_MPUREAD(x) +#define INV_I2C_INC_MPUWRITE(x) +#define INV_I2C_INC_ACCELREAD(x) +#define INV_I2C_INC_ACCELWRITE(x) +#define INV_I2C_INC_COMPASSREAD(x) +#define INV_I2C_INC_COMPASSWRITE(x) + +#define INV_I2C_INC_TEMPREAD(x) + +#define INV_I2C_SETIRQ(type, irq) +#define INV_I2C_INC_COMPASSIRQ() +#define INV_I2C_INC_ACCELIRQ() + +#endif /* CONFIG_INV_TESTING */ + +#endif /* _INV_COUNTERS_H_ */ + diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 13515f40700..e145931ef1b 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -66,11 +66,6 @@ static const char * const iio_chan_type_name_spec[] = {  	[IIO_ALTVOLTAGE] = "altvoltage",  	[IIO_CCT] = "cct",  	[IIO_PRESSURE] = "pressure", -	[IIO_HEARTRATE] = "heartrate", -	[IIO_PEDOMETER] = "pedometer", -	[IIO_PASSIVE] = "passive", -	[IIO_GESTURE] = "gesture", -	[IIO_FUSION] = "fusion",  };  static const char * const iio_modifier_names[] = { diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 5ef1a396e0c..e86ccb49d9d 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -32,6 +32,31 @@ config SENSORS_LM3533  	  changes. The ALS-control output values can be set per zone for the  	  three current output channels. +config CM32181 +       tristate "CM32181 driver" +       depends on I2C +       help +         Say Y here if you use cm32181. +          +         This option enables ambient light sensor using +         Capella cm32181 device driver. +          +         To compile this driver as a module, choose M here: +         the module will be called cm32181. + +config CM3391 +       tristate "CM3391 driver" +       depends on I2C +       help +         Say Y here if you use cm3391. +          +         This option enables ambient light sensor using +         Capella cm3391 device driver. +          +         To compile this driver as a module, choose M here: +         the module will be called cm3391. + +  config SENSORS_TSL2563  	tristate "TAOS TSL2560, TSL2561, TSL2562 and TSL2563 ambient light sensors"  	depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 040d9c75f8e..fdf3b87e1f1 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -7,3 +7,5 @@ obj-$(CONFIG_SENSORS_LM3533)	+= lm3533-als.o  obj-$(CONFIG_SENSORS_TSL2563)	+= tsl2563.o  obj-$(CONFIG_VCNL4000)		+= vcnl4000.o  obj-$(CONFIG_HID_SENSOR_ALS)	+= hid-sensor-als.o +obj-$(CONFIG_CM32181)     	+= cm32181.o +obj-$(CONFIG_CM3391)     	+= cm3391.o
\ No newline at end of file diff --git a/drivers/iio/light/cm3391.c b/drivers/iio/light/cm3391.c new file mode 100644 index 00000000000..ed2d84a61fe --- /dev/null +++ b/drivers/iio/light/cm3391.c @@ -0,0 +1,784 @@ +/* + * Copyright (C) 2014-2015 Capella Microsystems Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2, as published + * by the Free Software Foundation. + * + * Special thanks Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> + * help to add ACPI support. + * + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/regulator/consumer.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> + +#include <linux/init.h> + +#ifdef	CONFIG_ACPI +#include <linux/acpi.h> +#endif /* CONFIG_ACPI */ + +#define OLDAPI + +/* Registers Address */ +#define CM3391_REG_ADDR_CMD 0x00 +#define CM3391_REG_ADDR_TEST 0x01 +#define CM3391_REG_ADDR_WH 0x02 +#define CM3391_REG_ADDR_WL 0x03 +#define CM3391_REG_ADDR_RED 0x08 +#define CM3391_REG_ADDR_ALS 0x09 +#define CM3391_REG_ADDR_BLUE 0x0A +#define CM3391_REG_ADDR_CLEAR 0x0B +#define CM3391_REG_ADDR_ID 0x0C +#define CM3391_REG_ADDR_STATUS 0x0D + +/* Number of Configurable Registers */ +#define CM3391_CONF_REG_NUM 16 + +/* CMD register */ +#define CM3391_CMD_CS_DISABLE BIT(0) +#define CM3391_CMD_CS_INT_EN BIT(8) + +#define CM3391_CMD_CS_PERS_SHIFT 10 +#define CM3391_CMD_CS_PERS_MASK (0x03 << CM3391_CMD_CS_PERS_SHIFT) +#define CM3391_CMD_CS_PERS_DEFAULT (0x01 << CM3391_CMD_CS_PERS_SHIFT) + +#define CM3391_CMD_CS_IT_SHIFT 4 +#define CM3391_CMD_CS_IT_MASK (0x07 << CM3391_CMD_CS_IT_SHIFT) +#define CM3391_CMD_CS_IT_DEFAULT (0x01 << CM3391_CMD_CS_IT_SHIFT) + +#define CM3391_CMD_CS_HS_SHIFT 3 +#define CM3391_CMD_CS_HS_MASK (0x01 << CM3391_CMD_CS_HS_SHIFT) +#define CM3391_CMD_CS_HS_DEFAULT (0x00 << CM3391_CMD_CS_HS_SHIFT) + +#define CM3391_CMD_DEFAULT (\ +		CM3391_CMD_CS_PERS_DEFAULT |\ +		CM3391_CMD_CS_IT_DEFAULT |\ +		CM3391_CMD_CS_HS_DEFAULT) + +#define CM3391_WH_DEFAULT 0xFFFF +#define CM3391_WL_DEFAULT 0x0000 + +#define CM3391_CALIBSCALE_DEFAULT 100000 +#define CM3391_CALIBSCALE_RESOLUTION 100000 +#define CM3391_MLUX_PER_LUX 1000 +#define CM3391_THRESHOLD_PERCENT 10	/* 10 percent */ + +#define CM3391_MLUX_PER_BIT_DEFAULT 550 +#define CM3391_MLUX_PER_BIT_BASE_IT 100000 +static const int CM3391_cs_it_bits[] = { 0, 1, 2, 3, 4}; +static const int CM3391_cs_it_values[] = { +			50000, 100000, 200000, 400000, 800000}; + +struct cm3391_cs_info { +	u32 id; +	int regs_bmp; +	int calibscale; +	int mlux_per_bit; +	int mlux_per_bit_base_it; +	const int *cs_it_bits; +	const int *cs_it_values; +	const int num_cs_it; +	int als_raw; +}; + +static struct cm3391_cs_info cm3391_cs_info_default = { +	.id = 3391, +	.regs_bmp = 0x0F, +	.calibscale = CM3391_CALIBSCALE_DEFAULT, +	.mlux_per_bit = CM3391_MLUX_PER_BIT_DEFAULT, +	.mlux_per_bit_base_it = CM3391_MLUX_PER_BIT_BASE_IT, +	.cs_it_bits = CM3391_cs_it_bits, +	.cs_it_values = CM3391_cs_it_values, +	.num_cs_it = ARRAY_SIZE(CM3391_cs_it_bits), +}; + +struct cm3391_chip { +	struct i2c_client *client; +	struct mutex lock; +	u16 conf_regs[CM3391_CONF_REG_NUM]; +	struct cm3391_cs_info *cs_info; +}; + +static int cm3391_get_lux(struct cm3391_chip *chip); +static int cm3391_threshold_update(struct cm3391_chip *chip, int percent); +static int cm3391_read_cs_it(struct cm3391_chip *chip, int *val2); + +/** + * cm3391_interrupt_config() - Enable/Disable CM3391 interrupt + * @chip:	pointer of struct cm3391. + * @enable:	0 to disable; otherwise to enable + * + * Config CM3391 interrupt control bit. + * + * Return: 0 for success; otherwise for error code. + */ +static int cm3391_interrupt_config(struct cm3391_chip *chip, int enable) +{ +	struct i2c_client *client = chip->client; +	struct cm3391_cs_info *cs_info = chip->cs_info; +	int status; + +	if (!cs_info) +		return -ENODEV; + +	/* Force to clean interrupt */ +	status = i2c_smbus_read_word_data(client, +			CM3391_REG_ADDR_STATUS); +	if (status < 0) +		return -ENODEV; + +	if (enable) +		chip->conf_regs[CM3391_REG_ADDR_CMD] |= +			CM3391_CMD_CS_INT_EN; +	else +		chip->conf_regs[CM3391_REG_ADDR_CMD] &= +			~CM3391_CMD_CS_INT_EN; + +	status = i2c_smbus_write_word_data(client, CM3391_REG_ADDR_CMD, +		chip->conf_regs[CM3391_REG_ADDR_CMD]); + +	if (status < 0) +		return -ENODEV; + +	return status; +} + +#ifdef CONFIG_ACPI +/** + * cm3391_acpi_get_cpm_info() - Get CPM object from ACPI + * @client	pointer of struct i2c_client. + * @obj_name	pointer of ACPI object name. + * @count	maximum size of return array. + * @vals	pointer of array for return elements. + * + * Convert ACPI CPM table to array. Special thanks Srinivas Pandruvada's + * help to implement this routine. + * + * Return: -ENODEV for fail.  Otherwise is number of elements. + */ +static int cm3391_acpi_get_cpm_info(struct i2c_client *client, char *obj_name, +							int count, u64 *vals) +{ +	acpi_handle handle; +	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; +	int i; +	acpi_status status; +	union acpi_object *cpm; + +	handle = ACPI_HANDLE(&client->dev); +	if (!handle) +		return -ENODEV; + +	status = acpi_evaluate_object(handle, obj_name, NULL, &buffer); +	if (ACPI_FAILURE(status)) { +		dev_err(&client->dev, "object %s not found\n", obj_name); +		return -ENODEV; +	} + +	cpm = buffer.pointer; +	for (i = 0; i < cpm->package.count && i < count; ++i) { +		union acpi_object *elem; +		elem = &(cpm->package.elements[i]); +		vals[i] = elem->integer.value; +	} + +	kfree(buffer.pointer); + +	return cpm->package.count; +} +#endif /* CONFIG_ACPI */ + +/** + * cm3391_reg_init() - Initialize CM3391 registers + * @chip:	pointer of struct cm3391. + * + * Initialize CM3391 color sensor register to default values. + * +  Return: 0 for success; otherwise for error code. + */ +static int cm3391_reg_init(struct cm3391_chip *chip) +{ +	struct i2c_client *client = chip->client; +	int i; +	s32 ret; +	struct cm3391_cs_info *cs_info; +#ifdef CONFIG_ACPI +	int cpm_elem_count; +	u64 cpm_elems[20]; +#endif /* CONFIG_ACPI */ + + +	/* Default device */ +	cs_info = chip->cs_info = &cm3391_cs_info_default; +	chip->conf_regs[CM3391_REG_ADDR_CMD] = CM3391_CMD_DEFAULT; +	chip->conf_regs[CM3391_REG_ADDR_WH] = CM3391_WH_DEFAULT; +	chip->conf_regs[CM3391_REG_ADDR_WL] = CM3391_WL_DEFAULT; + +	/* Disable interrupt */ +	cm3391_interrupt_config(chip, 0); + +	/* Disable Test Mode */ +	i2c_smbus_write_word_data(client, CM3391_REG_ADDR_TEST, 0x0000); + +	/* Disable device */ +	i2c_smbus_write_word_data(client, CM3391_REG_ADDR_CMD, +		CM3391_CMD_CS_DISABLE); + +	/* Identify device */ +	ret = i2c_smbus_read_word_data(client, CM3391_REG_ADDR_ID); +	if (ret < 0) +        return ret; + +	if ((ret & 0xFF) != 0x91) +		return -ENODEV; + +#ifdef CONFIG_ACPI +#error ACPI is enabled, strange! OLIO MFJ + +	if (ACPI_HANDLE(&client->dev)) { +		/* Load from ACPI */ +		cpm_elem_count = cm3391_acpi_get_cpm_info(client, "CPM0", +							ARRAY_SIZE(cpm_elems), +							cpm_elems); +		if (cpm_elem_count > 0) { +			int header_num = 3; +			int reg_num = cpm_elem_count - header_num; + +			cs_info->id = cpm_elems[0]; +			cs_info->regs_bmp = cpm_elems[2]; +			for (i = 0; i < reg_num; i++) +				if (cs_info->regs_bmp & (1<<i)) +					chip->conf_regs[i] = +						cpm_elems[header_num+i]; +		} + +		cpm_elem_count = cm3391_acpi_get_cpm_info(client, "CPM1", +							ARRAY_SIZE(cpm_elems), +							cpm_elems); +		if (cpm_elem_count > 0) { +			cs_info->mlux_per_bit = (int)cpm_elems[0] / 100; +			cs_info->calibscale = (int)cpm_elems[1]; +		} +	} +#endif /* CONFIG_ACPI */ + +	/* Force to disable interrupt */ +	chip->conf_regs[CM3391_REG_ADDR_CMD] &= ~CM3391_CMD_CS_INT_EN; + +	/* Initialize registers */ +	for (i = 0; i < CM3391_CONF_REG_NUM; i++) { +		if (cs_info->regs_bmp & (1<<i)) { +			ret = i2c_smbus_write_word_data(client, i, +						chip->conf_regs[i]); +			if (ret < 0) +                return ret; +		} +	} + +	return 0; +} + +/** + *  cm3391_read_cs_it() - Get sensor integration time (ms) + *  @chip:	pointer of struct cm3391 + *  @val2:	pointer of int to load the cs_it value. + * + *  Report the current integration time in milliseconds. + * + *  Return: IIO_VAL_INT_PLUS_MICRO for success, otherwise -EINVAL. + */ +static int cm3391_read_cs_it(struct cm3391_chip *chip, int *val2) +{ +	struct cm3391_cs_info *cs_info = chip->cs_info; +	u16 cs_it; +	int i; + +	cs_it = chip->conf_regs[CM3391_REG_ADDR_CMD]; +	cs_it &= CM3391_CMD_CS_IT_MASK; +	cs_it >>= CM3391_CMD_CS_IT_SHIFT; +	for (i = 0; i < cs_info->num_cs_it; i++) { +		if (cs_it == cs_info->cs_it_bits[i]) { +			*val2 = cs_info->cs_it_values[i]; +			return IIO_VAL_INT_PLUS_MICRO; +		} +	} + +	return -EINVAL; +} + +#ifndef OLDAPI +/** + * cm3391_write_cs_it() - Write sensor integration time + * @chip:	pointer of struct cm3391. + * @val:	integration time in milliseconds. + * + * Convert integration time (ms) to sensor value. + * + * Return: i2c_smbus_write_word_data command return value. + */ +static int cm3391_write_cs_it(struct cm3391_chip *chip, int val) +{ +	struct i2c_client *client = chip->client; +	struct cm3391_cs_info *cs_info = chip->cs_info; +	u16 cs_it; +	int ret, i; + +	for (i = 0; i < cs_info->num_cs_it; i++) +		if (val <= cs_info->cs_it_values[i]) +			break; +	if (i >= cs_info->num_cs_it) +		i = cs_info->num_cs_it - 1; + +	cs_it = cs_info->cs_it_bits[i]; +	cs_it <<= CM3391_CMD_CS_IT_SHIFT; + +	mutex_lock(&chip->lock); +	chip->conf_regs[CM3391_REG_ADDR_CMD] &= +			~CM3391_CMD_CS_IT_MASK; +	chip->conf_regs[CM3391_REG_ADDR_CMD] |= +			cs_it; +	ret = i2c_smbus_write_word_data(client, CM3391_REG_ADDR_CMD, +			chip->conf_regs[CM3391_REG_ADDR_CMD]); +	mutex_unlock(&chip->lock); + +	return ret; +} +#endif	/* !OLDAPI */ + +/** + * cm3391_get_lux() - report current lux value + * @chip:	pointer of struct cm3391. + * + * Convert sensor raw data to lux.  It depends on integration + * time and calibscale variable. + * + * Return: Positive value is lux, otherwise is error code. + */ +static int cm3391_get_lux(struct cm3391_chip *chip) +{ +	struct i2c_client *client = chip->client; +	struct cm3391_cs_info *cs_info = chip->cs_info; +	int ret; +	int cs_it; +	u64 tmp; + +	/* Calculate mlux per bit based on cs_it */ +	ret = cm3391_read_cs_it(chip, &cs_it); +	if (ret < 0) +		return -EINVAL; +	tmp = (__force u64)cs_info->mlux_per_bit; +	tmp *= cs_info->mlux_per_bit_base_it; +	tmp = div_u64(tmp, cs_it); + +	/* Get als_raw */ +	if (!(chip->conf_regs[CM3391_REG_ADDR_CMD] & CM3391_CMD_CS_INT_EN)) +		cs_info->als_raw = i2c_smbus_read_word_data( +					client, +					CM3391_REG_ADDR_ALS); +	if (cs_info->als_raw < 0) +		return cs_info->als_raw; + +	tmp *= cs_info->als_raw; +	tmp *= cs_info->calibscale; +	tmp = div_u64(tmp, CM3391_CALIBSCALE_RESOLUTION); +	tmp = div_u64(tmp, CM3391_MLUX_PER_LUX); + +	if (tmp > 0xFFFF) +		tmp = 0xFFFF; + +	return (int)tmp; +} + +static int cm3391_read_raw(struct iio_dev *indio_dev, +			struct iio_chan_spec const *chan, +			int *val, int *val2, long mask) +{ +	struct cm3391_chip *chip = iio_priv(indio_dev); +	struct cm3391_cs_info *cs_info = chip->cs_info; +	int ret; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		ret = i2c_smbus_read_word_data(chip->client, chan->address); +		if (ret < 0) { +			return ret; +		} +		*val = ret; +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_PROCESSED: +		ret = cm3391_get_lux(chip); +		if (ret < 0) +			return ret; +		*val = ret; +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_CALIBSCALE: +		*val = cs_info->calibscale; +		return IIO_VAL_INT; +#ifndef OLDAPI +	case IIO_CHAN_INFO_INT_TIME: +		*val = 0; +		ret = cm3391_read_cs_it(chip, val2); +		return ret; +#endif	/* !OLDAPI */ +	} + +	return -EINVAL; +} + +static int cm3391_write_raw(struct iio_dev *indio_dev, +			struct iio_chan_spec const *chan, +			int val, int val2, long mask) +{ +	struct cm3391_chip *chip = iio_priv(indio_dev); +	struct cm3391_cs_info *cs_info = chip->cs_info; +#ifndef OLDAPI +	long ms; +#endif	/* !OLDAPI */ + +	switch (mask) { +	case IIO_CHAN_INFO_CALIBSCALE: +		cs_info->calibscale = val; +		return val; +#ifndef OLDAPI +	case IIO_CHAN_INFO_INT_TIME: +		ms = val * 1000000 + val2; +		return cm3391_write_cs_it(chip, (int)ms); +#endif	/* !OLDAPI */ +	} + +	return -EINVAL; +} + +/** + * cm3391_get_it_available() - Get available IT value + * @dev:	pointer of struct device. + * @attr:	pointer of struct device_attribute. + * @buf:	pointer of return string buffer. + * + * Display the available integration time in milliseconds. + * + * Return: string length. + */ +static ssize_t cm3391_get_it_available(struct device *dev, +			struct device_attribute *attr, char *buf) +{ +	struct cm3391_chip *chip = iio_priv(dev_to_iio_dev(dev)); +	struct cm3391_cs_info *cs_info = chip->cs_info; +	int i, len; + +	for (i = 0, len = 0; i < cs_info->num_cs_it; i++) +		len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ", +			cs_info->cs_it_values[i]/1000000, +			cs_info->cs_it_values[i]%1000000); +	return len + scnprintf(buf + len, PAGE_SIZE - len, "\n"); +} + +/** + * cm3391_threshold_update() - Update the threshold registers. + * @chip:	pointer of struct cm3391_chip. + * @percent:	+/- percent. + * + * Based on the current ALS value, update the hi and low threshold registers. + * + * Return: 0 for success; otherwise for error code. + */ +static int cm3391_threshold_update(struct cm3391_chip *chip, int percent) +{ +	struct i2c_client *client = chip->client; +	struct cm3391_cs_info *cs_info = chip->cs_info; +	int ret; +	int wh, wl; + +	ret = cs_info->als_raw = i2c_smbus_read_word_data(client, +					CM3391_REG_ADDR_ALS); +	if (ret < 0) +		return ret; + +	wh = wl = ret; +	ret *= percent; +	ret /= 100; +	if (ret < 1) +		ret = 1; +	wh += ret; +	wl -= ret; +	if (wh > 65535) +		wh = 65535; +	if (wl < 0) +		wl = 0; + +	chip->conf_regs[CM3391_REG_ADDR_WH] = wh; +	ret = i2c_smbus_write_word_data( +		client, +		CM3391_REG_ADDR_WH, +		chip->conf_regs[CM3391_REG_ADDR_WH]); +	if (ret < 0) +		return ret; + +	chip->conf_regs[CM3391_REG_ADDR_WL] = wl; +	ret = i2c_smbus_write_word_data( +		client, +		CM3391_REG_ADDR_WL, +		chip->conf_regs[CM3391_REG_ADDR_WL]); + +	return ret; +} + +/** + * cm3391_event_handler() - Interrupt handling routine. + * @irq:	irq number. + * @private:	pointer of void. + * + * Clean interrupt and reset threshold registers. + * + * Return: IRQ_HANDLED. + */ +static irqreturn_t cm3391_event_handler(int irq, void *private) +{ +	struct iio_dev *dev_info = private; +	struct cm3391_chip *chip = iio_priv(dev_info); +	int ret; + +	mutex_lock(&chip->lock); + +	/* Disable interrupt */ +	ret = cm3391_interrupt_config(chip, 0); +	if (ret < 0) +		goto error_handler_unlock; + +	/* Update Hi/Lo windows */ +	ret = cm3391_threshold_update(chip, CM3391_THRESHOLD_PERCENT); +	if (ret < 0) +		goto error_handler_unlock; + +	/* Enable interrupt */ +	cm3391_interrupt_config(chip, 1); + +error_handler_unlock: +	mutex_unlock(&chip->lock); +	return IRQ_HANDLED; +} + +#ifdef OLDAPI +#define CM3391_COLOR_CHANNEL(_color, _addr) { \ +	.type = IIO_INTENSITY, \ +	.modified = 1, \ +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ +	.channel2 = IIO_MOD_LIGHT_##_color, \ +	.address = _addr, \ +} + +static const struct iio_chan_spec cm3391_channels[] = { +	{ +		.type = IIO_LIGHT, +		.info_mask_separate = +			BIT(IIO_CHAN_INFO_PROCESSED) | +			BIT(IIO_CHAN_INFO_CALIBSCALE) +	}, +	CM3391_COLOR_CHANNEL(RED, CM3391_REG_ADDR_RED), +	CM3391_COLOR_CHANNEL(GREEN, CM3391_REG_ADDR_ALS), +	CM3391_COLOR_CHANNEL(BLUE, CM3391_REG_ADDR_BLUE), +	CM3391_COLOR_CHANNEL(CLEAR, CM3391_REG_ADDR_CLEAR), +}; + +#else +#define CM3391_COLOR_CHANNEL(_color, _addr) { \ +	.type = IIO_INTENSITY, \ +	.modified = 1, \ +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ +	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \ +	.channel2 = IIO_MOD_LIGHT_##_color, \ +	.address = _addr, \ +} + +static const struct iio_chan_spec cm3391_channels[] = { +	{ +		.type = IIO_LIGHT, +		.info_mask_separate = +			BIT(IIO_CHAN_INFO_PROCESSED) | +			BIT(IIO_CHAN_INFO_CALIBSCALE) | +			BIT(IIO_CHAN_INFO_INT_TIME), +	}, +	CM3391_COLOR_CHANNEL(RED, CM3391_REG_ADDR_RED), +	CM3391_COLOR_CHANNEL(GREEN, CM3391_REG_ADDR_ALS), +	CM3391_COLOR_CHANNEL(BLUE, CM3391_REG_ADDR_BLUE), +	CM3391_COLOR_CHANNEL(CLEAR, CM3391_REG_ADDR_CLEAR), +}; +#endif	/* !OLDAPI */ + +static IIO_DEVICE_ATTR(in_illuminance_integration_time_available, +			S_IRUGO, cm3391_get_it_available, NULL, 0); + +static struct attribute *cm3391_attributes[] = { +	&iio_dev_attr_in_illuminance_integration_time_available.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group cm3391_attribute_group = { +	.attrs = cm3391_attributes +}; + +static const struct iio_info cm3391_info = { +	.driver_module		= THIS_MODULE, +	.read_raw		= &cm3391_read_raw, +	.write_raw		= &cm3391_write_raw, +	.attrs			= &cm3391_attribute_group, +}; + +static int cm3391_probe(struct i2c_client *client, +			const struct i2c_device_id *id) +{ +	struct cm3391_chip *chip; +	struct iio_dev *indio_dev; +	int ret; + +#ifdef OLDAPI +	indio_dev = iio_device_alloc(sizeof(*chip)); +	if (!indio_dev) { +		dev_err(&client->dev, "iio_device_alloc fails\n"); +		return -ENOMEM; +	} +#else +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); +	if (!indio_dev) { +		dev_err(&client->dev, "devm_iio_device_alloc failed\n"); +		return -ENOMEM; +	} +#endif	/* OLDAPI */ + +	chip = iio_priv(indio_dev); +	i2c_set_clientdata(client, indio_dev); +	chip->client = client; + +	mutex_init(&chip->lock); +	indio_dev->dev.parent = &client->dev; +	indio_dev->channels = cm3391_channels; +	indio_dev->num_channels = ARRAY_SIZE(cm3391_channels); +	indio_dev->info = &cm3391_info; +	if (id && id->name) +		indio_dev->name = id->name; +	else +		indio_dev->name = (char *)dev_name(&client->dev); +	indio_dev->channels = cm3391_channels; +	indio_dev->num_channels = ARRAY_SIZE(cm3391_channels); +	indio_dev->modes = INDIO_DIRECT_MODE; + +	ret = cm3391_reg_init(chip); +	if (ret) { +		dev_err(&client->dev, +			"%s: register init failed\n", +			__func__); +		return ret; +	} + +	if (client->irq) { +		ret = request_threaded_irq(client->irq, +					NULL, +					cm3391_event_handler, +					IRQF_TRIGGER_FALLING | IRQF_ONESHOT, +					"cm3391_event", +					indio_dev); + +		if (ret < 0) { +			dev_err(&client->dev, "irq request error %d\n", +				-ret); +			goto error_disable_int; +		} +	} + +	ret = iio_device_register(indio_dev); +	if (ret < 0) { +		dev_err(&client->dev, +			"%s: regist device failed\n", +			__func__); +		goto error_free_irq; +	} + +	if (client->irq) { +		ret = cm3391_threshold_update(chip, CM3391_THRESHOLD_PERCENT); +		if (ret < 0) +			goto error_free_irq; + +		ret = cm3391_interrupt_config(chip, 1); +		if (ret < 0) +			goto error_free_irq; +	} + +	return 0; + +error_free_irq: +	if (client->irq) +		free_irq(client->irq, indio_dev); +#ifdef OLDAPI +	iio_device_free(indio_dev); +#endif	/* OLDAPI */ +error_disable_int: +	cm3391_interrupt_config(chip, 0); + +	return ret; +} + +static int cm3391_remove(struct i2c_client *client) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(client); +	struct cm3391_chip *chip = iio_priv(indio_dev); + +	cm3391_interrupt_config(chip, 0); +	if (client->irq) +		free_irq(client->irq, indio_dev); +	iio_device_unregister(indio_dev); +#ifdef OLDAPI +	iio_device_free(indio_dev); +#endif	/* OLDAPI */ +	return 0; +} + +static const struct i2c_device_id cm3391_id[] = { +	{ "cm3391", 0}, +	{ } +}; + +MODULE_DEVICE_TABLE(i2c, cm3391_id); + +static const struct of_device_id cm3391_of_match[] = { +	{ .compatible = "capella,cm3391" }, +	{ } +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id cm3391_acpi_match[] = { +	{ "CPLM3391", 0}, +	{}, +}; + +MODULE_DEVICE_TABLE(acpi, cm3391_acpi_match); +#endif /* CONFIG_ACPI */ + +static struct i2c_driver cm3391_driver = { +	.driver = { +		.name	= "cm3391", +#ifdef CONFIG_ACPI +		.acpi_match_table = ACPI_PTR(cm3391_acpi_match), +#endif /* CONFIG_ACPI */ +		.of_match_table = of_match_ptr(cm3391_of_match), +		.owner	= THIS_MODULE, +	}, +	.id_table	= cm3391_id, +	.probe		= cm3391_probe, +	.remove		= cm3391_remove, +}; + +module_i2c_driver(cm3391_driver); + +MODULE_AUTHOR("Kevin Tsai <ktsai@capellamicro.com>"); +MODULE_DESCRIPTION("CM3391 color sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index 7f328e37fba..f9ea48f27d0 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -11,3 +11,5 @@ st_magn-$(CONFIG_IIO_BUFFER) += st_magn_buffer.o  obj-$(CONFIG_IIO_ST_MAGN_I2C_3AXIS) += st_magn_i2c.o  obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o + +obj-y += inv_compass/ diff --git a/drivers/iio/magnetometer/inv_compass/Kconfig b/drivers/iio/magnetometer/inv_compass/Kconfig new file mode 100755 index 00000000000..aa0ffde1869 --- /dev/null +++ b/drivers/iio/magnetometer/inv_compass/Kconfig @@ -0,0 +1,47 @@ +# +# Kconfig for Invensense IIO compass drivers of 3rd party compass devices. +# + +# Yamaha YAS530/YAS532/YAS533 +config INV_YAS53X_IIO +    tristate "Invensense IIO driver for Yamaha YAS530/YAS532/YAS533 compass" +    depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF +    default n +    help +      This driver supports the Yamaha YAS530/YAS532/YAS533. It is the Invensense +      implementation of YAS53x series compass devices. +      This driver can be built as a module. The module will be called +      inv_yas53x_iio. + +# Aichi AMI306 +config INV_AMI306_IIO +    tristate "Invensense IIO driver for Aichi AMI306 compass" +    depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF +    default n +    help +      This driver supports the Aichi AMI306 compass. It is the Invensense +      IIO implementation for the AMI306 compass device. +      This driver can be built as a module. The module will be called +      inv-ami306-iio. + +# Asahi Kasei AK8975/AK8972/AK8963 +config INV_AK89XX_IIO +    tristate "Invensense IIO driver for Asahi Kasei AK8975/AK8972/AK8963 compass" +    depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF +    default n +    help +      This driver supports the Asahi Kasei AK8975/AK8972/AK8963 compasses. It is the Invensense +      IIO implementation of AK89xx series compass devices. +      This driver can be built as a module. The module will be called +      inv-ak89xx-iio. + +# Asahi Kasei AK09911 +config INV_AK09911_IIO +    tristate "Invensense IIO driver for Asahi Kasei AK09911 compass" +    depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF +    default n +    help +      This driver supports the Asahi Kasei AK09911 compasses. It is the Invensense +      IIO implementation of AK09911 compass devices. +      This driver can be built as a module. The module will be called +      inv-ak09911-iio. diff --git a/drivers/iio/magnetometer/inv_compass/Makefile b/drivers/iio/magnetometer/inv_compass/Makefile new file mode 100755 index 00000000000..568e5e2bf71 --- /dev/null +++ b/drivers/iio/magnetometer/inv_compass/Makefile @@ -0,0 +1,48 @@ +# +# Makefile for Invensense IIO compass drivers of 3rd party compass devices. +# + +# Yamaha YAS530/YAS532/YAS533 +obj-$(CONFIG_INV_YAS53X_IIO) += inv_yas53x.o + +inv_yas53x-objs := inv_yas53x_core.o +inv_yas53x-objs += inv_yas53x_ring.o +inv_yas53x-objs += inv_yas53x_trigger.o + +CFLAGS_inv_yas53x_core.o    += -Idrivers/staging/iio +CFLAGS_inv_yas53x_ring.o    += -Idrivers/staging/iio +CFLAGS_inv_yas53x_trigger.o += -Idrivers/staging/iio + +# Aichi AMI306 +obj-$(CONFIG_INV_AMI306_IIO) += inv-ami306-iio.o + +inv-ami306-iio-objs := inv_ami306_core.o +inv-ami306-iio-objs += inv_ami306_ring.o +inv-ami306-iio-objs += inv_ami306_trigger.o + +CFLAGS_inv_ami306_core.o    += -Idrivers/staging/iio +CFLAGS_inv_ami306_ring.o    += -Idrivers/staging/iio +CFLAGS_inv_ami306_trigger.o += -Idrivers/staging/iio + +# Asahi Kasei AK8975/AK8972/AK8963 +obj-$(CONFIG_INV_AK89XX_IIO) += inv-ak89xx-iio.o + +inv-ak89xx-iio-objs := inv_ak89xx_core.o +inv-ak89xx-iio-objs += inv_ak89xx_ring.o +inv-ak89xx-iio-objs += inv_ak89xx_trigger.o + +CFLAGS_inv_ak89xx_core.o    += -Idrivers/staging/iio +CFLAGS_inv_ak89xx_ring.o    += -Idrivers/staging/iio +CFLAGS_inv_ak89xx_trigger.o += -Idrivers/staging/iio + +# Asahi Kasei AK09911 +obj-$(CONFIG_INV_AK09911_IIO) += inv-ak09911-iio.o + +inv-ak09911-iio-objs := inv_ak09911_core.o +inv-ak09911-iio-objs += inv_ak09911_ring.o +inv-ak09911-iio-objs += inv_ak09911_trigger.o + +CFLAGS_inv_ak09911_core.o    += -Idrivers/staging/iio +CFLAGS_inv_ak09911_ring.o    += -Idrivers/staging/iio +CFLAGS_inv_ak09911_trigger.o += -Idrivers/staging/iio + diff --git a/drivers/iio/magnetometer/inv_compass/README b/drivers/iio/magnetometer/inv_compass/README new file mode 100755 index 00000000000..54f2bb8ded2 --- /dev/null +++ b/drivers/iio/magnetometer/inv_compass/README @@ -0,0 +1,176 @@ +Kernel driver +Author: Invensense <http://invensense.com> + +Table of Contents: +================== +- Description +- Integrating the Driver in the Linux Kernel +- Board and Platform Data +    > Platform Data +- Board File Modifications for compass +    > AMI306 +    > YAS530/532/533 +- IIO Subsystem +    > Communicating with the Driver in Userspace +- Streaming Data to an Userspace Application +- Test Applications +    > Running Test Applications with AMI306 or YAS53x + +Description +=========== +This document describes how to install the Invensense device driver for AMI306 +and YAS53x series compass chip into a Linux kernel. The Invensense driver +currently supports the following sensors: +- AMI306 +- YAS530 +- YAS532 +- YAS533 + +Please refer to the appropriate product specification +document for further information regarding the slave address. + +The following files are included in this package: +- Kconfig +- Makefile +- inv_ami306_core.c +- inv_ami306_ring.c +- inv_ami306_trigger.c +- inv_ami306_iio.h +- inv_yas53x_core.c +- inv_yas53x_ring.c +- inv_yas53x_trigger.c +- inv_yas53x_iio.h + +Integrating the Driver in the Linux Kernel +========================================== +Please add the files as follows: +- Add all above files to drivers/staging/iio/magnetometer/inv_compass +(another directory is acceptable, but this is the recommended destination) + +In order to see the driver in menuconfig when building the kernel, please +make modifications as shown below: + +    modify "drivers/staging/iio/magnetometer/Kconfig" with: +    >> source "drivers/staging/iio/magnetometer/inv_compass/Kconfig" + +    modify "drivers/staging/iio/magnetometer/Makefile" with: +    >> obj-y += inv_compass/ + + +Board and Platform Data +======================= +In order to recognize the Invensense device on the I2C bus, the board file must +be modified. +The i2c_board_info instance must be defined as shown below. + +Platform Data +------------- +The platform data (orientation matrix and secondary bus configurations) must be +modified as show below, according to your particular platform configuration. + +Board File Modifications for Secondary I2C Configuration +======================================================== +For the Panda Board, the board file can be found at +arch/arm/mach-omap2/board-omap4panda.c. +Please modify the pertinent baord file in your system according to the examples +shown below: + +AMI306 +------------------------------------------------- +static struct mpu_platform_data compass_data = { +        .orientation = {   0,  0,  1, +                           0,  1,  0, +                           1,  0,  0 }, +}; + +static struct i2c_board_info __initdata chip_board_info[] = { +        { +                I2C_BOARD_INFO("ami306", 0x0E), +                .platform_data = &compass_data, +        }, +}; + +YAS53x(Use YAS532 as an example) +------------------------------------------------- +static struct mpu_platform_data compass_data = { +        .orientation = {   0,  -1, 0, +                           1,  0,  0, +                           0,  0,  1 }, +}; + +static struct i2c_board_info __initdata compass_board_info[] = { +        { +                I2C_BOARD_INFO("yas532", 0x2E), +                .platform_data = &compass_data, +        }, +}; + +IIO subsystem +============= +A successful installation will create the following two new directories under +/sys/bus/iio/devices: +    - iio:device0 +    - trigger0 + +Also, a new file, "iio:device0", will be created in the /dev/ diretory. +(if you have more than one IIO device, the file will be named "iio:deviceX", +where X is a number) + + +Communicating with the Driver in Userspace +------------------------------------------ +The driver generates several files in sysfs upon installation. +These files are used to communicate with the driver. The files can be found +at /sys/bus/iio/devices/iio:device0 (or ../iio:deviceX as shown above). + +A brief description of the pertinent files for each Invensense device is shown +below: + +AMI306 +-------- +compass_matrix (read-only) +--show the orientation matrix obtained from the board file. + +sampling_frequency(read and write) +--show and change the sampling rate of the sensor. + +YAS53x +--------------------- +YAS53x has all the attributes AMI306 has. It has one more additional attribute: + +overunderflow(read-only) +--value 1 shows an overflow or underflow happens. Need to write into it to make +  it zero. + +Streaming Data to an Userspace Application +========================================== +When streaming data to an userspace application, we recommend that you access +compass data via /dev/iio:device0. + +Please follow the steps below to read data at a constant rate from the driver: + +1. Write the desired output rate to sampling_frequency. +2. Write 1 to enable to turn on the event. +3. Read /dev/iio:device0 to get a string of gyro/accel/compass data. +4. Parse this string to obtain each compass element. + +Test Applications +================= +A test application is located under software/simple_apps/mpu_iio. +This application is stand-alone in that it cannot be run concurrently with other +entities trying to access the device node(s) or sysfs entries; in particular, +the + +Running Test Applications +--------------------------------------------------------- +To run test applications with AMI306 or YAS53x devices, +please use the following commands: + +1. for ami306: +   mpu_iio -n ami306 -c 10 -l 3 + +2. for yas532: +   mpu_iio -n yas532 -c 10 -l 3 + +Please use mpu_iio.c and iio_utils.h as example code for your development +purposes. diff --git a/drivers/iio/magnetometer/inv_compass/inv_ak09911_core.c b/drivers/iio/magnetometer/inv_compass/inv_ak09911_core.c new file mode 100755 index 00000000000..a52fb403207 --- /dev/null +++ b/drivers/iio/magnetometer/inv_compass/inv_ak09911_core.c @@ -0,0 +1,512 @@ +/* +* Copyright (C) 2013 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +* GNU General Public License for more details. +*/ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> + +#include "inv_ak09911_iio.h" +#include "sysfs.h" +#include "inv_test/inv_counters.h" + +static s64 get_time_ns(void) +{ +	struct timespec ts; +	ktime_get_ts(&ts); +	return timespec_to_ns(&ts); +} + +/** + *  inv_serial_read() - Read one or more bytes from the device registers. + *  @st:     Device driver instance. + *  @reg:    First device register to be read from. + *  @length: Number of bytes to read. + *  @data:   Data read from device. + *  NOTE:    The slave register will not increment when reading from the FIFO. + */ +int inv_serial_read(struct inv_ak09911_state_s *st, u8 reg, u16 length, u8 *data) +{ +	int result; +	INV_I2C_INC_COMPASSWRITE(3); +	INV_I2C_INC_COMPASSREAD(length); +	result = i2c_smbus_read_i2c_block_data(st->i2c, reg, length, data); +	if (result != length) { +		if (result < 0) +			return result; +		else +			return -EINVAL; +	} else { +		return 0; +	} +} + +/** + *  inv_serial_single_write() - Write a byte to a device register. + *  @st:	Device driver instance. + *  @reg:	Device register to be written to. + *  @data:	Byte to write to device. + */ +int inv_serial_single_write(struct inv_ak09911_state_s *st, u8 reg, u8 data) +{ +	u8 d[1]; +	d[0] = data; +	INV_I2C_INC_COMPASSWRITE(3); + +	return i2c_smbus_write_i2c_block_data(st->i2c, reg, 1, d); +} + +static int ak09911_init(struct inv_ak09911_state_s *st) +{ +	int result = 0; +	unsigned char serial_data[3]; + +	result = inv_serial_single_write(st, AK09911_REG_CNTL, +					 AK09911_CNTL_MODE_POWER_DOWN); +	if (result) { +		pr_err("%s, line=%d\n", __func__, __LINE__); +		return result; +	} +	/* Wait at least 100us */ +	udelay(100); + +	result = inv_serial_single_write(st, AK09911_REG_CNTL, +					 AK09911_CNTL_MODE_FUSE_ACCESS); +	if (result) { +		pr_err("%s, line=%d\n", __func__, __LINE__); +		return result; +	} + +	/* Wait at least 200us */ +	udelay(200); + +	result = inv_serial_read(st, AK09911_FUSE_ASAX, 3, serial_data); +	if (result) { +		pr_err("%s, line=%d\n", __func__, __LINE__); +		return result; +	} + +	st->asa[0] = serial_data[0]; +	st->asa[1] = serial_data[1]; +	st->asa[2] = serial_data[2]; + +	result = inv_serial_single_write(st, AK09911_REG_CNTL, +					 AK09911_CNTL_MODE_POWER_DOWN); +	if (result) { +		pr_err("%s, line=%d\n", __func__, __LINE__); +		return result; +	} +	udelay(100); + +	return result; +} + +int ak09911_read(struct inv_ak09911_state_s *st, short rawfixed[3]) +{ +	unsigned char regs[8]; +	unsigned char *stat = ®s[0]; +	unsigned char *stat2 = ®s[8]; +	int result = 0; +	int status = 0; + +	result = inv_serial_read(st, AK09911_REG_ST1, 9, regs); +	if (result) { +		pr_err("%s, line=%d\n", __func__, __LINE__); +	return result; +	} + +	rawfixed[0] = (short)((regs[2]<<8) | regs[1]); +	rawfixed[1] = (short)((regs[4]<<8) | regs[3]); +	rawfixed[2] = (short)((regs[6]<<8) | regs[5]); + +	/* +	 * ST : data ready - +	 * Measurement has been completed and data is ready to be read. +	 */ +	if (*stat & 0x01) +		status = 0; +	/* +	 * ST2 : overflow - +	 * the sum of the absolute values of all axis |X|+|Y|+|Z| < 2400uT. +	 * This is likely to happen in presence of an external magnetic +	 * disturbance; it indicates, the sensor data is incorrect and should +	 * be ignored. +	 * An error is returned. +	 * HOFL bit clears when a new measurement starts. +	 */ +	if (*stat2 & 0x08) +		status = 0x08; +	/* +	 * ST : overrun - +	 * the previous sample was not fetched and lost. +	 * Valid in continuous measurement mode only. +	 * In single measurement mode this error should not occour and we +	 * don't consider this condition an error. +	 * DOR bit is self-clearing when ST2 or any meas. data register is +	 * read. +	 */ +	if (*stat & 0x02) { +		/* status = INV_ERROR_COMPASS_DATA_UNDERFLOW; */ +		status = 0; +	} + +	/* +	 * trigger next measurement if: +	 *	- stat is non zero; +	 *	- if stat is zero and stat2 is non zero. +	 * Won't trigger if data is not ready and there was no error. +	 */ +	result = inv_serial_single_write(st, AK09911_REG_CNTL, +				AK09911_CNTL_MODE_SNG_MEASURE); +	if (result) { +		pr_err("%s, line=%d\n", __func__, __LINE__); +		return result; +	} + +	if (status) +		pr_err("%s, line=%d, status=%d\n", __func__, __LINE__, status); + +	return status; +} + +/** + *  ak09911_read_raw() - read raw method. + */ +static int ak09911_read_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int *val, +			      int *val2, +			      long mask) { +	struct inv_ak09911_state_s  *st = iio_priv(indio_dev); +	int scale = 0; + +	switch (mask) { +	case 0: +		if (!(iio_buffer_enabled(indio_dev))) +			return -EINVAL; +		if (chan->type == IIO_MAGN) { +			*val = st->compass_data[chan->channel2 - IIO_MOD_X]; +			return IIO_VAL_INT; +		} + +		return -EINVAL; +	case IIO_CHAN_INFO_SCALE: +		scale = 19661; +		scale *= (1L << 15); +		*val = scale; +			return IIO_VAL_INT; +		return -EINVAL; +	default: +		return -EINVAL; +	} +} + +static ssize_t ak09911_value_show(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_ak09911_state_s *st = iio_priv(indio_dev); +	short c[3]; + +	mutex_lock(&indio_dev->mlock); +	c[0] = st->compass_data[0]; +	c[1] = st->compass_data[1]; +	c[2] = st->compass_data[2]; +	mutex_unlock(&indio_dev->mlock); +	return sprintf(buf, "%d, %d, %d\n", c[0], c[1], c[2]); +} + +static ssize_t ak09911_rate_show(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_ak09911_state_s *st = iio_priv(indio_dev); +	/* transform delay in ms to rate */ +	return sprintf(buf, "%d\n", (1000 / st->delay)); +} + +/** + * ak09911_matrix_show() - show orientation matrix + */ +static ssize_t ak09911_matrix_show(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	signed char *m; +	struct inv_ak09911_state_s *st = iio_priv(indio_dev); +	m = st->plat_data.orientation; +	return sprintf(buf, +		"%d,%d,%d,%d,%d,%d,%d,%d,%d\n", +		m[0],  m[1],  m[2],  m[3], m[4], m[5], m[6], m[7], m[8]); +} + +void set_ak09911_enable(struct iio_dev *indio_dev, bool enable) +{ +	struct inv_ak09911_state_s *st = iio_priv(indio_dev); +	int result = 0; + +	if (enable) { +			result = inv_serial_single_write(st, AK09911_REG_CNTL, +						AK09911_CNTL_MODE_SNG_MEASURE); +			if (result) +				pr_err("%s, line=%d\n", __func__, __LINE__); +			schedule_delayed_work(&st->work, +				msecs_to_jiffies(st->delay)); +	} else { +			cancel_delayed_work_sync(&st->work); +			result = inv_serial_single_write(st, AK09911_REG_CNTL, +						AK09911_CNTL_MODE_POWER_DOWN); +			if (result) +				pr_err("%s, line=%d\n", __func__, __LINE__); +			mdelay(1);	/* wait at least 100us */ +	} +} + +static ssize_t ak09911_rate_store(struct device *dev, +		struct device_attribute *attr, +		const char *buf, size_t count) +{ +	unsigned long data; +	int error; +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_ak09911_state_s *st = iio_priv(indio_dev); + +	error = kstrtoul(buf, 10, &data); +	if (error) +		return error; +	/* transform rate to delay in ms */ +	data = 1000 / data; +	if (data > AK09911_MAX_DELAY) +		data = AK09911_MAX_DELAY; +	if (data < AK09911_MIN_DELAY) +		data = AK09911_MIN_DELAY; +	st->delay = (unsigned int) data; +	return count; +} + +static void ak09911_work_func(struct work_struct *work) +{ +	struct inv_ak09911_state_s *st = +		container_of((struct delayed_work *)work, +			struct inv_ak09911_state_s, work); +	struct iio_dev *indio_dev = iio_priv_to_dev(st); +	unsigned long delay = msecs_to_jiffies(st->delay); + +	mutex_lock(&indio_dev->mlock); +	if (!(iio_buffer_enabled(indio_dev))) +		goto error_ret; + +	st->timestamp = get_time_ns(); +	schedule_delayed_work(&st->work, delay); +	inv_read_ak09911_fifo(indio_dev); +	INV_I2C_INC_COMPASSIRQ(); + +error_ret: +	mutex_unlock(&indio_dev->mlock); +} + +static const struct iio_chan_spec compass_channels[] = { +	{ +		.type = IIO_MAGN, +		.modified = 1, +		.channel2 = IIO_MOD_X, +		.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, +		.scan_index = INV_AK09911_SCAN_MAGN_X, +		.scan_type = IIO_ST('s', 16, 16, 0) +	}, { +		.type = IIO_MAGN, +		.modified = 1, +		.channel2 = IIO_MOD_Y, +		.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, +		.scan_index = INV_AK09911_SCAN_MAGN_Y, +		.scan_type = IIO_ST('s', 16, 16, 0) +	}, { +		.type = IIO_MAGN, +		.modified = 1, +		.channel2 = IIO_MOD_Z, +		.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, +		.scan_index = INV_AK09911_SCAN_MAGN_Z, +		.scan_type = IIO_ST('s', 16, 16, 0) +	}, +	IIO_CHAN_SOFT_TIMESTAMP(INV_AK09911_SCAN_TIMESTAMP) +}; + +static DEVICE_ATTR(value, S_IRUGO, ak09911_value_show, NULL); +static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR, ak09911_rate_show, +						ak09911_rate_store); +static DEVICE_ATTR(compass_matrix, S_IRUGO, ak09911_matrix_show, NULL); + +static struct attribute *inv_ak09911_attributes[] = { +	&dev_attr_value.attr, +	&dev_attr_sampling_frequency.attr, +	&dev_attr_compass_matrix.attr, +	NULL, +}; + +static const struct attribute_group inv_attribute_group = { +	.name = "ak09911", +	.attrs = inv_ak09911_attributes +}; + +static const struct iio_info ak09911_info = { +	.driver_module = THIS_MODULE, +	.read_raw = &ak09911_read_raw, +	.attrs = &inv_attribute_group, +}; + +/*constant IIO attribute */ +/** + *  inv_ak09911_probe() - probe function. + */ +static int inv_ak09911_probe(struct i2c_client *client, +	const struct i2c_device_id *id) +{ +	struct inv_ak09911_state_s *st; +	struct iio_dev *indio_dev; +	int result; +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { +		result = -ENODEV; +		goto out_no_free; +	} +	indio_dev = iio_allocate_device(sizeof(*st)); +	if (indio_dev == NULL) { +		result =  -ENOMEM; +		goto out_no_free; +	} +	st = iio_priv(indio_dev); +	st->i2c = client; +	st->sl_handle = client->adapter; +	st->plat_data = +		*(struct mpu_platform_data *)dev_get_platdata(&client->dev); +	st->i2c_addr = client->addr; +	st->delay = AK09911_DEFAULT_DELAY; +	st->compass_id = id->driver_data; + +	i2c_set_clientdata(client, indio_dev); +	result = ak09911_init(st); +	if (result) +		goto out_free; + +	indio_dev->dev.parent = &client->dev; +	indio_dev->name = id->name; +	indio_dev->channels = compass_channels; +	indio_dev->num_channels = ARRAY_SIZE(compass_channels); +	indio_dev->info = &ak09911_info; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->currentmode = INDIO_DIRECT_MODE; + +	result = inv_ak09911_configure_ring(indio_dev); +	if (result) +		goto out_free; +	result = iio_buffer_register(indio_dev, indio_dev->channels, +					indio_dev->num_channels); +	if (result) +		goto out_unreg_ring; +	result = inv_ak09911_probe_trigger(indio_dev); +	if (result) +		goto out_remove_ring; + +	result = iio_device_register(indio_dev); +	if (result) +		goto out_remove_trigger; +	INIT_DELAYED_WORK(&st->work, ak09911_work_func); +	pr_info("%s: Probe name %s\n", __func__, id->name); +	return 0; +out_remove_trigger: +	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) +		inv_ak09911_remove_trigger(indio_dev); +out_remove_ring: +	iio_buffer_unregister(indio_dev); +out_unreg_ring: +	inv_ak09911_unconfigure_ring(indio_dev); +out_free: +	iio_free_device(indio_dev); +out_no_free: +	dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); +	return -EIO; +} + +/** + *  inv_ak09911_remove() - remove function. + */ +static int inv_ak09911_remove(struct i2c_client *client) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(client); +	struct inv_ak09911_state_s *st = iio_priv(indio_dev); +	cancel_delayed_work_sync(&st->work); +	iio_device_unregister(indio_dev); +	inv_ak09911_remove_trigger(indio_dev); +	iio_buffer_unregister(indio_dev); +	inv_ak09911_unconfigure_ring(indio_dev); +	iio_free_device(indio_dev); + +	dev_info(&client->adapter->dev, "inv-ak09911-iio module removed.\n"); +	return 0; +} + +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +/* device id table is used to identify what device can be + * supported by this driver + */ +static const struct i2c_device_id inv_ak09911_id[] = { +	{"akm9911", COMPASS_ID_AK09911}, +	{} +}; + +MODULE_DEVICE_TABLE(i2c, inv_ak09911_id); + +static struct i2c_driver inv_ak09911_driver = { +	.class = I2C_CLASS_HWMON, +	.probe		=	inv_ak09911_probe, +	.remove		=	inv_ak09911_remove, +	.id_table	=	inv_ak09911_id, +	.driver = { +		.owner	=	THIS_MODULE, +		.name	=	"inv-ak09911-iio", +	}, +	.address_list = normal_i2c, +}; + +static int __init inv_ak09911_init(void) +{ +	int result = i2c_add_driver(&inv_ak09911_driver); +	if (result) { +		pr_err("%s failed\n", __func__); +		return result; +	} +	return 0; +} + +static void __exit inv_ak09911_exit(void) +{ +	i2c_del_driver(&inv_ak09911_driver); +} + +module_init(inv_ak09911_init); +module_exit(inv_ak09911_exit); + +MODULE_AUTHOR("Invensense Corporation"); +MODULE_DESCRIPTION("Invensense device driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("inv-ak09911-iio"); + diff --git a/drivers/iio/magnetometer/inv_compass/inv_ak09911_iio.h b/drivers/iio/magnetometer/inv_compass/inv_ak09911_iio.h new file mode 100755 index 00000000000..3c96a43d1d6 --- /dev/null +++ b/drivers/iio/magnetometer/inv_compass/inv_ak09911_iio.h @@ -0,0 +1,115 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ +#ifndef _INV_AK09911_IIO_H_ +#define _INV_AK09911_IIO_H_ + +#include <linux/i2c.h> +#include <linux/kfifo.h> +#include <linux/miscdevice.h> +#include <linux/input.h> +#include <linux/spinlock.h> +#include <linux/mpu.h> + +#include "iio.h" +#include "buffer.h" +#include "trigger.h" + +/** + *  struct inv_ak09911_state_s - Driver state variables. + *  @plat_data:     board file platform data. + *  @i2c:           i2c client handle. + *  @trig:          not used. for compatibility. + *  @work:          work data structure. + *  @delay:         delay between each scheduled work. + *  @compass_id:    id of compass. + *  @compass_data:  compass data. + *  @asa:           fuse data to adjust final data. + *  @timestamp:     timestamp of each data. + *  @i2c_addr:      i2c address + *  @sl_handle:		Handle to I2C port. + */ +struct inv_ak09911_state_s { +	struct mpu_platform_data plat_data; +	struct i2c_client *i2c; +	struct iio_trigger  *trig; +	struct delayed_work work; +	int delay;                 /* msec */ +	unsigned char compass_id; +	short compass_data[3]; +	u8 asa[3];	           /* axis sensitivity adjustment */ +	s64 timestamp; +	short i2c_addr; +	void *sl_handle; +}; + +/* scan element definition */ +enum inv_mpu_scan { +	INV_AK09911_SCAN_MAGN_X, +	INV_AK09911_SCAN_MAGN_Y, +	INV_AK09911_SCAN_MAGN_Z, +	INV_AK09911_SCAN_TIMESTAMP, +}; + +/*! \name ak09911 constant definition + \anchor ak09911_Def + Constant definitions of the ak09911.*/ +#define AK09911_MEASUREMENT_TIME_US	10000 + +/*! \name ak09911 operation mode + \anchor AK09911_Mode + Defines an operation mode of the ak09911.*/ +/*! @{*/ +#define AK09911_CNTL_MODE_SNG_MEASURE    0x01 +#define	AK09911_CNTL_MODE_SELF_TEST      0x08 +#define	AK09911_CNTL_MODE_FUSE_ACCESS    0x1F +#define	AK09911_CNTL_MODE_POWER_DOWN     0x00 +/*! @}*/ + +/*! \name ak09911 register address +\anchor AK09911_REG +Defines a register address of the ak09911.*/ +/*! @{*/ +#define AK09911_REG_WIA		0x00 +#define AK09911_REG_INFO	0x01 +#define AK09911_REG_ST1		0x10 +#define AK09911_REG_HXL		0x11 +#define AK09911_REG_ST2		0x18 +#define AK09911_REG_CNTL	0x31 +/*! @}*/ + +/*! \name ak09911 fuse-rom address +\anchor AK09911_FUSE +Defines a read-only address of the fuse ROM of the ak09911.*/ +/*! @{*/ +#define AK09911_FUSE_ASAX	0x60 +/*! @}*/ + +#define AK09911_MAX_DELAY        (200) +#define AK09911_MIN_DELAY        (10) +#define AK09911_DEFAULT_DELAY    (100) + +#define INV_ERROR_COMPASS_DATA_OVERFLOW  (-1) +#define INV_ERROR_COMPASS_DATA_NOT_READY (-2) + +int inv_ak09911_configure_ring(struct iio_dev *indio_dev); +void inv_ak09911_unconfigure_ring(struct iio_dev *indio_dev); +int inv_ak09911_probe_trigger(struct iio_dev *indio_dev); +void inv_ak09911_remove_trigger(struct iio_dev *indio_dev); +void set_ak09911_enable(struct iio_dev *indio_dev, bool enable); +int ak09911_read_raw_data(struct inv_ak09911_state_s *st, +				short dat[3]); +void inv_read_ak09911_fifo(struct iio_dev *indio_dev); +int ak09911_read(struct inv_ak09911_state_s *st, short rawfixed[3]); + +#endif + diff --git a/drivers/iio/magnetometer/inv_compass/inv_ak09911_ring.c b/drivers/iio/magnetometer/inv_compass/inv_ak09911_ring.c new file mode 100755 index 00000000000..ae3119bb5b0 --- /dev/null +++ b/drivers/iio/magnetometer/inv_compass/inv_ak09911_ring.c @@ -0,0 +1,139 @@ +/* +* Copyright (C) 2013 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +* GNU General Public License for more details. +*/ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> + +#include "iio.h" +#include "kfifo_buf.h" +#include "trigger_consumer.h" +#include "sysfs.h" + +#include "inv_ak09911_iio.h" + +static int put_scan_to_buf(struct iio_dev *indio_dev, unsigned char *d, +				short *s, int scan_index) +{ +	struct iio_buffer *ring = indio_dev->buffer; +	int st; +	int i, d_ind; + +	d_ind = 0; +	for (i = 0; i < 3; i++) { +		st = iio_scan_mask_query(indio_dev, ring, scan_index + i); +		if (st) { +			memcpy(&d[d_ind], &s[i], sizeof(s[i])); +			d_ind += sizeof(s[i]); +		} +	} + +	return d_ind; +} + +/** + *  inv_read_ak09911_fifo() - Transfer data from FIFO to ring buffer. + */ +void inv_read_ak09911_fifo(struct iio_dev *indio_dev) +{ +	struct inv_ak09911_state_s *st = iio_priv(indio_dev); +	struct iio_buffer *ring = indio_dev->buffer; +	int d_ind, i; +	s8 *tmp; +	s64 tmp_buf[2]; + +	if (!ak09911_read(st, st->compass_data)) { +		for (i = 0; i < 3; i++) { +			st->compass_data[i] = (short)(((int)st->compass_data[i] +						* (st->asa[i] + 128)) >> 7); +		} +		tmp = (u8 *)tmp_buf; +		d_ind = put_scan_to_buf(indio_dev, tmp, st->compass_data, +						INV_AK09911_SCAN_MAGN_X); +		if (ring->scan_timestamp) +			tmp_buf[(d_ind + 7)/8] = st->timestamp; +		ring->access->store_to(indio_dev->buffer, tmp, st->timestamp); +	} +} + +void inv_ak09911_unconfigure_ring(struct iio_dev *indio_dev) +{ +	iio_kfifo_free(indio_dev->buffer); +}; + +static int inv_ak09911_postenable(struct iio_dev *indio_dev) +{ +	struct inv_ak09911_state_s *st = iio_priv(indio_dev); +	struct iio_buffer *ring = indio_dev->buffer; + +	/* when all the outputs are disabled, even though buffer/enable is on, +	   do nothing */ +	if (!(iio_scan_mask_query(indio_dev, ring, INV_AK09911_SCAN_MAGN_X) || +		iio_scan_mask_query(indio_dev, ring, INV_AK09911_SCAN_MAGN_Y) || +		iio_scan_mask_query(indio_dev, ring, INV_AK09911_SCAN_MAGN_Z))) +		return 0; + +	set_ak09911_enable(indio_dev, true); +	schedule_delayed_work(&st->work, msecs_to_jiffies(st->delay)); + +	return 0; +} + +static int inv_ak09911_predisable(struct iio_dev *indio_dev) +{ +	struct iio_buffer *ring = indio_dev->buffer; +	struct inv_ak09911_state_s *st = iio_priv(indio_dev); + +	cancel_delayed_work_sync(&st->work); +	clear_bit(INV_AK09911_SCAN_MAGN_X, ring->scan_mask); +	clear_bit(INV_AK09911_SCAN_MAGN_Y, ring->scan_mask); +	clear_bit(INV_AK09911_SCAN_MAGN_Z, ring->scan_mask); +	set_ak09911_enable(indio_dev, false); + +	return 0; +} + +static const struct iio_buffer_setup_ops inv_ak09911_ring_setup_ops = { +	.preenable  = &iio_sw_buffer_preenable, +	.postenable = &inv_ak09911_postenable, +	.predisable = &inv_ak09911_predisable, +}; + +int inv_ak09911_configure_ring(struct iio_dev *indio_dev) +{ +	int ret = 0; +	struct iio_buffer *ring; + +	ring = iio_kfifo_allocate(indio_dev); +	if (!ring) { +		ret = -ENOMEM; +		return ret; +	} +	indio_dev->buffer = ring; +	/* setup ring buffer */ +	ring->scan_timestamp = true; +	indio_dev->setup_ops = &inv_ak09911_ring_setup_ops; + +	indio_dev->modes |= INDIO_BUFFER_TRIGGERED; +	return 0; +} + diff --git a/drivers/iio/magnetometer/inv_compass/inv_ak09911_trigger.c b/drivers/iio/magnetometer/inv_compass/inv_ak09911_trigger.c new file mode 100755 index 00000000000..7fc6096ecb0 --- /dev/null +++ b/drivers/iio/magnetometer/inv_compass/inv_ak09911_trigger.c @@ -0,0 +1,75 @@ +/* +* Copyright (C) 2013 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +* GNU General Public License for more details. +*/ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> + +#include "iio.h" +#include "sysfs.h" +#include "trigger.h" +#include "inv_ak09911_iio.h" + +static const struct iio_trigger_ops inv_ak09911_trigger_ops = { +	.owner = THIS_MODULE, +}; + +int inv_ak09911_probe_trigger(struct iio_dev *indio_dev) +{ +	int ret; +	struct inv_ak09911_state_s *st = iio_priv(indio_dev); + +	st->trig = iio_allocate_trigger("%s-dev%d", +					indio_dev->name, +					indio_dev->id); +	if (st->trig == NULL) { +		ret = -ENOMEM; +		goto error_ret; +	} +	/* select default trigger */ +	st->trig->dev.parent = &st->i2c->dev; +	st->trig->private_data = indio_dev; +	st->trig->ops = &inv_ak09911_trigger_ops; +	ret = iio_trigger_register(st->trig); + +	/* select default trigger */ +	indio_dev->trig = st->trig; +	if (ret) +		goto error_free_trig; + +	return 0; + +error_free_trig: +	iio_free_trigger(st->trig); +error_ret: +	return ret; +} + +void inv_ak09911_remove_trigger(struct iio_dev *indio_dev) +{ +	struct inv_ak09911_state_s *st = iio_priv(indio_dev); + +	iio_trigger_unregister(st->trig); +	iio_free_trigger(st->trig); +} + diff --git a/drivers/iio/magnetometer/inv_compass/inv_ak89xx_core.c b/drivers/iio/magnetometer/inv_compass/inv_ak89xx_core.c new file mode 100755 index 00000000000..a781fd39822 --- /dev/null +++ b/drivers/iio/magnetometer/inv_compass/inv_ak89xx_core.c @@ -0,0 +1,590 @@ +/* +* Copyright (C) 2013 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +* GNU General Public License for more details. +*/ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> + +#include "inv_ak89xx_iio.h" +#include "sysfs.h" +#include "inv_test/inv_counters.h" + +static s64 get_time_ns(void) +{ +	struct timespec ts; +	ktime_get_ts(&ts); +	return timespec_to_ns(&ts); +} + +/** + *  inv_serial_read() - Read one or more bytes from the device registers. + *  @st:     Device driver instance. + *  @reg:    First device register to be read from. + *  @length: Number of bytes to read. + *  @data:   Data read from device. + *  NOTE:    The slave register will not increment when reading from the FIFO. + */ +int inv_serial_read(struct inv_ak89xx_state_s *st, u8 reg, u16 length, u8 *data) +{ +	int result; +	INV_I2C_INC_COMPASSWRITE(3); +	INV_I2C_INC_COMPASSREAD(length); +	result = i2c_smbus_read_i2c_block_data(st->i2c, reg, length, data); +	if (result != length) { +		if (result < 0) +			return result; +		else +			return -EINVAL; +	} else { +		return 0; +	} +} + +/** + *  inv_serial_single_write() - Write a byte to a device register. + *  @st:	Device driver instance. + *  @reg:	Device register to be written to. + *  @data:	Byte to write to device. + */ +int inv_serial_single_write(struct inv_ak89xx_state_s *st, u8 reg, u8 data) +{ +	u8 d[1]; +	d[0] = data; +	INV_I2C_INC_COMPASSWRITE(3); + +	return i2c_smbus_write_i2c_block_data(st->i2c, reg, 1, d); +} + +static int ak89xx_init(struct inv_ak89xx_state_s *st) +{ +	int result = 0; +	unsigned char serial_data[3]; + +	result = inv_serial_single_write(st, AK89XX_REG_CNTL, +					 AK89XX_CNTL_MODE_POWER_DOWN); +	if (result) { +		pr_err("%s, line=%d\n", __func__, __LINE__); +		return result; +	} +	/* Wait at least 100us */ +	udelay(100); + +	result = inv_serial_single_write(st, AK89XX_REG_CNTL, +					 AK89XX_CNTL_MODE_FUSE_ACCESS); +	if (result) { +		pr_err("%s, line=%d\n", __func__, __LINE__); +		return result; +	} + +	/* Wait at least 200us */ +	udelay(200); + +	result = inv_serial_read(st, AK89XX_FUSE_ASAX, 3, serial_data); +	if (result) { +		pr_err("%s, line=%d\n", __func__, __LINE__); +		return result; +	} + +	st->asa[0] = serial_data[0]; +	st->asa[1] = serial_data[1]; +	st->asa[2] = serial_data[2]; + +	result = inv_serial_single_write(st, AK89XX_REG_CNTL, +					 AK89XX_CNTL_MODE_POWER_DOWN); +	if (result) { +		pr_err("%s, line=%d\n", __func__, __LINE__); +		return result; +	} +	udelay(100); + +	return result; +} + +int ak89xx_read(struct inv_ak89xx_state_s *st, short rawfixed[3]) +{ +	unsigned char regs[8]; +	unsigned char *stat = ®s[0]; +	unsigned char *stat2 = ®s[7]; +	int result = 0; +	int status = 0; + +	result = inv_serial_read(st, AK89XX_REG_ST1, 8, regs); +	if (result) { +		pr_err("%s, line=%d\n", __func__, __LINE__); +	return result; +	} + +	rawfixed[0] = (short)((regs[2]<<8) | regs[1]); +	rawfixed[1] = (short)((regs[4]<<8) | regs[3]); +	rawfixed[2] = (short)((regs[6]<<8) | regs[5]); + +	/* +	 * ST : data ready - +	 * Measurement has been completed and data is ready to be read. +	 */ +	if (*stat & 0x01) +		status = 0; + +	/* +	 * ST2 : data error - +	 * occurs when data read is started outside of a readable period; +	 * data read would not be correct. +	 * Valid in continuous measurement mode only. +	 * In single measurement mode this error should not occour but we +	 * stil account for it and return an error, since the data would be +	 * corrupted. +	 * DERR bit is self-clearing when ST2 register is read. +	 */ +	if (*stat2 & 0x04) +		status = 0x04; +	/* +	 * ST2 : overflow - +	 * the sum of the absolute values of all axis |X|+|Y|+|Z| < 2400uT. +	 * This is likely to happen in presence of an external magnetic +	 * disturbance; it indicates, the sensor data is incorrect and should +	 * be ignored. +	 * An error is returned. +	 * HOFL bit clears when a new measurement starts. +	 */ +	if (*stat2 & 0x08) +		status = 0x08; +	/* +	 * ST : overrun - +	 * the previous sample was not fetched and lost. +	 * Valid in continuous measurement mode only. +	 * In single measurement mode this error should not occour and we +	 * don't consider this condition an error. +	 * DOR bit is self-clearing when ST2 or any meas. data register is +	 * read. +	 */ +	if (*stat & 0x02) { +		/* status = INV_ERROR_COMPASS_DATA_UNDERFLOW; */ +		status = 0; +	} + +	/* +	 * trigger next measurement if: +	 *	- stat is non zero; +	 *	- if stat is zero and stat2 is non zero. +	 * Won't trigger if data is not ready and there was no error. +	 */ +	if (1) { +		unsigned char scale = 0; +		if (st->compass_id == COMPASS_ID_AK8963) +			scale = st->compass_scale; +		result = inv_serial_single_write(st, AK89XX_REG_CNTL, +				(scale << 4) | AK89XX_CNTL_MODE_SNG_MEASURE); +		if (result) { +			pr_err("%s, line=%d\n", __func__, __LINE__); +			return result; +		} +	} else +		pr_err("%s, no next measure(0x%x,0x%x)\n", __func__, +			*stat, *stat2); + +	if (status) +		pr_err("%s, line=%d, status=%d\n", __func__, __LINE__, status); + +	return status; +} + +/** + *  ak89xx_read_raw() - read raw method. + */ +static int ak89xx_read_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int *val, +			      int *val2, +			      long mask) { +	struct inv_ak89xx_state_s  *st = iio_priv(indio_dev); +	int scale = 0; + +	switch (mask) { +	case 0: +		if (!(iio_buffer_enabled(indio_dev))) +			return -EINVAL; +		if (chan->type == IIO_MAGN) { +			*val = st->compass_data[chan->channel2 - IIO_MOD_X]; +			return IIO_VAL_INT; +		} + +		return -EINVAL; +	case IIO_CHAN_INFO_SCALE: +		if (chan->type == IIO_MAGN) { +			if (st->compass_id == COMPASS_ID_AK8975) +				scale = 9830; +			else if (st->compass_id == COMPASS_ID_AK8972) +				scale = 19661; +			else if (st->compass_id == COMPASS_ID_AK8963) { +				if (st->compass_scale) +					scale = 4915;	/* 16 bit */ +				else +					scale = 19661;	/* 14 bit */ +		} +		scale *= (1L << 15); +		*val = scale; +			return IIO_VAL_INT; +		} +		return -EINVAL; +	default: +		return -EINVAL; +	} +} + +static ssize_t ak89xx_value_show(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_ak89xx_state_s *st = iio_priv(indio_dev); +	short c[3]; + +	mutex_lock(&indio_dev->mlock); +	c[0] = st->compass_data[0]; +	c[1] = st->compass_data[1]; +	c[2] = st->compass_data[2]; +	mutex_unlock(&indio_dev->mlock); +	return sprintf(buf, "%d, %d, %d\n", c[0], c[1], c[2]); +} + +static ssize_t ak89xx_scale_show(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_ak89xx_state_s *st = iio_priv(indio_dev); +	int scale = 0; + +	if (st->compass_id == COMPASS_ID_AK8975) +		scale = 9830; +	else if (st->compass_id == COMPASS_ID_AK8972) +		scale = 19661; +	else if (st->compass_id == COMPASS_ID_AK8963) { +		if (st->compass_scale) +			scale = 4915;	/* 16 bit */ +		else +			scale = 19661;	/* 14 bit */ +	} +	scale *= (1L << 15); +	return sprintf(buf, "%d\n", scale); +} + +static ssize_t ak89xx_rate_show(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_ak89xx_state_s *st = iio_priv(indio_dev); +	/* transform delay in ms to rate */ +	return sprintf(buf, "%d\n", (1000 / st->delay)); +} + +/** + * ak89xx_matrix_show() - show orientation matrix + */ +static ssize_t ak89xx_matrix_show(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	signed char *m; +	struct inv_ak89xx_state_s *st = iio_priv(indio_dev); +	m = st->plat_data.orientation; +	return sprintf(buf, +		"%d,%d,%d,%d,%d,%d,%d,%d,%d\n", +		m[0],  m[1],  m[2],  m[3], m[4], m[5], m[6], m[7], m[8]); +} + +void set_ak89xx_enable(struct iio_dev *indio_dev, bool enable) +{ +	struct inv_ak89xx_state_s *st = iio_priv(indio_dev); +	int result = 0; +	unsigned char scale = 0; + +	if (st->compass_id == COMPASS_ID_AK8963) +		scale = st->compass_scale; + +	if (enable) { +			result = inv_serial_single_write(st, AK89XX_REG_CNTL, +				(scale << 4) | AK89XX_CNTL_MODE_SNG_MEASURE); +			if (result) +				pr_err("%s, line=%d\n", __func__, __LINE__); +			schedule_delayed_work(&st->work, +				msecs_to_jiffies(st->delay)); +	} else { +			cancel_delayed_work_sync(&st->work); +			result = inv_serial_single_write(st, AK89XX_REG_CNTL, +				(scale << 4) | AK89XX_CNTL_MODE_POWER_DOWN); +			if (result) +				pr_err("%s, line=%d\n", __func__, __LINE__); +			mdelay(1);	/* wait at least 100us */ +	} +} + +static ssize_t ak89xx_scale_store(struct device *dev, +		struct device_attribute *attr, +		const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_ak89xx_state_s *st = iio_priv(indio_dev); +	unsigned long data, result; + +	result = kstrtoul(buf, 10, &data); +	if (result) +		return result; +	if (st->compass_id == COMPASS_ID_AK8963) +		st->compass_scale = !!data; +	return count; +} + +static ssize_t ak89xx_rate_store(struct device *dev, +		struct device_attribute *attr, +		const char *buf, size_t count) +{ +	unsigned long data; +	int error; +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_ak89xx_state_s *st = iio_priv(indio_dev); + +	error = kstrtoul(buf, 10, &data); +	if (error) +		return error; +	/* transform rate to delay in ms */ +	data = 1000 / data; +	if (data > AK89XX_MAX_DELAY) +		data = AK89XX_MAX_DELAY; +	if (data < AK89XX_MIN_DELAY) +		data = AK89XX_MIN_DELAY; +	st->delay = (unsigned int) data; +	return count; +} + +static void ak89xx_work_func(struct work_struct *work) +{ +	struct inv_ak89xx_state_s *st = +		container_of((struct delayed_work *)work, +			struct inv_ak89xx_state_s, work); +	struct iio_dev *indio_dev = iio_priv_to_dev(st); +	unsigned long delay = msecs_to_jiffies(st->delay); + +	mutex_lock(&indio_dev->mlock); +	if (!(iio_buffer_enabled(indio_dev))) +		goto error_ret; + +	st->timestamp = get_time_ns(); +	schedule_delayed_work(&st->work, delay); +	inv_read_ak89xx_fifo(indio_dev); +	INV_I2C_INC_COMPASSIRQ(); + +error_ret: +	mutex_unlock(&indio_dev->mlock); +} + +static const struct iio_chan_spec compass_channels[] = { +	{ +		.type = IIO_MAGN, +		.modified = 1, +		.channel2 = IIO_MOD_X, +		.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, +		.scan_index = INV_AK89XX_SCAN_MAGN_X, +		.scan_type = IIO_ST('s', 16, 16, 0) +	}, { +		.type = IIO_MAGN, +		.modified = 1, +		.channel2 = IIO_MOD_Y, +		.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, +		.scan_index = INV_AK89XX_SCAN_MAGN_Y, +		.scan_type = IIO_ST('s', 16, 16, 0) +	}, { +		.type = IIO_MAGN, +		.modified = 1, +		.channel2 = IIO_MOD_Z, +		.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, +		.scan_index = INV_AK89XX_SCAN_MAGN_Z, +		.scan_type = IIO_ST('s', 16, 16, 0) +	}, +	IIO_CHAN_SOFT_TIMESTAMP(INV_AK89XX_SCAN_TIMESTAMP) +}; + +static DEVICE_ATTR(value, S_IRUGO, ak89xx_value_show, NULL); +static DEVICE_ATTR(scale, S_IRUGO | S_IWUSR, ak89xx_scale_show, +						ak89xx_scale_store); +static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR, ak89xx_rate_show, +						ak89xx_rate_store); +static DEVICE_ATTR(compass_matrix, S_IRUGO, ak89xx_matrix_show, NULL); + +static struct attribute *inv_ak89xx_attributes[] = { +	&dev_attr_value.attr, +	&dev_attr_scale.attr, +	&dev_attr_sampling_frequency.attr, +	&dev_attr_compass_matrix.attr, +	NULL, +}; + +static const struct attribute_group inv_attribute_group = { +	.name = "ak89xx", +	.attrs = inv_ak89xx_attributes +}; + +static const struct iio_info ak89xx_info = { +	.driver_module = THIS_MODULE, +	.read_raw = &ak89xx_read_raw, +	.attrs = &inv_attribute_group, +}; + +/*constant IIO attribute */ +/** + *  inv_ak89xx_probe() - probe function. + */ +static int inv_ak89xx_probe(struct i2c_client *client, +	const struct i2c_device_id *id) +{ +	struct inv_ak89xx_state_s *st; +	struct iio_dev *indio_dev; +	int result; +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { +		result = -ENODEV; +		goto out_no_free; +	} +	indio_dev = iio_allocate_device(sizeof(*st)); +	if (indio_dev == NULL) { +		result =  -ENOMEM; +		goto out_no_free; +	} +	st = iio_priv(indio_dev); +	st->i2c = client; +	st->sl_handle = client->adapter; +	st->plat_data = +		*(struct mpu_platform_data *)dev_get_platdata(&client->dev); +	st->i2c_addr = client->addr; +	st->delay = AK89XX_DEFAULT_DELAY; +	st->compass_id = id->driver_data; +	st->compass_scale = 0; + +	i2c_set_clientdata(client, indio_dev); +	result = ak89xx_init(st); +	if (result) +		goto out_free; + +	indio_dev->dev.parent = &client->dev; +	indio_dev->name = id->name; +	indio_dev->channels = compass_channels; +	indio_dev->num_channels = ARRAY_SIZE(compass_channels); +	indio_dev->info = &ak89xx_info; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->currentmode = INDIO_DIRECT_MODE; + +	result = inv_ak89xx_configure_ring(indio_dev); +	if (result) +		goto out_free; +	result = iio_buffer_register(indio_dev, indio_dev->channels, +					indio_dev->num_channels); +	if (result) +		goto out_unreg_ring; +	result = inv_ak89xx_probe_trigger(indio_dev); +	if (result) +		goto out_remove_ring; + +	result = iio_device_register(indio_dev); +	if (result) +		goto out_remove_trigger; +	INIT_DELAYED_WORK(&st->work, ak89xx_work_func); +	pr_info("%s: Probe name %s\n", __func__, id->name); +	return 0; +out_remove_trigger: +	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) +		inv_ak89xx_remove_trigger(indio_dev); +out_remove_ring: +	iio_buffer_unregister(indio_dev); +out_unreg_ring: +	inv_ak89xx_unconfigure_ring(indio_dev); +out_free: +	iio_free_device(indio_dev); +out_no_free: +	dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); +	return -EIO; +} + +/** + *  inv_ak89xx_remove() - remove function. + */ +static int inv_ak89xx_remove(struct i2c_client *client) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(client); +	struct inv_ak89xx_state_s *st = iio_priv(indio_dev); +	cancel_delayed_work_sync(&st->work); +	iio_device_unregister(indio_dev); +	inv_ak89xx_remove_trigger(indio_dev); +	iio_buffer_unregister(indio_dev); +	inv_ak89xx_unconfigure_ring(indio_dev); +	iio_free_device(indio_dev); + +	dev_info(&client->adapter->dev, "inv-ak89xx-iio module removed.\n"); +	return 0; +} + +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +/* device id table is used to identify what device can be + * supported by this driver + */ +static const struct i2c_device_id inv_ak89xx_id[] = { +	{"akm8975", COMPASS_ID_AK8975}, +	{"akm8972", COMPASS_ID_AK8972}, +	{"akm8963", COMPASS_ID_AK8963}, +	{} +}; + +MODULE_DEVICE_TABLE(i2c, inv_ak89xx_id); + +static struct i2c_driver inv_ak89xx_driver = { +	.class = I2C_CLASS_HWMON, +	.probe		=	inv_ak89xx_probe, +	.remove		=	inv_ak89xx_remove, +	.id_table	=	inv_ak89xx_id, +	.driver = { +		.owner	=	THIS_MODULE, +		.name	=	"inv-ak89xx-iio", +	}, +	.address_list = normal_i2c, +}; + +static int __init inv_ak89xx_init(void) +{ +	int result = i2c_add_driver(&inv_ak89xx_driver); +	if (result) { +		pr_err("%s failed\n", __func__); +		return result; +	} +	return 0; +} + +static void __exit inv_ak89xx_exit(void) +{ +	i2c_del_driver(&inv_ak89xx_driver); +} + +module_init(inv_ak89xx_init); +module_exit(inv_ak89xx_exit); + +MODULE_AUTHOR("Invensense Corporation"); +MODULE_DESCRIPTION("Invensense device driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("inv-ak89xx-iio"); + diff --git a/drivers/iio/magnetometer/inv_compass/inv_ak89xx_iio.h b/drivers/iio/magnetometer/inv_compass/inv_ak89xx_iio.h new file mode 100755 index 00000000000..9a9f14a73ec --- /dev/null +++ b/drivers/iio/magnetometer/inv_compass/inv_ak89xx_iio.h @@ -0,0 +1,144 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ +#ifndef _INV_AK89XX_IIO_H_ +#define _INV_AK89XX_IIO_H_ + +#include <linux/i2c.h> +#include <linux/kfifo.h> +#include <linux/miscdevice.h> +#include <linux/input.h> +#include <linux/spinlock.h> +#include <linux/mpu.h> + +#include "iio.h" +#include "buffer.h" +#include "trigger.h" + +/** + *  struct inv_ak89xx_state_s - Driver state variables. + *  @plat_data:     board file platform data. + *  @i2c:           i2c client handle. + *  @trig:          not used. for compatibility. + *  @work:          work data structure. + *  @delay:         delay between each scheduled work. + *  @dev:           Represents read-only node for accessing buffered data. + *  @inv_dev:       Handle to input device. + *  @sl_handle:		Handle to I2C port. + */ +struct inv_ak89xx_state_s { +	struct mpu_platform_data plat_data; +	struct i2c_client *i2c; +	struct iio_trigger  *trig; +	struct delayed_work work; +	int delay;                 /* msec */ +	unsigned char compass_id; +	int compass_scale;         /* for ak8963, 1:16-bit, 0:14-bit */ +	short compass_data[3]; +	u8 asa[3];	           /* axis sensitivity adjustment */ +	s64 timestamp; +	short i2c_addr; +	void *sl_handle; +	struct device *inv_dev; +	struct input_dev *idev; +}; + +/* scan element definition */ +enum inv_mpu_scan { +	INV_AK89XX_SCAN_MAGN_X, +	INV_AK89XX_SCAN_MAGN_Y, +	INV_AK89XX_SCAN_MAGN_Z, +	INV_AK89XX_SCAN_TIMESTAMP, +}; + +#define AK89XX_I2C_NAME "ak89xx" + +#define SENSOR_DATA_SIZE    8 +#define YPR_DATA_SIZE       12 +#define RWBUF_SIZE          16 + +#define ACC_DATA_FLAG       0 +#define MAG_DATA_FLAG       1 +#define ORI_DATA_FLAG       2 +#define AKM_NUM_SENSORS     3 + +#define ACC_DATA_READY		(1 << (ACC_DATA_FLAG)) +#define MAG_DATA_READY		(1 << (MAG_DATA_FLAG)) +#define ORI_DATA_READY		(1 << (ORI_DATA_FLAG)) + +#define AKM_MINOR_NUMBER	254 + +/*! \name AK89XX constant definition + \anchor AK89XX_Def + Constant definitions of the AK89XX.*/ +#define AK89XX_MEASUREMENT_TIME_US	10000 + +/*! \name AK89XX operation mode + \anchor AK89XX_Mode + Defines an operation mode of the AK89XX.*/ +/*! @{*/ +#define AK89XX_CNTL_MODE_SNG_MEASURE    0x01 +#define	AK89XX_CNTL_MODE_SELF_TEST      0x08 +#define	AK89XX_CNTL_MODE_FUSE_ACCESS    0x0F +#define	AK89XX_CNTL_MODE_POWER_DOWN     0x00 +/*! @}*/ + +/*! \name AK89XX register address +\anchor AK89XX_REG +Defines a register address of the AK89XX.*/ +/*! @{*/ +#define AK89XX_REG_WIA		0x00 +#define AK89XX_REG_INFO		0x01 +#define AK89XX_REG_ST1		0x02 +#define AK89XX_REG_HXL		0x03 +#define AK89XX_REG_HXH		0x04 +#define AK89XX_REG_HYL		0x05 +#define AK89XX_REG_HYH		0x06 +#define AK89XX_REG_HZL		0x07 +#define AK89XX_REG_HZH		0x08 +#define AK89XX_REG_ST2		0x09 +#define AK89XX_REG_CNTL		0x0A +#define AK89XX_REG_RSV		0x0B +#define AK89XX_REG_ASTC		0x0C +#define AK89XX_REG_TS1		0x0D +#define AK89XX_REG_TS2		0x0E +#define AK89XX_REG_I2CDIS	0x0F +/*! @}*/ + +/*! \name AK89XX fuse-rom address +\anchor AK89XX_FUSE +Defines a read-only address of the fuse ROM of the AK89XX.*/ +/*! @{*/ +#define AK89XX_FUSE_ASAX	0x10 +#define AK89XX_FUSE_ASAY	0x11 +#define AK89XX_FUSE_ASAZ	0x12 +/*! @}*/ + +#define AK89XX_MAX_DELAY        (200) +#define AK89XX_MIN_DELAY        (10) +#define AK89XX_DEFAULT_DELAY    (100) + +#define INV_ERROR_COMPASS_DATA_OVERFLOW  (-1) +#define INV_ERROR_COMPASS_DATA_NOT_READY (-2) + +int inv_ak89xx_configure_ring(struct iio_dev *indio_dev); +void inv_ak89xx_unconfigure_ring(struct iio_dev *indio_dev); +int inv_ak89xx_probe_trigger(struct iio_dev *indio_dev); +void inv_ak89xx_remove_trigger(struct iio_dev *indio_dev); +void set_ak89xx_enable(struct iio_dev *indio_dev, bool enable); +int ak89xx_read_raw_data(struct inv_ak89xx_state_s *st, +				short dat[3]); +void inv_read_ak89xx_fifo(struct iio_dev *indio_dev); +int ak89xx_read(struct inv_ak89xx_state_s *st, short rawfixed[3]); + +#endif + diff --git a/drivers/iio/magnetometer/inv_compass/inv_ak89xx_ring.c b/drivers/iio/magnetometer/inv_compass/inv_ak89xx_ring.c new file mode 100755 index 00000000000..a8c1090bed5 --- /dev/null +++ b/drivers/iio/magnetometer/inv_compass/inv_ak89xx_ring.c @@ -0,0 +1,138 @@ +/* +* Copyright (C) 2013 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +* GNU General Public License for more details. +*/ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> + +#include "iio.h" +#include "kfifo_buf.h" +#include "trigger_consumer.h" +#include "sysfs.h" + +#include "inv_ak89xx_iio.h" + +static int put_scan_to_buf(struct iio_dev *indio_dev, unsigned char *d, +				short *s, int scan_index) +{ +	struct iio_buffer *ring = indio_dev->buffer; +	int st; +	int i, d_ind; + +	d_ind = 0; +	for (i = 0; i < 3; i++) { +		st = iio_scan_mask_query(indio_dev, ring, scan_index + i); +		if (st) { +			memcpy(&d[d_ind], &s[i], sizeof(s[i])); +			d_ind += sizeof(s[i]); +		} +	} + +	return d_ind; +} + +/** + *  inv_read_ak89xx_fifo() - Transfer data from FIFO to ring buffer. + */ +void inv_read_ak89xx_fifo(struct iio_dev *indio_dev) +{ +	struct inv_ak89xx_state_s *st = iio_priv(indio_dev); +	struct iio_buffer *ring = indio_dev->buffer; +	int d_ind; +	s8 *tmp; +	s64 tmp_buf[2]; + +	if (!ak89xx_read(st, st->compass_data)) { +		st->compass_data[0] = (short)(((int)st->compass_data[0] * (st->asa[0] + 128)) >> 8); +		st->compass_data[1] = (short)(((int)st->compass_data[1] * (st->asa[1] + 128)) >> 8); +		st->compass_data[2] = (short)(((int)st->compass_data[2] * (st->asa[2] + 128)) >> 8); +		tmp = (u8 *)tmp_buf; +		d_ind = put_scan_to_buf(indio_dev, tmp, st->compass_data, +						INV_AK89XX_SCAN_MAGN_X); +		if (ring->scan_timestamp) +			tmp_buf[(d_ind + 7)/8] = st->timestamp; +		ring->access->store_to(indio_dev->buffer, tmp, st->timestamp); +	} +} + +void inv_ak89xx_unconfigure_ring(struct iio_dev *indio_dev) +{ +	iio_kfifo_free(indio_dev->buffer); +}; + +static int inv_ak89xx_postenable(struct iio_dev *indio_dev) +{ +	struct inv_ak89xx_state_s *st = iio_priv(indio_dev); +	struct iio_buffer *ring = indio_dev->buffer; + +	/* when all the outputs are disabled, even though buffer/enable is on, +	   do nothing */ +	if (!(iio_scan_mask_query(indio_dev, ring, INV_AK89XX_SCAN_MAGN_X) || +		iio_scan_mask_query(indio_dev, ring, INV_AK89XX_SCAN_MAGN_Y) || +		iio_scan_mask_query(indio_dev, ring, INV_AK89XX_SCAN_MAGN_Z))) +		return 0; + +	set_ak89xx_enable(indio_dev, true); +	schedule_delayed_work(&st->work, msecs_to_jiffies(st->delay)); + +	return 0; +} + +static int inv_ak89xx_predisable(struct iio_dev *indio_dev) +{ +	struct iio_buffer *ring = indio_dev->buffer; +	struct inv_ak89xx_state_s *st = iio_priv(indio_dev); + +	cancel_delayed_work_sync(&st->work); +	clear_bit(INV_AK89XX_SCAN_MAGN_X, ring->scan_mask); +	clear_bit(INV_AK89XX_SCAN_MAGN_Y, ring->scan_mask); +	clear_bit(INV_AK89XX_SCAN_MAGN_Z, ring->scan_mask); +	set_ak89xx_enable(indio_dev, false); + +	return 0; +} + +static const struct iio_buffer_setup_ops inv_ak89xx_ring_setup_ops = { +	.preenable  = &iio_sw_buffer_preenable, +	.postenable = &inv_ak89xx_postenable, +	.predisable = &inv_ak89xx_predisable, +}; + +int inv_ak89xx_configure_ring(struct iio_dev *indio_dev) +{ +	int ret = 0; +	struct iio_buffer *ring; + +	ring = iio_kfifo_allocate(indio_dev); +	if (!ring) { +		ret = -ENOMEM; +		return ret; +	} +	indio_dev->buffer = ring; +	/* setup ring buffer */ +	ring->scan_timestamp = true; +	indio_dev->setup_ops = &inv_ak89xx_ring_setup_ops; + +	indio_dev->modes |= INDIO_BUFFER_TRIGGERED; +	return 0; +} + diff --git a/drivers/iio/magnetometer/inv_compass/inv_ak89xx_trigger.c b/drivers/iio/magnetometer/inv_compass/inv_ak89xx_trigger.c new file mode 100755 index 00000000000..04c77ab5c79 --- /dev/null +++ b/drivers/iio/magnetometer/inv_compass/inv_ak89xx_trigger.c @@ -0,0 +1,75 @@ +/* +* Copyright (C) 2013 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +* GNU General Public License for more details. +*/ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> + +#include "iio.h" +#include "sysfs.h" +#include "trigger.h" +#include "inv_ak89xx_iio.h" + +static const struct iio_trigger_ops inv_ak89xx_trigger_ops = { +	.owner = THIS_MODULE, +}; + +int inv_ak89xx_probe_trigger(struct iio_dev *indio_dev) +{ +	int ret; +	struct inv_ak89xx_state_s *st = iio_priv(indio_dev); + +	st->trig = iio_allocate_trigger("%s-dev%d", +					indio_dev->name, +					indio_dev->id); +	if (st->trig == NULL) { +		ret = -ENOMEM; +		goto error_ret; +	} +	/* select default trigger */ +	st->trig->dev.parent = &st->i2c->dev; +	st->trig->private_data = indio_dev; +	st->trig->ops = &inv_ak89xx_trigger_ops; +	ret = iio_trigger_register(st->trig); + +	/* select default trigger */ +	indio_dev->trig = st->trig; +	if (ret) +		goto error_free_trig; + +	return 0; + +error_free_trig: +	iio_free_trigger(st->trig); +error_ret: +	return ret; +} + +void inv_ak89xx_remove_trigger(struct iio_dev *indio_dev) +{ +	struct inv_ak89xx_state_s *st = iio_priv(indio_dev); + +	iio_trigger_unregister(st->trig); +	iio_free_trigger(st->trig); +} + diff --git a/drivers/iio/magnetometer/inv_compass/inv_ami306_core.c b/drivers/iio/magnetometer/inv_compass/inv_ami306_core.c new file mode 100755 index 00000000000..612ba72b59e --- /dev/null +++ b/drivers/iio/magnetometer/inv_compass/inv_ami306_core.c @@ -0,0 +1,570 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +* +*/ + +/** + *  @addtogroup  DRIVERS + *  @brief       Hardware drivers. + * + *  @{ + *      @file    inv_ami306_core.c + *      @brief   Invensense implementation for AMI306 + *      @details This driver currently works for the AMI306 + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> + +#include "inv_ami306_iio.h" +#include "sysfs.h" +#include "inv_test/inv_counters.h" + +static unsigned char late_initialize = true; + +s32 i2c_write(const struct i2c_client *client, +		u8 command, u8 length, const u8 *values) +{ +	INV_I2C_INC_COMPASSWRITE(3); +	return i2c_smbus_write_i2c_block_data(client, command, length, values); +} + +s32 i2c_read(const struct i2c_client *client, +		u8 command, u8 length, u8 *values) +{ +	INV_I2C_INC_COMPASSWRITE(3); +	INV_I2C_INC_COMPASSREAD(length); +	return i2c_smbus_read_i2c_block_data(client, command, length, values); +} + +static int ami306_read_param(struct inv_ami306_state_s *st) +{ +	int result = 0; +	unsigned char regs[AMI_PARAM_LEN]; +	struct ami_sensor_parametor *param = &st->param; + +	result = i2c_read(st->i2c, REG_AMI_SENX, +			AMI_PARAM_LEN, regs); +	if (result < 0) +		return result; + +	/* Little endian 16 bit registers */ +	param->m_gain.x = le16_to_cpup((__le16 *)(®s[0])); +	param->m_gain.y = le16_to_cpup((__le16 *)(®s[2])); +	param->m_gain.z = le16_to_cpup((__le16 *)(®s[4])); + +	param->m_interference.xy = regs[7]; +	param->m_interference.xz = regs[6]; +	param->m_interference.yx = regs[9]; +	param->m_interference.yz = regs[8]; +	param->m_interference.zx = regs[11]; +	param->m_interference.zy = regs[10]; + +	param->m_offset.x = AMI_STANDARD_OFFSET; +	param->m_offset.y = AMI_STANDARD_OFFSET; +	param->m_offset.z = AMI_STANDARD_OFFSET; + +	param->m_gain_cor.x = AMI_GAIN_COR_DEFAULT; +	param->m_gain_cor.y = AMI_GAIN_COR_DEFAULT; +	param->m_gain_cor.z = AMI_GAIN_COR_DEFAULT; + +	return 0; +} + +static int ami306_write_offset(const struct i2c_client *client, +				unsigned char *fine) +{ +	int result = 0; +	unsigned char dat[3]; +	dat[0] = (0x7f & fine[0]); +	dat[1] = 0; +	result = i2c_write(client, REG_AMI_OFFX, 2, dat); +	dat[0] = (0x7f & fine[1]); +	dat[1] = 0; +	result = i2c_write(client, REG_AMI_OFFY, 2, dat); +	dat[0] = (0x7f & fine[2]); +	dat[1] = 0; +	result = i2c_write(client, REG_AMI_OFFZ, 2, dat); + +	return result; +} + +static int ami306_wait_data_ready(struct inv_ami306_state_s *st, +				unsigned long usecs, unsigned long times) +{ +	int result = 0; +	unsigned char buf; + +	for (; 0 < times; --times) { +		udelay(usecs); +		result = i2c_read(st->i2c, REG_AMI_STA1, 1, &buf); +		if (result < 0) +			return INV_ERROR_COMPASS_DATA_NOT_READY; +		if (buf & AMI_STA1_DRDY_BIT) +			return 0; +		else if (buf & AMI_STA1_DOR_BIT) +			return INV_ERROR_COMPASS_DATA_OVERFLOW; +	} + +	return INV_ERROR_COMPASS_DATA_NOT_READY; +} +int ami306_read_raw_data(struct inv_ami306_state_s *st, +			short dat[3]) +{ +	int result; +	unsigned char buf[6]; +	result = i2c_read(st->i2c, REG_AMI_DATAX, sizeof(buf), buf); +	if (result < 0) +		return result; +	dat[0] = le16_to_cpup((__le16 *)(&buf[0])); +	dat[1] = le16_to_cpup((__le16 *)(&buf[2])); +	dat[2] = le16_to_cpup((__le16 *)(&buf[4])); + +	return 0; +} + +#define AMI_WAIT_DATAREADY_RETRY                3       /* retry times */ +#define AMI_DRDYWAIT                            800     /* u(micro) sec */ +static int ami306_force_measurement(struct inv_ami306_state_s *st, +					short ver[3]) +{ +	int result; +	int status; +	char buf; +	buf = AMI_CTRL3_FORCE_BIT; +	result = i2c_write(st->i2c, REG_AMI_CTRL3, 1, &buf); +	if (result < 0) +		return result; + +	result = ami306_wait_data_ready(st, +			AMI_DRDYWAIT, AMI_WAIT_DATAREADY_RETRY); +	if (result && result != INV_ERROR_COMPASS_DATA_OVERFLOW) +		return result; +	/*  READ DATA X,Y,Z */ +	status = ami306_read_raw_data(st, ver); +	if (status) +		return status; + +	return result; +} + +static int ami306_initial_b0_adjust(struct inv_ami306_state_s *st) +{ +	int result; +	unsigned char fine[3] = { 0 }; +	short data[3]; +	int diff[3] = { 0x7fff, 0x7fff, 0x7fff }; +	int fn = 0; +	int ax = 0; +	unsigned char buf[3]; + +	buf[0] = AMI_CTRL2_DREN; +	result = i2c_write(st->i2c, REG_AMI_CTRL2, 1, buf); +	if (result) +		return result; + +	buf[0] = AMI_CTRL4_HS & 0xFF; +	buf[1] = (AMI_CTRL4_HS >> 8) & 0xFF; +	result = i2c_write(st->i2c, REG_AMI_CTRL4, 2, buf); +	if (result < 0) +		return result; + +	for (fn = 0; fn < AMI_FINE_MAX; ++fn) { /* fine 0 -> 95 */ +		fine[0] = fine[1] = fine[2] = fn; +		result = ami306_write_offset(st->i2c, fine); +		if (result) +			return result; + +		result = ami306_force_measurement(st, data); +		if (result) +			return result; + +		for (ax = 0; ax < 3; ax++) { +			/* search point most close to zero. */ +			if (diff[ax] > abs(data[ax])) { +				st->fine[ax] = fn; +				diff[ax] = abs(data[ax]); +			} +		} +	} +	result = ami306_write_offset(st->i2c, st->fine); +	if (result) +		return result; + +	/* Software Reset */ +	buf[0] = AMI_CTRL3_SRST_BIT; +	result = i2c_write(st->i2c, REG_AMI_CTRL3, 1, buf); +	if (result < 0) +		return result; +	else +		return 0; +} + +static int ami306_start_sensor(struct inv_ami306_state_s *st) +{ +	int result = 0; +	unsigned char buf[2]; + +	/* Step 1 */ +	buf[0] = (AMI_CTRL1_PC1 | AMI_CTRL1_FS1_FORCE); +	result = i2c_write(st->i2c, REG_AMI_CTRL1, 1, buf); +	if (result < 0) +		return result; +	/* Step 2 */ +	buf[0] = AMI_CTRL2_DREN; +	result = i2c_write(st->i2c, REG_AMI_CTRL2, 1, buf); +	if (result < 0) +		return result; +	/* Step 3 */ +	buf[0] = (AMI_CTRL4_HS & 0xFF); +	buf[1] = (AMI_CTRL4_HS >> 8) & 0xFF; + +	result = i2c_write(st->i2c, REG_AMI_CTRL4, 2, buf); +	if (result < 0) +		return result; + +	/* Step 4 */ +	result = ami306_write_offset(st->i2c, st->fine); + +	return result; +} + +int set_ami306_enable(struct iio_dev *indio_dev, int state) +{ +	struct inv_ami306_state_s *st = iio_priv(indio_dev); +	int result; +	char buf; + +	buf = (AMI_CTRL1_PC1 | AMI_CTRL1_FS1_FORCE); +	result = i2c_write(st->i2c, REG_AMI_CTRL1, 1, &buf); +	if (result < 0) +		return result; + +	result =  ami306_read_param(st); +	if (result) +		return result; +	if (late_initialize) { +		result = ami306_initial_b0_adjust(st); +		if (result) +			return result; +		late_initialize = false; +	} +	result = ami306_start_sensor(st); +	if (result) +		return result; +	buf = AMI_CTRL3_FORCE_BIT; +	st->timestamp = iio_get_time_ns(); +	result = i2c_write(st->i2c, REG_AMI_CTRL3, 1, &buf); +	if (result) +		return result; + +	return 0; +} + +/** + *  ami306_read_raw() - read raw method. + */ +static int ami306_read_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int *val, +			      int *val2, +			      long mask) { +	struct inv_ami306_state_s  *st = iio_priv(indio_dev); + +	switch (mask) { +	case 0: +		if (!(iio_buffer_enabled(indio_dev))) +			return -EINVAL; +		if (chan->type == IIO_MAGN) { +			*val = st->compass_data[chan->channel2 - IIO_MOD_X]; +			return IIO_VAL_INT; +		} + +		return -EINVAL; +	case IIO_CHAN_INFO_SCALE: +		if (chan->type == IIO_MAGN) { +			*val = AMI_SCALE; +			return IIO_VAL_INT; +		} +		return -EINVAL; +	default: +		return -EINVAL; +	} +} + +/** + * inv_compass_matrix_show() - show orientation matrix + */ +static ssize_t inv_compass_matrix_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	signed char *m; +	struct inv_ami306_state_s *st = iio_priv(indio_dev); +	m = st->plat_data.orientation; +	return sprintf(buf, +	"%d,%d,%d,%d,%d,%d,%d,%d,%d\n", +		m[0],  m[1],  m[2],  m[3], m[4], m[5], m[6], m[7], m[8]); +} + +static ssize_t ami306_rate_store(struct device *dev, +		struct device_attribute *attr, +		const char *buf, size_t count) +{ +	unsigned long data; +	int error; +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_ami306_state_s *st = iio_priv(indio_dev); + +	error = kstrtoul(buf, 10, &data); +	if (error) +		return error; +	if (0 == data) +		return -EINVAL; +	/* transform rate to delay in ms */ +	data = 1000 / data; +	if (data > AMI_MAX_DELAY) +		data = AMI_MAX_DELAY; +	if (data < AMI_MIN_DELAY) +		data = AMI_MIN_DELAY; +	st->delay = data; +	return count; +} + +static ssize_t ami306_rate_show(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_ami306_state_s *st = iio_priv(indio_dev); +	/* transform delay in ms to rate */ +	return sprintf(buf, "%d\n", 1000 / st->delay); +} + + +static void ami306_work_func(struct work_struct *work) +{ +	struct inv_ami306_state_s *st = +		container_of((struct delayed_work *)work, +			struct inv_ami306_state_s, work); +	struct iio_dev *indio_dev = iio_priv_to_dev(st); +	unsigned long delay = msecs_to_jiffies(st->delay); + +	mutex_lock(&indio_dev->mlock); +	if (!(iio_buffer_enabled(indio_dev))) +		goto error_ret; + +	st->timestamp = iio_get_time_ns(); +	schedule_delayed_work(&st->work, delay); +	inv_read_ami306_fifo(indio_dev); +	INV_I2C_INC_COMPASSIRQ(); + +error_ret: +	mutex_unlock(&indio_dev->mlock); +} + +static const struct iio_chan_spec compass_channels[] = { +	{ +		.type = IIO_MAGN, +		.modified = 1, +		.channel2 = IIO_MOD_X, +		.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, +		.scan_index = INV_AMI306_SCAN_MAGN_X, +		.scan_type = IIO_ST('s', 16, 16, 0) +	}, { +		.type = IIO_MAGN, +		.modified = 1, +		.channel2 = IIO_MOD_Y, +		.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, +		.scan_index = INV_AMI306_SCAN_MAGN_Y, +		.scan_type = IIO_ST('s', 16, 16, 0) +	}, { +		.type = IIO_MAGN, +		.modified = 1, +		.channel2 = IIO_MOD_Z, +		.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, +		.scan_index = INV_AMI306_SCAN_MAGN_Z, +		.scan_type = IIO_ST('s', 16, 16, 0) +	}, +	IIO_CHAN_SOFT_TIMESTAMP(INV_AMI306_SCAN_TIMESTAMP) +}; + +static DEVICE_ATTR(compass_matrix, S_IRUGO, inv_compass_matrix_show, NULL); +static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR, ami306_rate_show, +		ami306_rate_store); + +static struct attribute *inv_ami306_attributes[] = { +	&dev_attr_compass_matrix.attr, +	&dev_attr_sampling_frequency.attr, +	NULL, +}; +static const struct attribute_group inv_attribute_group = { +	.name = "ami306", +	.attrs = inv_ami306_attributes +}; + +static const struct iio_info ami306_info = { +	.driver_module = THIS_MODULE, +	.read_raw = &ami306_read_raw, +	.attrs = &inv_attribute_group, +}; + +/*constant IIO attribute */ +/** + *  inv_ami306_probe() - probe function. + */ +static int inv_ami306_probe(struct i2c_client *client, +	const struct i2c_device_id *id) +{ +	struct inv_ami306_state_s *st; +	struct iio_dev *indio_dev; +	int result; +	char data; +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { +		result = -ENODEV; +		goto out_no_free; +	} +	indio_dev = iio_allocate_device(sizeof(*st)); +	if (indio_dev == NULL) { +		result =  -ENOMEM; +		goto out_no_free; +	} +	st = iio_priv(indio_dev); +	st->i2c = client; +	st->plat_data = +		*(struct mpu_platform_data *)dev_get_platdata(&client->dev); +	st->delay = 10; + +	/* Make state variables available to all _show and _store functions. */ +	i2c_set_clientdata(client, indio_dev); +	result = i2c_read(st->i2c, REG_AMI_WIA, 1, &data); +	if (result < 0) +		goto out_free; +	if (data != DATA_WIA) +		goto out_free; + +	indio_dev->dev.parent = &client->dev; +	indio_dev->name = id->name; +	indio_dev->channels = compass_channels; +	indio_dev->num_channels = ARRAY_SIZE(compass_channels); +	indio_dev->info = &ami306_info; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->currentmode = INDIO_DIRECT_MODE; + +	result = inv_ami306_configure_ring(indio_dev); +	if (result) +		goto out_free; +	result = iio_buffer_register(indio_dev, indio_dev->channels, +					indio_dev->num_channels); +	if (result) +		goto out_unreg_ring; +	result = inv_ami306_probe_trigger(indio_dev); +	if (result) +		goto out_remove_ring; + +	result = iio_device_register(indio_dev); +	if (result) +		goto out_remove_trigger; +	INIT_DELAYED_WORK(&st->work, ami306_work_func); +	pr_info("%s: Probe name %s\n", __func__, id->name); +	return 0; +out_remove_trigger: +	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) +		inv_ami306_remove_trigger(indio_dev); +out_remove_ring: +	iio_buffer_unregister(indio_dev); +out_unreg_ring: +	inv_ami306_unconfigure_ring(indio_dev); +out_free: +	iio_free_device(indio_dev); +out_no_free: +	dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); +	return -EIO; +} + +/** + *  inv_ami306_remove() - remove function. + */ +static int inv_ami306_remove(struct i2c_client *client) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(client); +	struct inv_ami306_state_s *st = iio_priv(indio_dev); +	cancel_delayed_work_sync(&st->work); +	iio_device_unregister(indio_dev); +	inv_ami306_remove_trigger(indio_dev); +	iio_buffer_unregister(indio_dev); +	inv_ami306_unconfigure_ring(indio_dev); +	iio_free_device(indio_dev); + +	dev_info(&client->adapter->dev, "inv-ami306-iio module removed.\n"); +	return 0; +} +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; +/* device id table is used to identify what device can be + * supported by this driver + */ +static const struct i2c_device_id inv_ami306_id[] = { +	{"ami306", 0}, +	{} +}; + +MODULE_DEVICE_TABLE(i2c, inv_ami306_id); + +static struct i2c_driver inv_ami306_driver = { +	.class = I2C_CLASS_HWMON, +	.probe		=	inv_ami306_probe, +	.remove		=	inv_ami306_remove, +	.id_table	=	inv_ami306_id, +	.driver = { +		.owner	=	THIS_MODULE, +		.name	=	"inv-ami306-iio", +	}, +	.address_list = normal_i2c, +}; + +static int __init inv_ami306_init(void) +{ +	int result = i2c_add_driver(&inv_ami306_driver); +	if (result) { +		pr_err("%s failed\n", __func__); +		return result; +	} +	return 0; +} + +static void __exit inv_ami306_exit(void) +{ +	i2c_del_driver(&inv_ami306_driver); +} + +module_init(inv_ami306_init); +module_exit(inv_ami306_exit); + +MODULE_AUTHOR("Invensense Corporation"); +MODULE_DESCRIPTION("Invensense device driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("inv-ami306-iio"); +/** + *  @} + */ + diff --git a/drivers/iio/magnetometer/inv_compass/inv_ami306_iio.h b/drivers/iio/magnetometer/inv_compass/inv_ami306_iio.h new file mode 100755 index 00000000000..fa4f4ee1e5d --- /dev/null +++ b/drivers/iio/magnetometer/inv_compass/inv_ami306_iio.h @@ -0,0 +1,159 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +* +*/ + +/** + *  @addtogroup DRIVERS + *  @brief      Hardware drivers. + * + *  @{ + *      @file  inv_ami306_iio.h + *      @brief Struct definitions for the Invensense implementation + *              of ami306 driver. + */ + +#ifndef _INV_GYRO_H_ +#define _INV_GYRO_H_ + +#include <linux/i2c.h> +#include <linux/kfifo.h> +#include <linux/miscdevice.h> +#include <linux/input.h> +#include <linux/spinlock.h> +#include <linux/mpu.h> + +#include "iio.h" +#include "buffer.h" +#include "trigger.h" + +/** axis sensitivity(gain) calibration parameter information  */ +struct ami_vector3d { +	signed short x;                 /**< X-axis  */ +	signed short y;                 /**< Y-axis  */ +	signed short z;                 /**< Z-axis  */ +}; + +/** axis interference information  */ +struct ami_interference { +	/**< Y-axis magnetic field for X-axis correction value  */ +	signed short xy; +	/**< Z-axis magnetic field for X-axis correction value  */ +	signed short xz; +	/**< X-axis magnetic field for Y-axis correction value  */ +	signed short yx; +	/**< Z-axis magnetic field for Y-axis correction value  */ +	signed short yz; +	/**< X-axis magnetic field for Z-axis correction value  */ +	signed short zx; +	/**< Y-axis magnetic field for Z-axis correction value  */ +	signed short zy; +}; + +/** sensor calibration Parameter information  */ +struct ami_sensor_parametor { +	/**< geomagnetic field sensor gain  */ +	struct ami_vector3d m_gain; +	/**< geomagnetic field sensor gain correction parameter  */ +	struct ami_vector3d m_gain_cor; +	/**< geomagnetic field sensor offset  */ +	struct ami_vector3d m_offset; +	/**< geomagnetic field sensor axis interference parameter */ +	struct ami_interference m_interference; +}; + +/** + *  struct inv_ami306_state_s - Driver state variables. + *  @plat_data:         board file platform data. + *  @i2c:		i2c client handle. + *  @trig:              not used. for compatibility. + *  @param:             ami specific sensor data. + *  @work:              work data structure. + *  @delay:             delay between each scheduled work. + *  @fine:               fine tunign parameters. + *  @compass_data:      compass data store. + *  @timestamp:         time stamp. + */ +struct inv_ami306_state_s { +	struct mpu_platform_data plat_data; +	struct i2c_client *i2c; +	struct iio_trigger  *trig; +	struct ami_sensor_parametor param; +	struct delayed_work work; +	int delay; +	s8 fine[3]; +	short compass_data[3]; +	s64 timestamp; +}; +/* scan element definition */ +enum inv_mpu_scan { +	INV_AMI306_SCAN_MAGN_X, +	INV_AMI306_SCAN_MAGN_Y, +	INV_AMI306_SCAN_MAGN_Z, +	INV_AMI306_SCAN_TIMESTAMP, +}; + +#define REG_AMI_WIA                     0x0f +#define REG_AMI_DATAX                   0x10 +#define REG_AMI_STA1                    0x18 +#define REG_AMI_CTRL1                   0x1b +#define REG_AMI_CTRL2                   0x1c +#define REG_AMI_CTRL3                   0x1d +#define REG_AMI_B0X                     0x20 +#define REG_AMI_B0Y                     0x22 +#define REG_AMI_B0Z                     0x24 +#define REG_AMI_CTRL5                   0x40 +#define REG_AMI_CTRL4                   0x5c +#define REG_AMI_TEMP                    0x60 +#define REG_AMI_SENX                    0x96 +#define REG_AMI_OFFX                    0x6c +#define REG_AMI_OFFY                    0x72 +#define REG_AMI_OFFZ                    0x78 + + +#define DATA_WIA                        0x46 +#define AMI_CTRL1_PC1                   0x80 +#define AMI_CTRL1_FS1_FORCE             0x02 +#define AMI_CTRL1_ODR1                  0x10 +#define AMI_CTRL2_DREN                  0x08 +#define AMI_CTRL2_DRP                   0x04 +#define AMI_CTRL3_FORCE_BIT             0x40 +#define AMI_CTRL3_B0_LO_BIT             0x10 +#define AMI_CTRL3_SRST_BIT              0x80 +#define AMI_CTRL4_HS                    0xa07e +#define AMI_CTRL4_AB                    0x0001 +#define AMI_STA1_DRDY_BIT               0x40 +#define AMI_STA1_DOR_BIT                0x20 + +#define AMI_PARAM_LEN                   12 +#define AMI_STANDARD_OFFSET             0x800 +#define AMI_GAIN_COR_DEFAULT            1000 +#define AMI_FINE_MAX			96 +#define AMI_MAX_DELAY                   1000 +#define AMI_MIN_DELAY                   10 +#define AMI_SCALE                       (5461 * (1<<15)) + +#define INV_ERROR_COMPASS_DATA_OVERFLOW  (-1) +#define INV_ERROR_COMPASS_DATA_NOT_READY (-2) + + +int inv_ami306_configure_ring(struct iio_dev *indio_dev); +void inv_ami306_unconfigure_ring(struct iio_dev *indio_dev); +int inv_ami306_probe_trigger(struct iio_dev *indio_dev); +void inv_ami306_remove_trigger(struct iio_dev *indio_dev); +int set_ami306_enable(struct iio_dev *indio_dev, int state); +int ami306_read_raw_data(struct inv_ami306_state_s *st, +				short dat[3]); +int inv_read_ami306_fifo(struct iio_dev *indio_dev); + +#endif  /* #ifndef _INV_GYRO_H_ */ + diff --git a/drivers/iio/magnetometer/inv_compass/inv_ami306_ring.c b/drivers/iio/magnetometer/inv_compass/inv_ami306_ring.c new file mode 100755 index 00000000000..ed91cf49516 --- /dev/null +++ b/drivers/iio/magnetometer/inv_compass/inv_ami306_ring.c @@ -0,0 +1,163 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +* +*/ + +/** + *  @addtogroup  DRIVERS + *  @brief       Hardware drivers. + * + *  @{ + *      @file    inv_ami306_ring.c + *      @brief   Invensense implementation for AMI306 + *      @details This driver currently works for the AMI306 + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> + +#include "iio.h" +#include "kfifo_buf.h" +#include "trigger_consumer.h" +#include "sysfs.h" + +#include "inv_ami306_iio.h" + +static int put_scan_to_buf(struct iio_dev *indio_dev, unsigned char *d, +				short *s, int scan_index) { +	struct iio_buffer *ring = indio_dev->buffer; +	int st; +	int i, d_ind; +	d_ind = 0; +	for (i = 0; i < 3; i++) { +		st = iio_scan_mask_query(indio_dev, ring, scan_index + i); +		if (st) { +			memcpy(&d[d_ind], &s[i], sizeof(s[i])); +			d_ind += sizeof(s[i]); +		} +	} +	return d_ind; +} + +/** + *  inv_read_fifo() - Transfer data from FIFO to ring buffer. + */ +int inv_read_ami306_fifo(struct iio_dev *indio_dev) +{ +	struct inv_ami306_state_s *st = iio_priv(indio_dev); +	struct iio_buffer *ring = indio_dev->buffer; +	int result, status, d_ind; +	char b; +	char *tmp; +	s64 tmp_buf[2]; + +	result = i2c_smbus_read_i2c_block_data(st->i2c, REG_AMI_STA1, 1, &b); +	if (result < 0) +		goto end_session; +	if (b & AMI_STA1_DRDY_BIT) { +		status = ami306_read_raw_data(st, st->compass_data); +		if (status) { +			pr_err("error reading raw\n"); +			goto end_session; +		} +		tmp = (unsigned char *)tmp_buf; +		d_ind = put_scan_to_buf(indio_dev, tmp, st->compass_data, +						INV_AMI306_SCAN_MAGN_X); +		if (ring->scan_timestamp) +			tmp_buf[(d_ind + 7)/8] = st->timestamp; +		ring->access->store_to(indio_dev->buffer, tmp, st->timestamp); +	} else if (b & AMI_STA1_DOR_BIT) +		pr_err("not ready\n"); +end_session: +	b = AMI_CTRL3_FORCE_BIT; +	result = i2c_smbus_write_i2c_block_data(st->i2c, REG_AMI_CTRL3, 1, &b); + +	return IRQ_HANDLED; +} + +void inv_ami306_unconfigure_ring(struct iio_dev *indio_dev) +{ +	iio_kfifo_free(indio_dev->buffer); +}; +static int inv_ami306_postenable(struct iio_dev *indio_dev) +{ +	struct inv_ami306_state_s *st = iio_priv(indio_dev); +	struct iio_buffer *ring = indio_dev->buffer; +	int result; + +	/* when all the outputs are disabled, even though buffer/enable is on, +	   do nothing */ +	if (!(iio_scan_mask_query(indio_dev, ring, INV_AMI306_SCAN_MAGN_X) || +	    iio_scan_mask_query(indio_dev, ring, INV_AMI306_SCAN_MAGN_Y) || +	    iio_scan_mask_query(indio_dev, ring, INV_AMI306_SCAN_MAGN_Z))) +		return 0; + +	result = set_ami306_enable(indio_dev, true); +	if (result) +		return result; +	schedule_delayed_work(&st->work, msecs_to_jiffies(st->delay)); + +	return 0; +} + +static int inv_ami306_predisable(struct iio_dev *indio_dev) +{ +	struct iio_buffer *ring = indio_dev->buffer; +	struct inv_ami306_state_s *st = iio_priv(indio_dev); + +	cancel_delayed_work_sync(&st->work); +	clear_bit(INV_AMI306_SCAN_MAGN_X, ring->scan_mask); +	clear_bit(INV_AMI306_SCAN_MAGN_Y, ring->scan_mask); +	clear_bit(INV_AMI306_SCAN_MAGN_Z, ring->scan_mask); + +	return 0; +} + +static const struct iio_buffer_setup_ops inv_ami306_ring_setup_ops = { +	.preenable = &iio_sw_buffer_preenable, +	.postenable = &inv_ami306_postenable, +	.predisable = &inv_ami306_predisable, +}; + +int inv_ami306_configure_ring(struct iio_dev *indio_dev) +{ +	int ret = 0; +	struct iio_buffer *ring; + +	ring = iio_kfifo_allocate(indio_dev); +	if (!ring) { +		ret = -ENOMEM; +		return ret; +	} +	indio_dev->buffer = ring; +	/* setup ring buffer */ +	ring->scan_timestamp = true; +	indio_dev->setup_ops = &inv_ami306_ring_setup_ops; + +	indio_dev->modes |= INDIO_BUFFER_TRIGGERED; +	return 0; +} +/** + *  @} + */ + diff --git a/drivers/iio/magnetometer/inv_compass/inv_ami306_trigger.c b/drivers/iio/magnetometer/inv_compass/inv_ami306_trigger.c new file mode 100755 index 00000000000..f7fe59ef5df --- /dev/null +++ b/drivers/iio/magnetometer/inv_compass/inv_ami306_trigger.c @@ -0,0 +1,90 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +* +*/ + +/** + *  @addtogroup  DRIVERS + *  @brief       Hardware drivers. + * + *  @{ + *      @file    inv_ami306_trigger.c + *      @brief   Invensense implementation for AMI306 + *      @details This driver currently works for the AMI306 + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> + +#include "iio.h" +#include "sysfs.h" +#include "trigger.h" +#include "inv_ami306_iio.h" + +static const struct iio_trigger_ops inv_ami306_trigger_ops = { +	.owner = THIS_MODULE, +}; + +int inv_ami306_probe_trigger(struct iio_dev *indio_dev) +{ +	int ret; +	struct inv_ami306_state_s *st = iio_priv(indio_dev); + +	st->trig = iio_allocate_trigger("%s-dev%d", +					indio_dev->name, +					indio_dev->id); +	if (st->trig == NULL) { +		ret = -ENOMEM; +		goto error_ret; +	} +	/* select default trigger */ +	st->trig->dev.parent = &st->i2c->dev; +	st->trig->private_data = indio_dev; +	st->trig->ops = &inv_ami306_trigger_ops; +	ret = iio_trigger_register(st->trig); + +	/* select default trigger */ +	indio_dev->trig = st->trig; +	if (ret) +		goto error_free_trig; + +	return 0; + +error_free_trig: +	iio_free_trigger(st->trig); +error_ret: +	return ret; +} + +void inv_ami306_remove_trigger(struct iio_dev *indio_dev) +{ +	struct inv_ami306_state_s *st = iio_priv(indio_dev); + +	iio_trigger_unregister(st->trig); +	iio_free_trigger(st->trig); +} +/** + *  @} + */ + diff --git a/drivers/iio/magnetometer/inv_compass/inv_yas53x_core.c b/drivers/iio/magnetometer/inv_compass/inv_yas53x_core.c new file mode 100755 index 00000000000..6af420bb5cf --- /dev/null +++ b/drivers/iio/magnetometer/inv_compass/inv_yas53x_core.c @@ -0,0 +1,969 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +* +*/ + +/** + *  @addtogroup  DRIVERS + *  @brief       Hardware drivers. + * + *  @{ + *      @file    inv_yas53x_core.c + *      @brief   Invensense implementation for yas530/yas532/yas533. + *      @details This driver currently works for yas530/yas532/yas533. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> + +#include "inv_yas53x_iio.h" +#include "sysfs.h" +#include "inv_test/inv_counters.h" + +/* -------------------------------------------------------------------------- */ +static int Cx, Cy1, Cy2; +static int /*a1, */ a2, a3, a4, a5, a6, a7, a8, a9; +static int k; + +static u8 dx, dy1, dy2; +static u8 d2, d3, d4, d5, d6, d7, d8, d9, d0; +static u8 dck, ver; + +/** + *  inv_serial_read() - Read one or more bytes from the device registers. + *  @st:	Device driver instance. + *  @reg:	First device register to be read from. + *  @length:	Number of bytes to read. + *  @data:	Data read from device. + *  NOTE: The slave register will not increment when reading from the FIFO. + */ +int inv_serial_read(struct inv_compass_state *st, u8 reg, u16 length, u8 *data) +{ +	int result; +	INV_I2C_INC_COMPASSWRITE(3); +	INV_I2C_INC_COMPASSREAD(length); +	result = i2c_smbus_read_i2c_block_data(st->client, reg, length, data); +	if (result != length) { +		if (result < 0) +			return result; +		else +			return -EINVAL; +	} else { +		return 0; +	} +} + +/** + *  inv_serial_single_write() - Write a byte to a device register. + *  @st:	Device driver instance. + *  @reg:	Device register to be written to. + *  @data:	Byte to write to device. + */ +int inv_serial_single_write(struct inv_compass_state *st, u8 reg, u8 data) +{ +	u8 d[1]; +	d[0] = data; +	INV_I2C_INC_COMPASSWRITE(3); + +	return i2c_smbus_write_i2c_block_data(st->client, reg, 1, d); +} + +static int set_hardware_offset(struct inv_compass_state *st, +			       char offset_x, char offset_y1, char offset_y2) +{ +	char data; +	int result = 0; + +	data = offset_x & 0x3f; +	result = inv_serial_single_write(st, YAS530_REGADDR_OFFSET_X, data); +	if (result) +		return result; + +	data = offset_y1 & 0x3f; +	result = inv_serial_single_write(st, YAS530_REGADDR_OFFSET_Y1, data); +	if (result) +		return result; + +	data = offset_y2 & 0x3f; +	result = inv_serial_single_write(st, YAS530_REGADDR_OFFSET_Y2, data); +	return result; +} + +static int set_measure_command(struct inv_compass_state *st) +{ +	int result = 0; +	result = inv_serial_single_write(st, +					 YAS530_REGADDR_MEASURE_COMMAND, 0x01); +	return result; +} + +static int measure_normal(struct inv_compass_state *st, +			  int *busy, unsigned short *t, +			  unsigned short *x, unsigned short *y1, +			  unsigned short *y2) +{ +	int result; +	ktime_t sleeptime; +	result = set_measure_command(st); +	sleeptime = ktime_set(0, 2 * NSEC_PER_MSEC); +	set_current_state(TASK_UNINTERRUPTIBLE); +	schedule_hrtimeout(&sleeptime, HRTIMER_MODE_REL); + +	result = st->read_data(st, busy, t, x, y1, y2); + +	return result; +} + +static int measure_int(struct inv_compass_state *st, +			  int *busy, unsigned short *t, +			  unsigned short *x, unsigned short *y1, +			  unsigned short *y2) +{ +	int result; +	if (st->first_read_after_reset) { +		st->first_read_after_reset = 0; +		result = 1; +	} else { +		result = st->read_data(st, busy, t, x, y1, y2); +	} +	result |= set_measure_command(st); + +	return result; +} + +static int yas530_read_data(struct inv_compass_state *st, +			  int *busy, u16 *t, u16 *x, u16 *y1, u16 *y2) +{ +	u8 data[8]; +	u16 b, to, xo, y1o, y2o; +	int result; + +	result = inv_serial_read(st, +				 YAS530_REGADDR_MEASURE_DATA, 8, data); +	if (result) +		return result; + +	b = (data[0] >> 7) & 0x01; +	to = (s16)(((data[0] << 2) & 0x1fc) | ((data[1] >> 6) & 0x03)); +	xo = (s16)(((data[2] << 5) & 0xfe0) | ((data[3] >> 3) & 0x1f)); +	y1o = (s16)(((data[4] << 5) & 0xfe0) | ((data[5] >> 3) & 0x1f)); +	y2o = (s16)(((data[6] << 5) & 0xfe0) | ((data[7] >> 3) & 0x1f)); + +	*busy = b; +	*t = to; +	*x = xo; +	*y1 = y1o; +	*y2 = y2o; + +	return 0; +} + +static int yas532_533_read_data(struct inv_compass_state *st, +			  int *busy, u16 *t, u16 *x, u16 *y1, u16 *y2) +{ +	u8 data[8]; +	u16 b, to, xo, y1o, y2o; +	int result; + +	result = inv_serial_read(st, +				 YAS530_REGADDR_MEASURE_DATA, 8, data); +	if (result) +		return result; + +	b = (data[0] >> 7) & 0x01; +	to = (s16)((((s32)data[0] << 3) & 0x3f8) | ((data[1] >> 5) & 0x07)); +	xo = (s16)((((s32)data[2] << 6) & 0x1fc0) | ((data[3] >> 2) & 0x3f)); +	y1o = (s16)((((s32)data[4] << 6) & 0x1fc0) | ((data[5] >> 2) & 0x3f)); +	y2o = (s16)((((s32)data[6] << 6) & 0x1fc0) | ((data[7] >> 2) & 0x3f)); + +	*busy = b; +	*t = to; +	*x = xo; +	*y1 = y1o; +	*y2 = y2o; + +	return 0; +} + +static int check_offset(struct inv_compass_state *st, +			char offset_x, char offset_y1, char offset_y2, +			int *flag_x, int *flag_y1, int *flag_y2) +{ +	int result; +	int busy; +	short t, x, y1, y2; + +	result = set_hardware_offset(st, offset_x, offset_y1, offset_y2); +	if (result) +		return result; +	result = measure_normal(st, &busy, &t, &x, &y1, &y2); +	if (result) +		return result; +	*flag_x = 0; +	*flag_y1 = 0; +	*flag_y2 = 0; + +	if (x > st->center) +		*flag_x = 1; +	if (y1 > st->center) +		*flag_y1 = 1; +	if (y2 > st->center) +		*flag_y2 = 1; +	if (x < st->center) +		*flag_x = -1; +	if (y1 < st->center) +		*flag_y1 = -1; +	if (y2 < st->center) +		*flag_y2 = -1; + +	return result; +} + +static int measure_and_set_offset(struct inv_compass_state *st, +				  char *offset) +{ +	int i; +	int result = 0; +	char offset_x = 0, offset_y1 = 0, offset_y2 = 0; +	int flag_x = 0, flag_y1 = 0, flag_y2 = 0; +	static const int correct[5] = {16, 8, 4, 2, 1}; + +	for (i = 0; i < 5; i++) { +		result = check_offset(st, +				      offset_x, offset_y1, offset_y2, +				      &flag_x, &flag_y1, &flag_y2); +		if (result) +			return result; +		if (flag_x) +			offset_x += flag_x * correct[i]; +		if (flag_y1) +			offset_y1 += flag_y1 * correct[i]; +		if (flag_y2) +			offset_y2 += flag_y2 * correct[i]; +	} + +	result = set_hardware_offset(st, offset_x, offset_y1, offset_y2); +	if (result) +		return result; +	offset[0] = offset_x; +	offset[1] = offset_y1; +	offset[2] = offset_y2; + +	return result; +} + +static void coordinate_conversion(short x, short y1, short y2, short t, +				  int *xo, int *yo, int *zo) +{ +	int sx, sy1, sy2, sy, sz; +	int hx, hy, hz; + +	sx = x - (Cx * t) / 100; +	sy1 = y1 - (Cy1 * t) / 100; +	sy2 = y2 - (Cy2 * t) / 100; + +	sy = sy1 - sy2; +	sz = -sy1 - sy2; + +	hx = k * ((100 * sx + a2 * sy + a3 * sz) / 10); +	hy = k * ((a4 * sx + a5 * sy + a6 * sz) / 10); +	hz = k * ((a7 * sx + a8 * sy + a9 * sz) / 10); + +	*xo = hx; +	*yo = hy; +	*zo = hz; +} + +static int get_cal_data_yas532_533(struct inv_compass_state *st) +{ +	u8 data[YAS_YAS532_533_CAL_DATA_SIZE]; +	int result; + +	result = inv_serial_read(st, YAS530_REGADDR_CAL, +				YAS_YAS532_533_CAL_DATA_SIZE, data); +	if (result) +		return result; +	/* CAL data Second Read */ +	result = inv_serial_read(st, YAS530_REGADDR_CAL, +				YAS_YAS532_533_CAL_DATA_SIZE, data); +	if (result) +		return result; + +	dx = data[0]; +	dy1 = data[1]; +	dy2 = data[2]; +	d2 = (data[3] >> 2) & 0x03f; +	d3 = (u8)(((data[3] << 2) & 0x0c) | ((data[4] >> 6) & 0x03)); +	d4 = (u8)(data[4] & 0x3f); +	d5 = (data[5] >> 2) & 0x3f; +	d6 = (u8)(((data[5] << 4) & 0x30) | ((data[6] >> 4) & 0x0f)); +	d7 = (u8)(((data[6] << 3) & 0x78) | ((data[7] >> 5) & 0x07)); +	d8 = (u8)(((data[7] << 1) & 0x3e) | ((data[8] >> 7) & 0x01)); +	d9 = (u8)(((data[8] << 1) & 0xfe) | ((data[9] >> 7) & 0x01)); +	d0 = (u8)((data[9] >> 2) & 0x1f); +	dck = (u8)(((data[9] << 1) & 0x06) | ((data[10] >> 7) & 0x01)); +	ver = (u8)((data[13]) & 0x01); + +	Cx  = dx * 10 - 1280; +	Cy1 = dy1 * 10 - 1280; +	Cy2 = dy2 * 10 - 1280; +	a2  = d2 - 32; +	a3  = d3 - 8; +	a4  = d4 - 32; +	a5  = d5 + 38; +	a6  = d6 - 32; +	a7  = d7 - 64; +	a8  = d8 - 32; +	a9  = d9; +	k   = d0; + +	return 0; +} + +static int get_cal_data_yas530(struct inv_compass_state *st) +{ +	u8 data[YAS_YAS530_CAL_DATA_SIZE]; +	int result; +	/* CAL data read */ +	result = inv_serial_read(st, YAS530_REGADDR_CAL, +				YAS_YAS530_CAL_DATA_SIZE, data); +	if (result) +		return result; +	/* CAL data Second Read */ +	result = inv_serial_read(st, YAS530_REGADDR_CAL, +				YAS_YAS530_CAL_DATA_SIZE, data); +	if (result) +		return result; +	/*Cal data */ +	dx = data[0]; +	dy1 = data[1]; +	dy2 = data[2]; +	d2 = (data[3] >> 2) & 0x03f; +	d3 = ((data[3] << 2) & 0x0c) | ((data[4] >> 6) & 0x03); +	d4 = data[4] & 0x3f; +	d5 = (data[5] >> 2) & 0x3f; +	d6 = ((data[5] << 4) & 0x30) | ((data[6] >> 4) & 0x0f); +	d7 = ((data[6] << 3) & 0x78) | ((data[7] >> 5) & 0x07); +	d8 = ((data[7] << 1) & 0x3e) | ((data[8] >> 7) & 0x01); +	d9 = ((data[8] << 1) & 0xfe) | ((data[9] >> 7) & 0x01); +	d0 = (data[9] >> 2) & 0x1f; +	dck = ((data[9] << 1) & 0x06) | ((data[10] >> 7) & 0x01); +	ver = (u8)((data[15]) & 0x03); + +	/*Correction Data */ +	Cx = (int)dx * 6 - 768; +	Cy1 = (int)dy1 * 6 - 768; +	Cy2 = (int)dy2 * 6 - 768; +	a2 = (int)d2 - 32; +	a3 = (int)d3 - 8; +	a4 = (int)d4 - 32; +	a5 = (int)d5 + 38; +	a6 = (int)d6 - 32; +	a7 = (int)d7 - 64; +	a8 = (int)d8 - 32; +	a9 = (int)d9; +	k = (int)d0 + 10; + +	return 0; +} + + +static void thresh_filter_init(struct yas_thresh_filter *thresh_filter, +				int threshold) +{ +	thresh_filter->threshold = threshold; +	thresh_filter->last = 0; +} + +static void +adaptive_filter_init(struct yas_adaptive_filter *adap_filter, int len, +		int noise) +{ +	int i; + +	adap_filter->num = 0; +	adap_filter->index = 0; +	adap_filter->filter_noise = noise; +	adap_filter->filter_len = len; + +	for (i = 0; i < adap_filter->filter_len; ++i) +		adap_filter->sequence[i] = 0; +} + +static void yas_init_adap_filter(struct inv_compass_state *st) +{ +	struct yas_filter *f; +	int i; +	int noise[] = {YAS_MAG_DEFAULT_FILTER_NOISE_X, +			YAS_MAG_DEFAULT_FILTER_NOISE_Y, +			YAS_MAG_DEFAULT_FILTER_NOISE_Z}; + +	f = &st->filter; +	f->filter_len = YAS_MAG_DEFAULT_FILTER_LEN; +	for (i = 0; i < 3; i++) +		f->filter_noise[i] = noise[i]; + +	for (i = 0; i < 3; i++) { +		adaptive_filter_init(&f->adap_filter[i], f->filter_len, +				f->filter_noise[i]); +		thresh_filter_init(&f->thresh_filter[i], f->filter_thresh); +	} +} + +int yas53x_resume(struct inv_compass_state *st) +{ +	int result = 0; + +	unsigned char dummyData = 0x00; +	unsigned char read_reg[1]; + +	/* =============================================== */ + +	/* Step 1 - Test register initialization */ +	dummyData = 0x00; +	result = inv_serial_single_write(st, +					 YAS530_REGADDR_TEST1, dummyData); +	if (result) +		return result; +	result = +	    inv_serial_single_write(st, +				    YAS530_REGADDR_TEST2, dummyData); +	if (result) +		return result; +	/* Device ID read  */ +	result = inv_serial_read(st, +				 YAS530_REGADDR_DEVICE_ID, 1, read_reg); + +	/*Step 2 Read the CAL register */ +	st->get_cal_data(st); + +	/*Obtain the [49:47] bits */ +	dck &= 0x07; + +	/*Step 3 : Storing the CONFIG with the CLK value */ +	dummyData = 0x00 | (dck << 2); +	result = inv_serial_single_write(st, +					 YAS530_REGADDR_CONFIG, dummyData); +	if (result) +		return result; +	/*Step 4 : Set Acquisition Interval Register */ +	dummyData = 0x00; +	result = inv_serial_single_write(st, +					 YAS530_REGADDR_MEASURE_INTERVAL, +					 dummyData); +	if (result) +		return result; + +	/*Step 5 : Reset Coil */ +	dummyData = 0x00; +	result = inv_serial_single_write(st, +					 YAS530_REGADDR_ACTUATE_INIT_COIL, +					 dummyData); +	if (result) +		return result; +	/* Offset Measurement and Set */ +	result = measure_and_set_offset(st, st->offset); +	if (result) +		return result; +	st->first_measure_after_reset = 1; +	st->first_read_after_reset = 1; +	st->reset_timer = 0; + +	yas_init_adap_filter(st); + +	return result; +} + +static int inv_check_range(struct inv_compass_state *st, s16 x, s16 y1, s16 y2) +{ +	int result = 0; + +	if (x == 0) +		result |= 0x01; +	if (x == st->overflow_bound) +		result |= 0x02; +	if (y1 == 0) +		result |= 0x04; +	if (y1 == st->overflow_bound) +		result |= 0x08; +	if (y2 == 0) +		result |= 0x10; +	if (y2 == st->overflow_bound) +		result |= 0x20; + +	return result; +} +static int square(int data) +{ +	return data * data; +} + +static int +adaptive_filter_filter(struct yas_adaptive_filter *adap_filter, int in) +{ +	int avg, sum; +	int i; + +	if (adap_filter->filter_len == 0) +		return in; +	if (adap_filter->num < adap_filter->filter_len) { +		adap_filter->sequence[adap_filter->index++] = in / 100; +		adap_filter->num++; +		return in; +	} +	if (adap_filter->filter_len <= adap_filter->index) +		adap_filter->index = 0; +	adap_filter->sequence[adap_filter->index++] = in / 100; + +	avg = 0; +	for (i = 0; i < adap_filter->filter_len; i++) +		avg += adap_filter->sequence[i]; +	avg /= adap_filter->filter_len; + +	sum = 0; +	for (i = 0; i < adap_filter->filter_len; i++) +		sum += square(avg - adap_filter->sequence[i]); +	sum /= adap_filter->filter_len; + +	if (sum <= adap_filter->filter_noise) +		return avg * 100; + +	return ((in/100 - avg) * (sum - adap_filter->filter_noise) / sum + avg) +		* 100; +} + +static int +thresh_filter_filter(struct yas_thresh_filter *thresh_filter, int in) +{ +	if (in < thresh_filter->last - thresh_filter->threshold +			|| thresh_filter->last +			+ thresh_filter->threshold < in) { +		thresh_filter->last = in; +		return in; +	} else { +		return thresh_filter->last; +	} +} + +static void +filter_filter(struct yas_filter *d, int *orig, int *filtered) +{ +	int i; + +	for (i = 0; i < 3; i++) { +		filtered[i] = adaptive_filter_filter(&d->adap_filter[i], +				orig[i]); +		filtered[i] = thresh_filter_filter(&d->thresh_filter[i], +				filtered[i]); +	} +} + +int yas53x_read(struct inv_compass_state *st, short rawfixed[3], +				int *overunderflow) +{ +	int result = 0; + +	int busy, i, ov; +	short t, x, y1, y2; +	s32 xyz[3], disturb[3]; + +	result = measure_int(st, &busy, &t, &x, &y1, &y2); +	if (result) +		return result; +	if (busy) +		return -1; +	coordinate_conversion(x, y1, y2, t, &xyz[0], &xyz[1], &xyz[2]); +	filter_filter(&st->filter, xyz, xyz); +	for (i = 0; i < 3; i++) +		rawfixed[i] = (short)(xyz[i] / 100); + +	if (st->first_measure_after_reset) { +		for (i = 0; i < 3; i++) +			st->base_compass_data[i] = rawfixed[i]; +		st->first_measure_after_reset = 0; +	} +	ov = 0; +	for (i = 0; i < 3; i++) { +		disturb[i] = abs(st->base_compass_data[i] - rawfixed[i]); +		if (disturb[i] > YAS_MAG_DISTURBURNCE_THRESHOLD) +			ov = 1; +	} +	if (ov) +		st->reset_timer += st->delay; +	else +		st->reset_timer = 0; + +	if (st->reset_timer > YAS_RESET_COIL_TIME_THRESHOLD) +		*overunderflow = (1<<8); +	else +		*overunderflow = 0; +	*overunderflow |= inv_check_range(st, x, y1, y2); + +	return 0; +} + +/** + *  yas53x_read_raw() - read raw method. + */ +static int yas53x_read_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int *val, +			      int *val2, +			      long mask) { +	struct inv_compass_state  *st = iio_priv(indio_dev); + +	switch (mask) { +	case 0: +		if (!(iio_buffer_enabled(indio_dev))) +			return -EINVAL; +		if (chan->type == IIO_MAGN) { +			*val = st->compass_data[chan->channel2 - IIO_MOD_X]; +			return IIO_VAL_INT; +		} + +		return -EINVAL; +	case IIO_CHAN_INFO_SCALE: +		if (chan->type == IIO_MAGN) { +			*val = YAS530_SCALE; +			return IIO_VAL_INT; +		} +		return -EINVAL; +	default: +		return -EINVAL; +	} +} + +/** + * inv_compass_matrix_show() - show orientation matrix + */ +static ssize_t inv_compass_matrix_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	signed char *m; +	struct inv_compass_state *st = iio_priv(indio_dev); +	m = st->plat_data.orientation; +	return sprintf(buf, +	"%d,%d,%d,%d,%d,%d,%d,%d,%d\n", +		m[0],  m[1],  m[2],  m[3], m[4], m[5], m[6], m[7], m[8]); +} + +static ssize_t yas53x_rate_store(struct device *dev, +		struct device_attribute *attr, +		const char *buf, size_t count) +{ +	u32 data; +	int error; +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_compass_state *st = iio_priv(indio_dev); + +	error = kstrtoint(buf, 10, &data); +	if (error) +		return error; +	if (0 == data) +		return -EINVAL; +	/* transform rate to delay in ms */ +	data = MSEC_PER_SEC / data; + +	if (data > YAS530_MAX_DELAY) +		data = YAS530_MAX_DELAY; +	if (data < YAS530_MIN_DELAY) +		data = YAS530_MIN_DELAY; +	st->delay = data; + +	return count; +} + +static ssize_t yas53x_rate_show(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_compass_state *st = iio_priv(indio_dev); +	/* transform delay in ms to rate */ +	return sprintf(buf, "%d\n", (int)MSEC_PER_SEC / st->delay); +} + +static ssize_t yas53x_overunderflow_store(struct device *dev, +		struct device_attribute *attr, +		const char *buf, size_t count) +{ +	u32 data; +	int error; +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_compass_state *st = iio_priv(indio_dev); + +	error = kstrtoint(buf, 10, &data); +	if (error) +		return error; +	if (data) +		return -EINVAL; +	st->overunderflow = data; + +	return count; +} + +static ssize_t yas53x_overunderflow_show(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_compass_state *st = iio_priv(indio_dev); + +	return sprintf(buf, "%d\n", st->overunderflow); +} + +void set_yas53x_enable(struct iio_dev *indio_dev, bool enable) +{ +	struct inv_compass_state *st = iio_priv(indio_dev); + +	yas_init_adap_filter(st); +	st->first_measure_after_reset = 1; +	st->first_read_after_reset = 1; +	schedule_delayed_work(&st->work, msecs_to_jiffies(st->delay)); +} + +static void yas53x_work_func(struct work_struct *work) +{ +	struct inv_compass_state *st = +		container_of((struct delayed_work *)work, +			struct inv_compass_state, work); +	struct iio_dev *indio_dev = iio_priv_to_dev(st); +	u32 delay = msecs_to_jiffies(st->delay); + +	mutex_lock(&indio_dev->mlock); +	if (!(iio_buffer_enabled(indio_dev))) +		goto error_ret; + +	schedule_delayed_work(&st->work, delay); +	inv_read_yas53x_fifo(indio_dev); +	INV_I2C_INC_COMPASSIRQ(); + +error_ret: +	mutex_unlock(&indio_dev->mlock); +} + +static const struct iio_chan_spec compass_channels[] = { +	{ +		.type = IIO_MAGN, +		.modified = 1, +		.channel2 = IIO_MOD_X, +		.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, +		.scan_index = INV_YAS53X_SCAN_MAGN_X, +		.scan_type = IIO_ST('s', 16, 16, 0) +	}, { +		.type = IIO_MAGN, +		.modified = 1, +		.channel2 = IIO_MOD_Y, +		.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, +		.scan_index = INV_YAS53X_SCAN_MAGN_Y, +		.scan_type = IIO_ST('s', 16, 16, 0) +	}, { +		.type = IIO_MAGN, +		.modified = 1, +		.channel2 = IIO_MOD_Z, +		.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, +		.scan_index = INV_YAS53X_SCAN_MAGN_Z, +		.scan_type = IIO_ST('s', 16, 16, 0) +	}, +	IIO_CHAN_SOFT_TIMESTAMP(INV_YAS53X_SCAN_TIMESTAMP) +}; + +static DEVICE_ATTR(compass_matrix, S_IRUGO, inv_compass_matrix_show, NULL); +static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR, yas53x_rate_show, +		yas53x_rate_store); +static DEVICE_ATTR(overunderflow, S_IRUGO | S_IWUSR, +		yas53x_overunderflow_show, yas53x_overunderflow_store); + +static struct attribute *inv_yas53x_attributes[] = { +	&dev_attr_compass_matrix.attr, +	&dev_attr_sampling_frequency.attr, +	&dev_attr_overunderflow.attr, +	NULL, +}; +static const struct attribute_group inv_attribute_group = { +	.name = "yas53x", +	.attrs = inv_yas53x_attributes +}; + +static const struct iio_info yas53x_info = { +	.driver_module = THIS_MODULE, +	.read_raw = &yas53x_read_raw, +	.attrs = &inv_attribute_group, +}; + +/*constant IIO attribute */ +/** + *  inv_yas53x_probe() - probe function. + */ +static int inv_yas53x_probe(struct i2c_client *client, +	const struct i2c_device_id *id) +{ +	struct inv_compass_state *st; +	struct iio_dev *indio_dev; +	int result; + +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { +		result = -ENODEV; +		goto out_no_free; +	} +	indio_dev = iio_allocate_device(sizeof(*st)); +	if (indio_dev == NULL) { +		result =  -ENOMEM; +		goto out_no_free; +	} +	st = iio_priv(indio_dev); +	st->client = client; +	st->plat_data = +		*(struct mpu_platform_data *)dev_get_platdata(&client->dev); +	st->delay = 10; + +	i2c_set_clientdata(client, indio_dev); + +	if (!strcmp(id->name, "yas530")) { +		st->read_data    = yas530_read_data; +		st->get_cal_data = get_cal_data_yas530; +		st->overflow_bound = YAS_YAS530_DATA_OVERFLOW; +		st->center     = YAS_YAS530_DATA_CENTER; +		st->filter.filter_thresh = YAS530_MAG_DEFAULT_FILTER_THRESH; +	} else { +		st->read_data    = yas532_533_read_data; +		st->get_cal_data = get_cal_data_yas532_533; +		st->overflow_bound = YAS_YAS532_533_DATA_OVERFLOW; +		st->center     = YAS_YAS532_533_DATA_CENTER; +		st->filter.filter_thresh = YAS532_MAG_DEFAULT_FILTER_THRESH; +	} +	st->upper_bound = st->center + (st->center >> 1); +	st->lower_bound = (st->center >> 1); + +	result = yas53x_resume(st); +	if (result) +		goto out_free; + +	indio_dev->dev.parent = &client->dev; +	indio_dev->name = id->name; +	indio_dev->channels = compass_channels; +	indio_dev->num_channels = ARRAY_SIZE(compass_channels); +	indio_dev->info = &yas53x_info; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->currentmode = INDIO_DIRECT_MODE; + +	result = inv_yas53x_configure_ring(indio_dev); +	if (result) +		goto out_free; +	result = iio_buffer_register(indio_dev, indio_dev->channels, +					indio_dev->num_channels); +	if (result) +		goto out_unreg_ring; +	result = inv_yas53x_probe_trigger(indio_dev); +	if (result) +		goto out_remove_ring; + +	result = iio_device_register(indio_dev); +	if (result) +		goto out_remove_trigger; +	INIT_DELAYED_WORK(&st->work, yas53x_work_func); +	pr_info("%s: Probe name %s\n", __func__, id->name); + +	return 0; +out_remove_trigger: +	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) +		inv_yas53x_remove_trigger(indio_dev); +out_remove_ring: +	iio_buffer_unregister(indio_dev); +out_unreg_ring: +	inv_yas53x_unconfigure_ring(indio_dev); +out_free: +	iio_free_device(indio_dev); +out_no_free: +	dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); +	return -EIO; +} + +/** + *  inv_yas53x_remove() - remove function. + */ +static int inv_yas53x_remove(struct i2c_client *client) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(client); +	struct inv_compass_state *st = iio_priv(indio_dev); +	cancel_delayed_work_sync(&st->work); +	iio_device_unregister(indio_dev); +	inv_yas53x_remove_trigger(indio_dev); +	iio_buffer_unregister(indio_dev); +	inv_yas53x_unconfigure_ring(indio_dev); +	iio_free_device(indio_dev); + +	dev_info(&client->adapter->dev, "inv_yas53x_iio module removed.\n"); +	return 0; +} +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; +/* device id table is used to identify what device can be + * supported by this driver + */ +static const struct i2c_device_id inv_yas53x_id[] = { +	{"yas530", 0}, +	{"yas532", 0}, +	{"yas533", 0}, +	{} +}; + +MODULE_DEVICE_TABLE(i2c, inv_yas53x_id); + +static struct i2c_driver inv_yas53x_driver = { +	.class = I2C_CLASS_HWMON, +	.probe		=	inv_yas53x_probe, +	.remove		=	inv_yas53x_remove, +	.id_table	=	inv_yas53x_id, +	.driver = { +		.owner	=	THIS_MODULE, +		.name	=	"inv_yas53x_iio", +	}, +	.address_list = normal_i2c, +}; + +static int __init inv_yas53x_init(void) +{ +	int result = i2c_add_driver(&inv_yas53x_driver); +	if (result) { +		pr_err("%s failed\n", __func__); +		return result; +	} +	return 0; +} + +static void __exit inv_yas53x_exit(void) +{ +	i2c_del_driver(&inv_yas53x_driver); +} + +module_init(inv_yas53x_init); +module_exit(inv_yas53x_exit); + +MODULE_AUTHOR("Invensense Corporation"); +MODULE_DESCRIPTION("Invensense device driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("inv_yas53x_iio"); +/** + *  @} + */ + diff --git a/drivers/iio/magnetometer/inv_compass/inv_yas53x_iio.h b/drivers/iio/magnetometer/inv_compass/inv_yas53x_iio.h new file mode 100755 index 00000000000..92bf0af7ec7 --- /dev/null +++ b/drivers/iio/magnetometer/inv_compass/inv_yas53x_iio.h @@ -0,0 +1,172 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +* +*/ + +/** + *  @addtogroup DRIVERS + *  @brief      Hardware drivers. + * + *  @{ + *      @file  inv_yas53x_iio.h + *      @brief Struct definitions for the Invensense implementation + *              of yas53x driver. + */ + +#ifndef _INV_GYRO_H_ +#define _INV_GYRO_H_ + +#include <linux/i2c.h> +#include <linux/kfifo.h> +#include <linux/miscdevice.h> +#include <linux/input.h> +#include <linux/spinlock.h> +#include <linux/mpu.h> + +#include "iio.h" +#include "buffer.h" +#include "trigger.h" + +#define YAS_MAG_MAX_FILTER_LEN			30 +struct yas_adaptive_filter { +	int num; +	int index; +	int filter_len; +	int filter_noise; +	int sequence[YAS_MAG_MAX_FILTER_LEN]; +}; + +struct yas_thresh_filter { +	int threshold; +	int last; +}; + +struct yas_filter { +	int filter_len; +	int filter_thresh; +	int filter_noise[3]; +	struct yas_adaptive_filter adap_filter[3]; +	struct yas_thresh_filter thresh_filter[3]; +}; + +/** + *  struct inv_compass_state - Driver state variables. + *  @plat_data:         mpu platform data from board file. + *  @client:		i2c client handle. + *  @chan_info:         channel information. + *  @trig:              IIO trigger. + *  @work:              work structure. + *  @delay:             delay to schedule the next work. + *  @overflow_bound:    bound to determine overflow. + *  @center:            center of the measurement. + *  @compass_data[3]:   compass data store. + *  @offset[3]:         yas530 specific data. + *  @base_compass_data[3]: first measure data after reset. + *  @first_measure_after_reset:1: flag for first measurement after reset. + *  @first_read_after_reset:1: flag for first read after reset. + *  @reset_timer: timer to accumulate overflow conditions. + *  @overunderflow:1:     overflow and underflow flag. + *  @filter: filter data structure. + *  @read_data:         function pointer of reading data from device. + *  @get_cal_data: function pointer of reading cal data. + */ +struct inv_compass_state { +	struct mpu_platform_data plat_data; +	struct i2c_client *client; +	struct iio_trigger  *trig; +	struct delayed_work work; +	s16 delay; +	s16 overflow_bound; +	s16 upper_bound; +	s16 lower_bound; +	s16 center; +	s16 compass_data[3]; +	s8 offset[3]; +	s16 base_compass_data[3]; +	u8 first_measure_after_reset:1; +	u8 first_read_after_reset:1; +	u8 overunderflow:1; +	s32 reset_timer; +	struct yas_filter filter; +	int (*read_data)(struct inv_compass_state *st, +			  int *, u16 *, u16 *, u16 *, u16 *); +	int (*get_cal_data)(struct inv_compass_state *); +}; + +/* scan element definition */ +enum inv_mpu_scan { +	INV_YAS53X_SCAN_MAGN_X, +	INV_YAS53X_SCAN_MAGN_Y, +	INV_YAS53X_SCAN_MAGN_Z, +	INV_YAS53X_SCAN_TIMESTAMP, +}; + +#define YAS530_REGADDR_DEVICE_ID          0x80 +#define YAS530_REGADDR_ACTUATE_INIT_COIL  0x81 +#define YAS530_REGADDR_MEASURE_COMMAND    0x82 +#define YAS530_REGADDR_CONFIG             0x83 +#define YAS530_REGADDR_MEASURE_INTERVAL   0x84 +#define YAS530_REGADDR_OFFSET_X           0x85 +#define YAS530_REGADDR_OFFSET_Y1          0x86 +#define YAS530_REGADDR_OFFSET_Y2          0x87 +#define YAS530_REGADDR_TEST1              0x88 +#define YAS530_REGADDR_TEST2              0x89 +#define YAS530_REGADDR_CAL                0x90 +#define YAS530_REGADDR_MEASURE_DATA       0xb0 + +#define YAS530_MAX_DELAY                  200 +#define YAS530_MIN_DELAY                  5 +#define YAS530_SCALE                      107374182L + +#define YAS_YAS530_VERSION_A		0	/* YAS530  (MS-3E Aver) */ +#define YAS_YAS530_VERSION_B		1	/* YAS530B (MS-3E Bver) */ +#define YAS_YAS530_VERSION_A_COEF	380 +#define YAS_YAS530_VERSION_B_COEF	550 +#define YAS_YAS530_DATA_CENTER		2048 +#define YAS_YAS530_DATA_OVERFLOW	4095 +#define YAS_YAS530_CAL_DATA_SIZE        16 + +/*filter related defines */ +#define YAS_MAG_DEFAULT_FILTER_NOISE_X          144 /* sd: 1200 nT */ +#define YAS_MAG_DEFAULT_FILTER_NOISE_Y          144 /* sd: 1200 nT */ +#define YAS_MAG_DEFAULT_FILTER_NOISE_Z          144 /* sd: 1200 nT */ +#define YAS_MAG_DEFAULT_FILTER_LEN              20 + +#define YAS530_MAG_DEFAULT_FILTER_THRESH        100 +#define YAS532_MAG_DEFAULT_FILTER_THRESH        300 + +#define YAS_YAS532_533_VERSION_AB	0 /* YAS532_533AB (MS-3R/3F ABver) */ +#define YAS_YAS532_533_VERSION_AC	1 /* YAS532_533AC (MS-3R/3F ACver) */ +#define YAS_YAS532_533_VERSION_AB_COEF	1800 +#define YAS_YAS532_533_VERSION_AC_COEF	900 +#define YAS_YAS532_533_DATA_CENTER      4096 +#define YAS_YAS532_533_DATA_OVERFLOW	8190 +#define YAS_YAS532_533_CAL_DATA_SIZE    14 + +#define YAS_MAG_DISTURBURNCE_THRESHOLD  1600 +#define YAS_RESET_COIL_TIME_THRESHOLD   3000 + +#define INV_ERROR_COMPASS_DATA_OVERFLOW  (-1) +#define INV_ERROR_COMPASS_DATA_NOT_READY (-2) + +int inv_yas53x_configure_ring(struct iio_dev *indio_dev); +void inv_yas53x_unconfigure_ring(struct iio_dev *indio_dev); +int inv_yas53x_probe_trigger(struct iio_dev *indio_dev); +void inv_yas53x_remove_trigger(struct iio_dev *indio_dev); +void set_yas53x_enable(struct iio_dev *indio_dev, bool enable); +void inv_read_yas53x_fifo(struct iio_dev *indio_dev); +int yas53x_read(struct inv_compass_state *st, short rawfixed[3], +				s32 *overunderflow); +int yas53x_resume(struct inv_compass_state *st); + +#endif  /* #ifndef _INV_GYRO_H_ */ + diff --git a/drivers/iio/magnetometer/inv_compass/inv_yas53x_ring.c b/drivers/iio/magnetometer/inv_compass/inv_yas53x_ring.c new file mode 100755 index 00000000000..efcf49c6839 --- /dev/null +++ b/drivers/iio/magnetometer/inv_compass/inv_yas53x_ring.c @@ -0,0 +1,165 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +* +*/ + +/** + *  @addtogroup  DRIVERS + *  @brief       Hardware drivers. + * + *  @{ + *      @file    inv_yas53x_ring.c + *      @brief   Invensense implementation for yas530/yas532/yas533. + *      @details This driver currently works for the yas530/yas532/yas533. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> + +#include "iio.h" +#include "kfifo_buf.h" +#include "trigger_consumer.h" +#include "sysfs.h" + +#include "inv_yas53x_iio.h" + +static s64 get_time_ns(void) +{ +	struct timespec ts; +	ktime_get_ts(&ts); + +	return timespec_to_ns(&ts); +} + +static int put_scan_to_buf(struct iio_dev *indio_dev, unsigned char *d, +				short *s, int scan_index) +{ +	struct iio_buffer *ring = indio_dev->buffer; +	int st; +	int i, d_ind; + +	d_ind = 0; +	for (i = 0; i < 3; i++) { +		st = iio_scan_mask_query(indio_dev, ring, scan_index + i); +		if (st) { +			memcpy(&d[d_ind], &s[i], sizeof(s[i])); +			d_ind += sizeof(s[i]); +		} +	} + +	return d_ind; +} + +/** + *  inv_read_yas53x_fifo() - Transfer data from FIFO to ring buffer. + */ +void inv_read_yas53x_fifo(struct iio_dev *indio_dev) +{ +	struct inv_compass_state *st = iio_priv(indio_dev); +	struct iio_buffer *ring = indio_dev->buffer; +	int d_ind; +	s32 overunderflow; +	s8 *tmp; +	s64 tmp_buf[2]; + +	if (!yas53x_read(st, st->compass_data, &overunderflow)) { +		tmp = (u8 *)tmp_buf; +		d_ind = put_scan_to_buf(indio_dev, tmp, st->compass_data, +						INV_YAS53X_SCAN_MAGN_X); +		if (ring->scan_timestamp) +			tmp_buf[(d_ind + 7) / 8] = get_time_ns(); +		ring->access->store_to(indio_dev->buffer, tmp, 0); + +		if (overunderflow) { +			yas53x_resume(st); +			if (!st->overunderflow) +				st->overunderflow = 1; +		} +	} +} + +void inv_yas53x_unconfigure_ring(struct iio_dev *indio_dev) +{ +	iio_kfifo_free(indio_dev->buffer); +}; + +static int inv_yas53x_postenable(struct iio_dev *indio_dev) +{ +	struct inv_compass_state *st = iio_priv(indio_dev); +	struct iio_buffer *ring = indio_dev->buffer; + +	/* when all the outputs are disabled, even though buffer/enable is on, +	   do nothing */ +	if (!(iio_scan_mask_query(indio_dev, ring, INV_YAS53X_SCAN_MAGN_X) || +	    iio_scan_mask_query(indio_dev, ring, INV_YAS53X_SCAN_MAGN_Y) || +	    iio_scan_mask_query(indio_dev, ring, INV_YAS53X_SCAN_MAGN_Z))) +		return 0; + +	set_yas53x_enable(indio_dev, true); +	schedule_delayed_work(&st->work, +		msecs_to_jiffies(st->delay)); + +	return 0; +} + +static int inv_yas53x_predisable(struct iio_dev *indio_dev) +{ +	struct inv_compass_state *st = iio_priv(indio_dev); +	struct iio_buffer *ring = indio_dev->buffer; + +	cancel_delayed_work_sync(&st->work); +	clear_bit(INV_YAS53X_SCAN_MAGN_X, ring->scan_mask); +	clear_bit(INV_YAS53X_SCAN_MAGN_Y, ring->scan_mask); +	clear_bit(INV_YAS53X_SCAN_MAGN_Z, ring->scan_mask); + +	return 0; +} + +static const struct iio_buffer_setup_ops inv_yas53x_ring_setup_ops = { +	.preenable = &iio_sw_buffer_preenable, +	.postenable = &inv_yas53x_postenable, +	.predisable = &inv_yas53x_predisable, +}; + +int inv_yas53x_configure_ring(struct iio_dev *indio_dev) +{ +	int ret = 0; +	struct iio_buffer *ring; + +	ring = iio_kfifo_allocate(indio_dev); +	if (!ring) { +		ret = -ENOMEM; +		return ret; +	} +	indio_dev->buffer = ring; +	/* setup ring buffer */ +	ring->scan_timestamp = true; +	indio_dev->setup_ops = &inv_yas53x_ring_setup_ops; + +	indio_dev->modes |= INDIO_BUFFER_TRIGGERED; +	return 0; +} +/** + *  @} + */ + diff --git a/drivers/iio/magnetometer/inv_compass/inv_yas53x_trigger.c b/drivers/iio/magnetometer/inv_compass/inv_yas53x_trigger.c new file mode 100755 index 00000000000..a20ce2baa7e --- /dev/null +++ b/drivers/iio/magnetometer/inv_compass/inv_yas53x_trigger.c @@ -0,0 +1,91 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +* +*/ + +/** + *  @addtogroup  DRIVERS + *  @brief       Hardware drivers. + * + *  @{ + *      @file    inv_yas53x_trigger.c + *      @brief   Invensense implementation for yas530/yas532/yas533 + *      @details This driver currently works for the yas530/yas532/yas533 + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> + +#include "iio.h" +#include "sysfs.h" +#include "trigger.h" + +#include "inv_yas53x_iio.h" + +static const struct iio_trigger_ops inv_yas53x_trigger_ops = { +	.owner = THIS_MODULE, +}; + +int inv_yas53x_probe_trigger(struct iio_dev *indio_dev) +{ +	int ret; +	struct inv_compass_state *st = iio_priv(indio_dev); + +	st->trig = iio_allocate_trigger("%s-dev%d", +					indio_dev->name, +					indio_dev->id); +	if (st->trig == NULL) { +		ret = -ENOMEM; +		goto error_ret; +	} +	/* select default trigger */ +	st->trig->dev.parent = &st->client->dev; +	st->trig->private_data = indio_dev; +	st->trig->ops = &inv_yas53x_trigger_ops; +	ret = iio_trigger_register(st->trig); + +	/* select default trigger */ +	indio_dev->trig = st->trig; +	if (ret) +		goto error_free_trig; + +	return 0; + +error_free_trig: +	iio_free_trigger(st->trig); +error_ret: +	return ret; +} + +void inv_yas53x_remove_trigger(struct iio_dev *indio_dev) +{ +	struct inv_compass_state *st = iio_priv(indio_dev); + +	iio_trigger_unregister(st->trig); +	iio_free_trigger(st->trig); +} +/** + *  @} + */ + diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 59aa24002c7..c4249f8b5ba 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2,6 +2,9 @@   * Atmel maXTouch Touchscreen driver   *   * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Copyright (C) 2011-2014 Atmel Corporation + * Copyright (C) 2012 Google, Inc. + *   * Author: Joonyoung Shim <jy0922.shim@samsung.com>   *   * This program is free software; you can redistribute  it and/or modify it @@ -13,40 +16,31 @@  #include <linux/module.h>  #include <linux/init.h> +#include <linux/completion.h>  #include <linux/delay.h>  #include <linux/firmware.h>  #include <linux/i2c.h>  #include <linux/i2c/atmel_mxt_ts.h>  #include <linux/input/mt.h>  #include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/of.h>  #include <linux/slab.h> +#include <linux/regulator/consumer.h> +#include <linux/gpio.h> -/* Version */ -#define MXT_VER_20		20 -#define MXT_VER_21		21 -#define MXT_VER_22		22 - -/* Slave addresses */ -#define MXT_APP_LOW		0x4a -#define MXT_APP_HIGH		0x4b -#define MXT_BOOT_LOW		0x24 -#define MXT_BOOT_HIGH		0x25 +#ifdef CONFIG_OF +#include <linux/of_gpio.h> +#endif -/* Firmware */ -#define MXT_FW_NAME		"maxtouch.fw" +/* Configuration file */ +#define MXT_CFG_MAGIC		"OBP_RAW V1"  /* Registers */ -#define MXT_INFO		0x00 -#define MXT_FAMILY_ID		0x00 -#define MXT_VARIANT_ID		0x01 -#define MXT_VERSION		0x02 -#define MXT_BUILD		0x03 -#define MXT_MATRIX_X_SIZE	0x04 -#define MXT_MATRIX_Y_SIZE	0x05 -#define MXT_OBJECT_NUM		0x06  #define MXT_OBJECT_START	0x07 -  #define MXT_OBJECT_SIZE		6 +#define MXT_INFO_CHECKSUM_SIZE	3 +#define MXT_MAX_BLOCK_WRITE	255  /* Object types */  #define MXT_DEBUG_DIAGNOSTIC_T37	37 @@ -76,6 +70,11 @@  #define MXT_SPT_DIGITIZER_T43		43  #define MXT_SPT_MESSAGECOUNT_T44	44  #define MXT_SPT_CTECONFIG_T46		46 +#define MXT_PROCI_ACTIVE_STYLUS_T63	63 +#define MXT_TOUCH_MULTITOUCHSCREEN_T100 100 + +/* MXT_GEN_MESSAGE_T5 object */ +#define MXT_RPTID_NOMSG		0xff  /* MXT_GEN_COMMAND_T6 field */  #define MXT_COMMAND_RESET	0 @@ -84,108 +83,102 @@  #define MXT_COMMAND_REPORTALL	3  #define MXT_COMMAND_DIAGNOSTIC	5 +/* Define for T6 status byte */ +#define MXT_T6_STATUS_RESET	(1 << 7) +#define MXT_T6_STATUS_OFL	(1 << 6) +#define MXT_T6_STATUS_SIGERR	(1 << 5) +#define MXT_T6_STATUS_CAL	(1 << 4) +#define MXT_T6_STATUS_CFGERR	(1 << 3) +#define MXT_T6_STATUS_COMSERR	(1 << 2) +  /* MXT_GEN_POWER_T7 field */ -#define MXT_POWER_IDLEACQINT	0 -#define MXT_POWER_ACTVACQINT	1 -#define MXT_POWER_ACTV2IDLETO	2 +struct t7_config { +	u8 idle; +	u8 active; +} __packed; -/* MXT_GEN_ACQUIRE_T8 field */ -#define MXT_ACQUIRE_CHRGTIME	0 -#define MXT_ACQUIRE_TCHDRIFT	2 -#define MXT_ACQUIRE_DRIFTST	3 -#define MXT_ACQUIRE_TCHAUTOCAL	4 -#define MXT_ACQUIRE_SYNC	5 -#define MXT_ACQUIRE_ATCHCALST	6 -#define MXT_ACQUIRE_ATCHCALSTHR	7 +#define MXT_POWER_CFG_RUN		0 +#define MXT_POWER_CFG_DEEPSLEEP		1  /* MXT_TOUCH_MULTI_T9 field */ -#define MXT_TOUCH_CTRL		0 -#define MXT_TOUCH_XORIGIN	1 -#define MXT_TOUCH_YORIGIN	2 -#define MXT_TOUCH_XSIZE		3 -#define MXT_TOUCH_YSIZE		4 -#define MXT_TOUCH_BLEN		6 -#define MXT_TOUCH_TCHTHR	7 -#define MXT_TOUCH_TCHDI		8 -#define MXT_TOUCH_ORIENT	9 -#define MXT_TOUCH_MOVHYSTI	11 -#define MXT_TOUCH_MOVHYSTN	12 -#define MXT_TOUCH_NUMTOUCH	14 -#define MXT_TOUCH_MRGHYST	15 -#define MXT_TOUCH_MRGTHR	16 -#define MXT_TOUCH_AMPHYST	17 -#define MXT_TOUCH_XRANGE_LSB	18 -#define MXT_TOUCH_XRANGE_MSB	19 -#define MXT_TOUCH_YRANGE_LSB	20 -#define MXT_TOUCH_YRANGE_MSB	21 -#define MXT_TOUCH_XLOCLIP	22 -#define MXT_TOUCH_XHICLIP	23 -#define MXT_TOUCH_YLOCLIP	24 -#define MXT_TOUCH_YHICLIP	25 -#define MXT_TOUCH_XEDGECTRL	26 -#define MXT_TOUCH_XEDGEDIST	27 -#define MXT_TOUCH_YEDGECTRL	28 -#define MXT_TOUCH_YEDGEDIST	29 -#define MXT_TOUCH_JUMPLIMIT	30 +#define MXT_T9_ORIENT		9 +#define MXT_T9_RANGE		18 -/* MXT_PROCI_GRIPFACE_T20 field */ -#define MXT_GRIPFACE_CTRL	0 -#define MXT_GRIPFACE_XLOGRIP	1 -#define MXT_GRIPFACE_XHIGRIP	2 -#define MXT_GRIPFACE_YLOGRIP	3 -#define MXT_GRIPFACE_YHIGRIP	4 -#define MXT_GRIPFACE_MAXTCHS	5 -#define MXT_GRIPFACE_SZTHR1	7 -#define MXT_GRIPFACE_SZTHR2	8 -#define MXT_GRIPFACE_SHPTHR1	9 -#define MXT_GRIPFACE_SHPTHR2	10 -#define MXT_GRIPFACE_SUPEXTTO	11 +/* MXT_TOUCH_MULTI_T9 status */ +#define MXT_T9_UNGRIP		(1 << 0) +#define MXT_T9_SUPPRESS		(1 << 1) +#define MXT_T9_AMP		(1 << 2) +#define MXT_T9_VECTOR		(1 << 3) +#define MXT_T9_MOVE		(1 << 4) +#define MXT_T9_RELEASE		(1 << 5) +#define MXT_T9_PRESS		(1 << 6) +#define MXT_T9_DETECT		(1 << 7) -/* MXT_PROCI_NOISE field */ -#define MXT_NOISE_CTRL		0 -#define MXT_NOISE_OUTFLEN	1 -#define MXT_NOISE_GCAFUL_LSB	3 -#define MXT_NOISE_GCAFUL_MSB	4 -#define MXT_NOISE_GCAFLL_LSB	5 -#define MXT_NOISE_GCAFLL_MSB	6 -#define MXT_NOISE_ACTVGCAFVALID	7 -#define MXT_NOISE_NOISETHR	8 -#define MXT_NOISE_FREQHOPSCALE	10 -#define MXT_NOISE_FREQ0		11 -#define MXT_NOISE_FREQ1		12 -#define MXT_NOISE_FREQ2		13 -#define MXT_NOISE_FREQ3		14 -#define MXT_NOISE_FREQ4		15 -#define MXT_NOISE_IDLEGCAFVALID	16 +struct t9_range { +	u16 x; +	u16 y; +} __packed; + +/* MXT_TOUCH_MULTI_T9 orient */ +#define MXT_T9_ORIENT_SWITCH	(1 << 0)  /* MXT_SPT_COMMSCONFIG_T18 */  #define MXT_COMMS_CTRL		0  #define MXT_COMMS_CMD		1 - -/* MXT_SPT_CTECONFIG_T28 field */ -#define MXT_CTE_CTRL		0 -#define MXT_CTE_CMD		1 -#define MXT_CTE_MODE		2 -#define MXT_CTE_IDLEGCAFDEPTH	3 -#define MXT_CTE_ACTVGCAFDEPTH	4 -#define MXT_CTE_VOLTAGE		5 - -#define MXT_VOLTAGE_DEFAULT	2700000 -#define MXT_VOLTAGE_STEP	10000 +#define MXT_COMMS_RETRIGEN      (1 << 6)  /* Define for MXT_GEN_COMMAND_T6 */  #define MXT_BOOT_VALUE		0xa5 +#define MXT_RESET_VALUE		0x01  #define MXT_BACKUP_VALUE	0x55 -#define MXT_BACKUP_TIME		50	/* msec */ -#define MXT_RESET_TIME		200	/* msec */ -#define MXT_FWRESET_TIME	175	/* msec */ +/* Define for MXT_PROCI_TOUCHSUPPRESSION_T42 */ +#define MXT_T42_MSG_TCHSUP	(1 << 0) + +/* T47 Stylus */ +#define MXT_TOUCH_MAJOR_T47_STYLUS	1 + +/* T63 Stylus */ +#define MXT_T63_STYLUS_PRESS	(1 << 0) +#define MXT_T63_STYLUS_RELEASE	(1 << 1) +#define MXT_T63_STYLUS_MOVE		(1 << 2) +#define MXT_T63_STYLUS_SUPPRESS	(1 << 3) + +#define MXT_T63_STYLUS_DETECT	(1 << 4) +#define MXT_T63_STYLUS_TIP		(1 << 5) +#define MXT_T63_STYLUS_ERASER	(1 << 6) +#define MXT_T63_STYLUS_BARREL	(1 << 7) + +#define MXT_T63_STYLUS_PRESSURE_MASK	0x3F + +/* T100 Multiple Touch Touchscreen */ +#define MXT_T100_CTRL		0 +#define MXT_T100_CFG1		1 +#define MXT_T100_TCHAUX		3 +#define MXT_T100_XRANGE		13 +#define MXT_T100_YRANGE		24 + +#define MXT_T100_CFG_SWITCHXY	(1 << 5) -/* MXT_SPT_GPIOPWM_T19 field */ -#define MXT_GPIO0_MASK		0x04 -#define MXT_GPIO1_MASK		0x08 -#define MXT_GPIO2_MASK		0x10 -#define MXT_GPIO3_MASK		0x20 +#define MXT_T100_TCHAUX_VECT	(1 << 0) +#define MXT_T100_TCHAUX_AMPL	(1 << 1) +#define MXT_T100_TCHAUX_AREA	(1 << 2) + +#define MXT_T100_DETECT		(1 << 7) +#define MXT_T100_TYPE_MASK	0x70 +#define MXT_T100_TYPE_STYLUS	0x20 + +/* Delay times */ +#define MXT_BACKUP_TIME		50	/* msec */ +#define MXT_RESET_TIME		200	/* msec */ +#define MXT_RESET_TIMEOUT	3000	/* msec */ +#define MXT_CRC_TIMEOUT		1000	/* msec */ +#define MXT_FW_RESET_TIME	3000	/* msec */ +#define MXT_FW_CHG_TIMEOUT	300	/* msec */ +#define MXT_WAKEUP_TIME		25	/* msec */ +#define MXT_REGULATOR_DELAY	150	/* msec */ +#define MXT_CHG_DELAY	        100	/* msec */ +#define MXT_POWERON_DELAY	150	/* msec */  /* Command to unlock bootloader */  #define MXT_UNLOCK_CMD_MSB	0xaa @@ -199,27 +192,16 @@  #define MXT_FRAME_CRC_PASS	0x04  #define MXT_APP_CRC_FAIL	0x40	/* valid 7 8 bit only */  #define MXT_BOOT_STATUS_MASK	0x3f - -/* Touch status */ -#define MXT_UNGRIP		(1 << 0) -#define MXT_SUPPRESS		(1 << 1) -#define MXT_AMP			(1 << 2) -#define MXT_VECTOR		(1 << 3) -#define MXT_MOVE		(1 << 4) -#define MXT_RELEASE		(1 << 5) -#define MXT_PRESS		(1 << 6) -#define MXT_DETECT		(1 << 7) - -/* Touch orient bits */ -#define MXT_XY_SWITCH		(1 << 0) -#define MXT_X_INVERT		(1 << 1) -#define MXT_Y_INVERT		(1 << 2) +#define MXT_BOOT_EXTENDED_ID	(1 << 5) +#define MXT_BOOT_ID_MASK	0x1f  /* Touchscreen absolute values */  #define MXT_MAX_AREA		0xff  #define MXT_PIXELS_PER_MM	20 +#define DEBUG_MSG_MAX		200 +  struct mxt_info {  	u8 family_id;  	u8 variant_id; @@ -233,16 +215,11 @@ struct mxt_info {  struct mxt_object {  	u8 type;  	u16 start_address; -	u8 size;		/* Size of each instance - 1 */ -	u8 instances;		/* Number of instances - 1 */ +	u8 size_minus_one; +	u8 instances_minus_one;  	u8 num_report_ids;  } __packed; -struct mxt_message { -	u8 reportid; -	u8 message[7]; -}; -  /* Each client has this additional data */  struct mxt_data {  	struct i2c_client *client; @@ -250,20 +227,89 @@ struct mxt_data {  	char phys[64];		/* device physical location */  	const struct mxt_platform_data *pdata;  	struct mxt_object *object_table; -	struct mxt_info info; -	bool is_tp; - +	struct mxt_info *info; +	void *raw_info_block;  	unsigned int irq;  	unsigned int max_x;  	unsigned int max_y; +	bool in_bootloader; +	u16 mem_size; +	u8 t100_aux_ampl; +	u8 t100_aux_area; +	u8 t100_aux_vect; +	struct bin_attribute mem_access_attr; +	bool debug_enabled; +	bool debug_v2_enabled; +	u8 *debug_msg_data; +	u16 debug_msg_count; +	struct bin_attribute debug_msg_attr; +	struct mutex debug_msg_lock; +	u8 max_reportid; +	u32 config_crc; +	u32 info_crc; +	u8 bootloader_addr; +	u8 *msg_buf; +	u8 t6_status; +	bool update_input; +	u8 last_message_count; +	u8 num_touchids; +	struct t7_config t7_cfg; +	u8 num_stylusids; +	unsigned long t15_keystatus; +	bool use_retrigen_workaround; +	bool use_regulator; +	struct regulator *reg_vdd; +	struct regulator *reg_avdd; +	char *fw_name; +	char *cfg_name;  	/* Cached parameters from object table */ +	u16 T5_address; +	u8 T5_msg_size;  	u8 T6_reportid; +	u16 T6_address; +	u16 T7_address;  	u8 T9_reportid_min;  	u8 T9_reportid_max; +	u8 T15_reportid_min; +	u8 T15_reportid_max; +	u16 T18_address;  	u8 T19_reportid; +	u8 T42_reportid_min; +	u8 T42_reportid_max; +	u16 T44_address; +	u8 T48_reportid; +	u8 T63_reportid_min; +	u8 T63_reportid_max; +	u8 T100_reportid_min; +	u8 T100_reportid_max; + +	/* for fw update in bootloader */ +	struct completion bl_completion; + +	/* for reset handling */ +	struct completion reset_completion; + +	/* for config update handling */ +	struct completion crc_completion; + +	/* Indicates whether device is in suspend */ +	bool suspended; + +	/* Indicates whether device is updating configuration */ +	bool updating_config;  }; +static size_t mxt_obj_size(const struct mxt_object *obj) +{ +	return obj->size_minus_one + 1; +} + +static size_t mxt_obj_instances(const struct mxt_object *obj) +{ +	return obj->instances_minus_one + 1; +} +  static bool mxt_object_readable(unsigned int type)  {  	switch (type) { @@ -297,98 +343,365 @@ static bool mxt_object_readable(unsigned int type)  	}  } -static bool mxt_object_writable(unsigned int type) +static void mxt_dump_message(struct mxt_data *data, u8 *message)  { -	switch (type) { -	case MXT_GEN_COMMAND_T6: -	case MXT_GEN_POWER_T7: -	case MXT_GEN_ACQUIRE_T8: -	case MXT_TOUCH_MULTI_T9: -	case MXT_TOUCH_KEYARRAY_T15: -	case MXT_TOUCH_PROXIMITY_T23: -	case MXT_TOUCH_PROXKEY_T52: -	case MXT_PROCI_GRIPFACE_T20: -	case MXT_PROCG_NOISE_T22: -	case MXT_PROCI_ONETOUCH_T24: -	case MXT_PROCI_TWOTOUCH_T27: -	case MXT_PROCI_GRIP_T40: -	case MXT_PROCI_PALM_T41: -	case MXT_PROCI_TOUCHSUPPRESSION_T42: -	case MXT_PROCI_STYLUS_T47: -	case MXT_PROCG_NOISESUPPRESSION_T48: -	case MXT_SPT_COMMSCONFIG_T18: -	case MXT_SPT_GPIOPWM_T19: -	case MXT_SPT_SELFTEST_T25: -	case MXT_SPT_CTECONFIG_T28: -	case MXT_SPT_DIGITIZER_T43: -	case MXT_SPT_CTECONFIG_T46: -		return true; +	dev_dbg(&data->client->dev, "MXT MSG: %*ph\n", +		       data->T5_msg_size, message); +} + +static void mxt_debug_msg_enable(struct mxt_data *data) +{ +	struct device *dev = &data->client->dev; + +	if (data->debug_v2_enabled) +		return; + +	mutex_lock(&data->debug_msg_lock); + +	data->debug_msg_data = kcalloc(DEBUG_MSG_MAX, +				data->T5_msg_size, GFP_KERNEL); +	if (!data->debug_msg_data) +		return; + +	data->debug_v2_enabled = true; +	mutex_unlock(&data->debug_msg_lock); + +	dev_info(dev, "Enabled message output\n"); +} + +static void mxt_debug_msg_disable(struct mxt_data *data) +{ +	struct device *dev = &data->client->dev; + +	if (!data->debug_v2_enabled) +		return; + +	dev_info(dev, "disabling message output\n"); +	data->debug_v2_enabled = false; + +	mutex_lock(&data->debug_msg_lock); +	kfree(data->debug_msg_data); +	data->debug_msg_data = NULL; +	data->debug_msg_count = 0; +	mutex_unlock(&data->debug_msg_lock); +	dev_info(dev, "Disabled message output\n"); +} + +static void mxt_debug_msg_add(struct mxt_data *data, u8 *msg) +{ +	struct device *dev = &data->client->dev; + +	mutex_lock(&data->debug_msg_lock); + +	if (!data->debug_msg_data) { +		dev_err(dev, "No buffer!\n"); +		return; +	} + +	if (data->debug_msg_count < DEBUG_MSG_MAX) { +		memcpy(data->debug_msg_data + +		       data->debug_msg_count * data->T5_msg_size, +		       msg, +		       data->T5_msg_size); +		data->debug_msg_count++; +	} else { +		dev_dbg(dev, "Discarding %u messages\n", data->debug_msg_count); +		data->debug_msg_count = 0; +	} + +	mutex_unlock(&data->debug_msg_lock); + +	sysfs_notify(&data->client->dev.kobj, NULL, "debug_notify"); +} + +static ssize_t mxt_debug_msg_write(struct file *filp, struct kobject *kobj, +	struct bin_attribute *bin_attr, char *buf, loff_t off, +	size_t count) +{ +	return -EIO; +} + +static ssize_t mxt_debug_msg_read(struct file *filp, struct kobject *kobj, +	struct bin_attribute *bin_attr, char *buf, loff_t off, size_t bytes) +{ +	struct device *dev = container_of(kobj, struct device, kobj); +	struct mxt_data *data = dev_get_drvdata(dev); +	int count; +	size_t bytes_read; + +	if (!data->debug_msg_data) { +		dev_err(dev, "No buffer!\n"); +		return 0; +	} + +	count = bytes / data->T5_msg_size; + +	if (count > DEBUG_MSG_MAX) +		count = DEBUG_MSG_MAX; + +	mutex_lock(&data->debug_msg_lock); + +	if (count > data->debug_msg_count) +		count = data->debug_msg_count; + +	bytes_read = count * data->T5_msg_size; + +	memcpy(buf, data->debug_msg_data, bytes_read); +	data->debug_msg_count = 0; + +	mutex_unlock(&data->debug_msg_lock); + +	return bytes_read; +} + +static int mxt_debug_msg_init(struct mxt_data *data) +{ +	sysfs_bin_attr_init(&data->debug_msg_attr); +	data->debug_msg_attr.attr.name = "debug_msg"; +	data->debug_msg_attr.attr.mode = 0666; +	data->debug_msg_attr.read = mxt_debug_msg_read; +	data->debug_msg_attr.write = mxt_debug_msg_write; +	data->debug_msg_attr.size = data->T5_msg_size * DEBUG_MSG_MAX; + +	if (sysfs_create_bin_file(&data->client->dev.kobj, +				  &data->debug_msg_attr) < 0) { +		dev_err(&data->client->dev, "Failed to create %s\n", +			data->debug_msg_attr.attr.name); +		return -EINVAL; +	} + +	return 0; +} + +static void mxt_debug_msg_remove(struct mxt_data *data) +{ +	if (data->debug_msg_attr.attr.name) +		sysfs_remove_bin_file(&data->client->dev.kobj, +				      &data->debug_msg_attr); +} + +static int mxt_wait_for_completion(struct mxt_data *data, +				   struct completion *comp, +				   unsigned int timeout_ms) +{ +	struct device *dev = &data->client->dev; +	unsigned long timeout = msecs_to_jiffies(timeout_ms); +	long ret; + +	ret = wait_for_completion_interruptible_timeout(comp, timeout); +	if (ret < 0) { +		return ret; +	} else if (ret == 0) { +		dev_err(dev, "Wait for completion timed out.\n"); +		return -ETIMEDOUT; +	} +	return 0; +} + +static int mxt_bootloader_read(struct mxt_data *data, +			       u8 *val, unsigned int count) +{ +	int ret; +	struct i2c_msg msg; + +	msg.addr = data->bootloader_addr; +	msg.flags = data->client->flags & I2C_M_TEN; +	msg.flags |= I2C_M_RD; +	msg.len = count; +	msg.buf = val; + +	ret = i2c_transfer(data->client->adapter, &msg, 1); +	if (ret == 1) { +		ret = 0; +	} else { +		ret = ret < 0 ? ret : -EIO; +		dev_err(&data->client->dev, "%s: i2c recv failed (%d)\n", +			__func__, ret); +	} + +	return ret; +} + +static int mxt_bootloader_write(struct mxt_data *data, +				const u8 * const val, unsigned int count) +{ +	int ret; +	struct i2c_msg msg; + +	msg.addr = data->bootloader_addr; +	msg.flags = data->client->flags & I2C_M_TEN; +	msg.len = count; +	msg.buf = (u8 *)val; + +	ret = i2c_transfer(data->client->adapter, &msg, 1); +	if (ret == 1) { +		ret = 0; +	} else { +		ret = ret < 0 ? ret : -EIO; +		dev_err(&data->client->dev, "%s: i2c send failed (%d)\n", +			__func__, ret); +	} + +	return ret; +} + +static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry) +{ +	u8 appmode = data->client->addr; +	u8 bootloader; +	u8 family_id = data->info ? data->info->family_id : 0; + +	switch (appmode) { +	case 0x4a: +	case 0x4b: +		/* Chips after 1664S use different scheme */ +		if (retry || family_id >= 0xa2) { +			bootloader = appmode - 0x24; +			break; +		} +		/* Fall through for normal case */ +	case 0x4c: +	case 0x4d: +	case 0x5a: +	case 0x5b: +		bootloader = appmode - 0x26; +		break; +  	default: -		return false; +		dev_err(&data->client->dev, +			"Appmode i2c address 0x%02x not found\n", +			appmode); +		return -EINVAL;  	} + +	data->bootloader_addr = bootloader; +	return 0; +} + +static int mxt_probe_bootloader(struct mxt_data *data, bool alt_address) +{ +	struct device *dev = &data->client->dev; +	int error; +	u8 val; +	bool crc_failure; + +	error = mxt_lookup_bootloader_address(data, alt_address); +	if (error) +		return error; + +	error = mxt_bootloader_read(data, &val, 1); +	if (error) +		return error; + +	/* Check app crc fail mode */ +	crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL; + +	dev_err(dev, "Detected bootloader, status:%02X%s\n", +			val, crc_failure ? ", APP_CRC_FAIL" : ""); + +	return 0;  } -static void mxt_dump_message(struct device *dev, -			     struct mxt_message *message) +static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val)  { -	dev_dbg(dev, "reportid: %u\tmessage: %*ph\n", -		message->reportid, 7, message->message); +	struct device *dev = &data->client->dev; +	u8 buf[3]; + +	if (val & MXT_BOOT_EXTENDED_ID) { +		if (mxt_bootloader_read(data, &buf[0], 3) != 0) { +			dev_err(dev, "%s: i2c failure\n", __func__); +			return val; +		} + +		dev_dbg(dev, "Bootloader ID:%d Version:%d\n", buf[1], buf[2]); + +		return buf[0]; +	} else { +		dev_dbg(dev, "Bootloader ID:%d\n", val & MXT_BOOT_ID_MASK); + +		return val; +	}  } -static int mxt_check_bootloader(struct i2c_client *client, -				     unsigned int state) +static int mxt_check_bootloader(struct mxt_data *data, unsigned int state, +				bool wait)  { +	struct device *dev = &data->client->dev;  	u8 val; +	int ret;  recheck: -	if (i2c_master_recv(client, &val, 1) != 1) { -		dev_err(&client->dev, "%s: i2c recv failed\n", __func__); -		return -EIO; +	if (wait) { +		/* +		 * In application update mode, the interrupt +		 * line signals state transitions. We must wait for the +		 * CHG assertion before reading the status byte. +		 * Once the status byte has been read, the line is deasserted. +		 */ +		ret = mxt_wait_for_completion(data, &data->bl_completion, +					      MXT_FW_CHG_TIMEOUT); +		if (ret) { +			/* +			 * TODO: handle -ERESTARTSYS better by terminating +			 * fw update process before returning to userspace +			 * by writing length 0x000 to device (iff we are in +			 * WAITING_FRAME_DATA state). +			 */ +			dev_err(dev, "Update wait error %d\n", ret); +			return ret; +		}  	} +	ret = mxt_bootloader_read(data, &val, 1); +	if (ret) +		return ret; + +	if (state == MXT_WAITING_BOOTLOAD_CMD) +		val = mxt_get_bootloader_version(data, val); +  	switch (state) {  	case MXT_WAITING_BOOTLOAD_CMD:  	case MXT_WAITING_FRAME_DATA: +	case MXT_APP_CRC_FAIL:  		val &= ~MXT_BOOT_STATUS_MASK;  		break;  	case MXT_FRAME_CRC_PASS: -		if (val == MXT_FRAME_CRC_CHECK) +		if (val == MXT_FRAME_CRC_CHECK) {  			goto recheck; +		} else if (val == MXT_FRAME_CRC_FAIL) { +			dev_err(dev, "Bootloader CRC fail\n"); +			return -EINVAL; +		}  		break;  	default:  		return -EINVAL;  	}  	if (val != state) { -		dev_err(&client->dev, "Unvalid bootloader mode state\n"); +		dev_err(dev, "Invalid bootloader state %02X != %02X\n", +			val, state);  		return -EINVAL;  	}  	return 0;  } -static int mxt_unlock_bootloader(struct i2c_client *client) +static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock)  { +	int ret;  	u8 buf[2]; -	buf[0] = MXT_UNLOCK_CMD_LSB; -	buf[1] = MXT_UNLOCK_CMD_MSB; - -	if (i2c_master_send(client, buf, 2) != 2) { -		dev_err(&client->dev, "%s: i2c send failed\n", __func__); -		return -EIO; +	if (unlock) { +		buf[0] = MXT_UNLOCK_CMD_LSB; +		buf[1] = MXT_UNLOCK_CMD_MSB; +	} else { +		buf[0] = 0x01; +		buf[1] = 0x01;  	} -	return 0; -} - -static int mxt_fw_write(struct i2c_client *client, -			     const u8 *data, unsigned int frame_size) -{ -	if (i2c_master_send(client, data, frame_size) != frame_size) { -		dev_err(&client->dev, "%s: i2c send failed\n", __func__); -		return -EIO; -	} +	ret = mxt_bootloader_write(data, buf, 2); +	if (ret) +		return ret;  	return 0;  } @@ -399,6 +712,7 @@ static int __mxt_read_reg(struct i2c_client *client,  	struct i2c_msg xfer[2];  	u8 buf[2];  	int ret; +	bool retry = false;  	buf[0] = reg & 0xff;  	buf[1] = (reg >> 8) & 0xff; @@ -415,22 +729,22 @@ static int __mxt_read_reg(struct i2c_client *client,  	xfer[1].len = len;  	xfer[1].buf = val; -	ret = i2c_transfer(client->adapter, xfer, 2); -	if (ret == 2) { -		ret = 0; -	} else { -		if (ret >= 0) -			ret = -EIO; -		dev_err(&client->dev, "%s: i2c transfer failed (%d)\n", -			__func__, ret); +retry_read: +	ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer)); +	if (ret != ARRAY_SIZE(xfer)) { +		if (!retry) { +			dev_dbg(&client->dev, "%s: i2c retry\n", __func__); +			msleep(MXT_WAKEUP_TIME); +			retry = true; +			goto retry_read; +		} else { +			dev_err(&client->dev, "%s: i2c transfer failed (%d)\n", +				__func__, ret); +			return -EIO; +		}  	} -	return ret; -} - -static int mxt_read_reg(struct i2c_client *client, u16 reg, u8 *val) -{ -	return __mxt_read_reg(client, reg, 1, val); +	return 0;  }  static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len, @@ -439,6 +753,7 @@ static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len,  	u8 *buf;  	size_t count;  	int ret; +	bool retry = false;  	count = len + 2;  	buf = kmalloc(count, GFP_KERNEL); @@ -449,14 +764,21 @@ static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len,  	buf[1] = (reg >> 8) & 0xff;  	memcpy(&buf[2], val, len); +retry_write:  	ret = i2c_master_send(client, buf, count); -	if (ret == count) { -		ret = 0; -	} else { -		if (ret >= 0) +	if (ret != count) { +		if (!retry) { +			dev_dbg(&client->dev, "%s: i2c retry\n", __func__); +			msleep(MXT_WAKEUP_TIME); +			retry = true; +			goto retry_write; +		} else { +			dev_err(&client->dev, "%s: i2c send failed (%d)\n", +				__func__, ret);  			ret = -EIO; -		dev_err(&client->dev, "%s: i2c send failed (%d)\n", -			__func__, ret); +		} +	} else { +		ret = 0;  	}  	kfree(buf); @@ -474,298 +796,1050 @@ mxt_get_object(struct mxt_data *data, u8 type)  	struct mxt_object *object;  	int i; -	for (i = 0; i < data->info.object_num; i++) { +	for (i = 0; i < data->info->object_num; i++) {  		object = data->object_table + i;  		if (object->type == type)  			return object;  	} -	dev_err(&data->client->dev, "Invalid object type\n"); +	dev_warn(&data->client->dev, "Invalid object type T%u\n", type);  	return NULL;  } -static int mxt_read_message(struct mxt_data *data, -				 struct mxt_message *message) +static void mxt_proc_t6_messages(struct mxt_data *data, u8 *msg)  { -	struct mxt_object *object; -	u16 reg; +	struct device *dev = &data->client->dev; +	u8 status = msg[1]; +	u32 crc = msg[2] | (msg[3] << 8) | (msg[4] << 16); -	object = mxt_get_object(data, MXT_GEN_MESSAGE_T5); -	if (!object) -		return -EINVAL; +	if (crc != data->config_crc) { +		data->config_crc = crc; +		dev_dbg(dev, "T6 Config Checksum: 0x%06X\n", crc); +	} -	reg = object->start_address; -	return __mxt_read_reg(data->client, reg, -			sizeof(struct mxt_message), message); -} +	complete(&data->crc_completion); -static int mxt_write_object(struct mxt_data *data, -				 u8 type, u8 offset, u8 val) -{ -	struct mxt_object *object; -	u16 reg; +	/* Detect reset */ +	if (status & MXT_T6_STATUS_RESET) +		complete(&data->reset_completion); -	object = mxt_get_object(data, type); -	if (!object || offset >= object->size + 1) -		return -EINVAL; +	/* Output debug if status has changed */ +	if (status != data->t6_status) +		dev_dbg(dev, "T6 Status 0x%02X%s%s%s%s%s%s%s\n", +			status, +			status == 0 ? " OK" : "", +			status & MXT_T6_STATUS_RESET ? " RESET" : "", +			status & MXT_T6_STATUS_OFL ? " OFL" : "", +			status & MXT_T6_STATUS_SIGERR ? " SIGERR" : "", +			status & MXT_T6_STATUS_CAL ? " CAL" : "", +			status & MXT_T6_STATUS_CFGERR ? " CFGERR" : "", +			status & MXT_T6_STATUS_COMSERR ? " COMSERR" : ""); -	reg = object->start_address; -	return mxt_write_reg(data->client, reg + offset, val); +	/* Save current status */ +	data->t6_status = status;  } -static void mxt_input_button(struct mxt_data *data, struct mxt_message *message) +static void mxt_input_button(struct mxt_data *data, u8 *message)  {  	struct input_dev *input = data->input_dev; +	const struct mxt_platform_data *pdata = data->pdata;  	bool button;  	int i;  	/* Active-low switch */ -	for (i = 0; i < MXT_NUM_GPIO; i++) { -		if (data->pdata->key_map[i] == KEY_RESERVED) +	for (i = 0; i < pdata->t19_num_keys; i++) { +		if (pdata->t19_keymap[i] == KEY_RESERVED)  			continue; -		button = !(message->message[0] & MXT_GPIO0_MASK << i); -		input_report_key(input, data->pdata->key_map[i], button); +		button = !(message[1] & (1 << i)); +		input_report_key(input, pdata->t19_keymap[i], button); +	} +} + +static void mxt_input_sync(struct mxt_data *data) +{ +	if (data->input_dev) { +		input_mt_report_pointer_emulation(data->input_dev, +				data->pdata->t19_num_keys); +		input_sync(data->input_dev);  	}  } -static void mxt_input_touchevent(struct mxt_data *data, -				      struct mxt_message *message, int id) +static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)  {  	struct device *dev = &data->client->dev; -	u8 status = message->message[0];  	struct input_dev *input_dev = data->input_dev; +	int id; +	u8 status;  	int x;  	int y;  	int area; -	int pressure; +	int amplitude; +	u8 vector; +	int tool; + +	id = message[0] - data->T9_reportid_min; +	status = message[1]; +	x = (message[2] << 4) | ((message[4] >> 4) & 0xf); +	y = (message[3] << 4) | ((message[4] & 0xf)); -	x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf); -	y = (message->message[2] << 4) | ((message->message[3] & 0xf)); +	/* Handle 10/12 bit switching */  	if (data->max_x < 1024) -		x = x >> 2; +		x >>= 2;  	if (data->max_y < 1024) -		y = y >> 2; +		y >>= 2; -	area = message->message[4]; -	pressure = message->message[5]; +	area = message[5]; + +	amplitude = message[6]; +	vector = message[7];  	dev_dbg(dev, -		"[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u\n", +		"[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u vector: %02X\n",  		id, -		(status & MXT_DETECT) ? 'D' : '.', -		(status & MXT_PRESS) ? 'P' : '.', -		(status & MXT_RELEASE) ? 'R' : '.', -		(status & MXT_MOVE) ? 'M' : '.', -		(status & MXT_VECTOR) ? 'V' : '.', -		(status & MXT_AMP) ? 'A' : '.', -		(status & MXT_SUPPRESS) ? 'S' : '.', -		(status & MXT_UNGRIP) ? 'U' : '.', -		x, y, area, pressure); +		(status & MXT_T9_DETECT) ? 'D' : '.', +		(status & MXT_T9_PRESS) ? 'P' : '.', +		(status & MXT_T9_RELEASE) ? 'R' : '.', +		(status & MXT_T9_MOVE) ? 'M' : '.', +		(status & MXT_T9_VECTOR) ? 'V' : '.', +		(status & MXT_T9_AMP) ? 'A' : '.', +		(status & MXT_T9_SUPPRESS) ? 'S' : '.', +		(status & MXT_T9_UNGRIP) ? 'U' : '.', +		x, y, area, amplitude, vector);  	input_mt_slot(input_dev, id); -	input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, -				   status & MXT_DETECT); -	if (status & MXT_DETECT) { +	if (status & MXT_T9_DETECT) { +		/* +		 * Multiple bits may be set if the host is slow to read +		 * the status messages, indicating all the events that +		 * have happened. +		 */ +		if (status & MXT_T9_RELEASE) { +			input_mt_report_slot_state(input_dev, +						   MT_TOOL_FINGER, 0); +			mxt_input_sync(data); +		} + +		/* A size of zero indicates touch is from a linked T47 Stylus */ +		if (area == 0) { +			area = MXT_TOUCH_MAJOR_T47_STYLUS; +			tool = MT_TOOL_PEN; +		} else { +			tool = MT_TOOL_FINGER; +		} + +		/* Touch active */ +		input_mt_report_slot_state(input_dev, tool, 1);  		input_report_abs(input_dev, ABS_MT_POSITION_X, x);  		input_report_abs(input_dev, ABS_MT_POSITION_Y, y); -		input_report_abs(input_dev, ABS_MT_PRESSURE, pressure); +		input_report_abs(input_dev, ABS_MT_PRESSURE, amplitude);  		input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area); +		input_report_abs(input_dev, ABS_MT_ORIENTATION, vector); +	} else { +		/* Touch no longer active, close out slot */ +		input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0);  	} + +	data->update_input = true;  } -static unsigned mxt_extract_T6_csum(const u8 *csum) +static void mxt_proc_t100_message(struct mxt_data *data, u8 *message)  { -	return csum[0] | (csum[1] << 8) | (csum[2] << 16); +	struct device *dev = &data->client->dev; +	struct input_dev *input_dev = data->input_dev; +	int id; +	u8 status; +	int x; +	int y; +	int tool; + +	id = message[0] - data->T100_reportid_min - 2; + +	/* ignore SCRSTATUS events */ +	if (id < 0) +		return; + +	status = message[1]; +	x = (message[3] << 8) | message[2]; +	y = (message[5] << 8) | message[4]; + +	dev_dbg(dev, +		"[%u] status:%02X x:%u y:%u area:%02X amp:%02X vec:%02X\n", +		id, +		status, +		x, y, +		data->t100_aux_area ? message[data->t100_aux_area] : 0, +		data->t100_aux_ampl ? message[data->t100_aux_ampl] : 0, +		data->t100_aux_vect ? message[data->t100_aux_vect] : 0); + +	input_mt_slot(input_dev, id); + +	if (status & MXT_T100_DETECT) { +		if ((status & MXT_T100_TYPE_MASK) == MXT_T100_TYPE_STYLUS) +			tool = MT_TOOL_PEN; +		else +			tool = MT_TOOL_FINGER; + +		/* Touch active */ +		input_mt_report_slot_state(input_dev, tool, 1); +		input_report_abs(input_dev, ABS_MT_POSITION_X, x); +		input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + +		if (data->t100_aux_ampl) +			input_report_abs(input_dev, ABS_MT_PRESSURE, +					 message[data->t100_aux_ampl]); + +		if (data->t100_aux_area) { +			if (tool == MT_TOOL_PEN) +				input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, +						 MXT_TOUCH_MAJOR_T47_STYLUS); +			else +				input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, +						 message[data->t100_aux_area]); +		} + +		if (data->t100_aux_vect) +			input_report_abs(input_dev, ABS_MT_ORIENTATION, +					 message[data->t100_aux_vect]); +	} else { +		/* Touch no longer active, close out slot */ +		input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); +	} + +	data->update_input = true;  } -static bool mxt_is_T9_message(struct mxt_data *data, struct mxt_message *msg) + +static void mxt_proc_t15_messages(struct mxt_data *data, u8 *msg)  { -	u8 id = msg->reportid; -	return (id >= data->T9_reportid_min && id <= data->T9_reportid_max); +	struct input_dev *input_dev = data->input_dev; +	struct device *dev = &data->client->dev; +	int key; +	bool curr_state, new_state; +	bool sync = false; +	unsigned long keystates = le32_to_cpu(msg[2]); + +	for (key = 0; key < data->pdata->t15_num_keys; key++) { +		curr_state = test_bit(key, &data->t15_keystatus); +		new_state = test_bit(key, &keystates); + +		if (!curr_state && new_state) { +			dev_dbg(dev, "T15 key press: %u\n", key); +			__set_bit(key, &data->t15_keystatus); +			input_event(input_dev, EV_KEY, +				    data->pdata->t15_keymap[key], 1); +			sync = true; +		} else if (curr_state && !new_state) { +			dev_dbg(dev, "T15 key release: %u\n", key); +			__clear_bit(key, &data->t15_keystatus); +			input_event(input_dev, EV_KEY, +				    data->pdata->t15_keymap[key], 0); +			sync = true; +		} +	} + +	if (sync) +		input_sync(input_dev);  } -static irqreturn_t mxt_interrupt(int irq, void *dev_id) +static void mxt_proc_t42_messages(struct mxt_data *data, u8 *msg)  { -	struct mxt_data *data = dev_id; -	struct mxt_message message; -	const u8 *payload = &message.message[0];  	struct device *dev = &data->client->dev; -	u8 reportid; -	bool update_input = false; +	u8 status = msg[1]; -	do { -		if (mxt_read_message(data, &message)) { -			dev_err(dev, "Failed to read message\n"); -			goto end; -		} +	if (status & MXT_T42_MSG_TCHSUP) +		dev_info(dev, "T42 suppress\n"); +	else +		dev_info(dev, "T42 normal\n"); +} -		reportid = message.reportid; +static int mxt_proc_t48_messages(struct mxt_data *data, u8 *msg) +{ +	struct device *dev = &data->client->dev; +	u8 status, state; -		if (reportid == data->T6_reportid) { -			u8 status = payload[0]; -			unsigned csum = mxt_extract_T6_csum(&payload[1]); -			dev_dbg(dev, "Status: %02x Config Checksum: %06x\n", -				status, csum); -		} else if (mxt_is_T9_message(data, &message)) { -			int id = reportid - data->T9_reportid_min; -			mxt_input_touchevent(data, &message, id); -			update_input = true; -		} else if (message.reportid == data->T19_reportid) { -			mxt_input_button(data, &message); -			update_input = true; -		} else { -			mxt_dump_message(dev, &message); -		} -	} while (reportid != 0xff); +	status = msg[1]; +	state  = msg[4]; -	if (update_input) { -		input_mt_report_pointer_emulation(data->input_dev, false); -		input_sync(data->input_dev); +	dev_dbg(dev, "T48 state %d status %02X %s%s%s%s%s\n", state, status, +		status & 0x01 ? "FREQCHG " : "", +		status & 0x02 ? "APXCHG " : "", +		status & 0x04 ? "ALGOERR " : "", +		status & 0x10 ? "STATCHG " : "", +		status & 0x20 ? "NLVLCHG " : ""); + +	return 0; +} + +static void mxt_proc_t63_messages(struct mxt_data *data, u8 *msg) +{ +	struct device *dev = &data->client->dev; +	struct input_dev *input_dev = data->input_dev; +	u8 id; +	u16 x, y; +	u8 pressure; + +	/* stylus slots come after touch slots */ +	id = data->num_touchids + (msg[0] - data->T63_reportid_min); + +	if (id < 0 || id > (data->num_touchids + data->num_stylusids)) { +		dev_err(dev, "invalid stylus id %d, max slot is %d\n", +			id, data->num_stylusids); +		return; +	} + +	x = msg[3] | (msg[4] << 8); +	y = msg[5] | (msg[6] << 8); +	pressure = msg[7] & MXT_T63_STYLUS_PRESSURE_MASK; + +	dev_dbg(dev, +		"[%d] %c%c%c%c x: %d y: %d pressure: %d stylus:%c%c%c%c\n", +		id, +		msg[1] & MXT_T63_STYLUS_SUPPRESS ? 'S' : '.', +		msg[1] & MXT_T63_STYLUS_MOVE     ? 'M' : '.', +		msg[1] & MXT_T63_STYLUS_RELEASE  ? 'R' : '.', +		msg[1] & MXT_T63_STYLUS_PRESS    ? 'P' : '.', +		x, y, pressure, +		msg[2] & MXT_T63_STYLUS_BARREL   ? 'B' : '.', +		msg[2] & MXT_T63_STYLUS_ERASER   ? 'E' : '.', +		msg[2] & MXT_T63_STYLUS_TIP      ? 'T' : '.', +		msg[2] & MXT_T63_STYLUS_DETECT   ? 'D' : '.'); + +	input_mt_slot(input_dev, id); + +	if (msg[2] & MXT_T63_STYLUS_DETECT) { +		input_mt_report_slot_state(input_dev, MT_TOOL_PEN, 1); +		input_report_abs(input_dev, ABS_MT_POSITION_X, x); +		input_report_abs(input_dev, ABS_MT_POSITION_Y, y); +		input_report_abs(input_dev, ABS_MT_PRESSURE, pressure); +	} else { +		input_mt_report_slot_state(input_dev, MT_TOOL_PEN, 0); +	} + +	input_report_key(input_dev, BTN_STYLUS, +			 (msg[2] & MXT_T63_STYLUS_ERASER)); +	input_report_key(input_dev, BTN_STYLUS2, +			 (msg[2] & MXT_T63_STYLUS_BARREL)); + +	mxt_input_sync(data); +} + +static int mxt_proc_message(struct mxt_data *data, u8 *message) +{ +	u8 report_id = message[0]; +	bool dump = data->debug_enabled; + +	if (report_id == MXT_RPTID_NOMSG) +		return 0; + +	if (report_id == data->T6_reportid) { +		mxt_proc_t6_messages(data, message); +	} else if (report_id >= data->T42_reportid_min +		   && report_id <= data->T42_reportid_max) { +		mxt_proc_t42_messages(data, message); +	} else if (report_id == data->T48_reportid) { +		mxt_proc_t48_messages(data, message); +	} else if (!data->input_dev || data->suspended) { +		/* +		 * Do not report events if input device is not +		 * yet registered or returning from suspend +		 */ +		mxt_dump_message(data, message); +	} else if (report_id >= data->T9_reportid_min +	    && report_id <= data->T9_reportid_max) { +		mxt_proc_t9_message(data, message); +	} else if (report_id >= data->T100_reportid_min +	    && report_id <= data->T100_reportid_max) { +		mxt_proc_t100_message(data, message); +	} else if (report_id == data->T19_reportid) { +		mxt_input_button(data, message); +		data->update_input = true; +	} else if (report_id >= data->T63_reportid_min +		   && report_id <= data->T63_reportid_max) { +		mxt_proc_t63_messages(data, message); +	} else if (report_id >= data->T15_reportid_min +		   && report_id <= data->T15_reportid_max) { +		mxt_proc_t15_messages(data, message); +	} else { +		dump = true; +	} + +	if (dump) +		mxt_dump_message(data, message); + +	if (data->debug_v2_enabled) +		mxt_debug_msg_add(data, message); + +	return 1; +} + +static int mxt_read_and_process_messages(struct mxt_data *data, u8 count) +{ +	struct device *dev = &data->client->dev; +	int ret; +	int i; +	u8 num_valid = 0; + +	/* Safety check for msg_buf */ +	if (count > data->max_reportid) +		return -EINVAL; + +	/* Process remaining messages if necessary */ +	ret = __mxt_read_reg(data->client, data->T5_address, +				data->T5_msg_size * count, data->msg_buf); +	if (ret) { +		dev_err(dev, "Failed to read %u messages (%d)\n", count, ret); +		return ret; +	} + +	for (i = 0;  i < count; i++) { +		ret = mxt_proc_message(data, +			data->msg_buf + data->T5_msg_size * i); + +		if (ret == 1) +			num_valid++; +	} + +	/* return number of messages read */ +	return num_valid; +} + +static irqreturn_t mxt_process_messages_t44(struct mxt_data *data) +{ +	struct device *dev = &data->client->dev; +	int ret; +	u8 count, num_left; + +	/* Read T44 and T5 together */ +	ret = __mxt_read_reg(data->client, data->T44_address, +		data->T5_msg_size + 1, data->msg_buf); +	if (ret) { +		dev_err(dev, "Failed to read T44 and T5 (%d)\n", ret); +		return IRQ_NONE; +	} + +	count = data->msg_buf[0]; + +	if (count == 0) { +		/* +		 * This condition is caused by the CHG line being configured +		 * in Mode 0. It results in unnecessary I2C operations but it +		 * is benign. +		 */ +		dev_dbg(dev, "Interrupt triggered but zero messages\n"); +		return IRQ_NONE; +	} else if (count > data->max_reportid) { +		dev_err(dev, "T44 count %d exceeded max report id\n", count); +		count = data->max_reportid; +	} + +	/* Process first message */ +	ret = mxt_proc_message(data, data->msg_buf + 1); +	if (ret < 0) { +		dev_warn(dev, "Unexpected invalid message\n"); +		return IRQ_NONE; +	} + +	num_left = count - 1; + +	/* Process remaining messages if necessary */ +	if (num_left) { +		ret = mxt_read_and_process_messages(data, num_left); +		if (ret < 0) +			goto end; +		else if (ret != num_left) +			dev_warn(dev, "Unexpected invalid message\n");  	}  end: +	if (data->update_input) { +		mxt_input_sync(data); +		data->update_input = false; +	} +  	return IRQ_HANDLED;  } -static int mxt_check_reg_init(struct mxt_data *data) +static int mxt_process_messages_until_invalid(struct mxt_data *data)  { -	const struct mxt_platform_data *pdata = data->pdata; -	struct mxt_object *object;  	struct device *dev = &data->client->dev; -	int index = 0; -	int i, size; +	int count, read; +	u8 tries = 2; + +	count = data->max_reportid; + +	/* Read messages until we force an invalid */ +	do { +		read = mxt_read_and_process_messages(data, count); +		if (read < count) +			return 0; +	} while (--tries); + +	if (data->update_input) { +		mxt_input_sync(data); +		data->update_input = false; +	} + +	dev_err(dev, "CHG pin isn't cleared\n"); +	return -EBUSY; +} + +static irqreturn_t mxt_process_messages(struct mxt_data *data) +{ +	int total_handled, num_handled; +	u8 count = data->last_message_count; + +	if (count < 1 || count > data->max_reportid) +		count = 1; + +	/* include final invalid message */ +	total_handled = mxt_read_and_process_messages(data, count + 1); +	if (total_handled < 0) +		return IRQ_NONE; +	/* if there were invalid messages, then we are done */ +	else if (total_handled <= count) +		goto update_count; + +	/* keep reading two msgs until one is invalid or reportid limit */ +	do { +		num_handled = mxt_read_and_process_messages(data, 2); +		if (num_handled < 0) +			return IRQ_NONE; + +		total_handled += num_handled; + +		if (num_handled < 2) +			break; +	} while (total_handled < data->num_touchids); + +update_count: +	data->last_message_count = total_handled; + +	if (data->update_input) { +		mxt_input_sync(data); +		data->update_input = false; +	} + +	return IRQ_HANDLED; +} + +static irqreturn_t mxt_interrupt(int irq, void *dev_id) +{ +	struct mxt_data *data = dev_id; + +	if (data->in_bootloader) { +		/* bootloader state transition completion */ +		complete(&data->bl_completion); +		return IRQ_HANDLED; +	} + +	if (!data->object_table) +		return IRQ_HANDLED; + +	if (data->T44_address) { +		return mxt_process_messages_t44(data); +	} else { +		return mxt_process_messages(data); +	} +} + +static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset, +			  u8 value, bool wait) +{ +	u16 reg; +	u8 command_register; +	int timeout_counter = 0;  	int ret; -	if (!pdata->config) { -		dev_dbg(dev, "No cfg data defined, skipping reg init\n"); +	reg = data->T6_address + cmd_offset; + +	ret = mxt_write_reg(data->client, reg, value); +	if (ret) +		return ret; + +	if (!wait)  		return 0; + +	do { +		msleep(20); +		ret = __mxt_read_reg(data->client, reg, 1, &command_register); +		if (ret) +			return ret; +	} while (command_register != 0 && timeout_counter++ <= 100); + +	if (timeout_counter > 100) { +		dev_err(&data->client->dev, "Command failed!\n"); +		return -EIO;  	} -	for (i = 0; i < data->info.object_num; i++) { -		object = data->object_table + i; +	return 0; +} + +static int mxt_soft_reset(struct mxt_data *data) +{ +	struct device *dev = &data->client->dev; +	int ret = 0; + +	dev_info(dev, "Resetting chip\n"); + +	INIT_COMPLETION(data->reset_completion); + +	ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_RESET_VALUE, false); +	if (ret) +		return ret; + +	ret = mxt_wait_for_completion(data, &data->reset_completion, +				      MXT_RESET_TIMEOUT); +	if (ret) +		return ret; + +	return 0; +} + +static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value) +{ +	/* +	 * On failure, CRC is set to 0 and config will always be +	 * downloaded. +	 */ +	data->config_crc = 0; +	INIT_COMPLETION(data->crc_completion); + +	mxt_t6_command(data, cmd, value, true); + +	/* +	 * Wait for crc message. On failure, CRC is set to 0 and config will +	 * always be downloaded. +	 */ +	mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT); +} + +static void mxt_calc_crc24(u32 *crc, u8 firstbyte, u8 secondbyte) +{ +	static const unsigned int crcpoly = 0x80001B; +	u32 result; +	u32 data_word; + +	data_word = (secondbyte << 8) | firstbyte; +	result = ((*crc << 1) ^ data_word); + +	if (result & 0x1000000) +		result ^= crcpoly; + +	*crc = result; +} + +static u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off) +{ +	u32 crc = 0; +	u8 *ptr = base + start_off; +	u8 *last_val = base + end_off - 1; + +	if (end_off < start_off) +		return -EINVAL; + +	while (ptr < last_val) { +		mxt_calc_crc24(&crc, *ptr, *(ptr + 1)); +		ptr += 2; +	} + +	/* if len is odd, fill the last byte with 0 */ +	if (ptr == last_val) +		mxt_calc_crc24(&crc, *ptr, 0); + +	/* Mask to 24-bit */ +	crc &= 0x00FFFFFF; + +	return crc; +} + +static int mxt_check_retrigen(struct mxt_data *data) +{ +	struct i2c_client *client = data->client; +	struct irq_data *irqd; +	int error; +	int val; + +	irqd = irq_get_irq_data(data->irq); +	if (irqd_get_trigger_type(irqd) & IRQF_TRIGGER_LOW) +		return 0; + +	if (data->T18_address) { +		error = __mxt_read_reg(client, +				       data->T18_address + MXT_COMMS_CTRL, +				       1, &val); +		if (error) +			return error; + +		if (val & MXT_COMMS_RETRIGEN) +			return 0; +	} + +	dev_warn(&client->dev, "Enabling RETRIGEN workaround\n"); +	data->use_retrigen_workaround = true; +	return 0; +} -		if (!mxt_object_writable(object->type)) +static int mxt_prepare_cfg_mem(struct mxt_data *data, +			       const struct firmware *cfg, +			       unsigned int data_pos, +			       unsigned int cfg_start_ofs, +			       u8 *config_mem, +			       size_t config_mem_size) +{ +	struct device *dev = &data->client->dev; +	struct mxt_object *object; +	unsigned int type, instance, size, byte_offset; +	int offset; +	int ret; +	int i; +	u16 reg; +	u8 val; + +	while (data_pos < cfg->size) { +		/* Read type, instance, length */ +		ret = sscanf(cfg->data + data_pos, "%x %x %x%n", +			     &type, &instance, &size, &offset); +		if (ret == 0) { +			/* EOF */ +			break; +		} else if (ret != 3) { +			dev_err(dev, "Bad format: failed to parse object\n"); +			return -EINVAL; +		} +		data_pos += offset; + +		object = mxt_get_object(data, type); +		if (!object) { +			/* Skip object */ +			for (i = 0; i < size; i++) { +				ret = sscanf(cfg->data + data_pos, "%hhx%n", +					     &val, &offset); +				if (ret != 1) { +					dev_err(dev, "Bad format in T%d at %d\n", +						type, i); +					return -EINVAL; +				} +				data_pos += offset; +			}  			continue; +		} + +		if (size > mxt_obj_size(object)) { +			/* +			 * Either we are in fallback mode due to wrong +			 * config or config from a later fw version, +			 * or the file is corrupt or hand-edited. +			 */ +			dev_warn(dev, "Discarding %zu byte(s) in T%u\n", +				 size - mxt_obj_size(object), type); +		} else if (mxt_obj_size(object) > size) { +			/* +			 * If firmware is upgraded, new bytes may be added to +			 * end of objects. It is generally forward compatible +			 * to zero these bytes - previous behaviour will be +			 * retained. However this does invalidate the CRC and +			 * will force fallback mode until the configuration is +			 * updated. We warn here but do nothing else - the +			 * malloc has zeroed the entire configuration. +			 */ +			dev_warn(dev, "Zeroing %zu byte(s) in T%d\n", +				 mxt_obj_size(object) - size, type); +		} -		size = (object->size + 1) * (object->instances + 1); -		if (index + size > pdata->config_length) { -			dev_err(dev, "Not enough config data!\n"); +		if (instance >= mxt_obj_instances(object)) { +			dev_err(dev, "Object instances exceeded!\n");  			return -EINVAL;  		} -		ret = __mxt_write_reg(data->client, object->start_address, -				size, &pdata->config[index]); -		if (ret) -			return ret; -		index += size; +		reg = object->start_address + mxt_obj_size(object) * instance; + +		for (i = 0; i < size; i++) { +			ret = sscanf(cfg->data + data_pos, "%hhx%n", +				     &val, +				     &offset); +			if (ret != 1) { +				dev_err(dev, "Bad format in T%d at %d\n", +					type, i); +				return -EINVAL; +			} +			data_pos += offset; + +			if (i > mxt_obj_size(object)) +				continue; + +			byte_offset = reg + i - cfg_start_ofs; + +			if (byte_offset >= 0 && byte_offset < config_mem_size) { +				*(config_mem + byte_offset) = val; +			} else { +				dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n", +					reg, object->type, byte_offset); +				return -EINVAL; +			} +		}  	}  	return 0;  } -static int mxt_make_highchg(struct mxt_data *data) +static int mxt_upload_cfg_mem(struct mxt_data *data, unsigned int cfg_start, +			      u8 *config_mem, size_t config_mem_size)  { -	struct device *dev = &data->client->dev; -	struct mxt_message message; -	int count = 10; +	unsigned int byte_offset = 0;  	int error; -	/* Read dummy message to make high CHG pin */ -	do { -		error = mxt_read_message(data, &message); -		if (error) +	/* Write configuration as blocks */ +	while (byte_offset < config_mem_size) { +		unsigned int size = config_mem_size - byte_offset; + +		if (size > MXT_MAX_BLOCK_WRITE) +			size = MXT_MAX_BLOCK_WRITE; + +		error = __mxt_write_reg(data->client, +					cfg_start + byte_offset, +					size, config_mem + byte_offset); +		if (error) { +			dev_err(&data->client->dev, +				"Config write error, ret=%d\n", error);  			return error; -	} while (message.reportid != 0xff && --count); +		} -	if (!count) { -		dev_err(dev, "CHG pin isn't cleared\n"); -		return -EBUSY; +		byte_offset += size;  	}  	return 0;  } -static void mxt_handle_pdata(struct mxt_data *data) +static int mxt_init_t7_power_cfg(struct mxt_data *data); + +/* + * mxt_update_cfg - download configuration to chip + * + * Atmel Raw Config File Format + * + * The first four lines of the raw config file contain: + *  1) Version + *  2) Chip ID Information (first 7 bytes of device memory) + *  3) Chip Information Block 24-bit CRC Checksum + *  4) Chip Configuration 24-bit CRC Checksum + * + * The rest of the file consists of one line per object instance: + *   <TYPE> <INSTANCE> <SIZE> <CONTENTS> + * + *   <TYPE> - 2-byte object type as hex + *   <INSTANCE> - 2-byte object instance number as hex + *   <SIZE> - 2-byte object size as hex + *   <CONTENTS> - array of <SIZE> 1-byte hex values + */ +static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)  { -	const struct mxt_platform_data *pdata = data->pdata; -	u8 voltage; +	struct device *dev = &data->client->dev; +	struct mxt_info cfg_info; +	int ret; +	int offset; +	int data_pos; +	int i; +	int cfg_start_ofs; +	u32 info_crc, config_crc, calculated_crc; +	u8 *config_mem; +	size_t config_mem_size; + +	mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); + +	if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) { +		dev_err(dev, "Unrecognised config file\n"); +		return -EINVAL; +	} -	/* Set touchscreen lines */ -	mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_XSIZE, -			pdata->x_line); -	mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_YSIZE, -			pdata->y_line); +	data_pos = strlen(MXT_CFG_MAGIC); -	/* Set touchscreen orient */ -	mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_ORIENT, -			pdata->orient); +	/* Load information block and check */ +	for (i = 0; i < sizeof(struct mxt_info); i++) { +		ret = sscanf(cfg->data + data_pos, "%hhx%n", +			     (unsigned char *)&cfg_info + i, +			     &offset); +		if (ret != 1) { +			dev_err(dev, "Bad format\n"); +			return -EINVAL; +		} -	/* Set touchscreen burst length */ -	mxt_write_object(data, MXT_TOUCH_MULTI_T9, -			MXT_TOUCH_BLEN, pdata->blen); +		data_pos += offset; +	} + +	if (cfg_info.family_id != data->info->family_id) { +		dev_err(dev, "Family ID mismatch!\n"); +		return -EINVAL; +	} + +	if (cfg_info.variant_id != data->info->variant_id) { +		dev_err(dev, "Variant ID mismatch!\n"); +		return -EINVAL; +	} + +	/* Read CRCs */ +	ret = sscanf(cfg->data + data_pos, "%x%n", &info_crc, &offset); +	if (ret != 1) { +		dev_err(dev, "Bad format: failed to parse Info CRC\n"); +		return -EINVAL; +	} +	data_pos += offset; + +	ret = sscanf(cfg->data + data_pos, "%x%n", &config_crc, &offset); +	if (ret != 1) { +		dev_err(dev, "Bad format: failed to parse Config CRC\n"); +		return -EINVAL; +	} +	data_pos += offset; -	/* Set touchscreen threshold */ -	mxt_write_object(data, MXT_TOUCH_MULTI_T9, -			MXT_TOUCH_TCHTHR, pdata->threshold); +	/* +	 * The Info Block CRC is calculated over mxt_info and the object +	 * table. If it does not match then we are trying to load the +	 * configuration from a different chip or firmware version, so +	 * the configuration CRC is invalid anyway. +	 */ +	if (info_crc == data->info_crc) { +		if (config_crc == 0 || data->config_crc == 0) { +			dev_info(dev, "CRC zero, attempting to apply config\n"); +		} else if (config_crc == data->config_crc) { +			dev_dbg(dev, "Config CRC 0x%06X: OK\n", +				 data->config_crc); +			return 0; +		} else { +			dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n", +				 data->config_crc, config_crc); +		} +	} else { +		dev_warn(dev, +			 "Warning: Info CRC error - device=0x%06X file=0x%06X\n", +			 data->info_crc, info_crc); +	} -	/* Set touchscreen resolution */ -	mxt_write_object(data, MXT_TOUCH_MULTI_T9, -			MXT_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff); -	mxt_write_object(data, MXT_TOUCH_MULTI_T9, -			MXT_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8); -	mxt_write_object(data, MXT_TOUCH_MULTI_T9, -			MXT_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff); -	mxt_write_object(data, MXT_TOUCH_MULTI_T9, -			MXT_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8); +	/* Malloc memory to store configuration */ +	cfg_start_ofs = MXT_OBJECT_START + +			data->info->object_num * sizeof(struct mxt_object) + +			MXT_INFO_CHECKSUM_SIZE; +	config_mem_size = data->mem_size - cfg_start_ofs; +	config_mem = kzalloc(config_mem_size, GFP_KERNEL); +	if (!config_mem) { +		dev_err(dev, "Failed to allocate memory\n"); +		return -ENOMEM; +	} -	/* Set touchscreen voltage */ -	if (pdata->voltage) { -		if (pdata->voltage < MXT_VOLTAGE_DEFAULT) { -			voltage = (MXT_VOLTAGE_DEFAULT - pdata->voltage) / -				MXT_VOLTAGE_STEP; -			voltage = 0xff - voltage + 1; -		} else -			voltage = (pdata->voltage - MXT_VOLTAGE_DEFAULT) / -				MXT_VOLTAGE_STEP; +	ret = mxt_prepare_cfg_mem(data, cfg, data_pos, cfg_start_ofs, +				  config_mem, config_mem_size); +	if (ret) +		goto release_mem; -		mxt_write_object(data, MXT_SPT_CTECONFIG_T28, -				MXT_CTE_VOLTAGE, voltage); +	/* Calculate crc of the received configs (not the raw config file) */ +	if (data->T7_address < cfg_start_ofs) { +		dev_err(dev, "Bad T7 address, T7addr = %x, config offset %x\n", +			data->T7_address, cfg_start_ofs); +		ret = 0; +		goto release_mem;  	} + +	calculated_crc = mxt_calculate_crc(config_mem, +					   data->T7_address - cfg_start_ofs, +					   config_mem_size); + +	if (config_crc > 0 && config_crc != calculated_crc) +		dev_warn(dev, "Config CRC error, calculated=%06X, file=%06X\n", +			 calculated_crc, config_crc); + +	ret = mxt_upload_cfg_mem(data, cfg_start_ofs, +				 config_mem, config_mem_size); +	if (ret) +		goto release_mem; + +	mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); + +	ret = mxt_check_retrigen(data); +	if (ret) +		goto release_mem; + +	ret = mxt_soft_reset(data); +	if (ret) +		goto release_mem; + +	dev_info(dev, "Config successfully updated\n"); + +	/* T7 config may have changed */ +	mxt_init_t7_power_cfg(data); + +release_mem: +	kfree(config_mem); +	return ret;  } -static int mxt_get_info(struct mxt_data *data) +static int mxt_acquire_irq(struct mxt_data *data)  { -	struct i2c_client *client = data->client; -	struct mxt_info *info = &data->info;  	int error; -	/* Read 7-byte info block starting at address 0 */ -	error = __mxt_read_reg(client, MXT_INFO, sizeof(*info), info); -	if (error) -		return error; +	enable_irq(data->irq); + +	if (data->use_retrigen_workaround) { +		error = mxt_process_messages_until_invalid(data); +		if (error) +			return error; +	}  	return 0;  } -static int mxt_get_object_table(struct mxt_data *data) +static void mxt_free_input_device(struct mxt_data *data) +{ +	if (data->input_dev) { +		input_unregister_device(data->input_dev); +		data->input_dev = NULL; +	} +} + +static void mxt_free_object_table(struct mxt_data *data) +{ +	mxt_debug_msg_remove(data); + +	data->object_table = NULL; +	data->info = NULL; +	kfree(data->raw_info_block); +	data->raw_info_block = NULL; +	kfree(data->msg_buf); +	data->msg_buf = NULL; + +	data->T5_address = 0; +	data->T5_msg_size = 0; +	data->T6_reportid = 0; +	data->T7_address = 0; +	data->T9_reportid_min = 0; +	data->T9_reportid_max = 0; +	data->T15_reportid_min = 0; +	data->T15_reportid_max = 0; +	data->T18_address = 0; +	data->T19_reportid = 0; +	data->T42_reportid_min = 0; +	data->T42_reportid_max = 0; +	data->T44_address = 0; +	data->T48_reportid = 0; +	data->T63_reportid_min = 0; +	data->T63_reportid_max = 0; +	data->T100_reportid_min = 0; +	data->T100_reportid_max = 0; +	data->max_reportid = 0; +} + +static int mxt_parse_object_table(struct mxt_data *data, +				  struct mxt_object *object_table)  {  	struct i2c_client *client = data->client; -	size_t table_size; -	int error;  	int i;  	u8 reportid; - -	table_size = data->info.object_num * sizeof(struct mxt_object); -	error = __mxt_read_reg(client, MXT_OBJECT_START, table_size, -			data->object_table); -	if (error) -		return error; +	u16 end_address;  	/* Valid Report IDs start counting from 1 */  	reportid = 1; -	for (i = 0; i < data->info.object_num; i++) { -		struct mxt_object *object = data->object_table + i; +	data->mem_size = 0; +	for (i = 0; i < data->info->object_num; i++) { +		struct mxt_object *object = object_table + i;  		u8 min_id, max_id;  		le16_to_cpus(&object->start_address); @@ -773,7 +1847,7 @@ static int mxt_get_object_table(struct mxt_data *data)  		if (object->num_report_ids) {  			min_id = reportid;  			reportid += object->num_report_ids * -					(object->instances + 1); +					mxt_obj_instances(object);  			max_id = reportid - 1;  		} else {  			min_id = 0; @@ -781,99 +1855,672 @@ static int mxt_get_object_table(struct mxt_data *data)  		}  		dev_dbg(&data->client->dev, -			"Type %2d Start %3d Size %3d Instances %2d ReportIDs %3u : %3u\n", -			object->type, object->start_address, object->size + 1, -			object->instances + 1, min_id, max_id); +			"T%u Start:%u Size:%zu Instances:%zu Report IDs:%u-%u\n", +			object->type, object->start_address, +			mxt_obj_size(object), mxt_obj_instances(object), +			min_id, max_id);  		switch (object->type) { +		case MXT_GEN_MESSAGE_T5: +			if (data->info->family_id == 0x80 && +			    data->info->version < 0x20) { +				/* +				 * On mXT224 firmware versions prior to V2.0 +				 * read and discard unused CRC byte otherwise +				 * DMA reads are misaligned. +				 */ +				data->T5_msg_size = mxt_obj_size(object); +			} else { +				/* CRC not enabled, so skip last byte */ +				data->T5_msg_size = mxt_obj_size(object) - 1; +			} +			data->T5_address = object->start_address; +			break;  		case MXT_GEN_COMMAND_T6:  			data->T6_reportid = min_id; +			data->T6_address = object->start_address; +			break; +		case MXT_GEN_POWER_T7: +			data->T7_address = object->start_address;  			break;  		case MXT_TOUCH_MULTI_T9: +			/* Only handle messages from first T9 instance */  			data->T9_reportid_min = min_id; -			data->T9_reportid_max = max_id; +			data->T9_reportid_max = min_id + +						object->num_report_ids - 1; +			data->num_touchids = object->num_report_ids; +			break; +		case MXT_TOUCH_KEYARRAY_T15: +			data->T15_reportid_min = min_id; +			data->T15_reportid_max = max_id; +			break; +		case MXT_SPT_COMMSCONFIG_T18: +			data->T18_address = object->start_address; +			break; +		case MXT_PROCI_TOUCHSUPPRESSION_T42: +			data->T42_reportid_min = min_id; +			data->T42_reportid_max = max_id; +			break; +		case MXT_SPT_MESSAGECOUNT_T44: +			data->T44_address = object->start_address;  			break;  		case MXT_SPT_GPIOPWM_T19:  			data->T19_reportid = min_id;  			break; +		case MXT_PROCG_NOISESUPPRESSION_T48: +			data->T48_reportid = min_id; +			break; +		case MXT_PROCI_ACTIVE_STYLUS_T63: +			/* Only handle messages from first T63 instance */ +			data->T63_reportid_min = min_id; +			data->T63_reportid_max = min_id; +			data->num_stylusids = 1; +			break; +		case MXT_TOUCH_MULTITOUCHSCREEN_T100: +			data->T100_reportid_min = min_id; +			data->T100_reportid_max = max_id; +			/* first two report IDs reserved */ +			data->num_touchids = object->num_report_ids - 2; +			break;  		} + +		end_address = object->start_address +			+ mxt_obj_size(object) * mxt_obj_instances(object) - 1; + +		if (end_address >= data->mem_size) +			data->mem_size = end_address + 1; +	} + +	/* Store maximum reportid */ +	data->max_reportid = reportid; + +	/* If T44 exists, T5 position has to be directly after */ +	if (data->T44_address && (data->T5_address != data->T44_address + 1)) { +		dev_err(&client->dev, "Invalid T44 position\n"); +		return -EINVAL; +	} + +	data->msg_buf = kcalloc(data->max_reportid, +				data->T5_msg_size, GFP_KERNEL); +	if (!data->msg_buf) { +		dev_err(&client->dev, "Failed to allocate message buffer\n"); +		return -ENOMEM;  	}  	return 0;  } -static void mxt_free_object_table(struct mxt_data *data) +static int mxt_read_info_block(struct mxt_data *data)  { -	kfree(data->object_table); -	data->object_table = NULL; -	data->T6_reportid = 0; -	data->T9_reportid_min = 0; -	data->T9_reportid_max = 0; -	data->T19_reportid = 0; +	struct i2c_client *client = data->client; +	int error; +	size_t size; +	void *id_buf, *buf; +	uint8_t num_objects; +	u32 calculated_crc; +	u8 *crc_ptr; + +	/* If info block already allocated, free it */ +	if (data->raw_info_block != NULL) +		mxt_free_object_table(data); + +	/* Read 7-byte ID information block starting at address 0 */ +	size = sizeof(struct mxt_info); +	id_buf = kzalloc(size, GFP_KERNEL); +	if (!id_buf) { +		dev_err(&client->dev, "Failed to allocate memory\n"); +		return -ENOMEM; +	} + +	error = __mxt_read_reg(client, 0, size, id_buf); +	if (error) { +		kfree(id_buf); +		return error; +	} + +	/* Resize buffer to give space for rest of info block */ +	num_objects = ((struct mxt_info *)id_buf)->object_num; +	size += (num_objects * sizeof(struct mxt_object)) +		+ MXT_INFO_CHECKSUM_SIZE; + +	buf = krealloc(id_buf, size, GFP_KERNEL); +	if (!buf) { +		dev_err(&client->dev, "Failed to allocate memory\n"); +		error = -ENOMEM; +		goto err_free_mem; +	} + +	/* Read rest of info block */ +	error = __mxt_read_reg(client, MXT_OBJECT_START, +			       size - MXT_OBJECT_START, +			       buf + MXT_OBJECT_START); +	if (error) +		goto err_free_mem; + +	/* Extract & calculate checksum */ +	crc_ptr = buf + size - MXT_INFO_CHECKSUM_SIZE; +	data->info_crc = crc_ptr[0] | (crc_ptr[1] << 8) | (crc_ptr[2] << 16); + +	calculated_crc = mxt_calculate_crc(buf, 0, +					   size - MXT_INFO_CHECKSUM_SIZE); + +	/* +	 * CRC mismatch can be caused by data corruption due to I2C comms +	 * issue or else device is not using Object Based Protocol (eg i2c-hid) +	 */ +	if ((data->info_crc == 0) || (data->info_crc != calculated_crc)) { +		dev_err(&client->dev, +			"Info Block CRC error calculated=0x%06X read=0x%06X\n", +			calculated_crc, data->info_crc); +		error = -EIO; +		goto err_free_mem; +	} + +	data->raw_info_block = buf; +	data->info = (struct mxt_info *)buf; + +	dev_info(&client->dev, +		 "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", +		 data->info->family_id, data->info->variant_id, +		 data->info->version >> 4, data->info->version & 0xf, +		 data->info->build, data->info->object_num); + +	/* Parse object table information */ +	error = mxt_parse_object_table(data, buf + MXT_OBJECT_START); +	if (error) { +		dev_err(&client->dev, "Error %d parsing object table\n", error); +		mxt_free_object_table(data); +		return error; +	} + +	data->object_table = (struct mxt_object *)(buf + MXT_OBJECT_START); + +	return 0; + +err_free_mem: +	kfree(buf); +	return error;  } -static int mxt_initialize(struct mxt_data *data) +static void mxt_regulator_enable(struct mxt_data *data) +{ +	int error; + +	gpio_set_value(data->pdata->gpio_reset, 0); + +	error = regulator_enable(data->reg_vdd); +	if (error) +		return; + +	error = regulator_enable(data->reg_avdd); +	if (error) +		return; + +	msleep(MXT_REGULATOR_DELAY); +	gpio_set_value(data->pdata->gpio_reset, 1); +	msleep(MXT_CHG_DELAY); + +retry_wait: +	INIT_COMPLETION(data->bl_completion); +	data->in_bootloader = true; +	error = mxt_wait_for_completion(data, &data->bl_completion, +					MXT_POWERON_DELAY); +	if (error == -EINTR) +		goto retry_wait; + +	data->in_bootloader = false; +} + +static void mxt_regulator_disable(struct mxt_data *data) +{ +	regulator_disable(data->reg_vdd); +	regulator_disable(data->reg_avdd); +} + +static void mxt_probe_regulators(struct mxt_data *data) +{ +	struct device *dev = &data->client->dev; +	int error; + +	/* +	 * According to maXTouch power sequencing specification, RESET line +	 * must be kept low until some time after regulators come up to +	 * voltage +	 */ +	if (!gpio_is_valid(data->pdata->gpio_reset)) { +		dev_dbg(dev, "Must have reset GPIO to use regulator support\n"); +		goto fail; +	} + +	data->reg_vdd = regulator_get(dev, "vdd"); +	if (IS_ERR(data->reg_vdd)) { +		error = PTR_ERR(data->reg_vdd); +		dev_err(dev, "Error %d getting vdd regulator\n", error); +		goto fail; +	} + +	data->reg_avdd = regulator_get(dev, "avdd"); +	if (IS_ERR(data->reg_avdd)) { +		error = PTR_ERR(data->reg_avdd); +		dev_err(dev, "Error %d getting avdd regulator\n", error); +		goto fail_release; +	} + +	data->use_regulator = true; +	mxt_regulator_enable(data); + +	dev_dbg(dev, "Initialised regulators\n"); +	return; + +fail_release: +	regulator_put(data->reg_vdd); +fail: +	data->reg_vdd = NULL; +	data->reg_avdd = NULL; +	data->use_regulator = false; +} + +static int mxt_read_t9_resolution(struct mxt_data *data)  {  	struct i2c_client *client = data->client; -	struct mxt_info *info = &data->info;  	int error; -	u8 val; +	struct t9_range range; +	unsigned char orient; +	struct mxt_object *object; -	error = mxt_get_info(data); +	object = mxt_get_object(data, MXT_TOUCH_MULTI_T9); +	if (!object) +		return -EINVAL; + +	error = __mxt_read_reg(client, +			       object->start_address + MXT_T9_RANGE, +			       sizeof(range), &range);  	if (error)  		return error; -	data->object_table = kcalloc(info->object_num, -				     sizeof(struct mxt_object), -				     GFP_KERNEL); -	if (!data->object_table) { -		dev_err(&client->dev, "Failed to allocate memory\n"); +	le16_to_cpus(&range.x); +	le16_to_cpus(&range.y); + +	error =  __mxt_read_reg(client, +				object->start_address + MXT_T9_ORIENT, +				1, &orient); +	if (error) +		return error; + +	/* Handle default values */ +	if (range.x == 0) +		range.x = 1023; + +	if (range.y == 0) +		range.y = 1023; + +	if (orient & MXT_T9_ORIENT_SWITCH) { +		data->max_x = range.y; +		data->max_y = range.x; +	} else { +		data->max_x = range.x; +		data->max_y = range.y; +	} + +	dev_dbg(&client->dev, +		"Touchscreen size X%uY%u\n", data->max_x, data->max_y); + +	return 0; +} + +static void mxt_start(struct mxt_data *data); +static void mxt_stop(struct mxt_data *data); +static int mxt_input_open(struct input_dev *dev); +static void mxt_input_close(struct input_dev *dev); + +static int mxt_initialize_t9_input_device(struct mxt_data *data) +{ +	struct device *dev = &data->client->dev; +	const struct mxt_platform_data *pdata = data->pdata; +	struct input_dev *input_dev; +	int error; +	unsigned int num_mt_slots; +	unsigned int mt_flags = 0; +	int i; + +	error = mxt_read_t9_resolution(data); +	if (error) +		dev_warn(dev, "Failed to initialize T9 resolution\n"); + +	input_dev = input_allocate_device(); +	if (!input_dev) { +		dev_err(dev, "Failed to allocate memory\n");  		return -ENOMEM;  	} -	/* Get object table information */ -	error = mxt_get_object_table(data); +	input_dev->name = "Atmel maXTouch Touchscreen"; +	input_dev->phys = data->phys; +	input_dev->id.bustype = BUS_I2C; +	input_dev->dev.parent = dev; +	input_dev->open = mxt_input_open; +	input_dev->close = mxt_input_close; + +	__set_bit(EV_ABS, input_dev->evbit); +	__set_bit(EV_KEY, input_dev->evbit); +	__set_bit(BTN_TOUCH, input_dev->keybit); + +	if (pdata->t19_num_keys) { +		__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); + +		for (i = 0; i < pdata->t19_num_keys; i++) +			if (pdata->t19_keymap[i] != KEY_RESERVED) +				input_set_capability(input_dev, EV_KEY, +						     pdata->t19_keymap[i]); + +		mt_flags |= INPUT_MT_POINTER; + +		input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); +		input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); +		input_abs_set_res(input_dev, ABS_MT_POSITION_X, +				  MXT_PIXELS_PER_MM); +		input_abs_set_res(input_dev, ABS_MT_POSITION_Y, +				  MXT_PIXELS_PER_MM); + +		input_dev->name = "Atmel maXTouch Touchpad"; +	} else { +		mt_flags |= INPUT_MT_DIRECT; +	} + +	/* For single touch */ +	input_set_abs_params(input_dev, ABS_X, +			     0, data->max_x, 0, 0); +	input_set_abs_params(input_dev, ABS_Y, +			     0, data->max_y, 0, 0); +	input_set_abs_params(input_dev, ABS_PRESSURE, +			     0, 255, 0, 0); + +	/* For multi touch */ +	num_mt_slots = data->num_touchids + data->num_stylusids; +	error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); +	if (error) { +		dev_err(dev, "Error %d initialising slots\n", error); +		goto err_free_mem; +	} + +	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, +			     0, MXT_MAX_AREA, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_POSITION_X, +			     0, data->max_x, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, +			     0, data->max_y, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_PRESSURE, +			     0, 255, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_ORIENTATION, +			     0, 255, 0, 0); + +	/* For T63 active stylus */ +	if (data->T63_reportid_min) { +		input_set_capability(input_dev, EV_KEY, BTN_STYLUS); +		input_set_capability(input_dev, EV_KEY, BTN_STYLUS2); +		input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, +			0, MT_TOOL_MAX, 0, 0); +	} + +	/* For T15 key array */ +	if (data->T15_reportid_min) { +		data->t15_keystatus = 0; + +		for (i = 0; i < data->pdata->t15_num_keys; i++) +			input_set_capability(input_dev, EV_KEY, +					     data->pdata->t15_keymap[i]); +	} + +	input_set_drvdata(input_dev, data); + +	error = input_register_device(input_dev); +	if (error) { +		dev_err(dev, "Error %d registering input device\n", error); +		goto err_free_mem; +	} + +	data->input_dev = input_dev; + +	return 0; + +err_free_mem: +	input_free_device(input_dev); +	return error; +} + +static int mxt_read_t100_config(struct mxt_data *data) +{ +	struct i2c_client *client = data->client; +	int error; +	struct mxt_object *object; +	u16 range_x, range_y; +	u8 cfg, tchaux; +	u8 aux; + +	object = mxt_get_object(data, MXT_TOUCH_MULTITOUCHSCREEN_T100); +	if (!object) +		return -EINVAL; + +	error = __mxt_read_reg(client, +			       object->start_address + MXT_T100_XRANGE, +			       sizeof(range_x), &range_x);  	if (error) -		goto err_free_object_table; +		return error; -	/* Check register init values */ -	error = mxt_check_reg_init(data); +	le16_to_cpus(&range_x); + +	error = __mxt_read_reg(client, +			       object->start_address + MXT_T100_YRANGE, +			       sizeof(range_y), &range_y);  	if (error) -		goto err_free_object_table; +		return error; + +	le16_to_cpus(&range_y); + +	error =  __mxt_read_reg(client, +				object->start_address + MXT_T100_CFG1, +				1, &cfg); +	if (error) +		return error; + +	error =  __mxt_read_reg(client, +				object->start_address + MXT_T100_TCHAUX, +				1, &tchaux); +	if (error) +		return error; + +	/* Handle default values */ +	if (range_x == 0) +		range_x = 1023; + +	/* Handle default values */ +	if (range_x == 0) +		range_x = 1023; + +	if (range_y == 0) +		range_y = 1023; + +	if (cfg & MXT_T100_CFG_SWITCHXY) { +		data->max_x = range_y; +		data->max_y = range_x; +	} else { +		data->max_x = range_x; +		data->max_y = range_y; +	} + +	/* allocate aux bytes */ +	aux = 6; + +	if (tchaux & MXT_T100_TCHAUX_VECT) +		data->t100_aux_vect = aux++; + +	if (tchaux & MXT_T100_TCHAUX_AMPL) +		data->t100_aux_ampl = aux++; + +	if (tchaux & MXT_T100_TCHAUX_AREA) +		data->t100_aux_area = aux++; + +	dev_info(&client->dev, +		 "T100 Touchscreen size X%uY%u\n", data->max_x, data->max_y); + +	return 0; +} + +static int mxt_initialize_t100_input_device(struct mxt_data *data) +{ +	struct device *dev = &data->client->dev; +	struct input_dev *input_dev; +	int error; + +	error = mxt_read_t100_config(data); +	if (error) +		dev_warn(dev, "Failed to initialize T9 resolution\n"); + +	input_dev = input_allocate_device(); +	if (!data || !input_dev) { +		dev_err(dev, "Failed to allocate memory\n"); +		return -ENOMEM; +	} + +	if (data->pdata->input_name) +		input_dev->name = data->pdata->input_name; +	else +		input_dev->name = "atmel_mxt_ts T100 touchscreen"; + +	input_dev->phys = data->phys; +	input_dev->id.bustype = BUS_I2C; +	input_dev->dev.parent = &data->client->dev; +	input_dev->open = mxt_input_open; +	input_dev->close = mxt_input_close; + +	set_bit(EV_ABS, input_dev->evbit); +	input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + +	/* For single touch */ +	input_set_abs_params(input_dev, ABS_X, +			     0, data->max_x, 0, 0); +	input_set_abs_params(input_dev, ABS_Y, +			     0, data->max_y, 0, 0); + +	if (data->t100_aux_ampl) +		input_set_abs_params(input_dev, ABS_PRESSURE, +				     0, 255, 0, 0); -	mxt_handle_pdata(data); +	/* For multi touch */ +	error = input_mt_init_slots(input_dev, data->num_touchids, +				    INPUT_MT_DIRECT); +	if (error) { +		dev_err(dev, "Error %d initialising slots\n", error); +		goto err_free_mem; +	} + +	input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_POSITION_X, +			     0, data->max_x, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, +			     0, data->max_y, 0, 0); + +	if (data->t100_aux_area) +		input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, +				     0, MXT_MAX_AREA, 0, 0); + +	if (data->t100_aux_ampl) +		input_set_abs_params(input_dev, ABS_MT_PRESSURE, +				     0, 255, 0, 0); + +	if (data->t100_aux_vect) +		input_set_abs_params(input_dev, ABS_MT_ORIENTATION, +				     0, 255, 0, 0); + +	input_set_drvdata(input_dev, data); + +	error = input_register_device(input_dev); +	if (error) { +		dev_err(dev, "Error %d registering input device\n", error); +		goto err_free_mem; +	} + +	data->input_dev = input_dev; + +	return 0; + +err_free_mem: +	input_free_device(input_dev); +	return error; +} + +static int mxt_configure_objects(struct mxt_data *data, +				 const struct firmware *cfg); + +static void mxt_config_cb(const struct firmware *cfg, void *ctx) +{ +	mxt_configure_objects(ctx, cfg); +	release_firmware(cfg); +} + +static int mxt_initialize(struct mxt_data *data) +{ +	struct i2c_client *client = data->client; +	int recovery_attempts = 0; +	int error; + +	while (1) { +		error = mxt_read_info_block(data); +		if (!error) +			break; -	/* Backup to memory */ -	mxt_write_object(data, MXT_GEN_COMMAND_T6, -			MXT_COMMAND_BACKUPNV, -			MXT_BACKUP_VALUE); -	msleep(MXT_BACKUP_TIME); +		/* Check bootloader state */ +		error = mxt_probe_bootloader(data, false); +		if (error) { +			dev_info(&client->dev, "Trying alternate bootloader address\n"); +			error = mxt_probe_bootloader(data, true); +			if (error) { +				/* Chip is not in appmode or bootloader mode */ +				return error; +			} +		} -	/* Soft reset */ -	mxt_write_object(data, MXT_GEN_COMMAND_T6, -			MXT_COMMAND_RESET, 1); -	msleep(MXT_RESET_TIME); +		/* OK, we are in bootloader, see if we can recover */ +		if (++recovery_attempts > 1) { +			dev_err(&client->dev, "Could not recover from bootloader mode\n"); +			/* +			 * We can reflash from this state, so do not +			 * abort initialization. +			 */ +			data->in_bootloader = true; +			return 0; +		} + +		/* Attempt to exit bootloader into app mode */ +		mxt_send_bootloader_cmd(data, false); +		msleep(MXT_FW_RESET_TIME); +	} -	/* Update matrix size at info struct */ -	error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val); +	error = mxt_check_retrigen(data);  	if (error)  		goto err_free_object_table; -	info->matrix_xsize = val; -	error = mxt_read_reg(client, MXT_MATRIX_Y_SIZE, &val); +	error = mxt_acquire_irq(data);  	if (error)  		goto err_free_object_table; -	info->matrix_ysize = val; -	dev_info(&client->dev, -			"Family ID: %u Variant ID: %u Major.Minor.Build: %u.%u.%02X\n", -			info->family_id, info->variant_id, info->version >> 4, -			info->version & 0xf, info->build); +	error = mxt_debug_msg_init(data); +	if (error) +		goto err_free_object_table; -	dev_info(&client->dev, -			"Matrix X Size: %u Matrix Y Size: %u Object Num: %u\n", -			info->matrix_xsize, info->matrix_ysize, -			info->object_num); +	if (data->cfg_name) { +		error = request_firmware_nowait(THIS_MODULE, true, +					data->cfg_name, &data->client->dev, +					GFP_KERNEL, data, mxt_config_cb); +		if (error) { +			dev_err(&client->dev, "Failed to invoke firmware loader: %d\n", +				error); +			goto err_free_object_table; +		} +	} else { +		error = mxt_configure_objects(data, NULL); +		if (error) +			goto err_free_object_table; +	}  	return 0; @@ -882,18 +2529,104 @@ err_free_object_table:  	return error;  } -static void mxt_calc_resolution(struct mxt_data *data) +static int mxt_set_t7_power_cfg(struct mxt_data *data, u8 sleep) +{ +	struct device *dev = &data->client->dev; +	int error; +	struct t7_config *new_config; +	struct t7_config deepsleep = { .active = 0, .idle = 0 }; + +	if (sleep == MXT_POWER_CFG_DEEPSLEEP) +		new_config = &deepsleep; +	else +		new_config = &data->t7_cfg; + +	error = __mxt_write_reg(data->client, data->T7_address, +				sizeof(data->t7_cfg), new_config); +	if (error) +		return error; + +	dev_dbg(dev, "Set T7 ACTV:%d IDLE:%d\n", +		new_config->active, new_config->idle); + +	return 0; +} + +static int mxt_init_t7_power_cfg(struct mxt_data *data) +{ +	struct device *dev = &data->client->dev; +	int error; +	bool retry = false; + +recheck: +	error = __mxt_read_reg(data->client, data->T7_address, +				sizeof(data->t7_cfg), &data->t7_cfg); +	if (error) +		return error; + +	if (data->t7_cfg.active == 0 || data->t7_cfg.idle == 0) { +		if (!retry) { +			dev_dbg(dev, "T7 cfg zero, resetting\n"); +			mxt_soft_reset(data); +			retry = true; +			goto recheck; +		} else { +			dev_dbg(dev, "T7 cfg zero after reset, overriding\n"); +			data->t7_cfg.active = 20; +			data->t7_cfg.idle = 100; +			return mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); +		} +	} + +	dev_dbg(dev, "Initialized power cfg: ACTV %d, IDLE %d\n", +		data->t7_cfg.active, data->t7_cfg.idle); +	return 0; +} + +static int mxt_configure_objects(struct mxt_data *data, +				 const struct firmware *cfg)  { -	unsigned int max_x = data->pdata->x_size - 1; -	unsigned int max_y = data->pdata->y_size - 1; +	struct device *dev = &data->client->dev; +	int error; + +	error = mxt_init_t7_power_cfg(data); +	if (error) { +		dev_err(dev, "Failed to initialize power cfg\n"); +		goto err_free_object_table; +	} -	if (data->pdata->orient & MXT_XY_SWITCH) { -		data->max_x = max_y; -		data->max_y = max_x; +	if (cfg) { +		error = mxt_update_cfg(data, cfg); +		if (error) +			dev_warn(dev, "Error %d updating config\n", error); +	} + +	if (data->T9_reportid_min) { +		error = mxt_initialize_t9_input_device(data); +		if (error) +			goto err_free_object_table; +	} else if (data->T100_reportid_min) { +		error = mxt_initialize_t100_input_device(data); +		if (error) +			goto err_free_object_table;  	} else { -		data->max_x = max_x; -		data->max_y = max_y; +		dev_warn(dev, "No touch object detected\n");  	} + +	return 0; + +err_free_object_table: +	mxt_free_object_table(data); +	return error; +} + +/* Configuration crc check sum is returned as hex xxxxxx */ +static ssize_t mxt_config_csum_show(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct mxt_data *data = dev_get_drvdata(dev); + +	return scnprintf(buf, PAGE_SIZE, "%06x\n", data->config_crc);  }  /* Firmware Version is returned as Major.Minor.Build */ @@ -901,9 +2634,13 @@ static ssize_t mxt_fw_version_show(struct device *dev,  				   struct device_attribute *attr, char *buf)  {  	struct mxt_data *data = dev_get_drvdata(dev); -	struct mxt_info *info = &data->info; + +	if (!data->object_table) +		return -EINVAL; +  	return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n", -			 info->version >> 4, info->version & 0xf, info->build); +			 data->info->version >> 4, data->info->version & 0xf, +			 data->info->build);  }  /* Hardware Version is returned as FamilyID.VariantID */ @@ -911,9 +2648,12 @@ static ssize_t mxt_hw_version_show(struct device *dev,  				   struct device_attribute *attr, char *buf)  {  	struct mxt_data *data = dev_get_drvdata(dev); -	struct mxt_info *info = &data->info; + +	if (!data->object_table) +		return -EINVAL; +  	return scnprintf(buf, PAGE_SIZE, "%u.%u\n", -			 info->family_id, info->variant_id); +			data->info->family_id, data->info->variant_id);  }  static ssize_t mxt_show_instance(char *buf, int count, @@ -922,11 +2662,11 @@ static ssize_t mxt_show_instance(char *buf, int count,  {  	int i; -	if (object->instances > 0) +	if (mxt_obj_instances(object) > 1)  		count += scnprintf(buf + count, PAGE_SIZE - count,  				   "Instance %u\n", instance); -	for (i = 0; i < object->size + 1; i++) +	for (i = 0; i < mxt_obj_size(object); i++)  		count += scnprintf(buf + count, PAGE_SIZE - count,  				"\t[%2u]: %02x (%d)\n", i, val[i], val[i]);  	count += scnprintf(buf + count, PAGE_SIZE - count, "\n"); @@ -944,13 +2684,16 @@ static ssize_t mxt_object_show(struct device *dev,  	int error;  	u8 *obuf; +	if (!data->object_table) +		return -EINVAL; +  	/* Pre-allocate buffer large enough to hold max sized object. */  	obuf = kmalloc(256, GFP_KERNEL);  	if (!obuf)  		return -ENOMEM;  	error = 0; -	for (i = 0; i < data->info.object_num; i++) { +	for (i = 0; i < data->info->object_num; i++) {  		object = data->object_table + i;  		if (!mxt_object_readable(object->type)) @@ -959,8 +2702,8 @@ static ssize_t mxt_object_show(struct device *dev,  		count += scnprintf(buf + count, PAGE_SIZE - count,  				"T%u:\n", object->type); -		for (j = 0; j < object->instances + 1; j++) { -			u16 size = object->size + 1; +		for (j = 0; j < mxt_obj_instances(object); j++) { +			u16 size = mxt_obj_size(object);  			u16 addr = object->start_address + j * size;  			error = __mxt_read_reg(data->client, addr, size, obuf); @@ -976,75 +2719,186 @@ done:  	return error ?: count;  } -static int mxt_load_fw(struct device *dev, const char *fn) +static int mxt_check_firmware_format(struct device *dev, +				     const struct firmware *fw) +{ +	unsigned int pos = 0; +	char c; + +	while (pos < fw->size) { +		c = *(fw->data + pos); + +		if (c < '0' || (c > '9' && c < 'A') || c > 'F') +			return 0; + +		pos++; +	} + +	/* +	 * To convert file try: +	 * xxd -r -p mXTXXX__APP_VX-X-XX.enc > maxtouch.fw +	 */ +	dev_err(dev, "Aborting: firmware file must be in binary format\n"); + +	return -EINVAL; +} + +static int mxt_load_fw(struct device *dev)  {  	struct mxt_data *data = dev_get_drvdata(dev); -	struct i2c_client *client = data->client;  	const struct firmware *fw = NULL;  	unsigned int frame_size;  	unsigned int pos = 0; +	unsigned int retry = 0; +	unsigned int frame = 0;  	int ret; -	ret = request_firmware(&fw, fn, dev); +	ret = request_firmware(&fw, data->fw_name, dev);  	if (ret) { -		dev_err(dev, "Unable to open firmware %s\n", fn); +		dev_err(dev, "Unable to open firmware %s\n", data->fw_name);  		return ret;  	} -	/* Change to the bootloader mode */ -	mxt_write_object(data, MXT_GEN_COMMAND_T6, -			MXT_COMMAND_RESET, MXT_BOOT_VALUE); -	msleep(MXT_RESET_TIME); +	/* Check for incorrect enc file */ +	ret = mxt_check_firmware_format(dev, fw); +	if (ret) +		goto release_firmware; -	/* Change to slave address of bootloader */ -	if (client->addr == MXT_APP_LOW) -		client->addr = MXT_BOOT_LOW; -	else -		client->addr = MXT_BOOT_HIGH; +	if (data->suspended) { +		if (data->use_regulator) +			mxt_regulator_enable(data); -	ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD); -	if (ret) -		goto out; +		enable_irq(data->irq); +		data->suspended = false; +	} + +	if (!data->in_bootloader) { +		/* Change to the bootloader mode */ +		data->in_bootloader = true; + +		ret = mxt_t6_command(data, MXT_COMMAND_RESET, +				     MXT_BOOT_VALUE, false); +		if (ret) +			goto release_firmware; + +		msleep(MXT_RESET_TIME); + +		/* Do not need to scan since we know family ID */ +		ret = mxt_lookup_bootloader_address(data, 0); +		if (ret) +			goto release_firmware; -	/* Unlock bootloader */ -	mxt_unlock_bootloader(client); +		mxt_free_input_device(data); +		mxt_free_object_table(data); +	} else { +		enable_irq(data->irq); +	} + +	INIT_COMPLETION(data->bl_completion); + +	ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false); +	if (ret) { +		/* Bootloader may still be unlocked from previous attempt */ +		ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, false); +		if (ret) +			goto disable_irq; +	} else { +		dev_info(dev, "Unlocking bootloader\n"); + +		/* Unlock bootloader */ +		ret = mxt_send_bootloader_cmd(data, true); +		if (ret) +			goto disable_irq; +	}  	while (pos < fw->size) { -		ret = mxt_check_bootloader(client, -						MXT_WAITING_FRAME_DATA); +		ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, true);  		if (ret) -			goto out; +			goto disable_irq;  		frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); -		/* We should add 2 at frame size as the the firmware data is not -		 * included the CRC bytes. -		 */ +		/* Take account of CRC bytes */  		frame_size += 2;  		/* Write one frame to device */ -		mxt_fw_write(client, fw->data + pos, frame_size); - -		ret = mxt_check_bootloader(client, -						MXT_FRAME_CRC_PASS); +		ret = mxt_bootloader_write(data, fw->data + pos, frame_size);  		if (ret) -			goto out; +			goto disable_irq; -		pos += frame_size; +		ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true); +		if (ret) { +			retry++; -		dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size); +			/* Back off by 20ms per retry */ +			msleep(retry * 20); + +			if (retry > 20) { +				dev_err(dev, "Retry count exceeded\n"); +				goto disable_irq; +			} +		} else { +			retry = 0; +			pos += frame_size; +			frame++; +		} + +		if (frame % 50 == 0) +			dev_dbg(dev, "Sent %d frames, %d/%zd bytes\n", +				frame, pos, fw->size);  	} -out: +	/* Wait for flash. */ +	ret = mxt_wait_for_completion(data, &data->bl_completion, +				      MXT_FW_RESET_TIME); +	if (ret) +		goto disable_irq; + +	dev_dbg(dev, "Sent %d frames, %d bytes\n", frame, pos); + +	/* +	 * Wait for device to reset. Some bootloader versions do not assert +	 * the CHG line after bootloading has finished, so ignore potential +	 * errors. +	 */ +	mxt_wait_for_completion(data, &data->bl_completion, MXT_FW_RESET_TIME); + +	data->in_bootloader = false; + +disable_irq: +	disable_irq(data->irq); +release_firmware:  	release_firmware(fw); +	return ret; +} -	/* Change to slave address of application */ -	if (client->addr == MXT_BOOT_LOW) -		client->addr = MXT_APP_LOW; +static int mxt_update_file_name(struct device *dev, char **file_name, +				const char *buf, size_t count) +{ +	char *file_name_tmp; + +	/* Simple sanity check */ +	if (count > 64) { +		dev_warn(dev, "File name too long\n"); +		return -EINVAL; +	} + +	file_name_tmp = krealloc(*file_name, count + 1, GFP_KERNEL); +	if (!file_name_tmp) { +		dev_warn(dev, "no memory\n"); +		return -ENOMEM; +	} + +	*file_name = file_name_tmp; +	memcpy(*file_name, buf, count); + +	/* Echo into the sysfs entry may append newline at the end of buf */ +	if (buf[count - 1] == '\n') +		(*file_name)[count - 1] = '\0';  	else -		client->addr = MXT_APP_HIGH; +		(*file_name)[count] = '\0'; -	return ret; +	return 0;  }  static ssize_t mxt_update_fw_store(struct device *dev, @@ -1054,42 +2908,211 @@ static ssize_t mxt_update_fw_store(struct device *dev,  	struct mxt_data *data = dev_get_drvdata(dev);  	int error; -	disable_irq(data->irq); +	error = mxt_update_file_name(dev, &data->fw_name, buf, count); +	if (error) +		return error; -	error = mxt_load_fw(dev, MXT_FW_NAME); +	error = mxt_load_fw(dev);  	if (error) {  		dev_err(dev, "The firmware update failed(%d)\n", error);  		count = error;  	} else { -		dev_dbg(dev, "The firmware update succeeded\n"); +		dev_info(dev, "The firmware update succeeded\n"); -		/* Wait for reset */ -		msleep(MXT_FWRESET_TIME); +		data->suspended = false; -		mxt_free_object_table(data); +		error = mxt_initialize(data); +		if (error) +			return error; +	} -		mxt_initialize(data); +	return count; +} + +static ssize_t mxt_update_cfg_store(struct device *dev, +		struct device_attribute *attr, +		const char *buf, size_t count) +{ +	struct mxt_data *data = dev_get_drvdata(dev); +	const struct firmware *cfg; +	int ret; + +	if (data->in_bootloader) { +		dev_err(dev, "Not in appmode\n"); +		return -EINVAL;  	} -	enable_irq(data->irq); +	ret = mxt_update_file_name(dev, &data->cfg_name, buf, count); +	if (ret) +		return ret; -	error = mxt_make_highchg(data); -	if (error) -		return error; +	ret = request_firmware(&cfg, data->cfg_name, dev); +	if (ret < 0) { +		dev_err(dev, "Failure to request config file %s\n", +			data->cfg_name); +		ret = -ENOENT; +		goto out; +	} -	return count; +	data->updating_config = true; + +	mxt_free_input_device(data); + +	if (data->suspended) { +		if (data->use_regulator) { +			enable_irq(data->irq); +			mxt_regulator_enable(data); +		} else { +			mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); +			mxt_acquire_irq(data); +		} + +		data->suspended = false; +	} + +	ret = mxt_configure_objects(data, cfg); +	if (ret) +		goto release; + +	ret = count; + +release: +	release_firmware(cfg); +out: +	data->updating_config = false; +	return ret; +} + +static ssize_t mxt_debug_enable_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct mxt_data *data = dev_get_drvdata(dev); +	char c; + +	c = data->debug_enabled ? '1' : '0'; +	return scnprintf(buf, PAGE_SIZE, "%c\n", c); +} + +static ssize_t mxt_debug_notify_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	return sprintf(buf, "0\n"); +} + +static ssize_t mxt_debug_v2_enable_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct mxt_data *data = dev_get_drvdata(dev); +	u8 i; +	ssize_t ret; + +	if (kstrtou8(buf, 0, &i) == 0 && i < 2) { +		if (i == 1) +			mxt_debug_msg_enable(data); +		else +			mxt_debug_msg_disable(data); + +		ret = count; +	} else { +		dev_dbg(dev, "debug_enabled write error\n"); +		ret = -EINVAL; +	} + +	return ret; +} + +static ssize_t mxt_debug_enable_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct mxt_data *data = dev_get_drvdata(dev); +	u8 i; +	ssize_t ret; + +	if (kstrtou8(buf, 0, &i) == 0 && i < 2) { +		data->debug_enabled = (i == 1); + +		dev_dbg(dev, "%s\n", i ? "debug enabled" : "debug disabled"); +		ret = count; +	} else { +		dev_dbg(dev, "debug_enabled write error\n"); +		ret = -EINVAL; +	} + +	return ret; +} + +static int mxt_check_mem_access_params(struct mxt_data *data, loff_t off, +				       size_t *count) +{ +	if (off >= data->mem_size) +		return -EIO; + +	if (off + *count > data->mem_size) +		*count = data->mem_size - off; + +	if (*count > MXT_MAX_BLOCK_WRITE) +		*count = MXT_MAX_BLOCK_WRITE; + +	return 0; +} + +static ssize_t mxt_mem_access_read(struct file *filp, struct kobject *kobj, +	struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) +{ +	struct device *dev = container_of(kobj, struct device, kobj); +	struct mxt_data *data = dev_get_drvdata(dev); +	int ret = 0; + +	ret = mxt_check_mem_access_params(data, off, &count); +	if (ret < 0) +		return ret; + +	if (count > 0) +		ret = __mxt_read_reg(data->client, off, count, buf); + +	return ret == 0 ? count : ret; +} + +static ssize_t mxt_mem_access_write(struct file *filp, struct kobject *kobj, +	struct bin_attribute *bin_attr, char *buf, loff_t off, +	size_t count) +{ +	struct device *dev = container_of(kobj, struct device, kobj); +	struct mxt_data *data = dev_get_drvdata(dev); +	int ret = 0; + +	ret = mxt_check_mem_access_params(data, off, &count); +	if (ret < 0) +		return ret; + +	if (count > 0) +		ret = __mxt_write_reg(data->client, off, count, buf); + +	return ret == 0 ? count : ret;  }  static DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL);  static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL);  static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL);  static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store); +static DEVICE_ATTR(update_cfg, S_IWUSR, NULL, mxt_update_cfg_store); +static DEVICE_ATTR(config_csum, S_IRUGO, mxt_config_csum_show, NULL); +static DEVICE_ATTR(debug_enable, S_IWUSR | S_IRUSR, mxt_debug_enable_show, +		   mxt_debug_enable_store); +static DEVICE_ATTR(debug_v2_enable, S_IWUSR | S_IRUSR, NULL, +		   mxt_debug_v2_enable_store); +static DEVICE_ATTR(debug_notify, S_IRUGO, mxt_debug_notify_show, NULL);  static struct attribute *mxt_attrs[] = {  	&dev_attr_fw_version.attr,  	&dev_attr_hw_version.attr,  	&dev_attr_object.attr,  	&dev_attr_update_fw.attr, +	&dev_attr_update_cfg.attr, +	&dev_attr_config_csum.attr, +	&dev_attr_debug_enable.attr, +	&dev_attr_debug_v2_enable.attr, +	&dev_attr_debug_notify.attr,  	NULL  }; @@ -1097,18 +3120,66 @@ static const struct attribute_group mxt_attr_group = {  	.attrs = mxt_attrs,  }; +static void mxt_reset_slots(struct mxt_data *data) +{ +	struct input_dev *input_dev = data->input_dev; +	unsigned int num_mt_slots; +	int id; + +	if (!input_dev) +		return; + +	num_mt_slots = data->num_touchids + data->num_stylusids; + +	for (id = 0; id < num_mt_slots; id++) { +		input_mt_slot(input_dev, id); +		input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); +	} + +	mxt_input_sync(data); +} +  static void mxt_start(struct mxt_data *data)  { -	/* Touch enable */ -	mxt_write_object(data, -			MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83); +	if (!data->suspended || data->in_bootloader) +		return; + +	if (data->use_regulator) { +		enable_irq(data->irq); + +		mxt_regulator_enable(data); +	} else { +		/* +		 * Discard any messages still in message buffer +		 * from before chip went to sleep +		 */ +		mxt_process_messages_until_invalid(data); + +		mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); + +		/* Recalibrate since chip has been in deep sleep */ +		mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false); + +		mxt_acquire_irq(data); +	} + +	data->suspended = false;  }  static void mxt_stop(struct mxt_data *data)  { -	/* Touch disable */ -	mxt_write_object(data, -			MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0); +	if (data->suspended || data->in_bootloader || data->updating_config) +		return; + +	disable_irq(data->irq); + +	if (data->use_regulator) +		mxt_regulator_disable(data); +	else +		mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP); + +	mxt_reset_slots(data); +	data->suspended = true;  }  static int mxt_input_open(struct input_dev *dev) @@ -1127,134 +3198,155 @@ static void mxt_input_close(struct input_dev *dev)  	mxt_stop(data);  } -static int mxt_probe(struct i2c_client *client, -		const struct i2c_device_id *id) +#ifdef CONFIG_OF +static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)  { -	const struct mxt_platform_data *pdata = client->dev.platform_data; -	struct mxt_data *data; -	struct input_dev *input_dev; -	int error; -	unsigned int num_mt_slots; +	struct mxt_platform_data *pdata; +	u32 *keymap; +	int proplen, ret; +	if (!client->dev.of_node) +		return ERR_PTR(-ENODEV); + +	pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);  	if (!pdata) -		return -EINVAL; +		return ERR_PTR(-ENOMEM); -	data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); -	input_dev = input_allocate_device(); -	if (!data || !input_dev) { -		dev_err(&client->dev, "Failed to allocate memory\n"); -		error = -ENOMEM; -		goto err_free_mem; -	} +	/* reset gpio */ +	pdata->gpio_reset = of_get_named_gpio_flags(client->dev.of_node, +		"atmel,reset-gpio", 0, NULL); -	data->is_tp = pdata && pdata->is_tp; +	of_property_read_string(client->dev.of_node, "atmel,cfg_name", +				&pdata->cfg_name); -	input_dev->name = (data->is_tp) ? "Atmel maXTouch Touchpad" : -					  "Atmel maXTouch Touchscreen"; -	snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0", -		 client->adapter->nr, client->addr); +	of_property_read_string(client->dev.of_node, "atmel,input_name", +				&pdata->input_name); -	input_dev->phys = data->phys; +	if (of_find_property(client->dev.of_node, "linux,gpio-keymap", +			     &proplen)) { +		pdata->t19_num_keys = proplen / sizeof(u32); -	input_dev->id.bustype = BUS_I2C; -	input_dev->dev.parent = &client->dev; -	input_dev->open = mxt_input_open; -	input_dev->close = mxt_input_close; +		keymap = devm_kzalloc(&client->dev, +				pdata->t19_num_keys * sizeof(keymap[0]), +				GFP_KERNEL); +		if (!keymap) +			return ERR_PTR(-ENOMEM); -	data->client = client; -	data->input_dev = input_dev; -	data->pdata = pdata; -	data->irq = client->irq; +		ret = of_property_read_u32_array(client->dev.of_node, +			"linux,gpio-keymap", keymap, pdata->t19_num_keys); +		if (ret) { +			dev_err(&client->dev, +				"Unable to read device tree key codes: %d\n", +				 ret); +			return NULL; +		} -	mxt_calc_resolution(data); +		pdata->t19_keymap = keymap; +	} -	error = mxt_initialize(data); -	if (error) -		goto err_free_mem; +	return pdata; +} +#else +static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) +{ +	struct mxt_platform_data *pdata; -	__set_bit(EV_ABS, input_dev->evbit); -	__set_bit(EV_KEY, input_dev->evbit); -	__set_bit(BTN_TOUCH, input_dev->keybit); +	pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); +	if (!pdata) +		return ERR_PTR(-ENOMEM); -	if (data->is_tp) { -		int i; -		__set_bit(INPUT_PROP_POINTER, input_dev->propbit); -		__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); +	/* Set default parameters */ +	pdata->irqflags = IRQF_TRIGGER_FALLING; -		for (i = 0; i < MXT_NUM_GPIO; i++) -			if (pdata->key_map[i] != KEY_RESERVED) -				__set_bit(pdata->key_map[i], input_dev->keybit); +	return pdata; +} +#endif -		__set_bit(BTN_TOOL_FINGER, input_dev->keybit); -		__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); -		__set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); -		__set_bit(BTN_TOOL_QUADTAP, input_dev->keybit); -		__set_bit(BTN_TOOL_QUINTTAP, input_dev->keybit); +static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ +	struct mxt_data *data; +	const struct mxt_platform_data *pdata; +	int error; -		input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); -		input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); -		input_abs_set_res(input_dev, ABS_MT_POSITION_X, -				  MXT_PIXELS_PER_MM); -		input_abs_set_res(input_dev, ABS_MT_POSITION_Y, -				  MXT_PIXELS_PER_MM); +	pdata = dev_get_platdata(&client->dev); +	if (!pdata) { +		pdata = mxt_parse_dt(client); +		if (IS_ERR(pdata)) +			return PTR_ERR(pdata);  	} -	/* For single touch */ -	input_set_abs_params(input_dev, ABS_X, -			     0, data->max_x, 0, 0); -	input_set_abs_params(input_dev, ABS_Y, -			     0, data->max_y, 0, 0); -	input_set_abs_params(input_dev, ABS_PRESSURE, -			     0, 255, 0, 0); +	data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); +	if (!data) { +		dev_err(&client->dev, "Failed to allocate memory\n"); +		return -ENOMEM; +	} -	/* For multi touch */ -	num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; -	error = input_mt_init_slots(input_dev, num_mt_slots, 0); -	if (error) -		goto err_free_object; -	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, -			     0, MXT_MAX_AREA, 0, 0); -	input_set_abs_params(input_dev, ABS_MT_POSITION_X, -			     0, data->max_x, 0, 0); -	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, -			     0, data->max_y, 0, 0); -	input_set_abs_params(input_dev, ABS_MT_PRESSURE, -			     0, 255, 0, 0); +	snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0", +		 client->adapter->nr, client->addr); -	input_set_drvdata(input_dev, data); +	data->client = client; +	data->pdata = pdata; +	data->irq = client->irq;  	i2c_set_clientdata(client, data); +	if (data->pdata->cfg_name) +		mxt_update_file_name(&data->client->dev, +				     &data->cfg_name, +				     data->pdata->cfg_name, +				     strlen(data->pdata->cfg_name)); + +	init_completion(&data->bl_completion); +	init_completion(&data->reset_completion); +	init_completion(&data->crc_completion); +	mutex_init(&data->debug_msg_lock); +  	error = request_threaded_irq(client->irq, NULL, mxt_interrupt,  				     pdata->irqflags | IRQF_ONESHOT,  				     client->name, data);  	if (error) {  		dev_err(&client->dev, "Failed to register interrupt\n"); -		goto err_free_object; +		goto err_free_mem;  	} -	error = mxt_make_highchg(data); -	if (error) -		goto err_free_irq; +	mxt_probe_regulators(data); -	error = input_register_device(input_dev); -	if (error) -		goto err_free_irq; +	disable_irq(data->irq);  	error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); +	if (error) { +		dev_err(&client->dev, "Failure %d creating sysfs group\n", +			error); +		goto err_free_irq; +	} + +	sysfs_bin_attr_init(&data->mem_access_attr); +	data->mem_access_attr.attr.name = "mem_access"; +	data->mem_access_attr.attr.mode = S_IRUGO | S_IWUSR; +	data->mem_access_attr.read = mxt_mem_access_read; +	data->mem_access_attr.write = mxt_mem_access_write; +	data->mem_access_attr.size = data->mem_size; + +	if (sysfs_create_bin_file(&client->dev.kobj, +				  &data->mem_access_attr) < 0) { +		dev_err(&client->dev, "Failed to create %s\n", +			data->mem_access_attr.attr.name); +		goto err_remove_sysfs_group; +	} + +	error = mxt_initialize(data);  	if (error) -		goto err_unregister_device; +		goto err_remove_mem_access;  	return 0; -err_unregister_device: -	input_unregister_device(input_dev); -	input_dev = NULL; +err_remove_mem_access: +	sysfs_remove_bin_file(&client->dev.kobj, &data->mem_access_attr); +	data->mem_access_attr.attr.name = NULL; +err_remove_sysfs_group: +	sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);  err_free_irq:  	free_irq(client->irq, data); -err_free_object: -	kfree(data->object_table);  err_free_mem: -	input_free_device(input_dev);  	kfree(data);  	return error;  } @@ -1263,10 +3355,16 @@ static int mxt_remove(struct i2c_client *client)  {  	struct mxt_data *data = i2c_get_clientdata(client); +	if (data->mem_access_attr.attr.name) +		sysfs_remove_bin_file(&client->dev.kobj, +				      &data->mem_access_attr); +  	sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);  	free_irq(data->irq, data); -	input_unregister_device(data->input_dev); -	kfree(data->object_table); +	regulator_put(data->reg_avdd); +	regulator_put(data->reg_vdd); +	mxt_free_input_device(data); +	mxt_free_object_table(data);  	kfree(data);  	return 0; @@ -1295,12 +3393,6 @@ static int mxt_resume(struct device *dev)  	struct mxt_data *data = i2c_get_clientdata(client);  	struct input_dev *input_dev = data->input_dev; -	/* Soft reset */ -	mxt_write_object(data, MXT_GEN_COMMAND_T6, -			MXT_COMMAND_RESET, 1); - -	msleep(MXT_RESET_TIME); -  	mutex_lock(&input_dev->mutex);  	if (input_dev->users) @@ -1314,6 +3406,12 @@ static int mxt_resume(struct device *dev)  static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume); +static const struct of_device_id mxt_of_match[] = { +	{ .compatible = "atmel,maxtouch", }, +	{}, +}; +MODULE_DEVICE_TABLE(of, mxt_of_match); +  static const struct i2c_device_id mxt_id[] = {  	{ "qt602240_ts", 0 },  	{ "atmel_mxt_ts", 0 }, @@ -1327,6 +3425,7 @@ static struct i2c_driver mxt_driver = {  	.driver = {  		.name	= "atmel_mxt_ts",  		.owner	= THIS_MODULE, +		.of_match_table = of_match_ptr(mxt_of_match),  		.pm	= &mxt_pm_ops,  	},  	.probe		= mxt_probe, diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c index a036a19040f..41b5f574ad0 100644 --- a/drivers/leds/leds-lm3530.c +++ b/drivers/leds/leds-lm3530.c @@ -1,12 +1,15 @@  /* + * Copyright (C) 2015 Olio Devices   * Copyright (C) 2011 ST-Ericsson SA.   * Copyright (C) 2009 Motorola, Inc.   *   * License Terms: GNU General Public License v2   * - * Simple driver for National Semiconductor LM3530 Backlight driver chip + * Simple driver for National Semiconductor LM3530 Backlight driver chip, + * with basic suspend and resume.   * - * Author: Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com> + * Author: Mattis Fjallstrom <mattis@oliodevices.com> + * based on leds-lm3530.c by Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>   * based on leds-lm3530.c by Dan Murphy <D.Murphy@motorola.com>   */ @@ -486,6 +489,64 @@ static int lm3530_remove(struct i2c_client *client)  	return 0;  } + +/** + * lm3530_shutdown - make sure brightness is set to 0 + *  + * The PMIC of the Olio H1 can't provide the power needed by the lm3530,  + * in other words, it does it's own power management. This means we need  + * to shut off power usage. Brightness of 0 should do this. + */ + +static void lm3530_shutdown(struct i2c_client *client) +{ +	struct lm3530_data *drvdata = i2c_get_clientdata(client); + +    lm3530_brightness_set(&(drvdata->led_dev), LED_OFF); + +	return; +} + +/** + * lm3530_suspend - suspend backlight + *  + * For now, off. Should also save the previous setting. + */ + +static int lm3530_suspend(struct device *dev) { +	struct led_classdev *led_cdev = dev_get_drvdata(dev); + +    printk ("OLIO %s:%s Suspending\n", __FILE__, __FUNCTION__); + +    lm3530_brightness_set(led_cdev, LED_OFF); + +    return 0; +} + +/** + * lm3530_resume - reset backlight + *  + * Turn the backlight on again (Does android take care of this for us?) + * For now, leave this as a no-op - Android turns on the lights. + */ + +static int lm3530_resume(struct device *dev) { +	struct led_classdev *led_cdev = dev_get_drvdata(dev); + +    printk ("OLIO %s:%s Resuming\n", __FILE__, __FUNCTION__); + +    /* lm3530_brightness_set(led_cdev, 100); */ + +    return 0; +} + + +/* ---------------------------------------------------------------------- */ + +static const struct dev_pm_ops lm3530_pm_ops = { +    SET_SYSTEM_SLEEP_PM_OPS(lm3530_suspend, lm3530_resume) +}; +  static const struct i2c_device_id lm3530_id[] = {  	{LM3530_NAME, 0},  	{} @@ -495,10 +556,12 @@ MODULE_DEVICE_TABLE(i2c, lm3530_id);  static struct i2c_driver lm3530_i2c_driver = {  	.probe = lm3530_probe,  	.remove = lm3530_remove, +    .shutdown = lm3530_shutdown,  	.id_table = lm3530_id,  	.driver = {  		.name = LM3530_NAME,  		.owner = THIS_MODULE, +        .pm   = &lm3530_pm_ops,  	},  }; diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index d7927720483..f4b99d84396 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -446,7 +446,7 @@ static void tps65910_power_off(void)  	tps65910 = dev_get_drvdata(&tps65910_i2c_client->dev);  	if (tps65910_reg_set_bits(tps65910, TPS65910_DEVCTRL, -			DEVCTRL_PWR_OFF_MASK) < 0) +			DEVCTRL_DEV_OFF_MASK) < 0)  		return;  	tps65910_reg_clear_bits(tps65910, TPS65910_DEVCTRL, diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 2d23d292943..023541d7a30 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -1502,7 +1502,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev)  		if (res)  			goto err_ecc_ioremap;  	} else { -		memcpy(&host->board, pdev->dev.platform_data, +		memcpy(&host->board, dev_get_platdata(&pdev->dev),  		       sizeof(struct atmel_nand_data));  	} diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 217459d02b2..ae8dd7c4103 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -411,7 +411,7 @@ static int au1550nd_probe(struct platform_device *pdev)  	struct resource *r;  	int ret, cs; -	pd = pdev->dev.platform_data; +	pd = dev_get_platdata(&pdev->dev);  	if (!pd) {  		dev_err(&pdev->dev, "missing platform data\n");  		return -ENODEV; diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 776df3694f7..d7f7df3cd96 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -171,7 +171,7 @@ static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev)  static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev)  { -	return pdev->dev.platform_data; +	return dev_get_platdata(&pdev->dev);  }  /* diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index c3e15a55817..758a111a3bc 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -530,7 +530,7 @@ MODULE_DEVICE_TABLE(of, davinci_nand_of_match);  static struct davinci_nand_pdata  	*nand_davinci_get_pdata(struct platform_device *pdev)  { -	if (!pdev->dev.platform_data && pdev->dev.of_node) { +	if (!dev_get_platdata(&pdev->dev) && pdev->dev.of_node) {  		struct davinci_nand_pdata *pdata;  		const char *mode;  		u32 prop; @@ -575,13 +575,13 @@ static struct davinci_nand_pdata  			pdata->bbt_options = NAND_BBT_USE_FLASH;  	} -	return pdev->dev.platform_data; +	return dev_get_platdata(&pdev->dev);  }  #else  static struct davinci_nand_pdata  	*nand_davinci_get_pdata(struct platform_device *pdev)  { -	return pdev->dev.platform_data; +	return dev_get_platdata(&pdev->dev);  }  #endif diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index 89065dd83d6..e826f898241 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -17,6 +17,7 @@   */  #include <linux/kernel.h> +#include <linux/err.h>  #include <linux/init.h>  #include <linux/slab.h>  #include <linux/module.h> @@ -86,59 +87,11 @@ static void gpio_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)  	gpio_nand_dosync(gpiomtd);  } -static void gpio_nand_writebuf(struct mtd_info *mtd, const u_char *buf, int len) -{ -	struct nand_chip *this = mtd->priv; - -	iowrite8_rep(this->IO_ADDR_W, buf, len); -} - -static void gpio_nand_readbuf(struct mtd_info *mtd, u_char *buf, int len) -{ -	struct nand_chip *this = mtd->priv; - -	ioread8_rep(this->IO_ADDR_R, buf, len); -} - -static void gpio_nand_writebuf16(struct mtd_info *mtd, const u_char *buf, -				 int len) -{ -	struct nand_chip *this = mtd->priv; - -	if (IS_ALIGNED((unsigned long)buf, 2)) { -		iowrite16_rep(this->IO_ADDR_W, buf, len>>1); -	} else { -		int i; -		unsigned short *ptr = (unsigned short *)buf; - -		for (i = 0; i < len; i += 2, ptr++) -			writew(*ptr, this->IO_ADDR_W); -	} -} - -static void gpio_nand_readbuf16(struct mtd_info *mtd, u_char *buf, int len) -{ -	struct nand_chip *this = mtd->priv; - -	if (IS_ALIGNED((unsigned long)buf, 2)) { -		ioread16_rep(this->IO_ADDR_R, buf, len>>1); -	} else { -		int i; -		unsigned short *ptr = (unsigned short *)buf; - -		for (i = 0; i < len; i += 2, ptr++) -			*ptr = readw(this->IO_ADDR_R); -	} -} -  static int gpio_nand_devready(struct mtd_info *mtd)  {  	struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); -	if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) -		return gpio_get_value(gpiomtd->plat.gpio_rdy); - -	return 1; +	return gpio_get_value(gpiomtd->plat.gpio_rdy);  }  #ifdef CONFIG_OF @@ -153,6 +106,9 @@ static int gpio_nand_get_config_of(const struct device *dev,  {  	u32 val; +	if (!dev->of_node) +		return -ENODEV; +  	if (!of_property_read_u32(dev->of_node, "bank-width", &val)) {  		if (val == 2) {  			plat->options |= NAND_BUSWIDTH_16; @@ -211,8 +167,8 @@ static inline int gpio_nand_get_config(const struct device *dev,  	if (!ret)  		return ret; -	if (dev->platform_data) { -		memcpy(plat, dev->platform_data, sizeof(*plat)); +	if (dev_get_platdata(dev)) { +		memcpy(plat, dev_get_platdata(dev), sizeof(*plat));  		return 0;  	} @@ -230,145 +186,100 @@ gpio_nand_get_io_sync(struct platform_device *pdev)  	return platform_get_resource(pdev, IORESOURCE_MEM, 1);  } -static int gpio_nand_remove(struct platform_device *dev) +static int gpio_nand_remove(struct platform_device *pdev)  { -	struct gpiomtd *gpiomtd = platform_get_drvdata(dev); -	struct resource *res; +	struct gpiomtd *gpiomtd = platform_get_drvdata(pdev);  	nand_release(&gpiomtd->mtd_info); -	res = gpio_nand_get_io_sync(dev); -	iounmap(gpiomtd->io_sync); -	if (res) -		release_mem_region(res->start, resource_size(res)); - -	res = platform_get_resource(dev, IORESOURCE_MEM, 0); -	iounmap(gpiomtd->nand_chip.IO_ADDR_R); -	release_mem_region(res->start, resource_size(res)); -  	if (gpio_is_valid(gpiomtd->plat.gpio_nwp))  		gpio_set_value(gpiomtd->plat.gpio_nwp, 0);  	gpio_set_value(gpiomtd->plat.gpio_nce, 1); -	gpio_free(gpiomtd->plat.gpio_cle); -	gpio_free(gpiomtd->plat.gpio_ale); -	gpio_free(gpiomtd->plat.gpio_nce); -	if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) -		gpio_free(gpiomtd->plat.gpio_nwp); -	if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) -		gpio_free(gpiomtd->plat.gpio_rdy); -  	return 0;  } -static void __iomem *request_and_remap(struct resource *res, size_t size, -					const char *name, int *err) -{ -	void __iomem *ptr; - -	if (!request_mem_region(res->start, resource_size(res), name)) { -		*err = -EBUSY; -		return NULL; -	} - -	ptr = ioremap(res->start, size); -	if (!ptr) { -		release_mem_region(res->start, resource_size(res)); -		*err = -ENOMEM; -	} -	return ptr; -} - -static int gpio_nand_probe(struct platform_device *dev) +static int gpio_nand_probe(struct platform_device *pdev)  {  	struct gpiomtd *gpiomtd; -	struct nand_chip *this; -	struct resource *res0, *res1; +	struct nand_chip *chip; +	struct resource *res;  	struct mtd_part_parser_data ppdata = {};  	int ret = 0; -	if (!dev->dev.of_node && !dev->dev.platform_data) -		return -EINVAL; - -	res0 = platform_get_resource(dev, IORESOURCE_MEM, 0); -	if (!res0) +	if (!pdev->dev.of_node && !dev_get_platdata(&pdev->dev))  		return -EINVAL; -	gpiomtd = devm_kzalloc(&dev->dev, sizeof(*gpiomtd), GFP_KERNEL); -	if (gpiomtd == NULL) { -		dev_err(&dev->dev, "failed to create NAND MTD\n"); +	gpiomtd = devm_kzalloc(&pdev->dev, sizeof(*gpiomtd), GFP_KERNEL); +	if (!gpiomtd) { +		dev_err(&pdev->dev, "failed to create NAND MTD\n");  		return -ENOMEM;  	} -	this = &gpiomtd->nand_chip; -	this->IO_ADDR_R = request_and_remap(res0, 2, "NAND", &ret); -	if (!this->IO_ADDR_R) { -		dev_err(&dev->dev, "unable to map NAND\n"); -		goto err_map; -	} +	chip = &gpiomtd->nand_chip; -	res1 = gpio_nand_get_io_sync(dev); -	if (res1) { -		gpiomtd->io_sync = request_and_remap(res1, 4, "NAND sync", &ret); -		if (!gpiomtd->io_sync) { -			dev_err(&dev->dev, "unable to map sync NAND\n"); -			goto err_sync; -		} +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(chip->IO_ADDR_R)) +		return PTR_ERR(chip->IO_ADDR_R); + +	res = gpio_nand_get_io_sync(pdev); +	if (res) { +		gpiomtd->io_sync = devm_ioremap_resource(&pdev->dev, res); +		if (IS_ERR(gpiomtd->io_sync)) +			return PTR_ERR(gpiomtd->io_sync);  	} -	ret = gpio_nand_get_config(&dev->dev, &gpiomtd->plat); +	ret = gpio_nand_get_config(&pdev->dev, &gpiomtd->plat);  	if (ret) -		goto err_nce; +		return ret; -	ret = gpio_request(gpiomtd->plat.gpio_nce, "NAND NCE"); +	ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nce, "NAND NCE");  	if (ret) -		goto err_nce; +		return ret;  	gpio_direction_output(gpiomtd->plat.gpio_nce, 1); +  	if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) { -		ret = gpio_request(gpiomtd->plat.gpio_nwp, "NAND NWP"); +		ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nwp, +					"NAND NWP");  		if (ret) -			goto err_nwp; -		gpio_direction_output(gpiomtd->plat.gpio_nwp, 1); +			return ret;  	} -	ret = gpio_request(gpiomtd->plat.gpio_ale, "NAND ALE"); + +	ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_ale, "NAND ALE");  	if (ret) -		goto err_ale; +		return ret;  	gpio_direction_output(gpiomtd->plat.gpio_ale, 0); -	ret = gpio_request(gpiomtd->plat.gpio_cle, "NAND CLE"); + +	ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_cle, "NAND CLE");  	if (ret) -		goto err_cle; +		return ret;  	gpio_direction_output(gpiomtd->plat.gpio_cle, 0); +  	if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) { -		ret = gpio_request(gpiomtd->plat.gpio_rdy, "NAND RDY"); +		ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_rdy, +					"NAND RDY");  		if (ret) -			goto err_rdy; +			return ret;  		gpio_direction_input(gpiomtd->plat.gpio_rdy); +		chip->dev_ready = gpio_nand_devready;  	} +	chip->IO_ADDR_W		= chip->IO_ADDR_R; +	chip->ecc.mode		= NAND_ECC_SOFT; +	chip->options		= gpiomtd->plat.options; +	chip->chip_delay	= gpiomtd->plat.chip_delay; +	chip->cmd_ctrl		= gpio_nand_cmd_ctrl; -	this->IO_ADDR_W  = this->IO_ADDR_R; -	this->ecc.mode   = NAND_ECC_SOFT; -	this->options    = gpiomtd->plat.options; -	this->chip_delay = gpiomtd->plat.chip_delay; - -	/* install our routines */ -	this->cmd_ctrl   = gpio_nand_cmd_ctrl; -	this->dev_ready  = gpio_nand_devready; +	gpiomtd->mtd_info.priv	= chip; +	gpiomtd->mtd_info.owner	= THIS_MODULE; -	if (this->options & NAND_BUSWIDTH_16) { -		this->read_buf   = gpio_nand_readbuf16; -		this->write_buf  = gpio_nand_writebuf16; -	} else { -		this->read_buf   = gpio_nand_readbuf; -		this->write_buf  = gpio_nand_writebuf; -	} +	platform_set_drvdata(pdev, gpiomtd); -	/* set the mtd private data for the nand driver */ -	gpiomtd->mtd_info.priv = this; -	gpiomtd->mtd_info.owner = THIS_MODULE; +	if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) +		gpio_direction_output(gpiomtd->plat.gpio_nwp, 1);  	if (nand_scan(&gpiomtd->mtd_info, 1)) { -		dev_err(&dev->dev, "no nand chips found?\n");  		ret = -ENXIO;  		goto err_wp;  	} @@ -377,38 +288,17 @@ static int gpio_nand_probe(struct platform_device *dev)  		gpiomtd->plat.adjust_parts(&gpiomtd->plat,  					   gpiomtd->mtd_info.size); -	ppdata.of_node = dev->dev.of_node; +	ppdata.of_node = pdev->dev.of_node;  	ret = mtd_device_parse_register(&gpiomtd->mtd_info, NULL, &ppdata,  					gpiomtd->plat.parts,  					gpiomtd->plat.num_parts); -	if (ret) -		goto err_wp; -	platform_set_drvdata(dev, gpiomtd); - -	return 0; +	if (!ret) +		return 0;  err_wp:  	if (gpio_is_valid(gpiomtd->plat.gpio_nwp))  		gpio_set_value(gpiomtd->plat.gpio_nwp, 0); -	if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) -		gpio_free(gpiomtd->plat.gpio_rdy); -err_rdy: -	gpio_free(gpiomtd->plat.gpio_cle); -err_cle: -	gpio_free(gpiomtd->plat.gpio_ale); -err_ale: -	if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) -		gpio_free(gpiomtd->plat.gpio_nwp); -err_nwp: -	gpio_free(gpiomtd->plat.gpio_nce); -err_nce: -	iounmap(gpiomtd->io_sync); -	if (res1) -		release_mem_region(res1->start, resource_size(res1)); -err_sync: -	iounmap(gpiomtd->nand_chip.IO_ADDR_R); -	release_mem_region(res0->start, resource_size(res0)); -err_map: +  	return ret;  } @@ -417,6 +307,7 @@ static struct platform_driver gpio_nand_driver = {  	.remove		= gpio_nand_remove,  	.driver		= {  		.name	= "gpio-nand", +		.owner	= THIS_MODULE,  		.of_match_table = of_match_ptr(gpio_nand_id_table),  	},  }; diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index b76460eeaf2..976d5662c3b 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -411,7 +411,7 @@ static int jz_nand_probe(struct platform_device *pdev)  	struct jz_nand *nand;  	struct nand_chip *chip;  	struct mtd_info *mtd; -	struct jz_nand_platform_data *pdata = pdev->dev.platform_data; +	struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);  	size_t chipnr, bank_idx;  	uint8_t nand_maf_id = 0, nand_dev_id = 0; @@ -549,7 +549,7 @@ err_free:  static int jz_nand_remove(struct platform_device *pdev)  {  	struct jz_nand *nand = platform_get_drvdata(pdev); -	struct jz_nand_platform_data *pdata = pdev->dev.platform_data; +	struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);  	size_t i;  	nand_release(&nand->mtd); diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c index fd1df5e13ae..a379c39dab0 100644 --- a/drivers/mtd/nand/lpc32xx_mlc.c +++ b/drivers/mtd/nand/lpc32xx_mlc.c @@ -696,7 +696,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)  	}  	lpc32xx_wp_disable(host); -	host->pdata = pdev->dev.platform_data; +	host->pdata = dev_get_platdata(&pdev->dev);  	nand_chip->priv = host;		/* link the private data structures */  	mtd->priv = nand_chip; diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c index be94ed5abef..81cd0719f03 100644 --- a/drivers/mtd/nand/lpc32xx_slc.c +++ b/drivers/mtd/nand/lpc32xx_slc.c @@ -798,7 +798,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)  	}  	lpc32xx_wp_disable(host); -	host->pdata = pdev->dev.platform_data; +	host->pdata = dev_get_platdata(&pdev->dev);  	mtd = &host->mtd;  	chip = &host->nand_chip; diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 07e5784e5cd..302c676c7ec 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1432,7 +1432,8 @@ static int mxcnd_probe(struct platform_device *pdev)  	err = mxcnd_probe_dt(host);  	if (err > 0) { -		struct mxc_nand_platform_data *pdata = pdev->dev.platform_data; +		struct mxc_nand_platform_data *pdata = +					dev_get_platdata(&pdev->dev);  		if (pdata) {  			host->pdata = *pdata;  			host->devtype_data = (struct mxc_nand_devtype_data *) diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 267264320e0..b5208d1c790 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -86,33 +86,17 @@ static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)   * @td: search pattern descriptor   *   * Check for a pattern at the given place. Used to search bad block tables and - * good / bad block identifiers. If the SCAN_EMPTY option is set then check, if - * all bytes except the pattern area contain 0xff. + * good / bad block identifiers.   */  static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)  { -	int end = 0; -	uint8_t *p = buf; -  	if (td->options & NAND_BBT_NO_OOB)  		return check_pattern_no_oob(buf, td); -	end = paglen + td->offs; -	if (td->options & NAND_BBT_SCANEMPTY) -		if (memchr_inv(p, 0xff, end)) -			return -1; -	p += end; -  	/* Compare the pattern */ -	if (memcmp(p, td->pattern, td->len)) +	if (memcmp(buf + paglen + td->offs, td->pattern, td->len))  		return -1; -	if (td->options & NAND_BBT_SCANEMPTY) { -		p += td->len; -		end += td->len; -		if (memchr_inv(p, 0xff, len - end)) -			return -1; -	}  	return 0;  } @@ -478,15 +462,9 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,  	else  		numpages = 1; -	if (!(bd->options & NAND_BBT_SCANEMPTY)) { -		/* We need only read few bytes from the OOB area */ -		scanlen = 0; -		readlen = bd->len; -	} else { -		/* Full page content should be read */ -		scanlen = mtd->writesize + mtd->oobsize; -		readlen = numpages * mtd->writesize; -	} +	/* We need only read few bytes from the OOB area */ +	scanlen = 0; +	readlen = bd->len;  	if (chip == -1) {  		/* @@ -865,7 +843,6 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b  {  	struct nand_chip *this = mtd->priv; -	bd->options &= ~NAND_BBT_SCANEMPTY;  	return create_bbt(mtd, this->buffers->databuf, bd, -1);  } diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 81b80af5587..64d8e32b6ca 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -25,10 +25,8 @@  #include <linux/of.h>  #include <linux/of_device.h> -#ifdef CONFIG_MTD_NAND_OMAP_BCH -#include <linux/bch.h> +#include <linux/mtd/nand_bch.h>  #include <linux/platform_data/elm.h> -#endif  #include <linux/platform_data/mtd-nand-omap2.h> @@ -141,6 +139,8 @@  #define BCH_ECC_SIZE0		0x0	/* ecc_size0 = 0, no oob protection */  #define BCH_ECC_SIZE1		0x20	/* ecc_size1 = 32 */ +#define BADBLOCK_MARKER_LENGTH		2 +  #ifdef CONFIG_MTD_NAND_OMAP_BCH  static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,  	0xac, 0x6b, 0xff, 0x99, 0x7b}; @@ -149,17 +149,6 @@ static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};  /* oob info generated runtime depending on ecc algorithm and layout selected */  static struct nand_ecclayout omap_oobinfo; -/* Define some generic bad / good block scan pattern which are used - * while scanning a device for factory marked good / bad blocks - */ -static uint8_t scan_ff_pattern[] = { 0xff }; -static struct nand_bbt_descr bb_descrip_flashbased = { -	.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, -	.offs = 0, -	.len = 1, -	.pattern = scan_ff_pattern, -}; -  struct omap_nand_info {  	struct nand_hw_control		controller; @@ -182,14 +171,10 @@ struct omap_nand_info {  	u_char				*buf;  	int					buf_len;  	struct gpmc_nand_regs		reg; - -#ifdef CONFIG_MTD_NAND_OMAP_BCH -	struct bch_control             *bch; -	struct nand_ecclayout           ecclayout; +	/* fields specific for BCHx_HW ECC scheme */  	bool				is_elm_used;  	struct device			*elm_dev;  	struct device_node		*of_node; -#endif  };  /** @@ -948,7 +933,7 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,  	u32 val;  	val = readl(info->reg.gpmc_ecc_config); -	if (((val >> ECC_CONFIG_CS_SHIFT)  & ~CS_MASK) != info->gpmc_cs) +	if (((val >> ECC_CONFIG_CS_SHIFT) & CS_MASK) != info->gpmc_cs)  		return -EINVAL;  	/* read ecc result */ @@ -1058,8 +1043,7 @@ static int omap_dev_ready(struct mtd_info *mtd)  	}  } -#ifdef CONFIG_MTD_NAND_OMAP_BCH - +#if defined(CONFIG_MTD_NAND_ECC_BCH) || defined(CONFIG_MTD_NAND_OMAP_BCH)  /**   * omap3_enable_hwecc_bch - Program OMAP3 GPMC to perform BCH ECC correction   * @mtd: MTD device structure @@ -1140,7 +1124,9 @@ static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode)  	/* Clear ecc and enable bits */  	writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);  } +#endif +#ifdef CONFIG_MTD_NAND_ECC_BCH  /**   * omap3_calculate_ecc_bch4 - Generate 7 bytes of ECC bytes   * @mtd: MTD device structure @@ -1225,7 +1211,9 @@ static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat,  	return 0;  } +#endif /* CONFIG_MTD_NAND_ECC_BCH */ +#ifdef CONFIG_MTD_NAND_OMAP_BCH  /**   * omap3_calculate_ecc_bch - Generate bytes of ECC bytes   * @mtd:	MTD device structure @@ -1463,7 +1451,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,  	/* Check if any error reported */  	if (!is_error_reported) -		return 0; +		return stat;  	/* Decode BCH error using ELM module */  	elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec); @@ -1519,38 +1507,6 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,  }  /** - * omap3_correct_data_bch - Decode received data and correct errors - * @mtd: MTD device structure - * @data: page data - * @read_ecc: ecc read from nand flash - * @calc_ecc: ecc read from HW ECC registers - */ -static int omap3_correct_data_bch(struct mtd_info *mtd, u_char *data, -				  u_char *read_ecc, u_char *calc_ecc) -{ -	int i, count; -	/* cannot correct more than 8 errors */ -	unsigned int errloc[8]; -	struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, -						   mtd); - -	count = decode_bch(info->bch, NULL, 512, read_ecc, calc_ecc, NULL, -			   errloc); -	if (count > 0) { -		/* correct errors */ -		for (i = 0; i < count; i++) { -			/* correct data only, not ecc bytes */ -			if (errloc[i] < 8*512) -				data[errloc[i]/8] ^= 1 << (errloc[i] & 7); -			pr_debug("corrected bitflip %u\n", errloc[i]); -		} -	} else if (count < 0) { -		pr_err("ecc unrecoverable error\n"); -	} -	return count; -} - -/**   * omap_write_page_bch - BCH ecc based write page function for entire page   * @mtd:		mtd info structure   * @chip:		nand chip info structure @@ -1637,207 +1593,58 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,  }  /** - * omap3_free_bch - Release BCH ecc resources - * @mtd: MTD device structure - */ -static void omap3_free_bch(struct mtd_info *mtd) -{ -	struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, -						   mtd); -	if (info->bch) { -		free_bch(info->bch); -		info->bch = NULL; -	} -} - -/** - * omap3_init_bch - Initialize BCH ECC - * @mtd: MTD device structure - * @ecc_opt: OMAP ECC mode (OMAP_ECC_BCH4_CODE_HW or OMAP_ECC_BCH8_CODE_HW) + * is_elm_present - checks for presence of ELM module by scanning DT nodes + * @omap_nand_info: NAND device structure containing platform data + * @bch_type: 0x0=BCH4, 0x1=BCH8, 0x2=BCH16   */ -static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt) +static int is_elm_present(struct omap_nand_info *info, +			struct device_node *elm_node, enum bch_ecc bch_type)  { -	int max_errors; -	struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, -						   mtd); -#ifdef CONFIG_MTD_NAND_OMAP_BCH8 -	const int hw_errors = BCH8_MAX_ERROR; -#else -	const int hw_errors = BCH4_MAX_ERROR; -#endif -	enum bch_ecc bch_type; -	const __be32 *parp; -	int lenp; -	struct device_node *elm_node; - -	info->bch = NULL; - -	max_errors = (ecc_opt == OMAP_ECC_BCH8_CODE_HW) ? -		BCH8_MAX_ERROR : BCH4_MAX_ERROR; -	if (max_errors != hw_errors) { -		pr_err("cannot configure %d-bit BCH ecc, only %d-bit supported", -		       max_errors, hw_errors); -		goto fail; -	} - -	info->nand.ecc.size = 512; -	info->nand.ecc.hwctl = omap3_enable_hwecc_bch; -	info->nand.ecc.mode = NAND_ECC_HW; -	info->nand.ecc.strength = max_errors; - -	if (hw_errors == BCH8_MAX_ERROR) -		bch_type = BCH8_ECC; -	else -		bch_type = BCH4_ECC; - -	/* Detect availability of ELM module */ -	parp = of_get_property(info->of_node, "elm_id", &lenp); -	if ((parp == NULL) && (lenp != (sizeof(void *) * 2))) { -		pr_err("Missing elm_id property, fall back to Software BCH\n"); -		info->is_elm_used = false; -	} else { -		struct platform_device *pdev; - -		elm_node = of_find_node_by_phandle(be32_to_cpup(parp)); -		pdev = of_find_device_by_node(elm_node); -		info->elm_dev = &pdev->dev; - -		if (elm_config(info->elm_dev, bch_type) == 0) -			info->is_elm_used = true; -	} - -	if (info->is_elm_used && (mtd->writesize <= 4096)) { - -		if (hw_errors == BCH8_MAX_ERROR) -			info->nand.ecc.bytes = BCH8_SIZE; -		else -			info->nand.ecc.bytes = BCH4_SIZE; - -		info->nand.ecc.correct = omap_elm_correct_data; -		info->nand.ecc.calculate = omap3_calculate_ecc_bch; -		info->nand.ecc.read_page = omap_read_page_bch; -		info->nand.ecc.write_page = omap_write_page_bch; -	} else { -		/* -		 * software bch library is only used to detect and -		 * locate errors -		 */ -		info->bch = init_bch(13, max_errors, -				0x201b /* hw polynomial */); -		if (!info->bch) -			goto fail; - -		info->nand.ecc.correct = omap3_correct_data_bch; - -		/* -		 * The number of corrected errors in an ecc block that will -		 * trigger block scrubbing defaults to the ecc strength (4 or 8) -		 * Set mtd->bitflip_threshold here to define a custom threshold. -		 */ - -		if (max_errors == 8) { -			info->nand.ecc.bytes = 13; -			info->nand.ecc.calculate = omap3_calculate_ecc_bch8; -		} else { -			info->nand.ecc.bytes = 7; -			info->nand.ecc.calculate = omap3_calculate_ecc_bch4; -		} -	} - -	pr_info("enabling NAND BCH ecc with %d-bit correction\n", max_errors); -	return 0; -fail: -	omap3_free_bch(mtd); -	return -1; -} - -/** - * omap3_init_bch_tail - Build an oob layout for BCH ECC correction. - * @mtd: MTD device structure - */ -static int omap3_init_bch_tail(struct mtd_info *mtd) -{ -	int i, steps, offset; -	struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, -						   mtd); -	struct nand_ecclayout *layout = &info->ecclayout; - -	/* build oob layout */ -	steps = mtd->writesize/info->nand.ecc.size; -	layout->eccbytes = steps*info->nand.ecc.bytes; - -	/* do not bother creating special oob layouts for small page devices */ -	if (mtd->oobsize < 64) { -		pr_err("BCH ecc is not supported on small page devices\n"); -		goto fail; +	struct platform_device *pdev; +	info->is_elm_used = false; +	/* check whether elm-id is passed via DT */ +	if (!elm_node) { +		pr_err("nand: error: ELM DT node not found\n"); +		return -ENODEV;  	} - -	/* reserve 2 bytes for bad block marker */ -	if (layout->eccbytes+2 > mtd->oobsize) { -		pr_err("no oob layout available for oobsize %d eccbytes %u\n", -		       mtd->oobsize, layout->eccbytes); -		goto fail; +	pdev = of_find_device_by_node(elm_node); +	/* check whether ELM device is registered */ +	if (!pdev) { +		pr_err("nand: error: ELM device not found\n"); +		return -ENODEV;  	} - -	/* ECC layout compatible with RBL for BCH8 */ -	if (info->is_elm_used && (info->nand.ecc.bytes == BCH8_SIZE)) -		offset = 2; -	else -		offset = mtd->oobsize - layout->eccbytes; - -	/* put ecc bytes at oob tail */ -	for (i = 0; i < layout->eccbytes; i++) -		layout->eccpos[i] = offset + i; - -	if (info->is_elm_used && (info->nand.ecc.bytes == BCH8_SIZE)) -		layout->oobfree[0].offset = 2 + layout->eccbytes * steps; -	else -		layout->oobfree[0].offset = 2; - -	layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; -	info->nand.ecc.layout = layout; - -	if (!(info->nand.options & NAND_BUSWIDTH_16)) -		info->nand.badblock_pattern = &bb_descrip_flashbased; +	/* ELM module available, now configure it */ +	info->elm_dev = &pdev->dev; +	if (elm_config(info->elm_dev, bch_type)) +		return -ENODEV; +	info->is_elm_used = true;  	return 0; -fail: -	omap3_free_bch(mtd); -	return -1; -} - -#else -static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt) -{ -	pr_err("CONFIG_MTD_NAND_OMAP_BCH is not enabled\n"); -	return -1; -} -static int omap3_init_bch_tail(struct mtd_info *mtd) -{ -	return -1; -} -static void omap3_free_bch(struct mtd_info *mtd) -{  } -#endif /* CONFIG_MTD_NAND_OMAP_BCH */ +#endif /* CONFIG_MTD_NAND_ECC_BCH */  static int omap_nand_probe(struct platform_device *pdev)  {  	struct omap_nand_info		*info;  	struct omap_nand_platform_data	*pdata; +	struct mtd_info			*mtd; +	struct nand_chip		*nand_chip; +	struct nand_ecclayout		*ecclayout;  	int				err; -	int				i, offset; -	dma_cap_mask_t mask; -	unsigned sig; +	int				i; +	dma_cap_mask_t			mask; +	unsigned			sig; +	unsigned			oob_index;  	struct resource			*res;  	struct mtd_part_parser_data	ppdata = {}; -	pdata = pdev->dev.platform_data; +	pdata = dev_get_platdata(&pdev->dev);  	if (pdata == NULL) {  		dev_err(&pdev->dev, "platform data missing\n");  		return -ENODEV;  	} -	info = kzalloc(sizeof(struct omap_nand_info), GFP_KERNEL); +	info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), +				GFP_KERNEL);  	if (!info)  		return -ENOMEM; @@ -1846,47 +1653,45 @@ static int omap_nand_probe(struct platform_device *pdev)  	spin_lock_init(&info->controller.lock);  	init_waitqueue_head(&info->controller.wq); -	info->pdev = pdev; - +	info->pdev		= pdev;  	info->gpmc_cs		= pdata->cs;  	info->reg		= pdata->reg; - -	info->mtd.priv		= &info->nand; -	info->mtd.name		= dev_name(&pdev->dev); -	info->mtd.owner		= THIS_MODULE; - -	info->nand.options	= pdata->devsize; -	info->nand.options	|= NAND_SKIP_BBTSCAN; -#ifdef CONFIG_MTD_NAND_OMAP_BCH  	info->of_node		= pdata->of_node; -#endif +	mtd			= &info->mtd; +	mtd->priv		= &info->nand; +	mtd->name		= dev_name(&pdev->dev); +	mtd->owner		= THIS_MODULE; +	nand_chip		= &info->nand; +	nand_chip->ecc.priv	= NULL; +	nand_chip->options	|= NAND_SKIP_BBTSCAN;  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	if (res == NULL) {  		err = -EINVAL;  		dev_err(&pdev->dev, "error getting memory resource\n"); -		goto out_free_info; +		goto return_error;  	}  	info->phys_base = res->start;  	info->mem_size = resource_size(res); -	if (!request_mem_region(info->phys_base, info->mem_size, -				pdev->dev.driver->name)) { +	if (!devm_request_mem_region(&pdev->dev, info->phys_base, +				info->mem_size,	pdev->dev.driver->name)) {  		err = -EBUSY; -		goto out_free_info; +		goto return_error;  	} -	info->nand.IO_ADDR_R = ioremap(info->phys_base, info->mem_size); -	if (!info->nand.IO_ADDR_R) { +	nand_chip->IO_ADDR_R = devm_ioremap(&pdev->dev, info->phys_base, +						info->mem_size); +	if (!nand_chip->IO_ADDR_R) {  		err = -ENOMEM; -		goto out_release_mem_region; +		goto return_error;  	} -	info->nand.controller = &info->controller; +	nand_chip->controller = &info->controller; -	info->nand.IO_ADDR_W = info->nand.IO_ADDR_R; -	info->nand.cmd_ctrl  = omap_hwcontrol; +	nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R; +	nand_chip->cmd_ctrl  = omap_hwcontrol;  	/*  	 * If RDY/BSY line is connected to OMAP then use the omap ready @@ -1896,27 +1701,37 @@ static int omap_nand_probe(struct platform_device *pdev)  	 * device and read status register until you get a failure or success  	 */  	if (pdata->dev_ready) { -		info->nand.dev_ready = omap_dev_ready; -		info->nand.chip_delay = 0; +		nand_chip->dev_ready = omap_dev_ready; +		nand_chip->chip_delay = 0;  	} else { -		info->nand.waitfunc = omap_wait; -		info->nand.chip_delay = 50; +		nand_chip->waitfunc = omap_wait; +		nand_chip->chip_delay = 50;  	} +	/* scan NAND device connected to chip controller */ +	nand_chip->options |= pdata->devsize & NAND_BUSWIDTH_16; +	if (nand_scan_ident(mtd, 1, NULL)) { +		pr_err("nand device scan failed, may be bus-width mismatch\n"); +		err = -ENXIO; +		goto return_error; +	} + +	/* check for small page devices */ +	if ((mtd->oobsize < 64) && (pdata->ecc_opt != OMAP_ECC_HAM1_CODE_HW)) { +		pr_err("small page devices are not supported\n"); +		err = -EINVAL; +		goto return_error; +	} + +	/* re-populate low-level callbacks based on xfer modes */  	switch (pdata->xfer_type) {  	case NAND_OMAP_PREFETCH_POLLED: -		info->nand.read_buf   = omap_read_buf_pref; -		info->nand.write_buf  = omap_write_buf_pref; +		nand_chip->read_buf   = omap_read_buf_pref; +		nand_chip->write_buf  = omap_write_buf_pref;  		break;  	case NAND_OMAP_POLLED: -		if (info->nand.options & NAND_BUSWIDTH_16) { -			info->nand.read_buf   = omap_read_buf16; -			info->nand.write_buf  = omap_write_buf16; -		} else { -			info->nand.read_buf   = omap_read_buf8; -			info->nand.write_buf  = omap_write_buf8; -		} +		/* Use nand_base defaults for {read,write}_buf */  		break;  	case NAND_OMAP_PREFETCH_DMA: @@ -1927,7 +1742,7 @@ static int omap_nand_probe(struct platform_device *pdev)  		if (!info->dma) {  			dev_err(&pdev->dev, "DMA engine request failed\n");  			err = -ENXIO; -			goto out_release_mem_region; +			goto return_error;  		} else {  			struct dma_slave_config cfg; @@ -1942,10 +1757,10 @@ static int omap_nand_probe(struct platform_device *pdev)  			if (err) {  				dev_err(&pdev->dev, "DMA engine slave config failed: %d\n",  					err); -				goto out_release_mem_region; +				goto return_error;  			} -			info->nand.read_buf   = omap_read_buf_dma_pref; -			info->nand.write_buf  = omap_write_buf_dma_pref; +			nand_chip->read_buf   = omap_read_buf_dma_pref; +			nand_chip->write_buf  = omap_write_buf_dma_pref;  		}  		break; @@ -1954,34 +1769,36 @@ static int omap_nand_probe(struct platform_device *pdev)  		if (info->gpmc_irq_fifo <= 0) {  			dev_err(&pdev->dev, "error getting fifo irq\n");  			err = -ENODEV; -			goto out_release_mem_region; +			goto return_error;  		} -		err = request_irq(info->gpmc_irq_fifo,	omap_nand_irq, -					IRQF_SHARED, "gpmc-nand-fifo", info); +		err = devm_request_irq(&pdev->dev, info->gpmc_irq_fifo, +					omap_nand_irq, IRQF_SHARED, +					"gpmc-nand-fifo", info);  		if (err) {  			dev_err(&pdev->dev, "requesting irq(%d) error:%d",  						info->gpmc_irq_fifo, err);  			info->gpmc_irq_fifo = 0; -			goto out_release_mem_region; +			goto return_error;  		}  		info->gpmc_irq_count = platform_get_irq(pdev, 1);  		if (info->gpmc_irq_count <= 0) {  			dev_err(&pdev->dev, "error getting count irq\n");  			err = -ENODEV; -			goto out_release_mem_region; +			goto return_error;  		} -		err = request_irq(info->gpmc_irq_count,	omap_nand_irq, -					IRQF_SHARED, "gpmc-nand-count", info); +		err = devm_request_irq(&pdev->dev, info->gpmc_irq_count, +					omap_nand_irq, IRQF_SHARED, +					"gpmc-nand-count", info);  		if (err) {  			dev_err(&pdev->dev, "requesting irq(%d) error:%d",  						info->gpmc_irq_count, err);  			info->gpmc_irq_count = 0; -			goto out_release_mem_region; +			goto return_error;  		} -		info->nand.read_buf  = omap_read_buf_irq_pref; -		info->nand.write_buf = omap_write_buf_irq_pref; +		nand_chip->read_buf  = omap_read_buf_irq_pref; +		nand_chip->write_buf = omap_write_buf_irq_pref;  		break; @@ -1989,118 +1806,241 @@ static int omap_nand_probe(struct platform_device *pdev)  		dev_err(&pdev->dev,  			"xfer_type(%d) not supported!\n", pdata->xfer_type);  		err = -EINVAL; -		goto out_release_mem_region; +		goto return_error;  	} -	/* select the ecc type */ -	if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_DEFAULT) -		info->nand.ecc.mode = NAND_ECC_SOFT; -	else if ((pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW) || -		(pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE)) { -		info->nand.ecc.bytes            = 3; -		info->nand.ecc.size             = 512; -		info->nand.ecc.strength         = 1; -		info->nand.ecc.calculate        = omap_calculate_ecc; -		info->nand.ecc.hwctl            = omap_enable_hwecc; -		info->nand.ecc.correct          = omap_correct_data; -		info->nand.ecc.mode             = NAND_ECC_HW; -	} else if ((pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) || -		   (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW)) { -		err = omap3_init_bch(&info->mtd, pdata->ecc_opt); -		if (err) { +	/* populate MTD interface based on ECC scheme */ +	nand_chip->ecc.layout	= &omap_oobinfo; +	ecclayout		= &omap_oobinfo; +	switch (pdata->ecc_opt) { +	case OMAP_ECC_HAM1_CODE_HW: +		pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n"); +		nand_chip->ecc.mode             = NAND_ECC_HW; +		nand_chip->ecc.bytes            = 3; +		nand_chip->ecc.size             = 512; +		nand_chip->ecc.strength         = 1; +		nand_chip->ecc.calculate        = omap_calculate_ecc; +		nand_chip->ecc.hwctl            = omap_enable_hwecc; +		nand_chip->ecc.correct          = omap_correct_data; +		/* define ECC layout */ +		ecclayout->eccbytes		= nand_chip->ecc.bytes * +							(mtd->writesize / +							nand_chip->ecc.size); +		if (nand_chip->options & NAND_BUSWIDTH_16) +			oob_index		= BADBLOCK_MARKER_LENGTH; +		else +			oob_index		= 1; +		for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) +			ecclayout->eccpos[i]	= oob_index; +		/* no reserved-marker in ecclayout for this ecc-scheme */ +		ecclayout->oobfree->offset	= +				ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; +		break; + +	case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: +#ifdef CONFIG_MTD_NAND_ECC_BCH +		pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n"); +		nand_chip->ecc.mode		= NAND_ECC_HW; +		nand_chip->ecc.size		= 512; +		nand_chip->ecc.bytes		= 7; +		nand_chip->ecc.strength		= 4; +		nand_chip->ecc.hwctl		= omap3_enable_hwecc_bch; +		nand_chip->ecc.correct		= nand_bch_correct_data; +		nand_chip->ecc.calculate	= omap3_calculate_ecc_bch4; +		/* define ECC layout */ +		ecclayout->eccbytes		= nand_chip->ecc.bytes * +							(mtd->writesize / +							nand_chip->ecc.size); +		oob_index			= BADBLOCK_MARKER_LENGTH; +		for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) { +			ecclayout->eccpos[i] = oob_index; +			if (((i + 1) % nand_chip->ecc.bytes) == 0) +				oob_index++; +		} +		/* include reserved-marker in ecclayout->oobfree calculation */ +		ecclayout->oobfree->offset	= 1 + +				ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; +		/* software bch library is used for locating errors */ +		nand_chip->ecc.priv		= nand_bch_init(mtd, +							nand_chip->ecc.size, +							nand_chip->ecc.bytes, +							&nand_chip->ecc.layout); +		if (!nand_chip->ecc.priv) { +			pr_err("nand: error: unable to use s/w BCH library\n");  			err = -EINVAL; -			goto out_release_mem_region;  		} -	} +		break; +#else +		pr_err("nand: error: CONFIG_MTD_NAND_ECC_BCH not enabled\n"); +		err = -EINVAL; +		goto return_error; +#endif -	/* DIP switches on some boards change between 8 and 16 bit -	 * bus widths for flash.  Try the other width if the first try fails. -	 */ -	if (nand_scan_ident(&info->mtd, 1, NULL)) { -		info->nand.options ^= NAND_BUSWIDTH_16; -		if (nand_scan_ident(&info->mtd, 1, NULL)) { -			err = -ENXIO; -			goto out_release_mem_region; +	case OMAP_ECC_BCH4_CODE_HW: +#ifdef CONFIG_MTD_NAND_OMAP_BCH +		pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n"); +		nand_chip->ecc.mode		= NAND_ECC_HW; +		nand_chip->ecc.size		= 512; +		/* 14th bit is kept reserved for ROM-code compatibility */ +		nand_chip->ecc.bytes		= 7 + 1; +		nand_chip->ecc.strength		= 4; +		nand_chip->ecc.hwctl		= omap3_enable_hwecc_bch; +		nand_chip->ecc.correct		= omap_elm_correct_data; +		nand_chip->ecc.calculate	= omap3_calculate_ecc_bch; +		nand_chip->ecc.read_page	= omap_read_page_bch; +		nand_chip->ecc.write_page	= omap_write_page_bch; +		/* define ECC layout */ +		ecclayout->eccbytes		= nand_chip->ecc.bytes * +							(mtd->writesize / +							nand_chip->ecc.size); +		oob_index			= BADBLOCK_MARKER_LENGTH; +		for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) +			ecclayout->eccpos[i]	= oob_index; +		/* reserved marker already included in ecclayout->eccbytes */ +		ecclayout->oobfree->offset	= +				ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; +		/* This ECC scheme requires ELM H/W block */ +		if (is_elm_present(info, pdata->elm_of_node, BCH4_ECC) < 0) { +			pr_err("nand: error: could not initialize ELM\n"); +			err = -ENODEV; +			goto return_error;  		} -	} +		break; +#else +		pr_err("nand: error: CONFIG_MTD_NAND_OMAP_BCH not enabled\n"); +		err = -EINVAL; +		goto return_error; +#endif -	/* rom code layout */ -	if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE) { +	case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: +#ifdef CONFIG_MTD_NAND_ECC_BCH +		pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n"); +		nand_chip->ecc.mode		= NAND_ECC_HW; +		nand_chip->ecc.size		= 512; +		nand_chip->ecc.bytes		= 13; +		nand_chip->ecc.strength		= 8; +		nand_chip->ecc.hwctl		= omap3_enable_hwecc_bch; +		nand_chip->ecc.correct		= nand_bch_correct_data; +		nand_chip->ecc.calculate	= omap3_calculate_ecc_bch8; +		/* define ECC layout */ +		ecclayout->eccbytes		= nand_chip->ecc.bytes * +							(mtd->writesize / +							nand_chip->ecc.size); +		oob_index			= BADBLOCK_MARKER_LENGTH; +		for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) { +			ecclayout->eccpos[i] = oob_index; +			if (((i + 1) % nand_chip->ecc.bytes) == 0) +				oob_index++; +		} +		/* include reserved-marker in ecclayout->oobfree calculation */ +		ecclayout->oobfree->offset	= 1 + +				ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; +		/* software bch library is used for locating errors */ +		nand_chip->ecc.priv		= nand_bch_init(mtd, +							nand_chip->ecc.size, +							nand_chip->ecc.bytes, +							&nand_chip->ecc.layout); +		if (!nand_chip->ecc.priv) { +			pr_err("nand: error: unable to use s/w BCH library\n"); +			err = -EINVAL; +			goto return_error; +		} +		break; +#else +		pr_err("nand: error: CONFIG_MTD_NAND_ECC_BCH not enabled\n"); +		err = -EINVAL; +		goto return_error; +#endif -		if (info->nand.options & NAND_BUSWIDTH_16) -			offset = 2; -		else { -			offset = 1; -			info->nand.badblock_pattern = &bb_descrip_flashbased; +	case OMAP_ECC_BCH8_CODE_HW: +#ifdef CONFIG_MTD_NAND_OMAP_BCH +		pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n"); +		nand_chip->ecc.mode		= NAND_ECC_HW; +		nand_chip->ecc.size		= 512; +		/* 14th bit is kept reserved for ROM-code compatibility */ +		nand_chip->ecc.bytes		= 13 + 1; +		nand_chip->ecc.strength		= 8; +		nand_chip->ecc.hwctl		= omap3_enable_hwecc_bch; +		nand_chip->ecc.correct		= omap_elm_correct_data; +		nand_chip->ecc.calculate	= omap3_calculate_ecc_bch; +		nand_chip->ecc.read_page	= omap_read_page_bch; +		nand_chip->ecc.write_page	= omap_write_page_bch; +		/* This ECC scheme requires ELM H/W block */ +		err = is_elm_present(info, pdata->elm_of_node, BCH8_ECC); +		if (err < 0) { +			pr_err("nand: error: could not initialize ELM\n"); +			goto return_error;  		} -		omap_oobinfo.eccbytes = 3 * (info->mtd.oobsize/16); -		for (i = 0; i < omap_oobinfo.eccbytes; i++) -			omap_oobinfo.eccpos[i] = i+offset; +		/* define ECC layout */ +		ecclayout->eccbytes		= nand_chip->ecc.bytes * +							(mtd->writesize / +							nand_chip->ecc.size); +		oob_index			= BADBLOCK_MARKER_LENGTH; +		for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) +			ecclayout->eccpos[i]	= oob_index; +		/* reserved marker already included in ecclayout->eccbytes */ +		ecclayout->oobfree->offset	= +				ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; +		break; +#else +		pr_err("nand: error: CONFIG_MTD_NAND_OMAP_BCH not enabled\n"); +		err = -EINVAL; +		goto return_error; +#endif -		omap_oobinfo.oobfree->offset = offset + omap_oobinfo.eccbytes; -		omap_oobinfo.oobfree->length = info->mtd.oobsize - -					(offset + omap_oobinfo.eccbytes); +	default: +		pr_err("nand: error: invalid or unsupported ECC scheme\n"); +		err = -EINVAL; +		goto return_error; +	} -		info->nand.ecc.layout = &omap_oobinfo; -	} else if ((pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) || -		   (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW)) { -		/* build OOB layout for BCH ECC correction */ -		err = omap3_init_bch_tail(&info->mtd); -		if (err) { -			err = -EINVAL; -			goto out_release_mem_region; -		} +	/* all OOB bytes from oobfree->offset till end off OOB are free */ +	ecclayout->oobfree->length = mtd->oobsize - ecclayout->oobfree->offset; +	/* check if NAND device's OOB is enough to store ECC signatures */ +	if (mtd->oobsize < (ecclayout->eccbytes + BADBLOCK_MARKER_LENGTH)) { +		pr_err("not enough OOB bytes required = %d, available=%d\n", +					   ecclayout->eccbytes, mtd->oobsize); +		err = -EINVAL; +		goto return_error;  	}  	/* second phase scan */ -	if (nand_scan_tail(&info->mtd)) { +	if (nand_scan_tail(mtd)) {  		err = -ENXIO; -		goto out_release_mem_region; +		goto return_error;  	}  	ppdata.of_node = pdata->of_node; -	mtd_device_parse_register(&info->mtd, NULL, &ppdata, pdata->parts, +	mtd_device_parse_register(mtd, NULL, &ppdata, pdata->parts,  				  pdata->nr_parts); -	platform_set_drvdata(pdev, &info->mtd); +	platform_set_drvdata(pdev, mtd);  	return 0; -out_release_mem_region: +return_error:  	if (info->dma)  		dma_release_channel(info->dma); -	if (info->gpmc_irq_count > 0) -		free_irq(info->gpmc_irq_count, info); -	if (info->gpmc_irq_fifo > 0) -		free_irq(info->gpmc_irq_fifo, info); -	release_mem_region(info->phys_base, info->mem_size); -out_free_info: -	kfree(info); - +	if (nand_chip->ecc.priv) { +		nand_bch_free(nand_chip->ecc.priv); +		nand_chip->ecc.priv = NULL; +	}  	return err;  }  static int omap_nand_remove(struct platform_device *pdev)  {  	struct mtd_info *mtd = platform_get_drvdata(pdev); +	struct nand_chip *nand_chip = mtd->priv;  	struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,  							mtd); -	omap3_free_bch(&info->mtd); - -	platform_set_drvdata(pdev, NULL); +	if (nand_chip->ecc.priv) { +		nand_bch_free(nand_chip->ecc.priv); +		nand_chip->ecc.priv = NULL; +	}  	if (info->dma)  		dma_release_channel(info->dma); - -	if (info->gpmc_irq_count > 0) -		free_irq(info->gpmc_irq_count, info); -	if (info->gpmc_irq_fifo > 0) -		free_irq(info->gpmc_irq_fifo, info); - -	/* Release NAND device, its internal structures and partitions */ -	nand_release(&info->mtd); -	iounmap(info->nand.IO_ADDR_R); -	release_mem_region(info->phys_base, info->mem_size); -	kfree(info); +	nand_release(mtd);  	return 0;  } diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index 8fbd0020861..834cb3541b8 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -130,8 +130,9 @@ static int __init orion_nand_probe(struct platform_device *pdev)  		if (!of_property_read_u32(pdev->dev.of_node,  						"chip-delay", &val))  			board->chip_delay = (u8)val; -	} else -		board = pdev->dev.platform_data; +	} else { +		board = dev_get_platdata(&pdev->dev); +	}  	mtd->priv = nc;  	mtd->owner = THIS_MODULE; diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index c004566a9ad..08766d1ea44 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -30,7 +30,7 @@ static const char *part_probe_types[] = { "cmdlinepart", NULL };   */  static int plat_nand_probe(struct platform_device *pdev)  { -	struct platform_nand_data *pdata = pdev->dev.platform_data; +	struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev);  	struct mtd_part_parser_data ppdata;  	struct plat_nand_data *data;  	struct resource *res; @@ -137,7 +137,7 @@ out_free:  static int plat_nand_remove(struct platform_device *pdev)  {  	struct plat_nand_data *data = platform_get_drvdata(pdev); -	struct platform_nand_data *pdata = pdev->dev.platform_data; +	struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev);  	struct resource *res;  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index dec80ca6a5c..501e380211e 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -506,6 +506,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,  	info->buf_count		= 0;  	info->oob_size		= 0;  	info->use_ecc		= 0; +	info->use_dma		= (use_dma) ? 1 : 0;  	info->is_ready		= 0;  	info->retcode		= ERR_NONE;  	if (info->cs != 0) @@ -589,12 +590,23 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,  				| addr_cycle;  		break; +	case NAND_CMD_PARAM: +		cmd = NAND_CMD_PARAM; +		info->buf_count = 256; +		info->ndcb0 |= NDCB0_CMD_TYPE(0) +				| NDCB0_ADDR_CYC(1) +				| cmd; +		info->ndcb1 = (column & 0xFF); +		info->data_size = 256; +		break; +  	case NAND_CMD_READID:  		cmd = host->cmdset->read_id;  		info->buf_count = host->read_id_bytes;  		info->ndcb0 |= NDCB0_CMD_TYPE(3)  				| NDCB0_ADDR_CYC(1)  				| cmd; +		info->ndcb1 = (column & 0xFF);  		info->data_size = 8;  		break; @@ -803,7 +815,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,  				    const struct pxa3xx_nand_flash *f)  {  	struct platform_device *pdev = info->pdev; -	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data; +	struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);  	struct pxa3xx_nand_host *host = info->host[info->cs];  	uint32_t ndcr = 0x0; /* enable all interrupts */ @@ -912,6 +924,18 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)  	return 0;  } +static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info) +{ +	struct platform_device *pdev = info->pdev; +	if (use_dma) { +		pxa_free_dma(info->data_dma_ch); +		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE, +				  info->data_buff, info->data_buff_phys); +	} else { +		kfree(info->data_buff); +	} +} +  static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)  {  	struct mtd_info *mtd; @@ -934,7 +958,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)  	struct pxa3xx_nand_host *host = mtd->priv;  	struct pxa3xx_nand_info *info = host->info_data;  	struct platform_device *pdev = info->pdev; -	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data; +	struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);  	struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;  	const struct pxa3xx_nand_flash *f = NULL;  	struct nand_chip *chip = mtd->priv; @@ -1034,13 +1058,11 @@ static int alloc_nand_resource(struct platform_device *pdev)  	struct resource *r;  	int ret, irq, cs; -	pdata = pdev->dev.platform_data; -	info = kzalloc(sizeof(*info) + (sizeof(*mtd) + -		       sizeof(*host)) * pdata->num_cs, GFP_KERNEL); -	if (!info) { -		dev_err(&pdev->dev, "failed to allocate memory\n"); +	pdata = dev_get_platdata(&pdev->dev); +	info = devm_kzalloc(&pdev->dev, sizeof(*info) + (sizeof(*mtd) + +			    sizeof(*host)) * pdata->num_cs, GFP_KERNEL); +	if (!info)  		return -ENOMEM; -	}  	info->pdev = pdev;  	for (cs = 0; cs < pdata->num_cs; cs++) { @@ -1069,20 +1091,21 @@ static int alloc_nand_resource(struct platform_device *pdev)  	spin_lock_init(&chip->controller->lock);  	init_waitqueue_head(&chip->controller->wq); -	info->clk = clk_get(&pdev->dev, NULL); +	info->clk = devm_clk_get(&pdev->dev, NULL);  	if (IS_ERR(info->clk)) {  		dev_err(&pdev->dev, "failed to get nand clock\n"); -		ret = PTR_ERR(info->clk); -		goto fail_free_mtd; +		return PTR_ERR(info->clk);  	} -	clk_enable(info->clk); +	ret = clk_prepare_enable(info->clk); +	if (ret < 0) +		return ret;  	/*  	 * This is a dirty hack to make this driver work from devicetree  	 * bindings. It can be removed once we have a prober DMA controller  	 * framework for DT.  	 */ -	if (pdev->dev.of_node && cpu_is_pxa3xx()) { +	if (pdev->dev.of_node && of_machine_is_compatible("marvell,pxa3xx")) {  		info->drcmr_dat = 97;  		info->drcmr_cmd = 99;  	} else { @@ -1090,7 +1113,7 @@ static int alloc_nand_resource(struct platform_device *pdev)  		if (r == NULL) {  			dev_err(&pdev->dev, "no resource defined for data DMA\n");  			ret = -ENXIO; -			goto fail_put_clk; +			goto fail_disable_clk;  		}  		info->drcmr_dat = r->start; @@ -1098,7 +1121,7 @@ static int alloc_nand_resource(struct platform_device *pdev)  		if (r == NULL) {  			dev_err(&pdev->dev, "no resource defined for command DMA\n");  			ret = -ENXIO; -			goto fail_put_clk; +			goto fail_disable_clk;  		}  		info->drcmr_cmd = r->start;  	} @@ -1107,34 +1130,20 @@ static int alloc_nand_resource(struct platform_device *pdev)  	if (irq < 0) {  		dev_err(&pdev->dev, "no IRQ resource defined\n");  		ret = -ENXIO; -		goto fail_put_clk; +		goto fail_disable_clk;  	}  	r = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (r == NULL) { -		dev_err(&pdev->dev, "no IO memory resource defined\n"); -		ret = -ENODEV; -		goto fail_put_clk; -	} - -	r = request_mem_region(r->start, resource_size(r), pdev->name); -	if (r == NULL) { -		dev_err(&pdev->dev, "failed to request memory resource\n"); -		ret = -EBUSY; -		goto fail_put_clk; -	} - -	info->mmio_base = ioremap(r->start, resource_size(r)); -	if (info->mmio_base == NULL) { -		dev_err(&pdev->dev, "ioremap() failed\n"); -		ret = -ENODEV; -		goto fail_free_res; +	info->mmio_base = devm_ioremap_resource(&pdev->dev, r); +	if (IS_ERR(info->mmio_base)) { +		ret = PTR_ERR(info->mmio_base); +		goto fail_disable_clk;  	}  	info->mmio_phys = r->start;  	ret = pxa3xx_nand_init_buff(info);  	if (ret) -		goto fail_free_io; +		goto fail_disable_clk;  	/* initialize all interrupts to be disabled */  	disable_int(info, NDSR_MASK); @@ -1152,21 +1161,9 @@ static int alloc_nand_resource(struct platform_device *pdev)  fail_free_buf:  	free_irq(irq, info); -	if (use_dma) { -		pxa_free_dma(info->data_dma_ch); -		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE, -			info->data_buff, info->data_buff_phys); -	} else -		kfree(info->data_buff); -fail_free_io: -	iounmap(info->mmio_base); -fail_free_res: -	release_mem_region(r->start, resource_size(r)); -fail_put_clk: -	clk_disable(info->clk); -	clk_put(info->clk); -fail_free_mtd: -	kfree(info); +	pxa3xx_nand_free_buff(info); +fail_disable_clk: +	clk_disable_unprepare(info->clk);  	return ret;  } @@ -1174,35 +1171,22 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)  {  	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);  	struct pxa3xx_nand_platform_data *pdata; -	struct resource *r;  	int irq, cs;  	if (!info)  		return 0; -	pdata = pdev->dev.platform_data; -	platform_set_drvdata(pdev, NULL); +	pdata = dev_get_platdata(&pdev->dev);  	irq = platform_get_irq(pdev, 0);  	if (irq >= 0)  		free_irq(irq, info); -	if (use_dma) { -		pxa_free_dma(info->data_dma_ch); -		dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE, -				info->data_buff, info->data_buff_phys); -	} else -		kfree(info->data_buff); - -	iounmap(info->mmio_base); -	r = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	release_mem_region(r->start, resource_size(r)); +	pxa3xx_nand_free_buff(info); -	clk_disable(info->clk); -	clk_put(info->clk); +	clk_disable_unprepare(info->clk);  	for (cs = 0; cs < pdata->num_cs; cs++)  		nand_release(info->host[cs]->mtd); -	kfree(info);  	return 0;  } @@ -1211,7 +1195,7 @@ static struct of_device_id pxa3xx_nand_dt_ids[] = {  	{ .compatible = "marvell,pxa3xx-nand" },  	{}  }; -MODULE_DEVICE_TABLE(of, i2c_pxa_dt_ids); +MODULE_DEVICE_TABLE(of, pxa3xx_nand_dt_ids);  static int pxa3xx_nand_probe_dt(struct platform_device *pdev)  { @@ -1255,7 +1239,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)  	if (ret)  		return ret; -	pdata = pdev->dev.platform_data; +	pdata = dev_get_platdata(&pdev->dev);  	if (!pdata) {  		dev_err(&pdev->dev, "no platform data defined\n");  		return -ENODEV; @@ -1302,7 +1286,7 @@ static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)  	struct mtd_info *mtd;  	int cs; -	pdata = pdev->dev.platform_data; +	pdata = dev_get_platdata(&pdev->dev);  	if (info->state) {  		dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);  		return -EAGAIN; @@ -1323,7 +1307,7 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)  	struct mtd_info *mtd;  	int cs; -	pdata = pdev->dev.platform_data; +	pdata = dev_get_platdata(&pdev->dev);  	/* We don't want to handle interrupt without calling mtd routine */  	disable_int(info, NDCR_INT_MASK); diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index d65afd23e17..b707d0f8247 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -150,7 +150,7 @@ static struct s3c2410_nand_info *to_nand_info(struct platform_device *dev)  static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev)  { -	return dev->dev.platform_data; +	return dev_get_platdata(&dev->dev);  }  static inline int allow_clk_suspend(struct s3c2410_nand_info *info) diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index e57e18e8c28..a3c84ebbe39 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -137,7 +137,7 @@ static void flctl_setup_dma(struct sh_flctl *flctl)  	dma_cap_mask_t mask;  	struct dma_slave_config cfg;  	struct platform_device *pdev = flctl->pdev; -	struct sh_flctl_platform_data *pdata = pdev->dev.platform_data; +	struct sh_flctl_platform_data *pdata = dev_get_platdata(&pdev->dev);  	int ret;  	if (!pdata) @@ -1131,7 +1131,7 @@ static int flctl_probe(struct platform_device *pdev)  	if (pdev->dev.of_node)  		pdata = flctl_parse_dt(&pdev->dev);  	else -		pdata = pdev->dev.platform_data; +		pdata = dev_get_platdata(&pdev->dev);  	if (!pdata) {  		dev_err(&pdev->dev, "no setup data defined\n"); diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 127bc427182..26b27201520 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -112,7 +112,7 @@ static int sharpsl_nand_probe(struct platform_device *pdev)  	struct resource *r;  	int err = 0;  	struct sharpsl_nand *sharpsl; -	struct sharpsl_nand_platform_data *data = pdev->dev.platform_data; +	struct sharpsl_nand_platform_data *data = dev_get_platdata(&pdev->dev);  	if (!data) {  		dev_err(&pdev->dev, "no platform data!\n"); diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index 508e9e04b09..396530d87ec 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -357,7 +357,7 @@ static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio)  static int tmio_probe(struct platform_device *dev)  { -	struct tmio_nand_data *data = dev->dev.platform_data; +	struct tmio_nand_data *data = dev_get_platdata(&dev->dev);  	struct resource *fcr = platform_get_resource(dev,  			IORESOURCE_MEM, 0);  	struct resource *ccr = platform_get_resource(dev, diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 7ed654c68b0..388b13a6d7e 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -87,7 +87,7 @@ static struct platform_device *mtd_to_platdev(struct mtd_info *mtd)  static void __iomem *ndregaddr(struct platform_device *dev, unsigned int reg)  {  	struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); -	struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; +	struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);  	return drvdata->base + (reg << plat->shift);  } @@ -138,7 +138,7 @@ static void txx9ndfmc_cmd_ctrl(struct mtd_info *mtd, int cmd,  	struct nand_chip *chip = mtd->priv;  	struct txx9ndfmc_priv *txx9_priv = chip->priv;  	struct platform_device *dev = txx9_priv->dev; -	struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; +	struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);  	if (ctrl & NAND_CTRL_CHANGE) {  		u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); @@ -225,7 +225,7 @@ static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)  static void txx9ndfmc_initialize(struct platform_device *dev)  { -	struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; +	struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);  	struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);  	int tmout = 100; @@ -274,7 +274,7 @@ static int txx9ndfmc_nand_scan(struct mtd_info *mtd)  static int __init txx9ndfmc_probe(struct platform_device *dev)  { -	struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; +	struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);  	int hold, spw;  	int i;  	struct txx9ndfmc_drvdata *drvdata; diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 66fe3b7e785..08d0085f3e9 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -133,7 +133,6 @@ static inline int onenand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_desc  {  	struct onenand_chip *this = mtd->priv; -        bd->options &= ~NAND_BBT_SCANEMPTY;  	return create_bbt(mtd, this->page_buf, bd, -1);  } diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 26037ca7efb..7bed61e6229 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -22,7 +22,10 @@   * Datasheets:   * http://focus.ti.com/docs/prod/folders/print/bq27000.html   * http://focus.ti.com/docs/prod/folders/print/bq27500.html + * http://www.ti.com/product/bq27411-g1 + * http://www.ti.com/product/bq27421-g1   * http://www.ti.com/product/bq27425-g1 + * http://www.ti.com/product/bq27441-g1   */  #include <linux/module.h> @@ -41,48 +44,215 @@  #define DRIVER_VERSION			"1.2.0" -#define BQ27x00_REG_TEMP		0x06 -#define BQ27x00_REG_VOLT		0x08 -#define BQ27x00_REG_AI			0x14 -#define BQ27x00_REG_FLAGS		0x0A -#define BQ27x00_REG_TTE			0x16 -#define BQ27x00_REG_TTF			0x18 -#define BQ27x00_REG_TTECP		0x26 -#define BQ27x00_REG_NAC			0x0C /* Nominal available capacity */ -#define BQ27x00_REG_LMD			0x12 /* Last measured discharge */ -#define BQ27x00_REG_CYCT		0x2A /* Cycle count total */ -#define BQ27x00_REG_AE			0x22 /* Available energy */ -#define BQ27x00_POWER_AVG		0x24 +#define INVALID_REG_ADDR		0xFF -#define BQ27000_REG_RSOC		0x0B /* Relative State-of-Charge */ -#define BQ27000_REG_ILMD		0x76 /* Initial last measured discharge */ -#define BQ27000_FLAG_EDVF		BIT(0) /* Final End-of-Discharge-Voltage flag */ -#define BQ27000_FLAG_EDV1		BIT(1) /* First End-of-Discharge-Voltage flag */ -#define BQ27000_FLAG_CI			BIT(4) /* Capacity Inaccurate flag */ -#define BQ27000_FLAG_FC			BIT(5) -#define BQ27000_FLAG_CHGS		BIT(7) /* Charge state flag */ +enum bq27xxx_reg_index { +	BQ27XXX_REG_CTRL = 0, +	BQ27XXX_REG_TEMP, +	BQ27XXX_REG_INT_TEMP, +	BQ27XXX_REG_VOLT, +	BQ27XXX_REG_AI, +	BQ27XXX_REG_FLAGS, +	BQ27XXX_REG_TTE, +	BQ27XXX_REG_TTF, +	BQ27XXX_REG_TTES, +	BQ27XXX_REG_TTECP, +	BQ27XXX_REG_NAC, +	BQ27XXX_REG_FCC, +	BQ27XXX_REG_CYCT, +	BQ27XXX_REG_AE, +	BQ27XXX_REG_SOC, +	BQ27XXX_REG_DCAP, +	BQ27XXX_POWER_AVG, +	NUM_REGS +}; + +/* bq27500 registers */ +static __initdata u8 bq27500_regs[NUM_REGS] = { +	0x00,	/* CONTROL	*/ +	0x06,	/* TEMP		*/ +	0xFF,	/* INT TEMP -NA	*/ +	0x08,	/* VOLT		*/ +	0x14,	/* AVG CURR	*/ +	0x0A,	/* FLAGS	*/ +	0x16,	/* TTE		*/ +	0x18,	/* TTF		*/ +	0x1c,	/* TTES		*/ +	0x26,	/* TTECP	*/ +	0x0C,	/* NAC		*/ +	0x12,	/* LMD(FCC)	*/ +	0x2A,	/* CYCT		*/ +	0x22,	/* AE		*/ +	0x2C,	/* SOC(RSOC)	*/ +	0x3C,	/* DCAP(ILMD)	*/ +	0x24,	/* AP		*/ +}; + +/* bq27520 registers */ +static __initdata u8 bq27520_regs[] = { +	0x00,	/* CONTROL	*/ +	0x06,	/* TEMP		*/ +	0xFF,	/* INT TEMP - NA*/ +	0x08,	/* VOLT		*/ +	0x14,	/* AVG CURR	*/ +	0x0A,	/* FLAGS	*/ +	0x16,	/* TTE		*/ +	0x18,	/* TTF		*/ +	0x1c,	/* TTES		*/ +	0x26,	/* TTECP	*/ +	0x0C,	/* NAC		*/ +	0x12,	/* LMD		*/ +	0xFF,	/* CYCT - NA	*/ +	0x22,	/* AE		*/ +	0x2C,	/* SOC(RSOC	*/ +	0xFF,	/* DCAP(ILMD) - NA */ +	0x24,	/* AP		*/ +}; + +/* bq2753x registers */ +static __initdata u8 bq2753x_regs[] = { +	0x00,	/* CONTROL	*/ +	0x06,	/* TEMP		*/ +	0xFF,	/* INT TEMP - NA*/ +	0x08,	/* VOLT		*/ +	0x14,	/* AVG CURR	*/ +	0x0A,	/* FLAGS	*/ +	0x16,	/* TTE		*/ +	0xFF,	/* TTF - NA	*/ +	0xFF,	/* TTES - NA	*/ +	0xFF,	/* TTECP - NA	*/ +	0x0C,	/* NAC		*/ +	0x12,	/* LMD(FCC)	*/ +	0x2A,	/* CYCT		*/ +	0xFF,	/* AE - NA	*/ +	0x2C,	/* SOC(RSOC)	*/ +	0xFF,	/* DCAP(ILMD) - NA */ +	0x24,	/* AP		*/ +}; + +/* bq27200 registers */ +static __initdata u8 bq27200_regs[NUM_REGS] = { +	0x00,	/* CONTROL	*/ +	0x06,	/* TEMP		*/ +	0xFF,	/* INT TEMP - NA	*/ +	0x08,	/* VOLT		*/ +	0x14,	/* AVG CURR	*/ +	0x0A,	/* FLAGS	*/ +	0x16,	/* TTE		*/ +	0x18,	/* TTF		*/ +	0x1c,	/* TTES		*/ +	0x26,	/* TTECP	*/ +	0x0C,	/* NAC		*/ +	0x12,	/* LMD(FCC)	*/ +	0x2A,	/* CYCT		*/ +	0x22,	/* AE		*/ +	0x0B,	/* SOC(RSOC)	*/ +	0x76,	/* DCAP(ILMD)	*/ +	0x24,	/* AP		*/ +}; + +/* bq274xx registers */ +static __initdata u8 bq274xx_regs[NUM_REGS] = { +	0x00,	/* CONTROL	*/ +	0x02,	/* TEMP		*/ +	0x1e,	/* INT TEMP	*/ +	0x04,	/* VOLT		*/ +	0x10,	/* AVG CURR	*/ +	0x06,	/* FLAGS	*/ +	0xFF,	/* TTE - NA	*/ +	0xFF,	/* TTF - NA	*/ +	0xFF,	/* TTES - NA	*/ +	0xFF,	/* TTECP - NA	*/ +	0x08,	/* NAC		*/ +	0x0E,	/* FCC		*/ +	0xFF,	/* CYCT - NA	*/ +	0xFF,	/* AE - NA	*/ +	0x1C,	/* SOC		*/ +	0x3C,	/* DCAP - NA	*/ +	0x18,	/* AP		*/ +}; + +/* bq276xx registers - same as bq274xx except CYCT */ +static __initdata u8 bq276xx_regs[NUM_REGS] = { +	0x00,	/* CONTROL	*/ +	0x02,	/* TEMP		*/ +	0x1e,	/* INT TEMP	*/ +	0x04,	/* VOLT		*/ +	0x10,	/* AVG CURR	*/ +	0x06,	/* FLAGS	*/ +	0xFF,	/* TTE - NA	*/ +	0xFF,	/* TTF - NA	*/ +	0xFF,	/* TTES - NA	*/ +	0xFF,	/* TTECP - NA	*/ +	0x08,	/* NAC		*/ +	0x0E,	/* FCC		*/ +	0x22,	/* CYCT		*/ +	0xFF,	/* AE - NA	*/ +	0x1C,	/* SOC		*/ +	0x3C,	/* DCAP - NA	*/ +	0x18,	/* AP		*/ +}; + +/* + * SBS Commands for DF access - these are pretty standard + * So, no need to go in the command array + */ +#define BLOCK_DATA_CLASS		0x3E +#define DATA_BLOCK			0x3F +#define BLOCK_DATA			0x40 +#define BLOCK_DATA_CHECKSUM		0x60 +#define BLOCK_DATA_CONTROL		0x61 + +/* bq274xx/bq276xx specific command information */ +#define BQ274XX_UNSEAL_KEY		0x80008000 +#define BQ274XX_SOFT_RESET		0x43 + +#define BQ274XX_FLAG_ITPOR				0x20 +#define BQ274XX_CTRL_STATUS_INITCOMP	0x80 -#define BQ27500_REG_SOC			0x2C -#define BQ27500_REG_DCAP		0x3C /* Design capacity */ -#define BQ27500_FLAG_DSC		BIT(0) -#define BQ27500_FLAG_SOCF		BIT(1) /* State-of-Charge threshold final */ -#define BQ27500_FLAG_SOC1		BIT(2) /* State-of-Charge threshold 1 */ -#define BQ27500_FLAG_FC			BIT(9) -#define BQ27500_FLAG_OTC		BIT(15) +#define BQ27XXX_FLAG_DSC		BIT(0) +#define BQ27XXX_FLAG_SOCF		BIT(1) /* State-of-Charge threshold final */ +#define BQ27XXX_FLAG_SOC1		BIT(2) /* State-of-Charge threshold 1 */ +#define BQ27XXX_FLAG_FC			BIT(9) +#define BQ27XXX_FLAG_OTD		BIT(14) +#define BQ27XXX_FLAG_OTC		BIT(15) -/* bq27425 register addresses are same as bq27x00 addresses minus 4 */ -#define BQ27425_REG_OFFSET		0x04 -#define BQ27425_REG_SOC			0x18 /* Register address plus offset */ +/* BQ27000 has different layout for Flags register */ +#define BQ27200_FLAG_EDVF		BIT(0) /* Final End-of-Discharge-Voltage flag */ +#define BQ27200_FLAG_EDV1		BIT(1) /* First End-of-Discharge-Voltage flag */ +#define BQ27200_FLAG_CI			BIT(4) /* Capacity Inaccurate flag */ +#define BQ27200_FLAG_FC			BIT(5) +#define BQ27200_FLAG_CHGS		BIT(7) /* Charge state flag */ -#define BQ27000_RS			20 /* Resistor sense */ -#define BQ27x00_POWER_CONSTANT		(256 * 29200 / 1000) +#define BQ27200_RS			20 /* Resistor sense */ +#define BQ27200_POWER_CONSTANT		(256 * 29200 / 1000) + +/* Subcommands of Control() */ +#define CONTROL_STATUS_SUBCMD		0x0000 +#define DEV_TYPE_SUBCMD			0x0001 +#define FW_VER_SUBCMD			0x0002 +#define DF_VER_SUBCMD			0x001F +#define RESET_SUBCMD			0x0041 +#define SET_CFGUPDATE_SUBCMD		0x0013 +#define SEAL_SUBCMD			0x0020 + +/* Location of SEAL enable bit in bq276xx DM */ +#define BQ276XX_OP_CFG_B_SUBCLASS	64 +#define BQ276XX_OP_CFG_B_OFFSET		2 +#define BQ276XX_OP_CFG_B_DEF_SEAL_BIT	(1 << 5)  struct bq27x00_device_info;  struct bq27x00_access_methods {  	int (*read)(struct bq27x00_device_info *di, u8 reg, bool single); +	int (*write)(struct bq27x00_device_info *di, u8 reg, int value, +			bool single); +	int (*blk_read)(struct bq27x00_device_info *di, u8 reg, u8 *data, +		u8 sz); +	int (*blk_write)(struct bq27x00_device_info *di, u8 reg, u8 *data, +		u8 sz);  }; -enum bq27x00_chip { BQ27000, BQ27500, BQ27425}; +enum bq27x00_chip { BQ27200, BQ27500, BQ27520, BQ274XX, BQ276XX, BQ2753X};  struct bq27x00_reg_cache {  	int temperature; @@ -98,6 +268,13 @@ struct bq27x00_reg_cache {  	int health;  }; +struct dm_reg { +	u8 subclass; +	u8 offset; +	u8 len; +	u32 data; +}; +  struct bq27x00_device_info {  	struct device 		*dev;  	int			id; @@ -114,9 +291,15 @@ struct bq27x00_device_info {  	struct bq27x00_access_methods bus;  	struct mutex lock; + +	int fw_ver; +	int df_ver; +	u8 regs[NUM_REGS]; +	struct dm_reg *dm_regs; +	u16 dm_regs_count;  }; -static enum power_supply_property bq27x00_battery_props[] = { +static __initdata enum power_supply_property bq27x00_battery_props[] = {  	POWER_SUPPLY_PROP_STATUS,  	POWER_SUPPLY_PROP_PRESENT,  	POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -137,7 +320,57 @@ static enum power_supply_property bq27x00_battery_props[] = {  	POWER_SUPPLY_PROP_HEALTH,  }; -static enum power_supply_property bq27425_battery_props[] = { +static __initdata enum power_supply_property bq27520_battery_props[] = { +	POWER_SUPPLY_PROP_STATUS, +	POWER_SUPPLY_PROP_PRESENT, +	POWER_SUPPLY_PROP_VOLTAGE_NOW, +	POWER_SUPPLY_PROP_CURRENT_NOW, +	POWER_SUPPLY_PROP_CAPACITY, +	POWER_SUPPLY_PROP_CAPACITY_LEVEL, +	POWER_SUPPLY_PROP_TEMP, +	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, +	POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, +	POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, +	POWER_SUPPLY_PROP_TECHNOLOGY, +	POWER_SUPPLY_PROP_CHARGE_FULL, +	POWER_SUPPLY_PROP_CHARGE_NOW, +	POWER_SUPPLY_PROP_ENERGY_NOW, +	POWER_SUPPLY_PROP_POWER_AVG, +	POWER_SUPPLY_PROP_HEALTH, +}; + +static __initdata enum power_supply_property bq2753x_battery_props[] = { +	POWER_SUPPLY_PROP_STATUS, +	POWER_SUPPLY_PROP_PRESENT, +	POWER_SUPPLY_PROP_VOLTAGE_NOW, +	POWER_SUPPLY_PROP_CURRENT_NOW, +	POWER_SUPPLY_PROP_CAPACITY, +	POWER_SUPPLY_PROP_CAPACITY_LEVEL, +	POWER_SUPPLY_PROP_TEMP, +	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, +	POWER_SUPPLY_PROP_TECHNOLOGY, +	POWER_SUPPLY_PROP_CHARGE_FULL, +	POWER_SUPPLY_PROP_CHARGE_NOW, +	POWER_SUPPLY_PROP_POWER_AVG, +	POWER_SUPPLY_PROP_HEALTH, +	POWER_SUPPLY_PROP_CYCLE_COUNT, +}; + +static __initdata enum power_supply_property bq274xx_battery_props[] = { +	POWER_SUPPLY_PROP_STATUS, +	POWER_SUPPLY_PROP_PRESENT, +	POWER_SUPPLY_PROP_VOLTAGE_NOW, +	POWER_SUPPLY_PROP_CURRENT_NOW, +	POWER_SUPPLY_PROP_CAPACITY, +	POWER_SUPPLY_PROP_CAPACITY_LEVEL, +	POWER_SUPPLY_PROP_TEMP, +	POWER_SUPPLY_PROP_TECHNOLOGY, +	POWER_SUPPLY_PROP_CHARGE_FULL, +	POWER_SUPPLY_PROP_CHARGE_NOW, +	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, +}; + +static __initdata enum power_supply_property bq276xx_battery_props[] = {  	POWER_SUPPLY_PROP_STATUS,  	POWER_SUPPLY_PROP_PRESENT,  	POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -149,6 +382,31 @@ static enum power_supply_property bq27425_battery_props[] = {  	POWER_SUPPLY_PROP_CHARGE_FULL,  	POWER_SUPPLY_PROP_CHARGE_NOW,  	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, +	POWER_SUPPLY_PROP_CYCLE_COUNT, +}; + +/* + * Ordering the parameters based on subclass and then offset will help in + * having fewer flash writes while updating. + * Customize these values and, if necessary, add more based on system needs. + */ +static struct dm_reg bq274xx_dm_regs[] = { +	{82, 0, 2, 16384},	/* Qmax */ +	{82, 5, 1, 0x81},	/* Load Select */ +	{82, 10, 2, 300},	/* Design Capacity */ +	{82, 12, 2, 1140},	/* Design Energy */ +	{82, 16, 2, 3090},	/* Terminate Voltage */ +	{82, 27, 2, 40},	/* Taper rate */ +}; + +static struct dm_reg bq276xx_dm_regs[] = { +	{64, 2, 1, 0x2C},	/* Op Config B */ +	{82, 0, 2, 1000},	/* Qmax */ +	{82, 2, 1, 0x81},	/* Load Select */ +	{82, 3, 2, 1340},	/* Design Capacity */ +	{82, 5, 2, 3700},	/* Design Energy */ +	{82, 9, 2, 3250},	/* Terminate Voltage */ +	{82, 20, 2, 110},	/* Taper rate */  };  static unsigned int poll_interval = 360; @@ -157,48 +415,319 @@ MODULE_PARM_DESC(poll_interval, "battery poll interval in seconds - " \  				"0 disables polling");  /* + * Forward Declarations + */ +static int read_dm_block(struct bq27x00_device_info *di, u8 subclass, +	u8 offset, u8 *data); + + +/*   * Common code for BQ27x00 devices   */ -static inline int bq27x00_read(struct bq27x00_device_info *di, u8 reg, +static inline int bq27xxx_read(struct bq27x00_device_info *di, int reg_index,  		bool single)  { -	if (di->chip == BQ27425) -		return di->bus.read(di, reg - BQ27425_REG_OFFSET, single); -	return di->bus.read(di, reg, single); +	int val; + +	/* Reports 0 for invalid/missing registers */ +	if (!di || di->regs[reg_index] == INVALID_REG_ADDR) +		return 0; + +	val = di->bus.read(di, di->regs[reg_index], single); + +	return val;  } +static inline int bq27xxx_write(struct bq27x00_device_info *di, int reg_index, +		int value, bool single) +{ +	if (!di || di->regs[reg_index] == INVALID_REG_ADDR) +		return -1; + +	return di->bus.write(di, di->regs[reg_index], value, single); +} + +static int control_cmd_wr(struct bq27x00_device_info *di, u16 cmd) +{ +	dev_dbg(di->dev, "%s: cmd - %04x\n", __func__, cmd); + +	return di->bus.write(di, BQ27XXX_REG_CTRL, cmd, false); +} + +static int control_cmd_read(struct bq27x00_device_info *di, u16 cmd) +{ +	dev_dbg(di->dev, "%s: cmd - %04x\n", __func__, cmd); + +	di->bus.write(di, BQ27XXX_REG_CTRL, cmd, false); + +	msleep(5); + +	return di->bus.read(di, BQ27XXX_REG_CTRL, false); +}  /* - * Higher versions of the chip like BQ27425 and BQ27500 - * differ from BQ27000 and BQ27200 in calculation of certain - * parameters. Hence we need to check for the chip type. + * It is assumed that the gauge is in unsealed mode when this function + * is called   */ -static bool bq27xxx_is_chip_version_higher(struct bq27x00_device_info *di) +static int bq276xx_seal_enabled(struct bq27x00_device_info *di)  { -	if (di->chip == BQ27425 || di->chip == BQ27500) -		return true; -	return false; +	u8 buf[32]; +	u8 op_cfg_b; + +	if (!read_dm_block(di, BQ276XX_OP_CFG_B_SUBCLASS, +		BQ276XX_OP_CFG_B_OFFSET, buf)) { +		return 1; /* Err on the side of caution and try to seal */ +	} + +	op_cfg_b = buf[BQ276XX_OP_CFG_B_OFFSET & 0x1F]; + +	if (op_cfg_b & BQ276XX_OP_CFG_B_DEF_SEAL_BIT) +		return 1; + +	return 0; +} + +#define SEAL_UNSEAL_POLLING_RETRY_LIMIT	1000 + +static inline int sealed(struct bq27x00_device_info *di) +{ +	return control_cmd_read(di, CONTROL_STATUS_SUBCMD) & (1 << 13); +} + +static int unseal(struct bq27x00_device_info *di, u32 key) +{ +	int i = 0; + +	dev_dbg(di->dev, "%s: key - %08x\n", __func__, key); + +	if (!sealed(di)) +		goto out; + +	di->bus.write(di, BQ27XXX_REG_CTRL, key & 0xFFFF, false); +	msleep(5); +	di->bus.write(di, BQ27XXX_REG_CTRL, (key & 0xFFFF0000) >> 16, false); +	msleep(5); + +	while (i < SEAL_UNSEAL_POLLING_RETRY_LIMIT) { +		i++; +		if (!sealed(di)) +			break; +		msleep(10); +	} + +out: +	if (i == SEAL_UNSEAL_POLLING_RETRY_LIMIT) { +		dev_err(di->dev, "%s: failed\n", __func__); +		return 0; +	} else { +		return 1; +	} +} + +static int seal(struct bq27x00_device_info *di) +{ +	int i = 0; +	int is_sealed; + +	dev_dbg(di->dev, "%s:\n", __func__); + +	is_sealed = sealed(di); +	if (is_sealed) +		return is_sealed; + +	if (di->chip == BQ276XX && !bq276xx_seal_enabled(di)) { +		dev_dbg(di->dev, "%s: sealing is not enabled\n", __func__); +		return is_sealed; +	} + +	di->bus.write(di, BQ27XXX_REG_CTRL, SEAL_SUBCMD, false); + +	while (i < SEAL_UNSEAL_POLLING_RETRY_LIMIT) { +		i++; +		is_sealed = sealed(di); +		if (is_sealed) +			break; +		msleep(10); +	} + +	if (!is_sealed) +		dev_err(di->dev, "%s: failed\n", __func__); + +	return is_sealed; +} + +#define CFG_UPDATE_POLLING_RETRY_LIMIT 50 +static int enter_cfg_update_mode(struct bq27x00_device_info *di) +{ +	int i = 0; +	u16 flags; + +	dev_dbg(di->dev, "%s:\n", __func__); + +	if (!unseal(di, BQ274XX_UNSEAL_KEY)) +		return 0; + +	control_cmd_wr(di, SET_CFGUPDATE_SUBCMD); +	msleep(5); + +	while (i < CFG_UPDATE_POLLING_RETRY_LIMIT) { +		i++; +		flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false); +		if (flags & (1 << 4)) +			break; +		msleep(100); +	} + +	if (i == CFG_UPDATE_POLLING_RETRY_LIMIT) { +		dev_err(di->dev, "%s: failed %04x\n", __func__, flags); +		return 0; +	} + +	return 1; +} + +static int exit_cfg_update_mode(struct bq27x00_device_info *di) +{ +	int i = 0; +	u16 flags; + +	dev_dbg(di->dev, "%s:\n", __func__); + +	control_cmd_wr(di, BQ274XX_SOFT_RESET); + +	while (i < CFG_UPDATE_POLLING_RETRY_LIMIT) { +		i++; +		flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false); +		if (!(flags & (1 << 4))) +			break; +		msleep(100); +	} + +	if (i == CFG_UPDATE_POLLING_RETRY_LIMIT) { +		dev_err(di->dev, "%s: failed %04x\n", __func__, flags); +		return 0; +	} + +	if (seal(di)) +		return 1; +	else +		return 0; +} +static u8 checksum(u8 *data) +{ +	u16 sum = 0; +	int i; + +	for (i = 0; i < 32; i++) +		sum += data[i]; + +	sum &= 0xFF; + +	return 0xFF - sum; +} + +#ifdef DEBUG +static void print_buf(const char *msg, u8 *buf) +{ +	int i; + +	printk("\nbq: %s buf: ", msg); +	for (i = 0; i < 32; i++) +		printk("%02x ", buf[i]); + +	printk("\n"); +} +#else +#define print_buf(a, b) +#endif + +static int update_dm_block(struct bq27x00_device_info *di, u8 subclass, +	u8 offset, u8 *data) +{ +	u8 buf[32]; +	u8 cksum; +	u8 blk_offset = offset >> 5; + +	dev_dbg(di->dev, "%s: subclass %d offset %d\n", +		__func__, subclass, offset); + +	di->bus.write(di, BLOCK_DATA_CONTROL, 0, true); +	msleep(5); + +	di->bus.write(di, BLOCK_DATA_CLASS, subclass, true); +	msleep(5); + +	di->bus.write(di, DATA_BLOCK, blk_offset, true); +	msleep(5); + +	di->bus.blk_write(di, BLOCK_DATA, data, 32); +	msleep(5); +	print_buf(__func__, data); + +	cksum = checksum(data); +	di->bus.write(di, BLOCK_DATA_CHECKSUM, cksum, true); +	msleep(5); + +	/* Read back and compare to make sure write is successful */ +	di->bus.write(di, DATA_BLOCK, blk_offset, true); +	msleep(5); +	di->bus.blk_read(di, BLOCK_DATA, buf, 32); +	if (memcmp(data, buf, 32)) { +		dev_err(di->dev, "%s: error updating subclass %d offset %d\n", +			__func__, subclass, offset); +		return 0; +	} else { +		return 1; +	} +} + +static int read_dm_block(struct bq27x00_device_info *di, u8 subclass, +	u8 offset, u8 *data) +{ +	u8 cksum_calc, cksum; +	u8 blk_offset = offset >> 5; + +	dev_dbg(di->dev, "%s: subclass %d offset %d\n", +		__func__, subclass, offset); + +	di->bus.write(di, BLOCK_DATA_CONTROL, 0, true); +	msleep(5); + +	di->bus.write(di, BLOCK_DATA_CLASS, subclass, true); +	msleep(5); + +	di->bus.write(di, DATA_BLOCK, blk_offset, true); +	msleep(5); + +	di->bus.blk_read(di, BLOCK_DATA, data, 32); + +	cksum_calc = checksum(data); +	cksum = di->bus.read(di, BLOCK_DATA_CHECKSUM, true); +	if (cksum != cksum_calc) { +		dev_err(di->dev, "%s: error reading subclass %d offset %d\n", +			__func__, subclass, offset); +		return 0; +	} + +	print_buf(__func__, data); + +	return 1;  }  /* - * Return the battery Relative State-of-Charge + * Return the battery State-of-Charge   * Or < 0 if something fails.   */ -static int bq27x00_battery_read_rsoc(struct bq27x00_device_info *di) +static int bq27x00_battery_read_soc(struct bq27x00_device_info *di)  { -	int rsoc; +	int soc; -	if (di->chip == BQ27500) -		rsoc = bq27x00_read(di, BQ27500_REG_SOC, false); -	else if (di->chip == BQ27425) -		rsoc = bq27x00_read(di, BQ27425_REG_SOC, false); -	else -		rsoc = bq27x00_read(di, BQ27000_REG_RSOC, true); +	soc = bq27xxx_read(di, BQ27XXX_REG_SOC, false); -	if (rsoc < 0) +	if (soc < 0)  		dev_dbg(di->dev, "error reading relative State-of-Charge\n"); -	return rsoc; +	return soc;  }  /* @@ -209,17 +738,17 @@ static int bq27x00_battery_read_charge(struct bq27x00_device_info *di, u8 reg)  {  	int charge; -	charge = bq27x00_read(di, reg, false); +	charge = bq27xxx_read(di, reg, false);  	if (charge < 0) {  		dev_dbg(di->dev, "error reading charge register %02x: %d\n",  			reg, charge);  		return charge;  	} -	if (bq27xxx_is_chip_version_higher(di)) -		charge *= 1000; +	if (di->chip == BQ27200) +		charge = charge * 3570 / BQ27200_RS;  	else -		charge = charge * 3570 / BQ27000_RS; +		charge *= 1000;  	return charge;  } @@ -231,49 +760,46 @@ static int bq27x00_battery_read_charge(struct bq27x00_device_info *di, u8 reg)  static inline int bq27x00_battery_read_nac(struct bq27x00_device_info *di)  {  	int flags; -	bool is_bq27500 = di->chip == BQ27500; -	bool is_higher = bq27xxx_is_chip_version_higher(di); -	flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500); -	if (flags >= 0 && !is_higher && (flags & BQ27000_FLAG_CI)) -		return -ENODATA; +	if (di->chip == BQ27200) { +		flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true); +		if (flags >= 0 && (flags & BQ27200_FLAG_CI)) +			return -ENODATA; +	} -	return bq27x00_battery_read_charge(di, BQ27x00_REG_NAC); +	return bq27x00_battery_read_charge(di, BQ27XXX_REG_NAC);  }  /*   * Return the battery Last measured discharge in µAh   * Or < 0 if something fails.   */ -static inline int bq27x00_battery_read_lmd(struct bq27x00_device_info *di) +static inline int bq27x00_battery_read_fcc(struct bq27x00_device_info *di)  { -	return bq27x00_battery_read_charge(di, BQ27x00_REG_LMD); +	return bq27x00_battery_read_charge(di, BQ27XXX_REG_FCC);  }  /* - * Return the battery Initial last measured discharge in µAh + * Return the Design Capacity in µAh   * Or < 0 if something fails.   */ -static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di) +static int bq27x00_battery_read_dcap(struct bq27x00_device_info *di)  { -	int ilmd; +	int dcap; -	if (bq27xxx_is_chip_version_higher(di)) -		ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false); -	else -		ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true); +	dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, false); -	if (ilmd < 0) { +	if (dcap < 0) {  		dev_dbg(di->dev, "error reading initial last measured discharge\n"); -		return ilmd; +		return dcap;  	} -	if (bq27xxx_is_chip_version_higher(di)) -		ilmd *= 1000; +	if (di->chip == BQ27200) +		dcap = dcap * 256 * 3570 / BQ27200_RS;  	else -		ilmd = ilmd * 256 * 3570 / BQ27000_RS; +		dcap *= 1000; -	return ilmd; +	return dcap;  }  /* @@ -284,16 +810,16 @@ static int bq27x00_battery_read_energy(struct bq27x00_device_info *di)  {  	int ae; -	ae = bq27x00_read(di, BQ27x00_REG_AE, false); +	ae = bq27xxx_read(di, BQ27XXX_REG_AE, false);  	if (ae < 0) {  		dev_dbg(di->dev, "error reading available energy\n");  		return ae;  	} -	if (di->chip == BQ27500) -		ae *= 1000; +	if (di->chip == BQ27200) +		ae = ae * 29200 / BQ27200_RS;  	else -		ae = ae * 29200 / BQ27000_RS; +		ae *= 1000;  	return ae;  } @@ -306,13 +832,13 @@ static int bq27x00_battery_read_temperature(struct bq27x00_device_info *di)  {  	int temp; -	temp = bq27x00_read(di, BQ27x00_REG_TEMP, false); +	temp = bq27xxx_read(di, BQ27XXX_REG_TEMP, false);  	if (temp < 0) {  		dev_err(di->dev, "error reading temperature\n");  		return temp;  	} -	if (!bq27xxx_is_chip_version_higher(di)) +	if (di->chip == BQ27200)  		temp = 5 * temp / 2;  	return temp; @@ -326,7 +852,7 @@ static int bq27x00_battery_read_cyct(struct bq27x00_device_info *di)  {  	int cyct; -	cyct = bq27x00_read(di, BQ27x00_REG_CYCT, false); +	cyct = bq27xxx_read(di, BQ27XXX_REG_CYCT, false);  	if (cyct < 0)  		dev_err(di->dev, "error reading cycle count total\n"); @@ -341,7 +867,7 @@ static int bq27x00_battery_read_time(struct bq27x00_device_info *di, u8 reg)  {  	int tval; -	tval = bq27x00_read(di, reg, false); +	tval = bq27xxx_read(di, reg, false);  	if (tval < 0) {  		dev_dbg(di->dev, "error reading time register %02x: %d\n",  			reg, tval); @@ -362,17 +888,25 @@ static int bq27x00_battery_read_pwr_avg(struct bq27x00_device_info *di, u8 reg)  {  	int tval; -	tval = bq27x00_read(di, reg, false); +	tval = bq27xxx_read(di, reg, false);  	if (tval < 0) {  		dev_err(di->dev, "error reading power avg rgister  %02x: %d\n",  			reg, tval);  		return tval;  	} -	if (di->chip == BQ27500) +	if (di->chip == BQ27200) +		return (tval * BQ27200_POWER_CONSTANT) / BQ27200_RS; +	else  		return tval; +} + +static int overtemperature(struct bq27x00_device_info *di, u16 flags) +{ +	if (di->chip == BQ27520) +		return flags & (BQ27XXX_FLAG_OTC | BQ27XXX_FLAG_OTD);  	else -		return (tval * BQ27x00_POWER_CONSTANT) / BQ27000_RS; +		return flags & BQ27XXX_FLAG_OTC;  }  /* @@ -381,25 +915,25 @@ static int bq27x00_battery_read_pwr_avg(struct bq27x00_device_info *di, u8 reg)   */  static int bq27x00_battery_read_health(struct bq27x00_device_info *di)  { -	int tval; +	u16 tval; -	tval = bq27x00_read(di, BQ27x00_REG_FLAGS, false); +	tval = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false);  	if (tval < 0) {  		dev_err(di->dev, "error reading flag register:%d\n", tval);  		return tval;  	} -	if ((di->chip == BQ27500)) { -		if (tval & BQ27500_FLAG_SOCF) +	if ((di->chip == BQ27200)) { +		if (tval & BQ27200_FLAG_EDV1)  			tval = POWER_SUPPLY_HEALTH_DEAD; -		else if (tval & BQ27500_FLAG_OTC) -			tval = POWER_SUPPLY_HEALTH_OVERHEAT;  		else  			tval = POWER_SUPPLY_HEALTH_GOOD;  		return tval;  	} else { -		if (tval & BQ27000_FLAG_EDV1) +		if (tval & BQ27XXX_FLAG_SOCF)  			tval = POWER_SUPPLY_HEALTH_DEAD; +		else if (overtemperature(di, tval)) +			tval = POWER_SUPPLY_HEALTH_OVERHEAT;  		else  			tval = POWER_SUPPLY_HEALTH_GOOD;  		return tval; @@ -411,13 +945,14 @@ static int bq27x00_battery_read_health(struct bq27x00_device_info *di)  static void bq27x00_update(struct bq27x00_device_info *di)  {  	struct bq27x00_reg_cache cache = {0, }; +	bool is_bq27200 = di->chip == BQ27200;  	bool is_bq27500 = di->chip == BQ27500; -	bool is_bq27425 = di->chip == BQ27425; +	bool is_bq274xx = di->chip == BQ274XX; +	bool is_bq276xx = di->chip == BQ276XX; -	cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500); +	cache.flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, !is_bq27500);  	if (cache.flags >= 0) { -		if (!is_bq27500 && !is_bq27425 -				&& (cache.flags & BQ27000_FLAG_CI)) { +		if (is_bq27200 && (cache.flags & BQ27200_FLAG_CI)) {  			dev_info(di->dev, "battery is not calibrated! ignoring capacity values\n");  			cache.capacity = -ENODATA;  			cache.energy = -ENODATA; @@ -427,31 +962,31 @@ static void bq27x00_update(struct bq27x00_device_info *di)  			cache.charge_full = -ENODATA;  			cache.health = -ENODATA;  		} else { -			cache.capacity = bq27x00_battery_read_rsoc(di); -			if (!is_bq27425) { +			cache.capacity = bq27x00_battery_read_soc(di); +			if (!(is_bq274xx || is_bq276xx)) {  				cache.energy = bq27x00_battery_read_energy(di);  				cache.time_to_empty =  					bq27x00_battery_read_time(di, -							BQ27x00_REG_TTE); +							BQ27XXX_REG_TTE);  				cache.time_to_empty_avg =  					bq27x00_battery_read_time(di, -							BQ27x00_REG_TTECP); +							BQ27XXX_REG_TTECP);  				cache.time_to_full =  					bq27x00_battery_read_time(di, -							BQ27x00_REG_TTF); +							BQ27XXX_REG_TTF);  			} -			cache.charge_full = bq27x00_battery_read_lmd(di); +			cache.charge_full = bq27x00_battery_read_fcc(di);  			cache.health = bq27x00_battery_read_health(di);  		}  		cache.temperature = bq27x00_battery_read_temperature(di); -		if (!is_bq27425) +		if (!is_bq274xx)  			cache.cycle_count = bq27x00_battery_read_cyct(di);  		cache.power_avg = -			bq27x00_battery_read_pwr_avg(di, BQ27x00_POWER_AVG); +			bq27x00_battery_read_pwr_avg(di, BQ27XXX_POWER_AVG);  		/* We only have to read charge design full once */  		if (di->charge_design_full <= 0) -			di->charge_design_full = bq27x00_battery_read_ilmd(di); +			di->charge_design_full = bq27x00_battery_read_dcap(di);  	}  	if (memcmp(&di->cache, &cache, sizeof(cache)) != 0) { @@ -462,11 +997,131 @@ static void bq27x00_update(struct bq27x00_device_info *di)  	di->last_update = jiffies;  } +static void copy_to_dm_buf_big_endian(struct bq27x00_device_info *di, +	u8 *buf, u8 offset, u8 sz, u32 val) +{ +	dev_dbg(di->dev, "%s: offset %d sz %d val %d\n", +		__func__, offset, sz, val); + +	switch (sz) { +	case 1: +		buf[offset] = (u8) val; +		break; +	case 2: +		put_unaligned_be16((u16) val, &buf[offset]); +		break; +	case 4: +		put_unaligned_be32(val, &buf[offset]); +		break; +	default: +		dev_err(di->dev, "%s: bad size for dm parameter - %d", +			__func__, sz); +		break; +	} +} + +static int rom_mode_gauge_init_completed(struct bq27x00_device_info *di) +{ +	dev_dbg(di->dev, "%s:\n", __func__); + +	return control_cmd_read(di, CONTROL_STATUS_SUBCMD) & +		BQ274XX_CTRL_STATUS_INITCOMP; +} + +static bool rom_mode_gauge_dm_initialized(struct bq27x00_device_info *di) +{ +	u16 flags; + +	flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false); + +	dev_dbg(di->dev, "%s: flags - 0x%04x\n", __func__, flags); + +	if (flags & BQ274XX_FLAG_ITPOR) +		return false; +	else +		return true; +} + +#define INITCOMP_TIMEOUT_MS		10000 +static void rom_mode_gauge_dm_init(struct bq27x00_device_info *di) +{ +	int i; +	int timeout = INITCOMP_TIMEOUT_MS; +	u8 subclass, offset; +	u32 blk_number; +	u32 blk_number_prev = 0; +	u8 buf[32]; +	bool buf_valid = false; +	struct dm_reg *dm_reg; + +	dev_dbg(di->dev, "%s:\n", __func__); + +	while (!rom_mode_gauge_init_completed(di) && timeout > 0) { +		msleep(100); +		timeout -= 100; +	} + +	if (timeout <= 0) { +		dev_err(di->dev, "%s: INITCOMP not set after %d seconds\n", +			__func__, INITCOMP_TIMEOUT_MS/100); +		return; +	} + +	if (!di->dm_regs || !di->dm_regs_count) { +		dev_err(di->dev, "%s: Data not available for DM initialization\n", +			__func__); +		return; +	} + +	enter_cfg_update_mode(di); +	for (i = 0; i < di->dm_regs_count; i++) { +		dm_reg = &di->dm_regs[i]; +		subclass = dm_reg->subclass; +		offset = dm_reg->offset; + +		/* +		 * Create a composite block number to see if the subsequent +		 * register also belongs to the same 32 btye block in the DM +		 */ +		blk_number = subclass << 8; +		blk_number |= offset >> 5; + +		if (blk_number == blk_number_prev) { +			copy_to_dm_buf_big_endian(di, buf, offset, +				dm_reg->len, dm_reg->data); +		} else { + +			if (buf_valid) +				update_dm_block(di, blk_number_prev >> 8, +					(blk_number_prev << 5) & 0xFF , buf); +			else +				buf_valid = true; + +			read_dm_block(di, dm_reg->subclass, dm_reg->offset, +				buf); +			copy_to_dm_buf_big_endian(di, buf, offset, +				dm_reg->len, dm_reg->data); +		} +		blk_number_prev = blk_number; +	} + +	/* Last buffer to be written */ +	if (buf_valid) +		update_dm_block(di, subclass, offset, buf); + +	exit_cfg_update_mode(di); +} +  static void bq27x00_battery_poll(struct work_struct *work)  {  	struct bq27x00_device_info *di =  		container_of(work, struct bq27x00_device_info, work.work); +	if (((di->chip == BQ274XX) || (di->chip == BQ276XX)) && +		!rom_mode_gauge_dm_initialized(di)) { +		rom_mode_gauge_dm_init(di); +	} +  	bq27x00_update(di);  	if (poll_interval > 0) { @@ -487,23 +1142,23 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di,  	int curr;  	int flags; -	curr = bq27x00_read(di, BQ27x00_REG_AI, false); +	curr = bq27xxx_read(di, BQ27XXX_REG_AI, false);  	if (curr < 0) {  		dev_err(di->dev, "error reading current\n");  		return curr;  	} -	if (bq27xxx_is_chip_version_higher(di)) { -		/* bq27500 returns signed value */ -		val->intval = (int)((s16)curr) * 1000; -	} else { -		flags = bq27x00_read(di, BQ27x00_REG_FLAGS, false); -		if (flags & BQ27000_FLAG_CHGS) { +	if (di->chip == BQ27200) { +		flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false); +		if (flags & BQ27200_FLAG_CHGS) {  			dev_dbg(di->dev, "negative current!\n");  			curr = -curr;  		} -		val->intval = curr * 3570 / BQ27000_RS; +		val->intval = curr * 3570 / BQ27200_RS; +	} else { +		/* Other gauges return signed value */ +		val->intval = (int)((s16)curr) * 1000;  	}  	return 0; @@ -514,22 +1169,22 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di,  {  	int status; -	if (bq27xxx_is_chip_version_higher(di)) { -		if (di->cache.flags & BQ27500_FLAG_FC) -			status = POWER_SUPPLY_STATUS_FULL; -		else if (di->cache.flags & BQ27500_FLAG_DSC) -			status = POWER_SUPPLY_STATUS_DISCHARGING; -		else -			status = POWER_SUPPLY_STATUS_CHARGING; -	} else { -		if (di->cache.flags & BQ27000_FLAG_FC) +	if (di->chip == BQ27200) { +		if (di->cache.flags & BQ27200_FLAG_FC)  			status = POWER_SUPPLY_STATUS_FULL; -		else if (di->cache.flags & BQ27000_FLAG_CHGS) +		else if (di->cache.flags & BQ27200_FLAG_CHGS)  			status = POWER_SUPPLY_STATUS_CHARGING;  		else if (power_supply_am_i_supplied(&di->bat))  			status = POWER_SUPPLY_STATUS_NOT_CHARGING;  		else  			status = POWER_SUPPLY_STATUS_DISCHARGING; +	} else { +		if (di->cache.flags & BQ27XXX_FLAG_FC) +			status = POWER_SUPPLY_STATUS_FULL; +		else if (di->cache.flags & BQ27XXX_FLAG_DSC) +			status = POWER_SUPPLY_STATUS_DISCHARGING; +		else +			status = POWER_SUPPLY_STATUS_CHARGING;  	}  	val->intval = status; @@ -542,21 +1197,21 @@ static int bq27x00_battery_capacity_level(struct bq27x00_device_info *di,  {  	int level; -	if (bq27xxx_is_chip_version_higher(di)) { -		if (di->cache.flags & BQ27500_FLAG_FC) +	if (di->chip == BQ27200) { +		if (di->cache.flags & BQ27200_FLAG_FC)  			level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; -		else if (di->cache.flags & BQ27500_FLAG_SOC1) +		else if (di->cache.flags & BQ27200_FLAG_EDV1)  			level = POWER_SUPPLY_CAPACITY_LEVEL_LOW; -		else if (di->cache.flags & BQ27500_FLAG_SOCF) +		else if (di->cache.flags & BQ27200_FLAG_EDVF)  			level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;  		else  			level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;  	} else { -		if (di->cache.flags & BQ27000_FLAG_FC) +		if (di->cache.flags & BQ27XXX_FLAG_FC)  			level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; -		else if (di->cache.flags & BQ27000_FLAG_EDV1) +		else if (di->cache.flags & BQ27XXX_FLAG_SOC1)  			level = POWER_SUPPLY_CAPACITY_LEVEL_LOW; -		else if (di->cache.flags & BQ27000_FLAG_EDVF) +		else if (di->cache.flags & BQ27XXX_FLAG_SOCF)  			level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;  		else  			level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; @@ -576,7 +1231,7 @@ static int bq27x00_battery_voltage(struct bq27x00_device_info *di,  {  	int volt; -	volt = bq27x00_read(di, BQ27x00_REG_VOLT, false); +	volt = bq27xxx_read(di, BQ27XXX_REG_VOLT, false);  	if (volt < 0) {  		dev_err(di->dev, "error reading voltage\n");  		return volt; @@ -690,17 +1345,41 @@ static void bq27x00_external_power_changed(struct power_supply *psy)  	schedule_delayed_work(&di->work, 0);  } -static int bq27x00_powersupply_init(struct bq27x00_device_info *di) +static void __init set_properties_array(struct bq27x00_device_info *di, +	enum power_supply_property *props, int num_props) +{ +	int tot_sz = num_props * sizeof(enum power_supply_property); + +	di->bat.properties = devm_kzalloc(di->dev, tot_sz, GFP_KERNEL); + +	if (di->bat.properties) { +		memcpy(di->bat.properties, props, tot_sz); +		di->bat.num_properties = num_props; +	} else { +		di->bat.num_properties = 0; +	} +} + +static int __init bq27x00_powersupply_init(struct bq27x00_device_info *di)  {  	int ret;  	di->bat.type = POWER_SUPPLY_TYPE_BATTERY; -	if (di->chip == BQ27425) { -		di->bat.properties = bq27425_battery_props; -		di->bat.num_properties = ARRAY_SIZE(bq27425_battery_props); +	if (di->chip == BQ274XX) { +		set_properties_array(di, bq274xx_battery_props, +			ARRAY_SIZE(bq274xx_battery_props)); +	} else if (di->chip == BQ276XX) { +		set_properties_array(di, bq276xx_battery_props, +			ARRAY_SIZE(bq276xx_battery_props)); +	} else if (di->chip == BQ27520) { +		set_properties_array(di, bq27520_battery_props, +			ARRAY_SIZE(bq27520_battery_props)); +	} else if (di->chip == BQ2753X) { +		set_properties_array(di, bq2753x_battery_props, +			ARRAY_SIZE(bq2753x_battery_props));  	} else { -		di->bat.properties = bq27x00_battery_props; -		di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props); +		set_properties_array(di, bq27x00_battery_props, +			ARRAY_SIZE(bq27x00_battery_props));  	}  	di->bat.get_property = bq27x00_battery_get_property;  	di->bat.external_power_changed = bq27x00_external_power_changed; @@ -748,7 +1427,7 @@ static void bq27x00_powersupply_unregister(struct bq27x00_device_info *di)  static DEFINE_IDR(battery_id);  static DEFINE_MUTEX(battery_mutex); -static int bq27x00_read_i2c(struct bq27x00_device_info *di, u8 reg, bool single) +static int bq27xxx_read_i2c(struct bq27x00_device_info *di, u8 reg, bool single)  {  	struct i2c_client *client = to_i2c_client(di->dev);  	struct i2c_msg msg[2]; @@ -782,20 +1461,205 @@ static int bq27x00_read_i2c(struct bq27x00_device_info *di, u8 reg, bool single)  	return ret;  } -static int bq27x00_battery_probe(struct i2c_client *client, +static int bq27xxx_write_i2c(struct bq27x00_device_info *di, u8 reg, int value, bool single) +{ +	struct i2c_client *client = to_i2c_client(di->dev); +	struct i2c_msg msg; +	unsigned char data[4]; +	int ret; + +	if (!client->adapter) +		return -ENODEV; + +	data[0] = reg; +	if (single) { +		data[1] = (unsigned char)value; +		msg.len = 2; +	} else { +		put_unaligned_le16(value, &data[1]); +		msg.len = 3; +	} + +	msg.buf = data; +	msg.addr = client->addr; +	msg.flags = 0; + +	ret = i2c_transfer(client->adapter, &msg, 1); +	if (ret < 0) +		return ret; + +	return 0; +} + +static int bq27xxx_read_i2c_blk(struct bq27x00_device_info *di, u8 reg, +	u8 *data, u8 len) +{ +	struct i2c_client *client = to_i2c_client(di->dev); +	struct i2c_msg msg[2]; +	int ret; + +	if (!client->adapter) +		return -ENODEV; + +	msg[0].addr = client->addr; +	msg[0].flags = 0; +	msg[0].buf = ® +	msg[0].len = 1; + +	msg[1].addr = client->addr; +	msg[1].flags = I2C_M_RD; +	msg[1].buf = data; +	msg[1].len = len; + +	ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); +	if (ret < 0) +		return ret; + +	return ret; +} + +static int bq27xxx_write_i2c_blk(struct bq27x00_device_info *di, u8 reg, +	u8 *data, u8 sz) +{ +	struct i2c_client *client = to_i2c_client(di->dev); +	struct i2c_msg msg; +	int ret; +	u8 buf[33]; + +	if (!client->adapter) +		return -ENODEV; + +	buf[0] = reg; +	memcpy(&buf[1], data, sz); + +	msg.buf = buf; +	msg.addr = client->addr; +	msg.flags = 0; +	msg.len = sz + 1; + +	ret = i2c_transfer(client->adapter, &msg, 1); +	if (ret < 0) +		return ret; + +	return 0; +} + +static int bq27x00_battery_reset(struct bq27x00_device_info *di) +{ +	dev_info(di->dev, "Gas Gauge Reset\n"); + +	bq27xxx_write(di, BQ27XXX_REG_CTRL, RESET_SUBCMD, false); + +	msleep(10); + +	return bq27xxx_read(di, BQ27XXX_REG_CTRL, false); +} + +static int bq27x00_battery_read_fw_version(struct bq27x00_device_info *di) +{ +	bq27xxx_write(di, BQ27XXX_REG_CTRL, FW_VER_SUBCMD, false); + +	msleep(10); + +	return bq27xxx_read(di, BQ27XXX_REG_CTRL, false); +} + +static int bq27x00_battery_read_device_type(struct bq27x00_device_info *di) +{ +	bq27xxx_write(di, BQ27XXX_REG_CTRL, DEV_TYPE_SUBCMD, false); + +	msleep(10); + +	return bq27xxx_read(di, BQ27XXX_REG_CTRL, false); +} + +static int bq27x00_battery_read_dataflash_version(struct bq27x00_device_info *di) +{ +	bq27xxx_write(di, BQ27XXX_REG_CTRL, DF_VER_SUBCMD, false); + +	msleep(10); + +	return bq27xxx_read(di, BQ27XXX_REG_CTRL, false); +} + +static ssize_t show_firmware_version(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct bq27x00_device_info *di = dev_get_drvdata(dev); +	int ver; + +	ver = bq27x00_battery_read_fw_version(di); + +	return sprintf(buf, "%d\n", ver); +} + +static ssize_t show_dataflash_version(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct bq27x00_device_info *di = dev_get_drvdata(dev); +	int ver; + +	ver = bq27x00_battery_read_dataflash_version(di); + +	return sprintf(buf, "%d\n", ver); +} + +static ssize_t show_device_type(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct bq27x00_device_info *di = dev_get_drvdata(dev); +	int dev_type; + +	dev_type = bq27x00_battery_read_device_type(di); + +	return sprintf(buf, "%d\n", dev_type); +} + +static ssize_t show_reset(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct bq27x00_device_info *di = dev_get_drvdata(dev); + +	bq27x00_battery_reset(di); + +	return sprintf(buf, "okay\n"); +} + +static DEVICE_ATTR(fw_version, S_IRUGO, show_firmware_version, NULL); +static DEVICE_ATTR(df_version, S_IRUGO, show_dataflash_version, NULL); +static DEVICE_ATTR(device_type, S_IRUGO, show_device_type, NULL); +static DEVICE_ATTR(reset, S_IRUGO, show_reset, NULL); + +static struct attribute *bq27x00_attributes[] = { +	&dev_attr_fw_version.attr, +	&dev_attr_df_version.attr, +	&dev_attr_device_type.attr, +	&dev_attr_reset.attr, +	NULL +}; + +static const struct attribute_group bq27x00_attr_group = { +	.attrs = bq27x00_attributes, +}; + +static int __init bq27x00_battery_probe(struct i2c_client *client,  				 const struct i2c_device_id *id)  {  	char *name;  	struct bq27x00_device_info *di;  	int num;  	int retval = 0; +	u8 *regs;  	/* Get new ID for the new battery device */ +	retval = idr_pre_get(&battery_id, GFP_KERNEL); +	if (retval == 0) +		return -ENOMEM;  	mutex_lock(&battery_mutex); -	num = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL); +	retval = idr_get_new(&battery_id, client, &num);  	mutex_unlock(&battery_mutex); -	if (num < 0) -		return num; +	if (retval < 0) +		return retval;  	name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num);  	if (!name) { @@ -815,13 +1679,52 @@ static int bq27x00_battery_probe(struct i2c_client *client,  	di->dev = &client->dev;  	di->chip = id->driver_data;  	di->bat.name = name; -	di->bus.read = &bq27x00_read_i2c; +	di->bus.read = &bq27xxx_read_i2c; +	di->bus.write = &bq27xxx_write_i2c; +	di->bus.blk_read = bq27xxx_read_i2c_blk; +	di->bus.blk_write = bq27xxx_write_i2c_blk; +	di->dm_regs = NULL; +	di->dm_regs_count = 0; + +	if (di->chip == BQ27200) +		regs = bq27200_regs; +	else if (di->chip == BQ27500) +		regs = bq27500_regs; +	else if (di->chip == BQ27520) +		regs = bq27520_regs; +	else if (di->chip == BQ2753X) +		regs = bq2753x_regs; +	else if (di->chip == BQ274XX) { +		regs = bq274xx_regs; +		di->dm_regs = bq274xx_dm_regs; +		di->dm_regs_count = ARRAY_SIZE(bq274xx_dm_regs); +	} else if (di->chip == BQ276XX) { +		/* commands are same as bq274xx, only DM is different */ +		regs = bq276xx_regs; +		di->dm_regs = bq276xx_dm_regs; +		di->dm_regs_count = ARRAY_SIZE(bq276xx_dm_regs); +	} else { +		dev_err(&client->dev, +			"Unexpected gas gague: %d\n", di->chip); +		regs = bq27520_regs; +	} + +	memcpy(di->regs, regs, NUM_REGS); + +	di->fw_ver = bq27x00_battery_read_fw_version(di); +	dev_info(&client->dev, "Gas Guage fw version is 0x%04x\n", di->fw_ver);  	retval = bq27x00_powersupply_init(di);  	if (retval)  		goto batt_failed_3; +	/* Schedule a polling after about 1 min */ +	schedule_delayed_work(&di->work, 60 * HZ); +  	i2c_set_clientdata(client, di); +	retval = sysfs_create_group(&client->dev.kobj, &bq27x00_attr_group); +	if (retval) +		dev_err(&client->dev, "could not create sysfs files\n");  	return 0; @@ -855,9 +1758,12 @@ static int bq27x00_battery_remove(struct i2c_client *client)  }  static const struct i2c_device_id bq27x00_id[] = { -	{ "bq27200", BQ27000 },	/* bq27200 is same as bq27000, but with i2c */ +	{ "bq27200", BQ27200 },  	{ "bq27500", BQ27500 }, -	{ "bq27425", BQ27425 }, +	{ "bq27520", BQ27520 }, +	{ "bq274xx", BQ274XX }, +	{ "bq276xx", BQ276XX }, +	{ "bq2753x", BQ2753X },  	{},  };  MODULE_DEVICE_TABLE(i2c, bq27x00_id); @@ -871,7 +1777,7 @@ static struct i2c_driver bq27x00_battery_driver = {  	.id_table = bq27x00_id,  }; -static inline int bq27x00_battery_i2c_init(void) +static inline int __init bq27x00_battery_i2c_init(void)  {  	int ret = i2c_add_driver(&bq27x00_battery_driver);  	if (ret) @@ -880,7 +1786,7 @@ static inline int bq27x00_battery_i2c_init(void)  	return ret;  } -static inline void bq27x00_battery_i2c_exit(void) +static inline void __exit bq27x00_battery_i2c_exit(void)  {  	i2c_del_driver(&bq27x00_battery_driver);  } @@ -929,7 +1835,7 @@ static int bq27000_read_platform(struct bq27x00_device_info *di, u8 reg,  	return pdata->read(dev, reg);  } -static int bq27000_battery_probe(struct platform_device *pdev) +static int __devinit bq27000_battery_probe(struct platform_device *pdev)  {  	struct bq27x00_device_info *di;  	struct bq27000_platform_data *pdata = pdev->dev.platform_data; @@ -954,7 +1860,7 @@ static int bq27000_battery_probe(struct platform_device *pdev)  	platform_set_drvdata(pdev, di);  	di->dev = &pdev->dev; -	di->chip = BQ27000; +	di->chip = BQ27200;  	di->bat.name = pdata->name ?: dev_name(&pdev->dev);  	di->bus.read = &bq27000_read_platform; @@ -966,27 +1872,25 @@ static int bq27000_battery_probe(struct platform_device *pdev)  	return 0;  err_free: -	platform_set_drvdata(pdev, NULL);  	kfree(di);  	return ret;  } -static int bq27000_battery_remove(struct platform_device *pdev) +static int __devexit bq27000_battery_remove(struct platform_device *pdev)  {  	struct bq27x00_device_info *di = platform_get_drvdata(pdev);  	bq27x00_powersupply_unregister(di); -	platform_set_drvdata(pdev, NULL);  	kfree(di);  	return 0;  } -static struct platform_driver bq27000_battery_driver = { +static struct platform_driver __initdata bq27000_battery_driver = {  	.probe	= bq27000_battery_probe, -	.remove = bq27000_battery_remove, +	.remove = __devexit_p(bq27000_battery_remove),  	.driver = {  		.name = "bq27000-battery",  		.owner = THIS_MODULE, @@ -997,7 +1901,7 @@ static inline int bq27x00_battery_platform_init(void)  {  	int ret = platform_driver_register(&bq27000_battery_driver);  	if (ret) -		printk(KERN_ERR "Unable to register BQ27000 platform driver\n"); +		printk(KERN_ERR "Unable to register BQ27200 platform driver\n");  	return ret;  } diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 815d6df8bd5..7c75ab88f9b 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3673,8 +3673,9 @@ regulator_register(const struct regulator_desc *regulator_desc,  add_dev:  	/* add consumers devices */ +  	if (init_data) { -		for (i = 0; i < init_data->num_consumer_supplies; i++) { +      for (i = 0; i < init_data->num_consumer_supplies; i++) {  			ret = set_consumer_device_supply(rdev,  				init_data->consumer_supplies[i].dev_name,  				init_data->consumer_supplies[i].supply); @@ -3684,13 +3685,14 @@ add_dev:  				goto unset_supplies;  			}  		} -	} +	}   	list_add(&rdev->list, ®ulator_list);  	rdev_init_debugfs(rdev);  out:  	mutex_unlock(®ulator_list_mutex); +  	return rdev;  unset_supplies: @@ -3710,7 +3712,8 @@ wash:  clean:  	kfree(rdev);  	rdev = ERR_PTR(ret); -	goto out; + +    goto out;  }  EXPORT_SYMBOL_GPL(regulator_register); @@ -3980,8 +3983,12 @@ static int __init regulator_init_complete(void)  	 * with DT to provide them just assume that a DT enabled  	 * system has full constraints.  	 */ + +#if 0 +    // TODO OLIO: This assumption isn't true for us. Not yet, at least.  	if (of_have_populated_dt())  		has_full_constraints = true; +#endif  	mutex_lock(®ulator_list_mutex); diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index 45c16447744..035eb750e28 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -906,7 +906,7 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic,  		}  		ret = tps65910_reg_write(pmic->mfd, sr_reg_add, 0);  		if (ret < 0) { -			dev_err(mfd->dev, "Error in settting sr register\n"); +			dev_err(mfd->dev, "Error in setting sr register\n");  			return ret;  		}  	} diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index db4d6dc0324..be8d14ab644 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -16,6 +16,7 @@ source "drivers/staging/iio/magnetometer/Kconfig"  source "drivers/staging/iio/meter/Kconfig"  source "drivers/staging/iio/resolver/Kconfig"  source "drivers/staging/iio/trigger/Kconfig" +source "drivers/staging/iio/imu/inv_mpu/Kconfig"  config IIO_DUMMY_EVGEN         tristate diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile index d87106135b2..9c91287a307 100644 --- a/drivers/staging/iio/Makefile +++ b/drivers/staging/iio/Makefile @@ -16,6 +16,7 @@ obj-y += cdc/  obj-y += frequency/  obj-y += gyro/  obj-y += impedance-analyzer/ +obj-y += imu/inv_mpu/  obj-y += light/  obj-y += magnetometer/  obj-y += meter/ diff --git a/drivers/staging/iio/imu/inv_mpu/Kconfig b/drivers/staging/iio/imu/inv_mpu/Kconfig new file mode 100644 index 00000000000..1b8c95484f0 --- /dev/null +++ b/drivers/staging/iio/imu/inv_mpu/Kconfig @@ -0,0 +1,32 @@ +# +# inv-mpu-iio driver for Invensense MPU devices and combos +# + +config INV_MPU_IIO +	tristate "Invensense MPU devices" +	depends on I2C && SYSFS && IIO && IIO_KFIFO_BUF && IIO_TRIGGER && !INV_MPU +	default n +	help +	  This driver supports the Invensense MPU devices. +	  This includes MPU6050/MPU3050/MPU9150/ITG3500/MPU6500/MPU9250. +	  This driver can be built as a module. The module will be called +	  inv-mpu-iio. + +config INV_IIO_MPU3050_ACCEL_SLAVE_BMA250 +	bool  "Invensense MPU3050 slave accelerometer device for bma250" +	depends on INV_MPU_IIO +	default n +	help +	  This is slave device enable MPU3050 accelerometer slave device. +	  Right now, it is only bma250. For other acceleromter device, +	  it can be added to this menu if the proper interface is filled. +	  There are some interface function to be defined. + +config DTS_INV_MPU_IIO +	bool  "Invensense MPU driver using Device Tree Structure (DTS)" +	depends on INV_MPU_IIO +	default n +	help +	  This enables Invensense MPU devices to use Device Tree Structure. +	  DTS must be enabled in the system. + diff --git a/drivers/staging/iio/imu/inv_mpu/Makefile b/drivers/staging/iio/imu/inv_mpu/Makefile new file mode 100644 index 00000000000..31b765b34d0 --- /dev/null +++ b/drivers/staging/iio/imu/inv_mpu/Makefile @@ -0,0 +1,37 @@ +# +# Makefile for Invensense inv-mpu-iio device. +# + +obj-$(CONFIG_INV_MPU_IIO) += inv-mpu-iio.o + +inv-mpu-iio-objs := inv_mpu_core.o +inv-mpu-iio-objs += inv_mpu_ring.o +inv-mpu-iio-objs += inv_mpu_trigger.o +inv-mpu-iio-objs += inv_mpu_misc.o +inv-mpu-iio-objs += inv_mpu3050_iio.o +inv-mpu-iio-objs += dmpDefaultMPU6050.o +inv-mpu-iio-objs += inv_slave_compass.o +inv-mpu-iio-objs += inv_slave_pressure.o + +CFLAGS_inv_mpu_core.o      += -Idrivers/staging/iio +CFLAGS_inv_mpu_ring.o      += -Idrivers/staging/iio +CFLAGS_inv_mpu_trigger.o   += -Idrivers/staging/iio +CFLAGS_inv_mpu_misc.o      += -Idrivers/staging/iio +CFLAGS_inv_mpu3050_iio.o   += -Idrivers/staging/iio +CFLAGS_dmpDefaultMPU6050.o += -Idrivers/staging/iio +CFLAGS_inv_slave_compass.o   += -Idrivers/staging/iio +CFLAGS_inv_slave_pressure.o   += -Idrivers/staging/iio + +# the Bosch BMA250 driver is added to the inv-mpu device driver because it +# must be connected to an MPU3050 device on the secondary slave bus. +ifeq ($(CONFIG_INV_IIO_MPU3050_ACCEL_SLAVE_BMA250), y) +inv-mpu-iio-objs += inv_slave_bma250.o +CFLAGS_inv_slave_bma250.o   += -Idrivers/staging/iio +endif + +# compile Invensense MPU IIO driver as DTS +ifeq ($(CONFIG_DTS_INV_MPU_IIO), y) +inv-mpu-iio-objs += inv_mpu_dts.o +CFLAGS_inv_mpu_dts.o   += -Idrivers/staging/iio +endif + diff --git a/drivers/staging/iio/imu/inv_mpu/README b/drivers/staging/iio/imu/inv_mpu/README new file mode 100644 index 00000000000..0963954a844 --- /dev/null +++ b/drivers/staging/iio/imu/inv_mpu/README @@ -0,0 +1,659 @@ +Kernel driver inv-mpu-iio +Author: Invensense <http://invensense.com> + +Table of Contents: +================== +- Description +- Integrating the Driver in the Linux Kernel +- Board and Platform Data +    > Interrupt Pin +    > Platform Data +- Board File Modifications for Secondary I2C Configuration +    > MPU-6050 + AKM8963 on the secondary I2C interface +    > MPU-6500 + AKM8963 on the secondary I2C interface +    > MPU-6515 + AKM8975 and BMP280 on the secondary I2C interface +    > MPU-9150 +    > MPU-9250 +    > MPU-3050 + BMA250 on the secondary I2C interface +- Board File Modifications for Invensense Devices +    > MPU-3050 +    > ITG-3500 +    > MPU-6050 +    > MPU-6515 +    > MPU-6500 +    > MPU-6XXX +    > MPU-9150 +    > MPU-9250 +- IIO Subsystem +    > Communicating with the Driver in Userspace +    > ITG-3500 +    > MPU-6050 and MPU-6500 +    > MPU-9150 +    > MPU-9250 +    > MPU-3050 + BMA250 on the secondary I2C interface +- Suspend and Resume +- Wake up CPU using event interrupt +- DMP Event +- Motion Event +- Streaming Data to an Userspace Application +- Recommended Sysfs Entry Setup Sequence +    > With DMP Firmware +    > Without DMP Firmware +- Test Applications +    > Running Test Applications with MPU-9150/MPU-6050/MPU-6500/MPU-9250 +    > Running Test Applications with MPU-3050/ITG-3500 + + +Description +=========== +This document describes how to install the Invensense device driver into a +Linux kernel. The Invensense driver currently supports the following sensors: +- ITG-3500 +- MPU-6050 +- MPU-9150 +- MPU-6500 +- MPU-9250 +- MPU-3050 +- MPU-6XXX(either MPU6050 or MPU6500, driver to do auto detection) + +The slave address of each device is either 0x68 or 0x69, depending on the AD0 +pin value of the device. Please refer to the appropriate product specification +document for further information regarding the AD0 pin. The driver supports both +addresses. + +The following files are included in this package: +- Kconfig +- Makefile +- inv_mpu_core.c +- inv_mpu_misc.c +- inv_mpu_trigger.c +- inv_mpu3050_iio.c +- inv_mpu_iio.h +- inv_mpu_ring.c +- inv_slave_bma250.c +- inv_slave_compass.c +- dmpDefaultMPU6050.c +- dmpkey.h +- dmpmap.h +- mpu.h + + +Integrating the Driver in the Linux Kernel +========================================== +Please add the files as follows: +- Add mpu.h to "kernel/include/linux". +- Add all other files to drivers/staging/iio/imu/inv_mpu +(another directory is acceptable, but this is the recommended destination) + +In order to see the driver in menuconfig when building the kernel, please +make modifications as shown below: + +    modify "drivers/staging/iio/imu/Kconfig" with: +    >> source "drivers/staging/iio/imu/inv_mpu/Kconfig" + +    modify "drivers/staging/iio/imu/Makefile" with: +    >> obj-y += inv_mpu/ + + +Board and Platform Data +======================= +In order to recognize the Invensense device on the I2C bus, the board file must +be modified. +The i2c_board_info instance must be defined as shown below. + +Interrupt Pin +------------- +The hardcoded value of 140 corresponds to the GPIO input pin connected to the +Invensense device's interrupt pin. +This pin will most likely be different for your platform, and the value should +be changed accordingly. + +Platform Data +------------- +The platform data (orientation matrix and secondary bus configurations) must be +modified as show below, according to your particular platform configuration. + +Please note that the MPU-9150 it is treated as a MPU-6050 with AKM8975 on the +device's secondary I2C interface. Thus the secondary I2C address must be +provided. + +Please note that the MPU-9250 it is treated as a MPU-6500 with AKM8963 on the +device's secondary I2C interface. Thus the secondary I2C address must be +provided. + +Board File Modifications for Secondary I2C Configuration +======================================================== +For the Panda Board, the board file can be found at +arch/arm/mach-omap2/board-omap4panda.c. +Please modify the pertinent baord file in your system according to the examples +shown below: + +MPU-6050 + AKM8963 on the secondary I2C interface +------------------------------------------------- +static struct mpu_platform_data gyro_platform_data = { +	.int_config  = 0x00, +	.level_shifter = 0, +	.orientation = {  -1,  0,  0, +			   0,  1,  0, +			   0,  0, -1 }, +	.sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS, +	.sec_slave_id   = COMPASS_ID_AK8963, +	.secondary_i2c_addr = 0x0E, +        .secondary_orientation = { 0,  1, 0, +                                   1,  0,  0, +                                   0,  0, -1 }, +}; + +MPU-6500 + AKM8963 on the secondary I2C interface +------------------------------------------------- +static struct mpu_platform_data gyro_platform_data = { +        .int_config  = 0x00, +        .level_shifter = 0, +        .orientation = {  -1,  0,  0, +                           0,  1,  0, +                           0,  0, -1 }, +        .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS, +        .sec_slave_id   = COMPASS_ID_AK8963, +        .secondary_i2c_addr = 0x0E, +        .secondary_orientation = { 0,  1, 0, +                                   1,  0,  0, +                                   0,  0, -1 }, +}; + +MPU-6500 + AK09911 on the secondary I2C interface +------------------------------------------------- +static struct mpu_platform_data gyro_platform_data = { +        .int_config  = 0x00, +        .level_shifter = 0, +        .orientation = {  -1,  0,  0, +                           0,  1,  0, +                           0,  0, -1 }, +        .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS, +        .sec_slave_id   = COMPASS_ID_AK09911, +        .secondary_i2c_addr = 0x0D, +        .secondary_orientation = { 0,  1, 0, +                                   1,  0,  0, +                                   0,  0, -1 }, +}; + +MPU-6515 + AK8975 + BMP280 on the secondary I2C interface +Note: sec_slave_XXX can only be compass or accelerometer in 3050 case. +      aux_slave_XXX can only be pressure. +-------------------------------------------------- +static struct mpu_platform_data mpu_data = { +        .int_config  = 0x00, +        .level_shifter = 0, +        .orientation = {  -1,  0,  0, +                           0,  1,  0, +                           0,  0, -1 }, +        .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS, +        .sec_slave_id   = COMPASS_ID_AK8975, +        .secondary_i2c_addr = 0x0E, +        .secondary_orientation = { 0,  1, 0, +                                   1,  0,  0, +                                   0,  0, -1 }, +        .aux_slave_type = SECONDARY_SLAVE_TYPE_PRESSURE, +        .aux_slave_id   = PRESSURE_ID_BMP280, +        .aux_i2c_addr = 0x77, +}; +static struct i2c_board_info __initdata chip_board_info[] = { +        { +                I2C_BOARD_INFO("mpu6515", 0x68), +                .irq = (IH_GPIO_BASE + MPUIRQ_GPIO), +                .platform_data = &mpu_data, +        } +}; + +MPU-9150 +-------- +For MPU-9150, please provide the following secondary I2C bus information. + +static struct mpu_platform_data gyro_platform_data = { +	.int_config  = 0x10, +	.level_shifter = 0, +	.orientation = {   1,  0,  0, +			   0,  1,  0, +			   0,  0,  1 }, +	.sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS, +	.sec_slave_id   = COMPASS_ID_AK8975, +	.secondary_i2c_addr = 0x0C, +        .secondary_orientation = { 0,  1, 0, +                                   1,  0,  0, +                                   0,  0, -1 }, +}; + +MPU-9250 +-------- +For MPU-9250, please provide the following secondary I2C bus information. + +static struct mpu_platform_data gyro_platform_data = { +        .int_config  = 0x00, +        .level_shifter = 0, +        .orientation = {   1,  0,  0, +                           0,  1,  0, +                           0,  0, 1 }, +        .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS, +        .sec_slave_id   = COMPASS_ID_AK8963, +        .secondary_i2c_addr = 0x0C, +        .secondary_orientation = { 0,  1, 0, +                                   -1, 0,  0, +                                   0,  0,  1 }, +}; + +MPU-3050 + BMA250 on the secondary I2C interface +------------------------------------------------ +For BMA250 on the secondary I2C bus, please provide the following information. + +static struct mpu_platform_data gyro_platform_data = { +	.int_config  = 0x10, +	.level_shifter = 0, +	.orientation = {  -1,  0,  0, +			   0,  1,  0, +			   0,  0, -1 }, +	.sec_slave_type = SECONDARY_SLAVE_TYPE_ACCEL, +	.sec_slave_id   = ACCEL_ID_BMA250, +	.secondary_i2c_addr = 0x18, +}; + + +Board File Modifications for Invensense Devices +=============================================== +For Invensense devices, please provide the i2c init data as shown in the +examples below. + +In the _i2c_init function, the device is registered in the following manner: + +    // arch/arm/mach-omap2/board-omap4panda.c +    // in static int __init omap4_panda_i2c_init(void) +    omap_register_i2c_bus(4, 400, +                          single_chip_board_info, +                          ARRAY_SIZE(single_chip_board_info)); + +MPU-3050 +-------- +static struct i2c_board_info __initdata single_chip_board_info[] = { +	{ +		I2C_BOARD_INFO("mpu3050", 0x68), +		.irq = (IH_GPIO_BASE + MPUIRQ_GPIO), +		.platform_data = &gyro_platform_data, +	}, +}; + +ITG-3500 +-------- +static struct i2c_board_info __initdata single_chip_board_info[] = { +	{ +		I2C_BOARD_INFO("itg3500", 0x68), +		.irq = (IH_GPIO_BASE + MPUIRQ_GPIO), +                .platform_data = &gyro_platform_data, +	}, +}; + +MPU6050 +------- +static struct i2c_board_info __initdata single_chip_board_info[] = { +	{ +		I2C_BOARD_INFO("mpu6050", 0x68), +		.irq = (IH_GPIO_BASE + MPUIRQ_GPIO), +		.platform_data = &gyro_platform_data, +	}, +}; + +MPU6500 +------- +static struct i2c_board_info __initdata single_chip_board_info[] = { +        { +                I2C_BOARD_INFO("mpu6500", 0x68), +                .irq = (IH_GPIO_BASE + MPUIRQ_GPIO), +                .platform_data = &gyro_platform_data, +        }, +}; + +MPU6XXX +------- +static struct i2c_board_info __initdata single_chip_board_info[] = { +        { +                I2C_BOARD_INFO("mpu6xxx", 0x68), +                .irq = (IH_GPIO_BASE + MPUIRQ_GPIO), +                .platform_data = &gyro_platform_data, +        }, +}; + +MPU9150 +------- +arch/arm/mach-omap2/board-omap4panda.c +static struct i2c_board_info __initdata single_chip_board_info[] = { +	{ +		I2C_BOARD_INFO("mpu9150", 0x68), +		.irq = (IH_GPIO_BASE + MPUIRQ_GPIO), +		.platform_data = &gyro_platform_data, +	}, +}; + +MPU9250 +------- +arch/arm/mach-omap2/board-omap4panda.c +static struct i2c_board_info __initdata single_chip_board_info[] = { +        { +                I2C_BOARD_INFO("mpu9250", 0x68), +                .irq = (IH_GPIO_BASE + MPUIRQ_GPIO), +                .platform_data = &gyro_platform_data, +        }, +}; + +IIO subsystem +============= +A successful installation will create the following two new directories under +/sys/bus/iio/devices: +    - iio:device0 +    - trigger0 + +Also, a new file, "iio:device0", will be created in the /dev/ diretory. +(if you have more than one IIO device, the file will be named "iio:deviceX", +where X is a number) + + +Communicating with the Driver in Userspace +------------------------------------------ +The driver generates several files in sysfs upon installation. +These files are used to communicate with the driver. The files can be found +at /sys/bus/iio/devices/iio:device0 (or ../iio:deviceX as shown above). + +A brief description of the pertinent files for each Invensense device is shown +below: + +ITG-3500 +-------- +temperature (Read-only) +--Read temperature data directly from the temperature register. + +sampling_frequency (Read/write) +--Configure the ADC sampling rate and FIFO output rate. + +sampling_frequency_available(read-only) +--show commonly used frequency + +clock_source (Read-only) +--Check which clock-source is used by the chip. + +power_state (Read/write) +--turn on/off the power supply + +self_test (read-only) +--read this entry trigger self test. The return value is D. +D is the success/fail. +For different chip, the result is different for success/fail. +1 means success 0 means fail. The LSB of D is for gyro; the bit +next to LSB of D is for accel. The bit 2 of D is for compass result. + +key (read-only) +--show the key value of this driver. Used by MPL. + +gyro_matrix (read-only) +--show the orientation matrix obtained from the board file. + +MPU-6050 and MPU-6500 +--------------------- +MPU-6050 and MPU-6500 have all sysfs files belonging to ITG-3500 (shown above). +In addition, it has the files below: + +gyro_enable (read/write) +--enable/disable gyro functionality. Affects raw_gyro. Turning this off this +  will shut down gyro and save power. + +accl_enable (read/write) +--enable/disable accelerometer functionality. Affects raw_accl. +Turning this off this will shut down accel and save power. + +firmware_loaded (read/write) +--Flag indicating the whether firmware is loaded or not in the DMP engine. +0 means no firmware loaded. 1 means firmware is already loaded . This +flag can only be written as 0. It internally updates to 1. + +dmp_on(read/write) +--This entry controls whether to run DMP or not. +Write 1 to enable DMP and write 0 to disable dmp. +Please note that firmware_loaded must be 1 in order to enable DMP. + +dmp_int_on(read/write) +--This entry controls whether dmp interrupt is on/off. +Please note that firmware_loaded must be 1. +Also, we'd like to remind you that it is sometimes advantageous to +turn interrupts off while the DMP is running. + +dmp_output_rate +--control dmp output rate when dmp is on. + +dmp_event_int_on(read/write) +--This entry controls whether dmp event interrupt is on/off. +Please note that turning this on will turn off the data interrupt. +Interrupts will be generated only when events occur. +This is useful for saving power when the system is waiting for a special event +to wake up. + +dmp_firmware (write only binary file) +--DMP firmware code is loaded into this entry. +If loading is successful, the firmware_loaded flag will be updated to 1. +In order to load new firmware, the firmware_loaded flag must be first set to 0. + +accel_matrix +--orientation matrix for accelerometer. + +quaternion_on +--Turn on/off quaterniion data output. DMP is required for this feature. + +pedometer_time +pedometer_steps, +--Pedometer related entries + +event_tap +event_display_orientation +event_accel_motion +event_smd +--Event related entries. +Please poll these entries to read their values. Direct reads will yield +meaningless results. +Further details are provided in the DMP Events section of this README. + +tap_on +--Controls tap function of DMP + +tap_time +tap_min_count +tap_threshold +--Tap related entries. Controls various parameters of tap function. + +display_orientation_on +--Turn on/off display orientation function of DMP. + +smd_enable +enable SMD(Significant Motion Detection) detection. + +smd_threshold +This set the threshold of the motion when SMD start to be triggered. The +value is in acclerometer counts. + +smd_delay_threshold +This sets the threshold of time after which SMD can be triggered. +The value is in seconds. + +smd_delay_threshold2 +This sets the threshold of time during which SMD can be triggered (after the +smd_delay_threshold timer has expired). +The value is in seconds. + +quaternion_on +--Turn on/off quaterniion data output. DMP is required for this feature. + +Low power accel motion interrupt related settings. +if motion_lpa_on is set, this will disable all engines except accel. Accel will +enter low power mode and the whole chip will be turned on/off at specific frequency. +----------------------------------------------------------------------------- +motion_lpa_threshold +--set motion threshold. in mg. The maximum is 1020mg and resolution is 32mg. + +motion_lpa_on +--turn on/off motion function. + +motion_lpa_freq +--motion lpa frequency. which determines power on/off frequency. +------------------------------------------------------------------------------ +MPU-9150 +-------- +MPU-9150 has all of MPU-6050's entries. It also has two additional entries, +described below. + +compass_enable (read/write) +--Enables compass function. + +compass_matrix (read-only) +--Compass orientation matrix + +MPU-3050 with BMA250 on secondary I2C interface +----------------------------------------------- +MPU-3050 with BMA250 on the secondary I2C interface has ever ITG-3500 entry. +It also has two additional entries, shown below: + +accl_matrix + +accl_enable + +Suspend and Resume +=================================================== +The suspend and resume functions are call backs registered to the system +and executed when the system goes in suspend and resumes. +It is enabled when CONFIG_PM is defined. +The current behavior is simple: +- suspend will turn off the chip +- resume will turn on the chip + +However, it is possible for the driver to do more complex things; +for example, leaving pedometers running when system is off. This can save whole +system power while letting pedometer working. Other behaviors are possible +too. + +Wake up CPU using event interrupt +==================================================== +It is common practice in current mobile device to save power as much as +possible. Ususally the device can sleep when not use and only be wake up when in use. +Invensense MPU series provides an efficient way of waking up CPU by events. +The events could be pedometer steps, tap action, SMD(significant Motion Detection), +or any customer defined actions. CPU can sleep to save power, when event happens, +DMP will calculate the motion data to detemine whether a pre-defined event happens +or not. If an pre-defined happens, an interrupt is generated to wake up the CPU +to do further processing.  +The requirements of doing event wake is the following: +1. Add enable_irq_wake() either in the driver or in the board file. +2. Connect the interrupt pin to the CPU wake up pin. +3. Configure CPU to be woken up by wake up pin. + +DMP Event +========= +A DMP Event is an event that is output by the DMP unit within the Invensense +device (MPU). +Only the MPU-6050, MPU-6500, MPU-9250, MPU-9150, MPU-9250 feature the DMP. + +There are four sysfs entries for DMP events: +- event_tap +- event_display_orientation +- event_accel_motion +- event_smd + +These events must be polled before reading. + +The proper method to poll sysfs is as follows: +1. open file. +2. dummy read. +3. poll. +4. once the poll passed, use fopen and fread to read the sysfs entry. +5. interpret the data. + +Streaming Data to an Userspace Application +========================================== +When streaming data to an userspace application, we recommend that you access +gyro/accel/compass data via /dev/iio:device0. + +Please follow the steps below to read data at a constant rate from the driver: + +1. Write a 1 to power_state to turn on the chip. This is the default setting +   after installing the driver. +2. Write the desired output rate to fifo_rate. +3. Write 1 to enable to turn on the event. +4. Read /dev/iio:device0 to get a string of gyro/accel/compass data. +5. Parse this string to obtain each gyro/accel/compass element. +6. If dmp firmware code is loaded, use "dmp_on" to enable/disable dmp. +7. If compass is enabled, the output will contain compass data. + + +Recommended Sysfs Entry Setup Senquence +======================================= + +Without DMP Firmware +-------------------- +1. Set "power_state" to 1, +2. Set the scale and fifo rate values according to your needs. +3. Set gyro_enable, accel_enable, and compass_enable according to your needs. +   For example: +    - If you only want gyro data, set accel_enable to 0 (and compass_enable to +      0, if applicable). +    - If you only want accel data, set gyro_enable to 0 (and compass_enable to +      0, if applicable). +    - If you only want compass data, set gyro_enable to 0 and accel_enable to 0. +4. Set "enable" to 1. +5. You will now get the output that you want. + +With DMP Firmware +----------------- +1. Set "power_state" to 1. +2. Write "0" to firmware_loaded if it is not zero already. +3. Load firmware into "dmp_firmware" as a whole. Don't split the DMP firmware +   image. +4. Make sure firmware_loaded is 1 after loading the DMP image. +5. Make appropriate configurations as shown above in the "without DMP firmware" +   case. +6. Set dmp_on to 1. +7. Set "enable" to 1. + +Please note that the enable function uses the enable entry under +"/sys/bus/iio/devices/iio:device0/buffer" + +Test Applications +================= +A test application is located under software/simple_apps/mpu_iio. +This application is stand-alone in that it cannot be run concurrently with other +entities trying to access the device node(s) or sysfs entries; in particular, +the + +Running Test Applications with MPU-9150/MPU-6050/MPU-6500/MPU-9250 +--------------------------------------------------------- +To run test applications with MPU-9150, MPU-9250, MPU-6050, or MPU-6500 devices, +please use the following commands: + +1. For tap/display orientation events: +   mpu_iio -c 10 -l 3 -p + +2. In addition, to test the motion interrupt (and no_motion on MPU6050) use: +   mpu_iio -c 10 -l 3 -p -m + +3. For printing data normally: +   mpu_iio -c 10 -l 3 -r + +Running Test Applications with MPU-3050/ITG-3500 +------------------------------------------------ +To run test applications with MPU-3050 or ITG-3500 devices, +please use the following command: + +1. For printing data normally: +   mpu_iio -c 10 -l 3 -r + +Please use mpu_iio.c and iio_utils.h as example code for your development +purposes. + +Stress test application +================================= +A stress test application is located under software/simple_apps/stress_iio. +This application simulates HAL's usage calls to the driver. It creates three +threads. One for data read; one for event read; one for sysfs control. +It can run without any parameters or run with some control parameters. Please +see README in the same directories for details. + diff --git a/drivers/staging/iio/imu/inv_mpu/dmpDefaultMPU6050.c b/drivers/staging/iio/imu/inv_mpu/dmpDefaultMPU6050.c new file mode 100644 index 00000000000..81e63960f39 --- /dev/null +++ b/drivers/staging/iio/imu/inv_mpu/dmpDefaultMPU6050.c @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2012 Invensense, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include "inv_mpu_iio.h" +#include "dmpKey.h" +#include "dmpmap.h" + +#define CFG_OUT_PRESS               (1823) +#define CFG_PED_ENABLE              (1936) +#define CFG_OUT_GYRO                (1755) +#define CFG_PEDSTEP_DET             (2417) +#define OUT_GYRO_DAT                (1764) +#define CFG_FIFO_INT                (1934) +#define OUT_CPASS_DAT               (1798) +#define CFG_AUTH                    (1051) +#define OUT_ACCL_DAT                (1730) +#define FCFG_1                      (1078) +#define FCFG_3                      (1103) +#define FCFG_2                      (1082) +#define CFG_OUT_CPASS               (1789) +#define FCFG_7                      (1089) +#define CFG_OUT_3QUAT               (1617) +#define OUT_PRESS_DAT               (1832) +#define OUT_3QUAT_DAT               (1627) +#define CFG_7                       (1300) +#define OUT_PQUAT_DAT               (1696) +#define CFG_OUT_6QUAT               (1652) +#define CFG_PED_INT                 (2406) +#define SMD_TP2                     (1265) +#define SMD_TP1                     (1244) +#define CFG_MOTION_BIAS             (1302) +#define CFG_OUT_ACCL                (1721) +#define CFG_OUT_STEPDET             (1587) +#define OUT_6QUAT_DAT               (1662) +#define CFG_OUT_PQUAT               (1687) + +#define D_0_22                  (22+512) +#define D_0_24                  (24+512) + +#define D_0_36                  (36) +#define D_0_52                  (52) +#define D_0_96                  (96) +#define D_0_104                 (104) +#define D_0_108                 (108) +#define D_0_163                 (163) +#define D_0_188                 (188) +#define D_0_192                 (192) +#define D_0_224                 (224) +#define D_0_228                 (228) +#define D_0_232                 (232) +#define D_0_236                 (236) + +#define D_1_2                   (256 + 2) +#define D_1_4                   (256 + 4) +#define D_1_8                   (256 + 8) +#define D_1_10                  (256 + 10) +#define D_1_24                  (256 + 24) +#define D_1_28                  (256 + 28) +#define D_1_36                  (256 + 36) +#define D_1_40                  (256 + 40) +#define D_1_44                  (256 + 44) +#define D_1_72                  (256 + 72) +#define D_1_74                  (256 + 74) +#define D_1_79                  (256 + 79) +#define D_1_88                  (256 + 88) +#define D_1_90                  (256 + 90) +#define D_1_92                  (256 + 92) +#define D_1_96                  (256 + 96) +#define D_1_98                  (256 + 98) +#define D_1_106                 (256 + 106) +#define D_1_108                 (256 + 108) +#define D_1_112                 (256 + 112) +#define D_1_128                 (256 + 144) +#define D_1_152                 (256 + 12) +#define D_1_160                 (256 + 160) +#define D_1_176                 (256 + 176) +#define D_1_178                 (256 + 178) +#define D_1_218                 (256 + 218) +#define D_1_232                 (256 + 232) +#define D_1_236                 (256 + 236) +#define D_1_240                 (256 + 240) +#define D_1_244                 (256 + 244) +#define D_1_250                 (256 + 250) +#define D_1_252                 (256 + 252) +#define D_2_12                  (512 + 12) +#define D_2_96                  (512 + 96) +#define D_2_108                 (512 + 108) +#define D_2_208                 (512 + 208) +#define D_2_224                 (512 + 224) +#define D_2_236                 (512 + 236) +#define D_2_244                 (512 + 244) +#define D_2_248                 (512 + 248) +#define D_2_252                 (512 + 252) +#define CPASS_BIAS_X            (30 * 16 + 4) +#define CPASS_BIAS_Y            (30 * 16 + 8) +#define CPASS_BIAS_Z            (30 * 16 + 12) +#define CPASS_MTX_00            (32 * 16 + 4) +#define CPASS_MTX_01            (32 * 16 + 8) +#define CPASS_MTX_02            (36 * 16 + 12) +#define CPASS_MTX_10            (33 * 16) +#define CPASS_MTX_11            (33 * 16 + 4) +#define CPASS_MTX_12            (33 * 16 + 8) +#define CPASS_MTX_20            (33 * 16 + 12) +#define CPASS_MTX_21            (34 * 16 + 4) +#define CPASS_MTX_22            (34 * 16 + 8) +#define D_EXT_GYRO_BIAS_X       (61 * 16) +#define D_EXT_GYRO_BIAS_Y       (61 * 16 + 4) +#define D_EXT_GYRO_BIAS_Z       (61 * 16 + 8) +#define D_ACT0                  (40 * 16) +#define D_ACSX                  (40 * 16 + 4) +#define D_ACSY                  (40 * 16 + 8) +#define D_ACSZ                  (40 * 16 + 12) + +#define FLICK_MSG               (45 * 16 + 4) +#define FLICK_COUNTER           (45 * 16 + 8) +#define FLICK_LOWER             (45 * 16 + 12) +#define FLICK_UPPER             (46 * 16 + 12) + +#define D_SMD_ENABLE            (18 * 16) +#define D_SMD_MOT_THLD          (21 * 16 + 8) +#define D_SMD_DELAY_THLD        (21 * 16 + 4) +#define D_SMD_DELAY2_THLD       (21 * 16 + 12) +#define D_SMD_EXE_STATE         (22 * 16) +#define D_SMD_DELAY_CNTR        (21 * 16) + +#define D_WF_GESTURE_TIME_THRSH	(25 * 16 + 8) +#define D_WF_GESTURE_TILT_ERROR	(25 * 16 + 12) +#define D_WF_GESTURE_TILT_THRSH	(26 * 16 + 8) +#define D_WF_GESTURE_TILT_REJECT_THRSH	(26 * 16 + 12) + +#define D_AUTH_OUT              (992) +#define D_AUTH_IN               (996) +#define D_AUTH_A                (1000) +#define D_AUTH_B                (1004) + +#define D_PEDSTD_BP_B           (768 + 0x1C) +#define D_PEDSTD_BP_A4          (768 + 0x40) +#define D_PEDSTD_BP_A3          (768 + 0x44) +#define D_PEDSTD_BP_A2          (768 + 0x48) +#define D_PEDSTD_BP_A1          (768 + 0x4C) +#define D_PEDSTD_SB             (768 + 0x28) +#define D_PEDSTD_SB_TIME        (768 + 0x2C) +#define D_PEDSTD_PEAKTHRSH      (768 + 0x98) +#define D_PEDSTD_TIML           (768 + 0x2A) +#define D_PEDSTD_TIMH           (768 + 0x2E) +#define D_PEDSTD_PEAK           (768 + 0X94) +#define D_PEDSTD_STEPCTR        (768 + 0x60) +#define D_PEDSTD_STEPCTR2       (58 * 16 + 8) +#define D_PEDSTD_TIMECTR        (964) +#define D_PEDSTD_DECI           (768 + 0xA0) +#define D_PEDSTD_SB2			(60 * 16 + 14) +#define D_STPDET_TIMESTAMP      (28 * 16 + 8) +#define D_PEDSTD_DRIVE_STATE    (58) + +#define D_HOST_NO_MOT           (976) +#define D_ACCEL_BIAS            (660) + +#define D_BM_BATCH_CNTR         (27 * 16 + 4) +#define D_BM_BATCH_THLD         (27 * 16 + 12) +#define D_BM_ENABLE             (28 * 16 + 6) +#define D_BM_NUMWORD_TOFILL     (28 * 16 + 4) + +#define D_SO_DATA               (4 * 16 + 2) + +#define D_P_HW_ID               (22 * 16 + 10) +#define D_P_INIT                (22 * 16 + 2) + +#define D_DMP_RUN_CNTR          (24*16) + +#define D_ODR_S0                (45*16+8) +#define D_ODR_S1                (45*16+12) +#define D_ODR_S2                (45*16+10) +#define D_ODR_S3                (45*16+14) +#define D_ODR_S4                (46*16+8) +#define D_ODR_S5                (46*16+12) +#define D_ODR_S6                (46*16+10) +#define D_ODR_S7                (46*16+14) +#define D_ODR_S8                (42*16+8) +#define D_ODR_S9                (42*16+12) + +#define D_ODR_CNTR_S0           (45*16) +#define D_ODR_CNTR_S1           (45*16+4) +#define D_ODR_CNTR_S2           (45*16+2) +#define D_ODR_CNTR_S3           (45*16+6) +#define D_ODR_CNTR_S4           (46*16) +#define D_ODR_CNTR_S5           (46*16+4) +#define D_ODR_CNTR_S6           (46*16+2) +#define D_ODR_CNTR_S7           (46*16+6) +#define D_ODR_CNTR_S8           (42*16) +#define D_ODR_CNTR_S9           (42*16+4) + +#define D_FS_LPQ0               (59*16) +#define D_FS_LPQ1               (59*16 + 4) +#define D_FS_LPQ2               (59*16 + 8) +#define D_FS_LPQ3               (59*16 + 12) + +#define D_FS_Q0                 (12*16) +#define D_FS_Q1                 (12*16 + 4) +#define D_FS_Q2                 (12*16 + 8) +#define D_FS_Q3                 (12*16 + 12) + +#define D_FS_9Q0                (2*16) +#define D_FS_9Q1                (2*16 + 4) +#define D_FS_9Q2                (2*16 + 8) +#define D_FS_9Q3                (2*16 + 12) + +static const struct tKeyLabel dmpTConfig[] = { +	{KEY_CFG_OUT_ACCL,              CFG_OUT_ACCL}, +	{KEY_CFG_OUT_GYRO,              CFG_OUT_GYRO}, +	{KEY_CFG_OUT_3QUAT,             CFG_OUT_3QUAT}, +	{KEY_CFG_OUT_6QUAT,             CFG_OUT_6QUAT}, +	{KEY_CFG_OUT_PQUAT,             CFG_OUT_PQUAT}, +	{KEY_CFG_PED_ENABLE,            CFG_PED_ENABLE}, +	{KEY_CFG_FIFO_INT,              CFG_FIFO_INT}, +	{KEY_CFG_AUTH,                  CFG_AUTH}, +	{KEY_FCFG_1,                    FCFG_1}, +	{KEY_FCFG_3,                    FCFG_3}, +	{KEY_FCFG_2,                    FCFG_2}, +	{KEY_FCFG_7,                    FCFG_7}, +	{KEY_CFG_7,                     CFG_7}, +	{KEY_CFG_MOTION_BIAS,           CFG_MOTION_BIAS}, +	{KEY_CFG_PEDSTEP_DET,           CFG_PEDSTEP_DET}, +	{KEY_D_0_22,                    D_0_22}, +	{KEY_D_0_96,                    D_0_96}, +	{KEY_D_0_104,                   D_0_104}, +	{KEY_D_0_108,                   D_0_108}, +	{KEY_D_1_36,                    D_1_36}, +	{KEY_D_1_40,                    D_1_40}, +	{KEY_D_1_44,                    D_1_44}, +	{KEY_D_1_72,                    D_1_72}, +	{KEY_D_1_74,                    D_1_74}, +	{KEY_D_1_79,                    D_1_79}, +	{KEY_D_1_88,                    D_1_88}, +	{KEY_D_1_90,                    D_1_90}, +	{KEY_D_1_92,                    D_1_92}, +	{KEY_D_1_160,                   D_1_160}, +	{KEY_D_1_176,                   D_1_176}, +	{KEY_D_1_218,                   D_1_218}, +	{KEY_D_1_232,                   D_1_232}, +	{KEY_D_1_250,                   D_1_250}, +	{KEY_DMP_SH_TH_Y,               DMP_SH_TH_Y}, +	{KEY_DMP_SH_TH_X,               DMP_SH_TH_X}, +	{KEY_DMP_SH_TH_Z,               DMP_SH_TH_Z}, +	{KEY_DMP_ORIENT,                DMP_ORIENT}, +	{KEY_D_AUTH_OUT,                D_AUTH_OUT}, +	{KEY_D_AUTH_IN,                 D_AUTH_IN}, +	{KEY_D_AUTH_A,                  D_AUTH_A}, +	{KEY_D_AUTH_B,                  D_AUTH_B}, +	{KEY_CPASS_BIAS_X,          CPASS_BIAS_X}, +	{KEY_CPASS_BIAS_Y,          CPASS_BIAS_Y}, +	{KEY_CPASS_BIAS_Z,          CPASS_BIAS_Z}, +	{KEY_CPASS_MTX_00,          CPASS_MTX_00}, +	{KEY_CPASS_MTX_01,          CPASS_MTX_01}, +	{KEY_CPASS_MTX_02,          CPASS_MTX_02}, +	{KEY_CPASS_MTX_10,          CPASS_MTX_10}, +	{KEY_CPASS_MTX_11,          CPASS_MTX_11}, +	{KEY_CPASS_MTX_12,          CPASS_MTX_12}, +	{KEY_CPASS_MTX_20,          CPASS_MTX_20}, +	{KEY_CPASS_MTX_21,          CPASS_MTX_21}, +	{KEY_CPASS_MTX_22,          CPASS_MTX_22}, +	{KEY_D_ACT0,                    D_ACT0}, +	{KEY_D_ACSX,                    D_ACSX}, +	{KEY_D_ACSY,                    D_ACSY}, +	{KEY_D_ACSZ,                    D_ACSZ}, +	{KEY_D_PEDSTD_BP_B,             D_PEDSTD_BP_B}, +	{KEY_D_PEDSTD_BP_A4,            D_PEDSTD_BP_A4}, +	{KEY_D_PEDSTD_BP_A3,            D_PEDSTD_BP_A3}, +	{KEY_D_PEDSTD_BP_A2,            D_PEDSTD_BP_A2}, +	{KEY_D_PEDSTD_BP_A1,            D_PEDSTD_BP_A1}, +	{KEY_D_PEDSTD_SB,               D_PEDSTD_SB}, +	{KEY_D_PEDSTD_SB_TIME,          D_PEDSTD_SB_TIME}, +	{KEY_D_PEDSTD_PEAKTHRSH,        D_PEDSTD_PEAKTHRSH}, +	{KEY_D_PEDSTD_TIML,             D_PEDSTD_TIML}, +	{KEY_D_PEDSTD_TIMH,             D_PEDSTD_TIMH}, +	{KEY_D_PEDSTD_PEAK,             D_PEDSTD_PEAK}, +	{KEY_D_PEDSTD_STEPCTR,          D_PEDSTD_STEPCTR}, +	{KEY_D_PEDSTD_STEPCTR2,         D_PEDSTD_STEPCTR2}, +	{KEY_D_PEDSTD_TIMECTR,          D_PEDSTD_TIMECTR}, +	{KEY_D_PEDSTD_DECI,             D_PEDSTD_DECI}, +	{KEY_D_PEDSTD_SB2,				D_PEDSTD_SB2}, +	{KEY_D_PEDSTD_DRIVE_STATE,      D_PEDSTD_DRIVE_STATE}, +	{KEY_D_STPDET_TIMESTAMP,		D_STPDET_TIMESTAMP}, +	{KEY_D_HOST_NO_MOT,             D_HOST_NO_MOT}, +	{KEY_D_ACCEL_BIAS,              D_ACCEL_BIAS}, +	{KEY_CFG_EXT_GYRO_BIAS_X,       D_EXT_GYRO_BIAS_X}, +	{KEY_CFG_EXT_GYRO_BIAS_Y,       D_EXT_GYRO_BIAS_Y}, +	{KEY_CFG_EXT_GYRO_BIAS_Z,       D_EXT_GYRO_BIAS_Z}, +	{KEY_CFG_PED_INT,               CFG_PED_INT}, +	{KEY_SMD_ENABLE,                D_SMD_ENABLE}, +	{KEY_SMD_ACCEL_THLD,            D_SMD_MOT_THLD}, +	{KEY_SMD_DELAY_THLD,            D_SMD_DELAY_THLD}, +	{KEY_SMD_DELAY2_THLD,           D_SMD_DELAY2_THLD}, +	{KEY_SMD_ENABLE_TESTPT1,        SMD_TP1}, +	{KEY_SMD_ENABLE_TESTPT2,        SMD_TP2}, +	{KEY_SMD_EXE_STATE,             D_SMD_EXE_STATE}, +	{KEY_SMD_DELAY_CNTR,            D_SMD_DELAY_CNTR}, +	{KEY_BM_ENABLE,                 D_BM_ENABLE}, +	{KEY_BM_BATCH_CNTR,             D_BM_BATCH_CNTR}, +	{KEY_BM_BATCH_THLD,             D_BM_BATCH_THLD}, +	{KEY_BM_NUMWORD_TOFILL,         D_BM_NUMWORD_TOFILL}, +	{KEY_SO_DATA,                   D_SO_DATA}, +	{KEY_P_INIT,                    D_P_INIT}, +	{KEY_CFG_OUT_CPASS,             CFG_OUT_CPASS}, +	{KEY_CFG_OUT_PRESS,             CFG_OUT_PRESS}, +	{KEY_CFG_OUT_STEPDET,           CFG_OUT_STEPDET}, +	{KEY_CFG_3QUAT_ODR,             D_ODR_S1}, +	{KEY_CFG_6QUAT_ODR,             D_ODR_S2}, +	{KEY_CFG_PQUAT6_ODR,            D_ODR_S3}, +	{KEY_CFG_ACCL_ODR,              D_ODR_S4}, +	{KEY_CFG_GYRO_ODR,              D_ODR_S5}, +	{KEY_CFG_CPASS_ODR,             D_ODR_S6}, +	{KEY_CFG_PRESS_ODR,             D_ODR_S7}, +	{KEY_CFG_9QUAT_ODR,             D_ODR_S8}, +	{KEY_CFG_PQUAT9_ODR,            D_ODR_S9}, +	{KEY_ODR_CNTR_3QUAT,            D_ODR_CNTR_S1}, +	{KEY_ODR_CNTR_6QUAT,            D_ODR_CNTR_S2}, +	{KEY_ODR_CNTR_PQUAT,            D_ODR_CNTR_S3}, +	{KEY_ODR_CNTR_ACCL,             D_ODR_CNTR_S4}, +	{KEY_ODR_CNTR_GYRO,             D_ODR_CNTR_S5}, +	{KEY_ODR_CNTR_CPASS,            D_ODR_CNTR_S6}, +	{KEY_ODR_CNTR_PRESS,            D_ODR_CNTR_S7}, +	{KEY_ODR_CNTR_9QUAT,			D_ODR_CNTR_S8}, +	{KEY_ODR_CNTR_PQUAT9,			D_ODR_CNTR_S9}, +	{KEY_DMP_RUN_CNTR,              D_DMP_RUN_CNTR}, +	{KEY_DMP_LPQ0,                  D_FS_LPQ0}, +	{KEY_DMP_LPQ1,                  D_FS_LPQ1}, +	{KEY_DMP_LPQ2,                  D_FS_LPQ2}, +	{KEY_DMP_LPQ3,                  D_FS_LPQ3}, +	{KEY_DMP_Q0,                    D_FS_Q0}, +	{KEY_DMP_Q1,                    D_FS_Q1}, +	{KEY_DMP_Q2,                    D_FS_Q2}, +	{KEY_DMP_Q3,                    D_FS_Q3}, +	{KEY_DMP_9Q0,					D_FS_9Q0}, +	{KEY_DMP_9Q1,					D_FS_9Q1}, +	{KEY_DMP_9Q2,					D_FS_9Q2}, +	{KEY_DMP_9Q3,					D_FS_9Q3}, +	{KEY_TEST_01,                   OUT_ACCL_DAT}, +	{KEY_TEST_02,                   OUT_GYRO_DAT}, +	{KEY_TEST_03,                   OUT_CPASS_DAT}, +	{KEY_TEST_04,                   OUT_PRESS_DAT}, +	{KEY_TEST_05,                   OUT_3QUAT_DAT}, +	{KEY_TEST_06,                   OUT_6QUAT_DAT}, +	{KEY_TEST_07,                   OUT_PQUAT_DAT} +}; +#define NUM_LOCAL_KEYS (sizeof(dmpTConfig)/sizeof(dmpTConfig[0])) + +static struct tKeyLabel keys[NUM_KEYS]; + +unsigned short inv_dmp_get_address(unsigned short key) +{ +	static int isSorted; + +	if (!isSorted) { +		int kk; +		for (kk = 0; kk < NUM_KEYS; ++kk) { +			keys[kk].addr = 0xffff; +			keys[kk].key = kk; +		} +		for (kk = 0; kk < NUM_LOCAL_KEYS; ++kk) +			keys[dmpTConfig[kk].key].addr = dmpTConfig[kk].addr; +		isSorted = 1; +	} +	if (key >= NUM_KEYS) { +		pr_err("ERROR!! key not exist=%d!\n", key); +		return 0xffff; +	} +	if (0xffff == keys[key].addr) +		pr_err("ERROR!!key not local=%d!\n", key); +	return keys[key].addr; +} diff --git a/drivers/staging/iio/imu/inv_mpu/dmpKey.h b/drivers/staging/iio/imu/inv_mpu/dmpKey.h new file mode 100644 index 00000000000..f6173b3c0f8 --- /dev/null +++ b/drivers/staging/iio/imu/inv_mpu/dmpKey.h @@ -0,0 +1,607 @@ +/* + * Copyright (C) 2012 Invensense, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ +#ifndef DMPKEY_H__ +#define DMPKEY_H__ + +#define KEY_CFG_25                  (0) +#define KEY_CFG_24                  (KEY_CFG_25 + 1) +#define KEY_CFG_26                  (KEY_CFG_24 + 1) +#define KEY_CFG_27                  (KEY_CFG_26 + 1) +#define KEY_CFG_21                  (KEY_CFG_27 + 1) +#define KEY_CFG_20                  (KEY_CFG_21 + 1) +#define KEY_CFG_TAP4                (KEY_CFG_20 + 1) +#define KEY_CFG_TAP5                (KEY_CFG_TAP4 + 1) +#define KEY_CFG_TAP6                (KEY_CFG_TAP5 + 1) +#define KEY_CFG_TAP7                (KEY_CFG_TAP6 + 1) +#define KEY_CFG_TAP0                (KEY_CFG_TAP7 + 1) +#define KEY_CFG_TAP1                (KEY_CFG_TAP0 + 1) +#define KEY_CFG_TAP2                (KEY_CFG_TAP1 + 1) +#define KEY_CFG_TAP3                (KEY_CFG_TAP2 + 1) +#define KEY_CFG_TAP_QUANTIZE        (KEY_CFG_TAP3 + 1) +#define KEY_CFG_TAP_JERK            (KEY_CFG_TAP_QUANTIZE + 1) +#define KEY_CFG_DR_INT              (KEY_CFG_TAP_JERK + 1) +#define KEY_CFG_AUTH                (KEY_CFG_DR_INT + 1) +#define KEY_CFG_TAP_SAVE_ACCB       (KEY_CFG_AUTH + 1) +#define KEY_CFG_TAP_CLEAR_STICKY    (KEY_CFG_TAP_SAVE_ACCB + 1) +#define KEY_CFG_FIFO_ON_EVENT       (KEY_CFG_TAP_CLEAR_STICKY + 1) +#define KEY_FCFG_ACCEL_INPUT        (KEY_CFG_FIFO_ON_EVENT + 1) +#define KEY_FCFG_ACCEL_INIT         (KEY_FCFG_ACCEL_INPUT + 1) +#define KEY_CFG_23                  (KEY_FCFG_ACCEL_INIT + 1) +#define KEY_FCFG_1                  (KEY_CFG_23 + 1) +#define KEY_FCFG_3                  (KEY_FCFG_1 + 1) +#define KEY_FCFG_2                  (KEY_FCFG_3 + 1) +#define KEY_CFG_3D                  (KEY_FCFG_2 + 1) +#define KEY_CFG_3B                  (KEY_CFG_3D + 1) +#define KEY_CFG_3C                  (KEY_CFG_3B + 1) +#define KEY_FCFG_5                  (KEY_CFG_3C + 1) +#define KEY_FCFG_4                  (KEY_FCFG_5 + 1) +#define KEY_FCFG_7                  (KEY_FCFG_4 + 1) +#define KEY_FCFG_FSCALE             (KEY_FCFG_7 + 1) +#define KEY_FCFG_AZ                 (KEY_FCFG_FSCALE + 1) +#define KEY_FCFG_6                  (KEY_FCFG_AZ + 1) +#define KEY_FCFG_LSB4               (KEY_FCFG_6 + 1) +#define KEY_CFG_12                  (KEY_FCFG_LSB4 + 1) +#define KEY_CFG_14                  (KEY_CFG_12 + 1) +#define KEY_CFG_15                  (KEY_CFG_14 + 1) +#define KEY_CFG_16                  (KEY_CFG_15 + 1) +#define KEY_CFG_18                  (KEY_CFG_16 + 1) +#define KEY_CFG_6                   (KEY_CFG_18 + 1) +#define KEY_CFG_7                   (KEY_CFG_6 + 1) +#define KEY_CFG_4                   (KEY_CFG_7 + 1) +#define KEY_CFG_5                   (KEY_CFG_4 + 1) +#define KEY_CFG_2                   (KEY_CFG_5 + 1) +#define KEY_CFG_3                   (KEY_CFG_2 + 1) +#define KEY_CFG_1                   (KEY_CFG_3 + 1) +#define KEY_CFG_EXTERNAL            (KEY_CFG_1 + 1) +#define KEY_CFG_8                   (KEY_CFG_EXTERNAL + 1) +#define KEY_CFG_9                   (KEY_CFG_8 + 1) +#define KEY_CFG_ORIENT_3            (KEY_CFG_9 + 1) +#define KEY_CFG_ORIENT_2            (KEY_CFG_ORIENT_3 + 1) +#define KEY_CFG_ORIENT_1            (KEY_CFG_ORIENT_2 + 1) +#define KEY_CFG_GYRO_SOURCE         (KEY_CFG_ORIENT_1 + 1) +#define KEY_CFG_ORIENT_IRQ_1        (KEY_CFG_GYRO_SOURCE + 1) +#define KEY_CFG_ORIENT_IRQ_2        (KEY_CFG_ORIENT_IRQ_1 + 1) +#define KEY_CFG_ORIENT_IRQ_3        (KEY_CFG_ORIENT_IRQ_2 + 1) +#define KEY_FCFG_MAG_VAL            (KEY_CFG_ORIENT_IRQ_3 + 1) +#define KEY_FCFG_MAG_MOV            (KEY_FCFG_MAG_VAL + 1) +#define KEY_CFG_LP_QUAT             (KEY_FCFG_MAG_MOV + 1) +#define KEY_CFG_GYRO_RAW_DATA       (KEY_CFG_LP_QUAT + 1) +#define KEY_CFG_EXT_GYRO_BIAS       (KEY_CFG_GYRO_RAW_DATA + 1) +#define KEY_CFG_EXT_GYRO_BIAS_X     (KEY_CFG_EXT_GYRO_BIAS + 1) +#define KEY_CFG_EXT_GYRO_BIAS_Y     (KEY_CFG_EXT_GYRO_BIAS_X + 1) +#define KEY_CFG_EXT_GYRO_BIAS_Z     (KEY_CFG_EXT_GYRO_BIAS_Y + 1) +#define KEY_bad_compass				(KEY_CFG_EXT_GYRO_BIAS_Z + 1) +#define KEY_COMPASS_CHG_SENSITIVITY (KEY_bad_compass + 1) +#define KEY_CCS_HEADING_THLD        (KEY_COMPASS_CHG_SENSITIVITY + 1) +#define KEY_CCS_TIME_THLD           (KEY_CCS_HEADING_THLD + 1) +#define KEY_CCS_DOTP_THLD           (KEY_CCS_TIME_THLD + 1) +#define KEY_CCS_COMP_CNTR           (KEY_CCS_DOTP_THLD + 1) +#define KEY_CFG_NM_DET              (KEY_CCS_COMP_CNTR + 1) +#define KEY_SMD_ENABLE              (KEY_CFG_NM_DET + 1) +#define KEY_SMD_ACCEL_THLD          (KEY_SMD_ENABLE + 1) +#define KEY_SMD_DELAY_THLD          (KEY_SMD_ACCEL_THLD + 1) +#define KEY_SMD_DELAY2_THLD         (KEY_SMD_DELAY_THLD + 1) +#define KEY_SMD_ENABLE_TESTPT1      (KEY_SMD_DELAY2_THLD + 1) +#define KEY_SMD_ENABLE_TESTPT2      (KEY_SMD_ENABLE_TESTPT1 + 1) +#define KEY_SMD_EXE_STATE           (KEY_SMD_ENABLE_TESTPT2 + 1) +#define KEY_SMD_DELAY_CNTR          (KEY_SMD_EXE_STATE + 1) + +#define KEY_BREAK (81) +#if KEY_SMD_DELAY_CNTR != KEY_BREAK +#error +#endif + +/* MPU6050 keys */ +#define KEY_CFG_ACCEL_FILTER        (KEY_BREAK + 1) +#define KEY_CFG_MOTION_BIAS         (KEY_CFG_ACCEL_FILTER + 1) +#define KEY_TEMPLABEL               (KEY_CFG_MOTION_BIAS + 1) + +#define KEY_D_0_22                  (KEY_TEMPLABEL + 1) +#define KEY_D_0_24                  (KEY_D_0_22 + 1) +#define KEY_D_0_36                  (KEY_D_0_24 + 1) +#define KEY_D_0_52                  (KEY_D_0_36 + 1) +#define KEY_D_0_96                  (KEY_D_0_52 + 1) +#define KEY_D_0_104                 (KEY_D_0_96 + 1) +#define KEY_D_0_108                 (KEY_D_0_104 + 1) +#define KEY_D_0_163                 (KEY_D_0_108 + 1) +#define KEY_D_0_188                 (KEY_D_0_163 + 1) +#define KEY_D_0_192                 (KEY_D_0_188 + 1) +#define KEY_D_0_224                 (KEY_D_0_192 + 1) +#define KEY_D_0_228                 (KEY_D_0_224 + 1) +#define KEY_D_0_232                 (KEY_D_0_228 + 1) +#define KEY_D_0_236                 (KEY_D_0_232 + 1) + +#define KEY_DMP_PREVPTAT            (KEY_D_0_236 + 1) +#define KEY_D_1_2                   (KEY_DMP_PREVPTAT + 1) +#define KEY_D_1_4                   (KEY_D_1_2 + 1) +#define KEY_D_1_8                   (KEY_D_1_4 + 1) +#define KEY_D_1_10                  (KEY_D_1_8 + 1) +#define KEY_D_1_24                  (KEY_D_1_10 + 1) +#define KEY_D_1_28                  (KEY_D_1_24 + 1) +#define KEY_D_1_36                  (KEY_D_1_28 + 1) +#define KEY_D_1_40                  (KEY_D_1_36 + 1) +#define KEY_D_1_44                  (KEY_D_1_40 + 1) +#define KEY_D_1_72                  (KEY_D_1_44 + 1) +#define KEY_D_1_74                  (KEY_D_1_72 + 1) +#define KEY_D_1_79                  (KEY_D_1_74 + 1) +#define KEY_D_1_88                  (KEY_D_1_79 + 1) +#define KEY_D_1_90                  (KEY_D_1_88 + 1) +#define KEY_D_1_92                  (KEY_D_1_90 + 1) +#define KEY_D_1_96                  (KEY_D_1_92 + 1) +#define KEY_D_1_98                  (KEY_D_1_96 + 1) +#define KEY_D_1_100                 (KEY_D_1_98 + 1) +#define KEY_D_1_106                 (KEY_D_1_100 + 1) +#define KEY_D_1_108                 (KEY_D_1_106 + 1) +#define KEY_D_1_112                 (KEY_D_1_108 + 1) +#define KEY_D_1_128                 (KEY_D_1_112 + 1) +#define KEY_D_1_152                 (KEY_D_1_128 + 1) +#define KEY_D_1_160                 (KEY_D_1_152 + 1) +#define KEY_D_1_168                 (KEY_D_1_160 + 1) +#define KEY_D_1_175                 (KEY_D_1_168 + 1) +#define KEY_D_1_176                 (KEY_D_1_175 + 1) +#define KEY_D_1_178                 (KEY_D_1_176 + 1) +#define KEY_D_1_179                 (KEY_D_1_178 + 1) +#define KEY_D_1_218                 (KEY_D_1_179 + 1) +#define KEY_D_1_232                 (KEY_D_1_218 + 1) +#define KEY_D_1_236                 (KEY_D_1_232 + 1) +#define KEY_D_1_240                 (KEY_D_1_236 + 1) +#define KEY_D_1_244                 (KEY_D_1_240 + 1) +#define KEY_D_1_250                 (KEY_D_1_244 + 1) +#define KEY_D_1_252                 (KEY_D_1_250 + 1) +#define KEY_D_2_12                  (KEY_D_1_252 + 1) +#define KEY_D_2_96                  (KEY_D_2_12 + 1) +#define KEY_D_2_108                 (KEY_D_2_96 + 1) +#define KEY_D_2_208                 (KEY_D_2_108 + 1) +#define KEY_FLICK_MSG               (KEY_D_2_208 + 1) +#define KEY_FLICK_COUNTER           (KEY_FLICK_MSG + 1) +#define KEY_FLICK_LOWER             (KEY_FLICK_COUNTER + 1) +#define KEY_CFG_FLICK_IN            (KEY_FLICK_LOWER + 1) +#define KEY_FLICK_UPPER             (KEY_CFG_FLICK_IN + 1) +#define KEY_CGNOTICE_INTR           (KEY_FLICK_UPPER + 1) +#define KEY_D_2_224                 (KEY_CGNOTICE_INTR + 1) +#define KEY_D_2_244                 (KEY_D_2_224 + 1) +#define KEY_D_2_248                 (KEY_D_2_244 + 1) +#define KEY_D_2_252                 (KEY_D_2_248 + 1) + +#define KEY_D_GYRO_BIAS_X               (KEY_D_2_252 + 1) +#define KEY_D_GYRO_BIAS_Y               (KEY_D_GYRO_BIAS_X + 1) +#define KEY_D_GYRO_BIAS_Z               (KEY_D_GYRO_BIAS_Y + 1) +#define KEY_D_ACC_BIAS_X                (KEY_D_GYRO_BIAS_Z + 1) +#define KEY_D_ACC_BIAS_Y                (KEY_D_ACC_BIAS_X + 1) +#define KEY_D_ACC_BIAS_Z                (KEY_D_ACC_BIAS_Y + 1) +#define KEY_D_GYRO_ENABLE               (KEY_D_ACC_BIAS_Z + 1) +#define KEY_D_ACCEL_ENABLE              (KEY_D_GYRO_ENABLE + 1) +#define KEY_D_QUAT_ENABLE               (KEY_D_ACCEL_ENABLE + 1) +#define KEY_D_OUTPUT_ENABLE             (KEY_D_QUAT_ENABLE + 1) +#define KEY_D_ACCEL_CNTR                (KEY_D_OUTPUT_ENABLE + 1) +#define KEY_D_GYRO_CNTR                 (KEY_D_ACCEL_CNTR + 1) +#define KEY_D_QUAT0_CNTR                (KEY_D_GYRO_CNTR + 1) +#define KEY_D_QUAT1_CNTR                (KEY_D_QUAT0_CNTR + 1) +#define KEY_D_QUAT2_CNTR                (KEY_D_QUAT1_CNTR + 1) +#define KEY_D_CR_TIME_G                 (KEY_D_QUAT2_CNTR + 1) +#define KEY_D_CR_TIME_A                 (KEY_D_CR_TIME_G + 1) +#define KEY_D_CR_TIME_Q                 (KEY_D_CR_TIME_A + 1) +#define KEY_D_CS_TAX                    (KEY_D_CR_TIME_Q + 1) +#define KEY_D_CS_TAY                    (KEY_D_CS_TAX + 1) +#define KEY_D_CS_TAZ                    (KEY_D_CS_TAY + 1) +#define KEY_D_CS_TGX                    (KEY_D_CS_TAZ + 1) +#define KEY_D_CS_TGY                    (KEY_D_CS_TGX + 1) +#define KEY_D_CS_TGZ                    (KEY_D_CS_TGY + 1) +#define KEY_D_CS_TQ0                    (KEY_D_CS_TGZ + 1) +#define KEY_D_CS_TQ1                    (KEY_D_CS_TQ0 + 1) +#define KEY_D_CS_TQ2                    (KEY_D_CS_TQ1 + 1) +#define KEY_D_CS_TQ3                    (KEY_D_CS_TQ2 + 1) + +/* Compass keys */ +#define KEY_CPASS_GAIN              (KEY_D_CS_TQ3 + 1) +#define KEY_CPASS_BIAS_X            (KEY_CPASS_GAIN + 1) +#define KEY_CPASS_BIAS_Y            (KEY_CPASS_BIAS_X + 1) +#define KEY_CPASS_BIAS_Z            (KEY_CPASS_BIAS_Y + 1) +#define KEY_CPASS_MTX_00            (KEY_CPASS_BIAS_Z + 1) +#define KEY_CPASS_MTX_01            (KEY_CPASS_MTX_00 + 1) +#define KEY_CPASS_MTX_02            (KEY_CPASS_MTX_01 + 1) +#define KEY_CPASS_MTX_10            (KEY_CPASS_MTX_02 + 1) +#define KEY_CPASS_MTX_11            (KEY_CPASS_MTX_10 + 1) +#define KEY_CPASS_MTX_12            (KEY_CPASS_MTX_11 + 1) +#define KEY_CPASS_MTX_20            (KEY_CPASS_MTX_12 + 1) +#define KEY_CPASS_MTX_21            (KEY_CPASS_MTX_20 + 1) +#define KEY_CPASS_MTX_22            (KEY_CPASS_MTX_21 + 1) + +/* Tap Keys */ +#define KEY_DMP_TAP_GATE              (KEY_CPASS_MTX_22 + 1) +#define KEY_DMP_TAPW_MIN              (KEY_DMP_TAP_GATE + 1) +#define KEY_DMP_TAP_THR_Z             (KEY_DMP_TAPW_MIN + 1) +#define KEY_DMP_TAP_PREV_JERK_Z       (KEY_DMP_TAP_THR_Z + 1) +#define KEY_DMP_TAP_MIN_TAPS          (KEY_DMP_TAP_PREV_JERK_Z + 1) +#define KEY_DMP_TAP_NEXT_TAP_THRES    (KEY_DMP_TAP_MIN_TAPS + 1) +#define KEY_DMP_TAP_SHAKE_REJECT      (KEY_DMP_TAP_NEXT_TAP_THRES + 1) +#define KEY_DMP_TAP_SHAKE_COUNT_MAX   (KEY_DMP_TAP_SHAKE_REJECT + 1) +#define KEY_DMP_TAP_SHAKE_TIMEOUT_MAX (KEY_DMP_TAP_SHAKE_COUNT_MAX + 1) +#define KEY_DMP_TAP_DIRECTION         (KEY_DMP_TAP_SHAKE_TIMEOUT_MAX + 1) +#define KEY_DMP_TAP_COUNT             (KEY_DMP_TAP_DIRECTION + 1) +/* Gesture Keys */ +#define KEY_DMP_SH_TH_Y             (KEY_DMP_TAP_COUNT + 1) +#define KEY_DMP_SH_TH_X             (KEY_DMP_SH_TH_Y + 1) +#define KEY_DMP_SH_TH_Z             (KEY_DMP_SH_TH_X + 1) +#define KEY_DMP_ORIENT              (KEY_DMP_SH_TH_Z + 1) +#define KEY_D_ACT0                  (KEY_DMP_ORIENT + 1) +#define KEY_D_ACSX                  (KEY_D_ACT0 + 1) +#define KEY_D_ACSY                  (KEY_D_ACSX + 1) +#define KEY_D_ACSZ                  (KEY_D_ACSY + 1) + +#define KEY_CFG_DISPLAY_ORIENT_INT  (KEY_D_ACSZ + 1) +#define KEY_NO_ORIENT_INTERRUPT     (KEY_CFG_DISPLAY_ORIENT_INT + 1) +#define KEY_X_GRT_Y_TMP2            (KEY_NO_ORIENT_INTERRUPT + 1) + +/*Shake Keys */ +#define KEY_D_0_64                  (KEY_X_GRT_Y_TMP2 + 1) +#define KEY_D_2_4                   (KEY_D_0_64 + 1) +#define KEY_D_2_8                   (KEY_D_2_4 + 1) +#define KEY_D_2_48                  (KEY_D_2_8 + 1) +#define KEY_D_2_92                  (KEY_D_2_48 + 1) +#define KEY_D_2_94                  (KEY_D_2_92 + 1) +#define KEY_D_2_160                 (KEY_D_2_94 + 1) +#define KEY_D_3_180                 (KEY_D_2_160 + 1) +#define KEY_D_3_184                 (KEY_D_3_180 + 1) +#define KEY_D_3_188                 (KEY_D_3_184 + 1) +#define KEY_D_3_208                 (KEY_D_3_188 + 1) +#define KEY_D_3_240                 (KEY_D_3_208 + 1) +#define KEY_RETRACTION_1            (KEY_D_3_240 + 1) +#define KEY_RETRACTION_2            (KEY_RETRACTION_1 + 1) +#define KEY_RETRACTION_3            (KEY_RETRACTION_2 + 1) +#define KEY_RETRACTION_4            (KEY_RETRACTION_3 + 1) +#define KEY_CFG_SHAKE_INT           (KEY_RETRACTION_4 + 1) + +/* Authenticate Keys */ +#define KEY_D_AUTH_OUT              (KEY_CFG_SHAKE_INT + 1) +#define KEY_D_AUTH_IN               (KEY_D_AUTH_OUT + 1) +#define KEY_D_AUTH_A                (KEY_D_AUTH_IN + 1) +#define KEY_D_AUTH_B                (KEY_D_AUTH_A + 1) + +/* Pedometer standalone only keys */ +#define KEY_D_PEDSTD_BP_B           (KEY_D_AUTH_B + 1) +#define KEY_D_PEDSTD_HP_A           (KEY_D_PEDSTD_BP_B + 1) +#define KEY_D_PEDSTD_HP_B           (KEY_D_PEDSTD_HP_A + 1) +#define KEY_D_PEDSTD_BP_A4          (KEY_D_PEDSTD_HP_B + 1) +#define KEY_D_PEDSTD_BP_A3          (KEY_D_PEDSTD_BP_A4 + 1) +#define KEY_D_PEDSTD_BP_A2          (KEY_D_PEDSTD_BP_A3 + 1) +#define KEY_D_PEDSTD_BP_A1          (KEY_D_PEDSTD_BP_A2 + 1) +#define KEY_D_PEDSTD_INT_THRSH      (KEY_D_PEDSTD_BP_A1 + 1) +#define KEY_D_PEDSTD_CLIP           (KEY_D_PEDSTD_INT_THRSH + 1) +#define KEY_D_PEDSTD_SB             (KEY_D_PEDSTD_CLIP + 1) +#define KEY_D_PEDSTD_SB_TIME        (KEY_D_PEDSTD_SB + 1) +#define KEY_D_PEDSTD_PEAKTHRSH      (KEY_D_PEDSTD_SB_TIME + 1) +#define KEY_D_PEDSTD_TIML           (KEY_D_PEDSTD_PEAKTHRSH + 1) +#define KEY_D_PEDSTD_TIMH           (KEY_D_PEDSTD_TIML + 1) +#define KEY_D_PEDSTD_PEAK           (KEY_D_PEDSTD_TIMH + 1) +#define KEY_D_PEDSTD_TIMECTR        (KEY_D_PEDSTD_PEAK + 1) +#define KEY_D_PEDSTD_STEPCTR        (KEY_D_PEDSTD_TIMECTR + 1) +#define KEY_D_PEDSTD_STEPCTR2		(KEY_D_PEDSTD_STEPCTR + 1) +#define KEY_D_PEDSTD_WALKTIME       (KEY_D_PEDSTD_STEPCTR2 + 1) +#define KEY_D_PEDSTD_DECI           (KEY_D_PEDSTD_WALKTIME + 1) +#define KEY_D_PEDSTD_SB2			(KEY_D_PEDSTD_DECI + 1) +#define KEY_D_PEDSTD_DRIVE_STATE    (KEY_D_PEDSTD_SB2 + 1) +#define KEY_CFG_PED_INT             (KEY_D_PEDSTD_DRIVE_STATE + 1) +#define KEY_CFG_PED_ENABLE          (KEY_CFG_PED_INT + 1) +#define KEY_D_STPDET_TIMESTAMP      (KEY_CFG_PED_ENABLE + 1) + +/*Host Based No Motion*/ +#define KEY_D_HOST_NO_MOT           (KEY_D_STPDET_TIMESTAMP + 1) + +/*Host Based Accel Bias*/ +#define KEY_D_ACCEL_BIAS            (KEY_D_HOST_NO_MOT + 1) + +/*Screen/Display Orientation Keys*/ +#define KEY_D_ORIENT_GAP            (KEY_D_ACCEL_BIAS + 1) +#define KEY_D_TILT0_H               (KEY_D_ORIENT_GAP + 1) +#define KEY_D_TILT0_L               (KEY_D_TILT0_H + 1) +#define KEY_D_TILT1_H               (KEY_D_TILT0_L + 1) +#define KEY_D_TILT1_L               (KEY_D_TILT1_H + 1) +#define KEY_D_TILT2_H               (KEY_D_TILT1_L + 1) +#define KEY_D_TILT2_L               (KEY_D_TILT2_H  + 1) +#define KEY_D_TILT3_H               (KEY_D_TILT2_L + 1) +#define KEY_D_TILT3_L               (KEY_D_TILT3_H + 1) + +#define KEY_STREAM_P_ACCEL_Z        (KEY_D_TILT3_L + 1) + +/* Batch mode */ +#define KEY_BM_ENABLE               (KEY_STREAM_P_ACCEL_Z + 1) +#define KEY_BM_BATCH_THLD           (KEY_BM_ENABLE + 1) +#define KEY_BM_BATCH_CNTR           (KEY_BM_BATCH_THLD + 1) +#define KEY_BM_NUMWORD_TOFILL       (KEY_BM_BATCH_CNTR + 1) + +/* Watermark */ +#define KEY_CFG_WATERMARK_H         (KEY_BM_NUMWORD_TOFILL + 1) +#define KEY_CFG_WATERMARK_L         (KEY_CFG_WATERMARK_H + 1) + +/* FIFO output control */ +#define KEY_CFG_OUT_ACCL            (KEY_CFG_WATERMARK_L + 1) +#define KEY_CFG_OUT_GYRO            (KEY_CFG_OUT_ACCL + 1) +#define KEY_CFG_OUT_3QUAT           (KEY_CFG_OUT_GYRO + 1) +#define KEY_CFG_OUT_6QUAT           (KEY_CFG_OUT_3QUAT + 1) +#define KEY_CFG_OUT_9QUAT           (KEY_CFG_OUT_6QUAT + 1) +#define KEY_CFG_OUT_PQUAT           (KEY_CFG_OUT_9QUAT + 1) +#define KEY_CFG_OUT_PQUAT9          (KEY_CFG_OUT_PQUAT + 1) +#define KEY_CFG_OUT_CPASS           (KEY_CFG_OUT_PQUAT9 + 1) +#define KEY_CFG_OUT_PRESS           (KEY_CFG_OUT_CPASS + 1) +#define KEY_CFG_OUT_STEPDET         (KEY_CFG_OUT_PRESS + 1) +#define KEY_CFG_FIFO_INT            (KEY_CFG_OUT_STEPDET + 1) + +/* Ped Step detection */ +#define KEY_CFG_PEDSTEP_DET         (KEY_CFG_FIFO_INT + 1) + +/* Screen Orientation data */ +#define KEY_SO_DATA                 (KEY_CFG_PEDSTEP_DET + 1) + +/* MPU for DMP Android K */ +#define KEY_P_INIT                  (KEY_SO_DATA + 1) +#define KEY_P_HW_ID                 (KEY_P_INIT + 1) + +/* DMP running counter */ +#define KEY_DMP_RUN_CNTR            (KEY_P_HW_ID + 1) + +/* Sensor's ODR */ +#define KEY_CFG_3QUAT_ODR           (KEY_DMP_RUN_CNTR + 1) +#define KEY_CFG_6QUAT_ODR           (KEY_CFG_3QUAT_ODR + 1) +#define KEY_CFG_9QUAT_ODR           (KEY_CFG_6QUAT_ODR + 1) +#define KEY_CFG_PQUAT6_ODR          (KEY_CFG_9QUAT_ODR + 1) +#define KEY_CFG_PQUAT9_ODR          (KEY_CFG_PQUAT6_ODR + 1) +#define KEY_CFG_ACCL_ODR            (KEY_CFG_PQUAT9_ODR + 1) +#define KEY_CFG_GYRO_ODR            (KEY_CFG_ACCL_ODR + 1) +#define KEY_CFG_CPASS_ODR           (KEY_CFG_GYRO_ODR + 1) +#define KEY_CFG_PRESS_ODR           (KEY_CFG_CPASS_ODR + 1) + +#define KEY_ODR_CNTR_3QUAT          (KEY_CFG_PRESS_ODR + 1) +#define KEY_ODR_CNTR_6QUAT          (KEY_ODR_CNTR_3QUAT + 1) +#define KEY_ODR_CNTR_9QUAT          (KEY_ODR_CNTR_6QUAT + 1) +#define KEY_ODR_CNTR_PQUAT          (KEY_ODR_CNTR_9QUAT + 1) +#define KEY_ODR_CNTR_PQUAT9         (KEY_ODR_CNTR_PQUAT + 1) +#define KEY_ODR_CNTR_ACCL           (KEY_ODR_CNTR_PQUAT9 + 1) +#define KEY_ODR_CNTR_GYRO           (KEY_ODR_CNTR_ACCL + 1) +#define KEY_ODR_CNTR_CPASS          (KEY_ODR_CNTR_GYRO + 1) +#define KEY_ODR_CNTR_PRESS          (KEY_ODR_CNTR_CPASS + 1) + +/* DMP fusion LP-Quat */ +#define KEY_DMP_LPQ0                (KEY_ODR_CNTR_PRESS + 1) +#define KEY_DMP_LPQ1                (KEY_DMP_LPQ0 + 1) +#define KEY_DMP_LPQ2                (KEY_DMP_LPQ1 + 1) +#define KEY_DMP_LPQ3                (KEY_DMP_LPQ2 + 1) + +/* DMP fusion 6-axis Quat */ +#define KEY_DMP_Q0                  (KEY_DMP_LPQ3 + 1) +#define KEY_DMP_Q1                  (KEY_DMP_Q0 + 1) +#define KEY_DMP_Q2                  (KEY_DMP_Q1 + 1) +#define KEY_DMP_Q3                  (KEY_DMP_Q2 + 1) + +/* 9-axis fusion */ +#define KEY_CPASS_VALID             (KEY_DMP_Q3 + 1) +#define KEY_9AXIS_ACCURACY          (KEY_CPASS_VALID + 1) +#define KEY_DMP_9Q0                 (KEY_9AXIS_ACCURACY + 1) +#define KEY_DMP_9Q1                 (KEY_DMP_9Q0 + 1) +#define KEY_DMP_9Q2                 (KEY_DMP_9Q1 + 1) +#define KEY_DMP_9Q3                 (KEY_DMP_9Q2 + 1) + +/* Test key */ +#define KEY_TEST_01                 (KEY_9AXIS_ACCURACY + 1) +#define KEY_TEST_02                 (KEY_TEST_01 + 1) +#define KEY_TEST_03                 (KEY_TEST_02 + 1) +#define KEY_TEST_04                 (KEY_TEST_03 + 1) +#define KEY_TEST_05                 (KEY_TEST_04 + 1) +#define KEY_TEST_06                 (KEY_TEST_05 + 1) +#define KEY_TEST_07                 (KEY_TEST_06 + 1) +#define KEY_TEST_XX                 (KEY_TEST_07 + 1) + +#define NUM_KEYS                    (KEY_TEST_XX + 1) + +struct tKeyLabel  { +	unsigned short key; +	unsigned short addr; +}; + +#define DINA0A 0x0a +#define DINA22 0x22 +#define DINA42 0x42 +#define DINA5A 0x5a + +#define DINA06 0x06 +#define DINA0E 0x0e +#define DINA16 0x16 +#define DINA1E 0x1e +#define DINA26 0x26 +#define DINA2E 0x2e +#define DINA36 0x36 +#define DINA3E 0x3e +#define DINA46 0x46 +#define DINA4E 0x4e +#define DINA56 0x56 +#define DINA5E 0x5e +#define DINA66 0x66 +#define DINA6E 0x6e +#define DINA76 0x76 +#define DINA7E 0x7e + +#define DINA00 0x00 +#define DINA08 0x08 +#define DINA10 0x10 +#define DINA18 0x18 +#define DINA20 0x20 +#define DINA28 0x28 +#define DINA30 0x30 +#define DINA38 0x38 +#define DINA40 0x40 +#define DINA48 0x48 +#define DINA50 0x50 +#define DINA58 0x58 +#define DINA60 0x60 +#define DINA68 0x68 +#define DINA70 0x70 +#define DINA78 0x78 + +#define DINA04 0x04 +#define DINA0C 0x0c +#define DINA14 0x14 +#define DINA1C 0x1C +#define DINA24 0x24 +#define DINA2C 0x2c +#define DINA34 0x34 +#define DINA3C 0x3c +#define DINA44 0x44 +#define DINA4C 0x4c +#define DINA54 0x54 +#define DINA5C 0x5c +#define DINA64 0x64 +#define DINA6C 0x6c +#define DINA74 0x74 +#define DINA7C 0x7c + +#define DINA01 0x01 +#define DINA09 0x09 +#define DINA11 0x11 +#define DINA19 0x19 +#define DINA21 0x21 +#define DINA29 0x29 +#define DINA31 0x31 +#define DINA39 0x39 +#define DINA41 0x41 +#define DINA49 0x49 +#define DINA51 0x51 +#define DINA59 0x59 +#define DINA61 0x61 +#define DINA69 0x69 +#define DINA71 0x71 +#define DINA79 0x79 + +#define DINA25 0x25 +#define DINA2D 0x2d +#define DINA35 0x35 +#define DINA3D 0x3d +#define DINA4D 0x4d +#define DINA55 0x55 +#define DINA5D 0x5D +#define DINA6D 0x6d +#define DINA75 0x75 +#define DINA7D 0x7d + +#define DINADC 0xdc +#define DINAF2 0xf2 +#define DINAAB 0xab +#define DINAAA 0xaa +#define DINAF1 0xf1 +#define DINADF 0xdf +#define DINADA 0xda +#define DINAB1 0xb1 +#define DINAB9 0xb9 +#define DINAF3 0xf3 +#define DINA8B 0x8b +#define DINAA3 0xa3 +#define DINA91 0x91 +#define DINAB6 0xb6 +#define DINAB4 0xb4 + +#define DINC00 0x00 +#define DINC01 0x01 +#define DINC02 0x02 +#define DINC03 0x03 +#define DINC08 0x08 +#define DINC09 0x09 +#define DINC0A 0x0a +#define DINC0B 0x0b +#define DINC10 0x10 +#define DINC11 0x11 +#define DINC12 0x12 +#define DINC13 0x13 +#define DINC18 0x18 +#define DINC19 0x19 +#define DINC1A 0x1a +#define DINC1B 0x1b + +#define DINC20 0x20 +#define DINC21 0x21 +#define DINC22 0x22 +#define DINC23 0x23 +#define DINC28 0x28 +#define DINC29 0x29 +#define DINC2A 0x2a +#define DINC2B 0x2b +#define DINC30 0x30 +#define DINC31 0x31 +#define DINC32 0x32 +#define DINC33 0x33 +#define DINC38 0x38 +#define DINC39 0x39 +#define DINC3A 0x3a +#define DINC3B 0x3b + +#define DINC40 0x40 +#define DINC41 0x41 +#define DINC42 0x42 +#define DINC43 0x43 +#define DINC48 0x48 +#define DINC49 0x49 +#define DINC4A 0x4a +#define DINC4B 0x4b +#define DINC50 0x50 +#define DINC51 0x51 +#define DINC52 0x52 +#define DINC53 0x53 +#define DINC58 0x58 +#define DINC59 0x59 +#define DINC5A 0x5a +#define DINC5B 0x5b + +#define DINC60 0x60 +#define DINC61 0x61 +#define DINC62 0x62 +#define DINC63 0x63 +#define DINC68 0x68 +#define DINC69 0x69 +#define DINC6A 0x6a +#define DINC6B 0x6b +#define DINC70 0x70 +#define DINC71 0x71 +#define DINC72 0x72 +#define DINC73 0x73 +#define DINC78 0x78 +#define DINC79 0x79 +#define DINC7A 0x7a +#define DINC7B 0x7b +#define DIND40 0x40 +#define DINA80 0x80 +#define DINA90 0x90 +#define DINAA0 0xa0 +#define DINAC9 0xc9 +#define DINACB 0xcb +#define DINACD 0xcd +#define DINACF 0xcf +#define DINAC8 0xc8 +#define DINACA 0xca +#define DINACC 0xcc +#define DINACE 0xce +#define DINAD8 0xd8 +#define DINADD 0xdd +#define DINAF8 0xf0 +#define DINAFE 0xfe + +#define DINBF8 0xf8 +#define DINAC0 0xb0 +#define DINAC1 0xb1 +#define DINAC2 0xb4 +#define DINAC3 0xb5 +#define DINAC4 0xb8 +#define DINAC5 0xb9 +#define DINBC0 0xc0 +#define DINBC2 0xc2 +#define DINBC4 0xc4 +#define DINBC6 0xc6 + +#endif diff --git a/drivers/staging/iio/imu/inv_mpu/dmpmap.h b/drivers/staging/iio/imu/inv_mpu/dmpmap.h new file mode 100644 index 00000000000..92936372224 --- /dev/null +++ b/drivers/staging/iio/imu/inv_mpu/dmpmap.h @@ -0,0 +1,263 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ + +#ifndef DMPMAP_H +#define DMPMAP_H + +#define DMP_PTAT    0 +#define DMP_XGYR    2 +#define DMP_YGYR    4 +#define DMP_ZGYR    6 +#define DMP_XACC    8 +#define DMP_YACC    10 +#define DMP_ZACC    12 +#define DMP_ADC1    14 +#define DMP_ADC2    16 +#define DMP_ADC3    18 +#define DMP_BIASUNC    20 +#define DMP_FIFORT    22 +#define DMP_INVGSFH    24 +#define DMP_INVGSFL    26 +#define DMP_1H    28 +#define DMP_1L    30 +#define DMP_BLPFSTCH    32 +#define DMP_BLPFSTCL    34 +#define DMP_BLPFSXH    36 +#define DMP_BLPFSXL    38 +#define DMP_BLPFSYH    40 +#define DMP_BLPFSYL    42 +#define DMP_BLPFSZH    44 +#define DMP_BLPFSZL    46 +#define DMP_BLPFMTC    48 +#define DMP_SMC    50 +#define DMP_BLPFMXH    52 +#define DMP_BLPFMXL    54 +#define DMP_BLPFMYH    56 +#define DMP_BLPFMYL    58 +#define DMP_BLPFMZH    60 +#define DMP_BLPFMZL    62 +#define DMP_BLPFC    64 +#define DMP_SMCTH    66 +#define DMP_TAP_DIRECTION 68 +#define DMP_TAP_COUNT 70 +#define DMP_TAP_GATE    72 +#define DMP_TAP_MIN_TAPS    74 +#define DMP_BERR2NH    76 +#define DMP_SMCINC    78 +#define DMP_TAP_SHAKE_REJECT    80 +#define DMP_ANGVBXL    82 +#define DMP_TAP_SHAKE_COUNT_MAX    84 +#define DMP_TAP_SHAKE_TIMEOUT_MAX    86 +#define DMP_TAP_THZ    88 +#define DMP_TAPW_MIN    90 +#define DMP_TAP_PREV_JERK_Z    92 +#define DMP_TAP_NEXT_TAP_THRES    94 +#define DMP_ATCH    96 +#define DMP_BIASUNCSF    98 +#define DMP_ACT2H    100 +#define DMP_ACT2L    102 +#define DMP_GSFH    104 +#define DMP_GSFL    106 +#define DMP_GH    108 +#define DMP_GL    110 +#define DMP_0_5H    112 +#define DMP_0_5L    114 +#define DMP_0_0H    116 +#define DMP_0_0L    118 +#define DMP_1_0H    120 +#define DMP_1_0L    122 +#define DMP_1_5H    124 +#define DMP_1_5L    126 +#define DMP_TMP1AH    128 +#define DMP_TMP1AL    130 +#define DMP_TMP2AH    132 +#define DMP_TMP2AL    134 +#define DMP_TMP3AH    136 +#define DMP_TMP3AL    138 +#define DMP_TMP4AH    140 +#define DMP_TMP4AL    142 +#define DMP_XACCW    144 +#define DMP_TMP5    146 +#define DMP_XACCB    148 +#define DMP_TMP8    150 +#define DMP_YACCB    152 +#define DMP_TMP9    154 +#define DMP_ZACCB    156 +#define DMP_TMP10    158 +#define DMP_DZH    160 +#define DMP_DZL    162 +#define DMP_XGCH    164 +#define DMP_XGCL    166 +#define DMP_YGCH    168 +#define DMP_YGCL    170 +#define DMP_ZGCH    172 +#define DMP_ZGCL    174 +#define DMP_YACCW    176 +#define DMP_TMP7    178 +#define DMP_AFB1H    180 +#define DMP_AFB1L    182 +#define DMP_AFB2H    184 +#define DMP_AFB2L    186 +#define DMP_MAGFBH    188 +#define DMP_MAGFBL    190 +#define DMP_QT1H    192 +#define DMP_QT1L    194 +#define DMP_QT2H    196 +#define DMP_QT2L    198 +#define DMP_QT3H    200 +#define DMP_QT3L    202 +#define DMP_QT4H    204 +#define DMP_QT4L    206 +#define DMP_CTRL1H    208 +#define DMP_CTRL1L    210 +#define DMP_CTRL2H    212 +#define DMP_CTRL2L    214 +#define DMP_CTRL3H    216 +#define DMP_CTRL3L    218 +#define DMP_CTRL4H    220 +#define DMP_CTRL4L    222 +#define DMP_CTRLS1    224 +#define DMP_CTRLSF1    226 +#define DMP_CTRLS2    228 +#define DMP_CTRLSF2    230 +#define DMP_CTRLS3    232 +#define DMP_CTRLSFNLL    234 +#define DMP_CTRLS4    236 +#define DMP_CTRLSFNL2    238 +#define DMP_CTRLSFNL    240 +#define DMP_TMP30    242 +#define DMP_CTRLSFJT    244 +#define DMP_TMP31    246 +#define DMP_TMP11    248 +#define DMP_CTRLSF2_2    250 +#define DMP_TMP12    252 +#define DMP_CTRLSF1_2    254 +#define DMP_PREVPTAT    256 +#define DMP_ACCZB    258 +#define DMP_ACCXB    264 +#define DMP_ACCYB    266 +#define DMP_1HB    272 +#define DMP_1LB    274 +#define DMP_0H    276 +#define DMP_0L    278 +#define DMP_ASR22H    280 +#define DMP_ASR22L    282 +#define DMP_ASR6H    284 +#define DMP_ASR6L    286 +#define DMP_TMP13    288 +#define DMP_TMP14    290 +#define DMP_FINTXH    292 +#define DMP_FINTXL    294 +#define DMP_FINTYH    296 +#define DMP_FINTYL    298 +#define DMP_FINTZH    300 +#define DMP_FINTZL    302 +#define DMP_TMP1BH    304 +#define DMP_TMP1BL    306 +#define DMP_TMP2BH    308 +#define DMP_TMP2BL    310 +#define DMP_TMP3BH    312 +#define DMP_TMP3BL    314 +#define DMP_TMP4BH    316 +#define DMP_TMP4BL    318 +#define DMP_STXG    320 +#define DMP_ZCTXG    322 +#define DMP_STYG    324 +#define DMP_ZCTYG    326 +#define DMP_STZG    328 +#define DMP_ZCTZG    330 +#define DMP_CTRLSFJT2    332 +#define DMP_CTRLSFJTCNT    334 +#define DMP_PVXG    336 +#define DMP_TMP15    338 +#define DMP_PVYG    340 +#define DMP_TMP16    342 +#define DMP_PVZG    344 +#define DMP_TMP17    346 +#define DMP_MNMFLAGH    352 +#define DMP_MNMFLAGL    354 +#define DMP_MNMTMH    356 +#define DMP_MNMTML    358 +#define DMP_MNMTMTHRH    360 +#define DMP_MNMTMTHRL    362 +#define DMP_MNMTHRH    364 +#define DMP_MNMTHRL    366 +#define DMP_ACCQD4H    368 +#define DMP_ACCQD4L    370 +#define DMP_ACCQD5H    372 +#define DMP_ACCQD5L    374 +#define DMP_ACCQD6H    376 +#define DMP_ACCQD6L    378 +#define DMP_ACCQD7H    380 +#define DMP_ACCQD7L    382 +#define DMP_ACCQD0H    384 +#define DMP_ACCQD0L    386 +#define DMP_ACCQD1H    388 +#define DMP_ACCQD1L    390 +#define DMP_ACCQD2H    392 +#define DMP_ACCQD2L    394 +#define DMP_ACCQD3H    396 +#define DMP_ACCQD3L    398 +#define DMP_XN2H    400 +#define DMP_XN2L    402 +#define DMP_XN1H    404 +#define DMP_XN1L    406 +#define DMP_YN2H    408 +#define DMP_YN2L    410 +#define DMP_YN1H    412 +#define DMP_YN1L    414 +#define DMP_YH    416 +#define DMP_YL    418 +#define DMP_B0H    420 +#define DMP_B0L    422 +#define DMP_A1H    424 +#define DMP_A1L    426 +#define DMP_A2H    428 +#define DMP_A2L    430 +#define DMP_SEM1    432 +#define DMP_FIFOCNT    434 +#define DMP_SH_TH_X    436 +#define DMP_PACKET    438 +#define DMP_SH_TH_Y    440 +#define DMP_FOOTER    442 +#define DMP_SH_TH_Z    444 +#define DMP_TEMP29    448 +#define DMP_TEMP30    450 +#define DMP_XACCB_PRE    452 +#define DMP_XACCB_PREL    454 +#define DMP_YACCB_PRE    456 +#define DMP_YACCB_PREL    458 +#define DMP_ZACCB_PRE    460 +#define DMP_ZACCB_PREL    462 +#define DMP_TMP22    464 +#define DMP_TAP_TIMER    466 +#define DMP_TAP_THX    468 +#define DMP_TAP_THY    472 +#define DMP_TMP25    480 +#define DMP_TMP26    482 +#define DMP_TMP27    484 +#define DMP_TMP28    486 +#define DMP_ORIENT    488 +#define DMP_THRSH    490 +#define DMP_ENDIANH    492 +#define DMP_ENDIANL    494 +#define DMP_BLPFNMTCH    496 +#define DMP_BLPFNMTCL    498 +#define DMP_BLPFNMXH    500 +#define DMP_BLPFNMXL    502 +#define DMP_BLPFNMYH    504 +#define DMP_BLPFNMYL    506 +#define DMP_BLPFNMZH    508 +#define DMP_BLPFNMZL    510 + +#endif diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu3050_iio.c b/drivers/staging/iio/imu/inv_mpu/inv_mpu3050_iio.c new file mode 100644 index 00000000000..1fc5ff98097 --- /dev/null +++ b/drivers/staging/iio/imu/inv_mpu/inv_mpu3050_iio.c @@ -0,0 +1,271 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +* GNU General Public License for more details. +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> + +#include "inv_mpu_iio.h" +#define MPU3050_NACK_MIN_TIME (2 * 1000) +#define MPU3050_NACK_MAX_TIME (3 * 1000) + +#define MPU3050_ONE_MPU_TIME 20 +#define MPU3050_BOGUS_ADDR  0x7F +int __attribute__((weak)) inv_register_mpu3050_slave(struct inv_mpu_state *st) +{ +	return 0; +} + +int set_3050_bypass(struct inv_mpu_state *st, bool enable) +{ +	struct inv_reg_map_s *reg; +	int result; +	u8 b; + +	reg = &st->reg; +	result = inv_i2c_read(st, reg->user_ctrl, 1, &b); +	if (result) +		return result; +	if (((b & BIT_3050_AUX_IF_EN) == 0) && enable) +		return 0; +	if ((b & BIT_3050_AUX_IF_EN) && (enable == 0)) +		return 0; +	b &= ~BIT_3050_AUX_IF_EN; +	if (!enable) { +		b |= BIT_3050_AUX_IF_EN; +		result = inv_i2c_single_write(st, reg->user_ctrl, b); +		return result; +	} else { +		/* Coming out of I2C is tricky due to several erratta.  Do not +		* modify this algorithm +		*/ +		/* +		* 1) wait for the right time and send the command to change +		* the aux i2c slave address to an invalid address that will +		* get nack'ed +		* +		* 0x00 is broadcast.  0x7F is unlikely to be used by any aux. +		*/ +		result = inv_i2c_single_write(st, REG_3050_SLAVE_ADDR, +						MPU3050_BOGUS_ADDR); +		if (result) +			return result; +		/* +		* 2) wait enough time for a nack to occur, then go into +		*    bypass mode: +		*/ +		usleep_range(MPU3050_NACK_MIN_TIME, MPU3050_NACK_MAX_TIME); +		result = inv_i2c_single_write(st, reg->user_ctrl, b); +		if (result) +			return result; +		/* +		* 3) wait for up to one MPU cycle then restore the slave +		*    address +		*/ +		msleep(MPU3050_ONE_MPU_TIME); + +		result = inv_i2c_single_write(st, REG_3050_SLAVE_ADDR, +			st->plat_data.secondary_i2c_addr); +		if (result) +			return result; +		result = inv_i2c_single_write(st, reg->user_ctrl, b); +		if (result) +			return result; +		usleep_range(MPU3050_NACK_MIN_TIME, MPU3050_NACK_MAX_TIME); +	} +	return 0; +} + +void inv_setup_reg_mpu3050(struct inv_reg_map_s *reg) +{ +	reg->fifo_en         = REG_3050_FIFO_EN; +	reg->sample_rate_div = REG_3050_SAMPLE_RATE_DIV; +	reg->lpf             = REG_3050_LPF; +	reg->fifo_count_h    = REG_3050_FIFO_COUNT_H; +	reg->fifo_r_w        = REG_3050_FIFO_R_W; +	reg->user_ctrl       = REG_3050_USER_CTRL; +	reg->pwr_mgmt_1      = REG_3050_PWR_MGMT_1; +	reg->raw_accel        = REG_3050_AUX_XOUT_H; +	reg->temperature     = REG_3050_TEMPERATURE; +	reg->int_enable      = REG_3050_INT_ENABLE; +	reg->int_status      = REG_3050_INT_STATUS; +} + +int inv_switch_3050_gyro_engine(struct inv_mpu_state *st, bool en) +{ +	struct inv_reg_map_s *reg; +	u8 data, p; +	int result; +	reg = &st->reg; +	if (en) { +		data = INV_CLK_PLL; +		p = (BITS_3050_POWER1 | data); +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p); +		if (result) +			return result; +		p = (BITS_3050_POWER2 | data); +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p); +		if (result) +			return result; +		p = data; +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p); +		msleep(SENSOR_UP_TIME); +	} else { +		p = BITS_3050_GYRO_STANDBY; +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, p); +	} + +	return result; +} + +int inv_switch_3050_accel_engine(struct inv_mpu_state *st, bool en) +{ +	int result; +	if (NULL == st->slave_accel) +		return -EPERM; +	if (en) +		result = st->slave_accel->resume(st); +	else +		result = st->slave_accel->suspend(st); + +	return result; +} + +/** + *  inv_init_config_mpu3050() - Initialize hardware, disable FIFO. + *  @st:	Device driver instance. + *  Initial configuration: + *  FSR: +/- 2000DPS + *  DLPF: 42Hz + *  FIFO rate: 50Hz + *  Clock source: Gyro PLL + */ +int inv_init_config_mpu3050(struct iio_dev *indio_dev) +{ +	struct inv_reg_map_s *reg; +	int result; +	u8 data; +	struct inv_mpu_state *st = iio_priv(indio_dev); + +	if (st->chip_config.is_asleep) +		return -EPERM; +	/*reading AUX VDDIO register */ +	result = inv_i2c_read(st, REG_3050_AUX_VDDIO, 1, &data); +	if (result) +		return result; +	data &= ~BIT_3050_VDDIO; +	if (st->plat_data.level_shifter) +		data |= BIT_3050_VDDIO; +	result = inv_i2c_single_write(st, REG_3050_AUX_VDDIO, data); +	if (result) +		return result; + +	reg = &st->reg; +	/*2000dps full scale range*/ +	result = inv_i2c_single_write(st, reg->lpf, +				(INV_FSR_2000DPS << GYRO_CONFIG_FSR_SHIFT) +				| INV_FILTER_42HZ); +	if (result) +		return result; +	st->chip_config.fsr = INV_FSR_2000DPS; +	st->chip_config.lpf = INV_FILTER_42HZ; +	st->chip_info.multi = 1; +	result = inv_i2c_single_write(st, reg->sample_rate_div, +					ONE_K_HZ/INIT_FIFO_RATE - 1); +	if (result) +		return result; +	st->chip_config.fifo_rate = INIT_FIFO_RATE; +	st->irq_dur_ns            = INIT_DUR_TIME; +	st->chip_config.prog_start_addr = DMP_START_ADDR; +	if ((SECONDARY_SLAVE_TYPE_ACCEL == st->plat_data.sec_slave_type) && +		st->slave_accel) { +		result = st->slave_accel->setup(st); +		if (result) +			return result; +		result = st->slave_accel->set_fs(st, INV_FS_02G); +		if (result) +			return result; +		result = st->slave_accel->set_lpf(st, INIT_FIFO_RATE); +		if (result) +			return result; +	} + +	return 0; +} + +/** + *  set_power_mpu3050() - set power of mpu3050. + *  @st:	Device driver instance. + *  @power_on:  on/off + */ +int set_power_mpu3050(struct inv_mpu_state *st, bool power_on) +{ +	struct inv_reg_map_s *reg; +	u8 data, p; +	int result; +	reg = &st->reg; +	if (power_on) { +		data = 0; +	} else { +		if (st->slave_accel) { +			result = st->slave_accel->suspend(st); +			if (result) +				return result; +		} +		data = BIT_SLEEP; +	} +	if (st->chip_config.gyro_enable) { +		p = (BITS_3050_POWER1 | INV_CLK_PLL); +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p); +		if (result) +			return result; + +		p = (BITS_3050_POWER2 | INV_CLK_PLL); +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p); +		if (result) +			return result; + +		p = INV_CLK_PLL; +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p); +		if (result) +			return result; +	} else { +		data |= (BITS_3050_GYRO_STANDBY | INV_CLK_INTERNAL); +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data); +		if (result) +			return result; +	} +	if (power_on) { +		msleep(POWER_UP_TIME); +		if (st->slave_accel) { +			result = st->slave_accel->resume(st); +			if (result) +				return result; +		} +	} +	st->chip_config.is_asleep = !power_on; + +	return 0; +} + diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_core.c b/drivers/staging/iio/imu/inv_mpu/inv_mpu_core.c new file mode 100644 index 00000000000..94c6b5d49ae --- /dev/null +++ b/drivers/staging/iio/imu/inv_mpu/inv_mpu_core.c @@ -0,0 +1,3148 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> +#include <linux/iio/sysfs.h> + +/* OLIO */ +#include "olio_specials.h" + +#include "inv_mpu_iio.h" + +#include "inv_test/inv_counters.h" + +#ifdef CONFIG_DTS_INV_MPU_IIO +#include "inv_mpu_dts.h" +#endif + +s64 get_time_ns(void) +{ +	struct timespec ts; +	ktime_get_ts(&ts); +	return timespec_to_ns(&ts); +} + +/* This is for compatibility for power state. Should remove once HAL +   does not use power_state sysfs entry */ +static bool fake_asleep; + +static const struct inv_hw_s hw_info[INV_NUM_PARTS] = { +	{119, "ITG3500"}, +	{ 63, "MPU3050"}, +	{117, "MPU6050"}, +	{118, "MPU9150"}, +	{128, "MPU6500"}, +	{128, "MPU9250"}, +	{128, "MPU9350"}, +	{128, "MPU6515"}, +}; + +static const u8 reg_gyro_offset[] = {REG_XG_OFFS_USRH, +					REG_XG_OFFS_USRH + 2, +					REG_XG_OFFS_USRH + 4}; + +const u8 reg_6050_accel_offset[] = {REG_XA_OFFS_H, +					REG_XA_OFFS_H + 2, +					REG_XA_OFFS_H + 4}; + +const u8 reg_6500_accel_offset[] = {REG_6500_XA_OFFS_H, +					REG_6500_YA_OFFS_H, +					REG_6500_ZA_OFFS_H}; +#ifdef CONFIG_INV_TESTING +static bool suspend_state; +static int inv_mpu_suspend(struct device *dev); +static int inv_mpu_resume(struct device *dev); +struct test_data_out { +	bool gyro; +	bool accel; +	bool compass; +	bool pressure; +	bool LPQ; +	bool SIXQ; +	bool PEDQ; +}; +static struct test_data_out data_out_control; +#endif + +static void inv_setup_reg(struct inv_reg_map_s *reg) +{ +	reg->sample_rate_div	= REG_SAMPLE_RATE_DIV; +	reg->lpf		= REG_CONFIG; +	reg->bank_sel		= REG_BANK_SEL; +	reg->user_ctrl		= REG_USER_CTRL; +	reg->fifo_en		= REG_FIFO_EN; +	reg->gyro_config	= REG_GYRO_CONFIG; +	reg->accel_config	= REG_ACCEL_CONFIG; +	reg->fifo_count_h	= REG_FIFO_COUNT_H; +	reg->fifo_r_w		= REG_FIFO_R_W; +	reg->raw_accel		= REG_RAW_ACCEL; +	reg->temperature	= REG_TEMPERATURE; +	reg->int_enable		= REG_INT_ENABLE; +	reg->int_status		= REG_INT_STATUS; +	reg->pwr_mgmt_1		= REG_PWR_MGMT_1; +	reg->pwr_mgmt_2		= REG_PWR_MGMT_2; +	reg->mem_start_addr	= REG_MEM_START_ADDR; +	reg->mem_r_w		= REG_MEM_RW; +	reg->prgm_strt_addrh	= REG_PRGM_STRT_ADDRH; +}; + +/** + *  inv_i2c_read_base() - Read one or more bytes from the device registers. + *  @st:	Device driver instance. + *  @i2c_addr:  i2c address of device. + *  @reg:	First device register to be read from. + *  @length:	Number of bytes to read. + *  @data:	Data read from device. + *  NOTE:This is not re-implementation of i2c_smbus_read because i2c + *       address could be specified in this case. We could have two different + *       i2c address due to secondary i2c interface. + */ +int inv_i2c_read_base(struct inv_mpu_state *st, u16 i2c_addr, +	u8 reg, u16 length, u8 *data) +{ +	struct i2c_msg msgs[2]; +	int res; + +	if (!data) +		return -EINVAL; + +	msgs[0].addr = i2c_addr; +	msgs[0].flags = 0;	/* write */ +	msgs[0].buf = ® +	msgs[0].len = 1; + +	msgs[1].addr = i2c_addr; +	msgs[1].flags = I2C_M_RD; +	msgs[1].buf = data; +	msgs[1].len = length; + +	res = i2c_transfer(st->sl_handle, msgs, 2); + +	if (res < 2) { +		if (res >= 0) +			res = -EIO; +	} else +		res = 0; + +	INV_I2C_INC_MPUWRITE(3); +	INV_I2C_INC_MPUREAD(length); +#ifdef CONFIG_DYNAMIC_DEBUG +	{ +		char *read = 0; +		pr_debug("%s RD%02X%02X%02X -> %s%s\n", st->hw->name, +			 i2c_addr, reg, length, +			 wr_pr_debug_begin(data, length, read), +			 wr_pr_debug_end(read)); +	} +#endif +	return res; +} + +/** + *  inv_i2c_single_write_base() - Write a byte to a device register. + *  @st:	Device driver instance. + *  @i2c_addr:  I2C address of the device. + *  @reg:	Device register to be written to. + *  @data:	Byte to write to device. + *  NOTE:This is not re-implementation of i2c_smbus_write because i2c + *       address could be specified in this case. We could have two different + *       i2c address due to secondary i2c interface. + */ +int inv_i2c_single_write_base(struct inv_mpu_state *st, +	u16 i2c_addr, u8 reg, u8 data) +{ +	u8 tmp[2]; +	struct i2c_msg msg; +	int res; +	tmp[0] = reg; +	tmp[1] = data; + +	msg.addr = i2c_addr; +	msg.flags = 0;	/* write */ +	msg.buf = tmp; +	msg.len = 2; + +	pr_debug("%s WR%02X%02X%02X\n", st->hw->name, i2c_addr, reg, data); +	INV_I2C_INC_MPUWRITE(3); + +	res = i2c_transfer(st->sl_handle, &msg, 1); +	if (res < 1) { +		if (res == 0) +			res = -EIO; +		return res; +	} else +		return 0; +} + +static int inv_switch_engine(struct inv_mpu_state *st, bool en, u32 mask) +{ +	struct inv_reg_map_s *reg; +	u8 data, mgmt_1; +	int result; + +	reg = &st->reg; +	/* Only when gyro is on, can +	   clock source be switched to gyro. Otherwise, it must be set to +	   internal clock */ +	if (BIT_PWR_GYRO_STBY == mask) { +		result = inv_i2c_read(st, reg->pwr_mgmt_1, 1, &mgmt_1); +		if (result) +			return result; + +		mgmt_1 &= ~BIT_CLK_MASK; +	} + +	if ((BIT_PWR_GYRO_STBY == mask) && (!en)) { +		/* turning off gyro requires switch to internal clock first. +		   Then turn off gyro engine */ +		mgmt_1 |= INV_CLK_INTERNAL; +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, +						mgmt_1); +		if (result) +			return result; +	} + +	result = inv_i2c_read(st, reg->pwr_mgmt_2, 1, &data); +	if (result) +		return result; +	if (en) +		data &= (~mask); +	else +		data |= mask; +	result = inv_i2c_single_write(st, reg->pwr_mgmt_2, data); +	if (result) +		return result; + +	if ((BIT_PWR_GYRO_STBY == mask) && en) { +		/* only gyro on needs sensor up time */ +		msleep(SENSOR_UP_TIME); +		/* after gyro is on & stable, switch internal clock to PLL */ +		mgmt_1 |= INV_CLK_PLL; +		result = inv_i2c_single_write(st, reg->pwr_mgmt_1, +						mgmt_1); +		if (result) +			return result; +	} +	if ((BIT_PWR_ACCEL_STBY == mask) && en) +		msleep(REG_UP_TIME); + +	return 0; +} + +/* + *  inv_lpa_freq() - store current low power frequency setting. + */ +static int inv_lpa_freq(struct inv_mpu_state *st, int lpa_freq) +{ +	unsigned long result; +	u8 d; +	/* 2, 4, 6, 7 corresponds to 0.98, 3.91, 15.63, 31.25 */ +	const u8 mpu6500_lpa_mapping[] = {2, 4, 6, 7}; + +	if (lpa_freq > MAX_LPA_FREQ_PARAM) +		return -EINVAL; + +	if (INV_MPU6500 == st->chip_type) { +		d = mpu6500_lpa_mapping[lpa_freq]; +		result = inv_i2c_single_write(st, REG_6500_LP_ACCEL_ODR, d); +		if (result) +			return result; +	} +	st->chip_config.lpa_freq = lpa_freq; + +	return 0; +} + +static int set_power_itg(struct inv_mpu_state *st, bool power_on) +{ +	struct inv_reg_map_s *reg; +	u8 data; +	int result; + +	if ((!power_on) == st->chip_config.is_asleep) +		return 0; +	reg = &st->reg; +	if (power_on) +		data = 0; +	else +		data = BIT_SLEEP; +	result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data); +	if (result) +		return result; + +	if (power_on) +		msleep(REG_UP_TIME); + +	st->chip_config.is_asleep = !power_on; + +	return 0; +} + +/** + *  inv_init_config() - Initialize hardware, disable FIFO. + *  @indio_dev:	Device driver instance. + *  Initial configuration: + *  FSR: +/- 2000DPS + *  DLPF: 42Hz + *  FIFO rate: 50Hz + */ +static int inv_init_config(struct iio_dev *indio_dev) +{ +	struct inv_reg_map_s *reg; +	int result, i; +	struct inv_mpu_state *st = iio_priv(indio_dev); +	const u8 *ch; +	u8 d[2]; + +	reg = &st->reg; + +	result = inv_i2c_single_write(st, reg->gyro_config, +		INV_FSR_2000DPS << GYRO_CONFIG_FSR_SHIFT); +	if (result) +		return result; + +	st->chip_config.fsr = INV_FSR_2000DPS; + +	result = inv_i2c_single_write(st, reg->lpf, INV_FILTER_42HZ); +	if (result) +		return result; +	st->chip_config.lpf = INV_FILTER_42HZ; + +	result = inv_i2c_single_write(st, reg->sample_rate_div, +					ONE_K_HZ / INIT_FIFO_RATE - 1); +	if (result) +		return result; +	st->chip_config.fifo_rate = INIT_FIFO_RATE; +	st->irq_dur_ns            = INIT_DUR_TIME; +	st->chip_config.prog_start_addr = DMP_START_ADDR; +	if (INV_MPU6050 == st->chip_type) +		st->self_test.samples = INIT_ST_MPU6050_SAMPLES; +	else +		st->self_test.samples = INIT_ST_SAMPLES; +	st->self_test.threshold = INIT_ST_THRESHOLD; +	st->batch.wake_fifo_on = true; +	if (INV_ITG3500 != st->chip_type) { +		st->chip_config.accel_fs = INV_FS_02G; +		result = inv_i2c_single_write(st, reg->accel_config, +			(INV_FS_02G << ACCEL_CONFIG_FSR_SHIFT)); +		if (result) +			return result; +		st->tap.time = INIT_TAP_TIME; +		st->tap.thresh = INIT_TAP_THRESHOLD; +		st->tap.min_count = INIT_TAP_MIN_COUNT; +		st->sample_divider = INIT_SAMPLE_DIVIDER; +		st->smd.threshold = MPU_INIT_SMD_THLD; +		st->smd.delay     = MPU_INIT_SMD_DELAY_THLD; +		st->smd.delay2    = MPU_INIT_SMD_DELAY2_THLD; +		st->ped.int_thresh = INIT_PED_INT_THRESH; +		st->ped.step_thresh = INIT_PED_THRESH; +		st->sensor[SENSOR_STEP].rate = MAX_DMP_OUTPUT_RATE; + +		result = inv_i2c_single_write(st, REG_ACCEL_MOT_DUR, +						INIT_MOT_DUR); +		if (result) +			return result; +		st->mot_int.mot_dur = INIT_MOT_DUR; + +		result = inv_i2c_single_write(st, REG_ACCEL_MOT_THR, +						INIT_MOT_THR); +		if (result) +			return result; +		st->mot_int.mot_thr = INIT_MOT_THR; + +		for (i = 0; i < 3; i++) { +			result = inv_i2c_read(st, reg_gyro_offset[i], 2, d); +			if (result) +				return result; +			st->rom_gyro_offset[i] = +					(short)be16_to_cpup((__be16 *)(d)); +			st->input_gyro_offset[i] = 0; +			st->input_gyro_dmp_bias[i] = 0; +		} +		if (INV_MPU6050 == st->chip_type) +			ch = reg_6050_accel_offset; +		else +			ch = reg_6500_accel_offset; +		for (i = 0; i < 3; i++) { +			result = inv_i2c_read(st, ch[i], 2, d); +			if (result) +				return result; +			st->rom_accel_offset[i] = +					(short)be16_to_cpup((__be16 *)(d)); +			st->input_accel_offset[i] = 0; +			st->input_accel_dmp_bias[i] = 0; +		} +		st->ped.step = 0; +		st->ped.time = 0; +	} + +	return 0; +} + +/* + *  inv_write_fsr() - Configure the gyro's scale range. + */ +static int inv_write_fsr(struct inv_mpu_state *st, int fsr) +{ +	struct inv_reg_map_s *reg; +	int result; + +	reg = &st->reg; +	if ((fsr < 0) || (fsr > MAX_GYRO_FS_PARAM)) +		return -EINVAL; +	if (fsr == st->chip_config.fsr) +		return 0; + +	if (INV_MPU3050 == st->chip_type) +		result = inv_i2c_single_write(st, reg->lpf, +			(fsr << GYRO_CONFIG_FSR_SHIFT) | st->chip_config.lpf); +	else +		result = inv_i2c_single_write(st, reg->gyro_config, +			fsr << GYRO_CONFIG_FSR_SHIFT); + +	if (result) +		return result; +	st->chip_config.fsr = fsr; + +	return 0; +} + +/* + *  inv_write_accel_fs() - Configure the accelerometer's scale range. + */ +static int inv_write_accel_fs(struct inv_mpu_state *st, int fs) +{ +	int result; +	struct inv_reg_map_s *reg; + +	reg = &st->reg; +	if (fs < 0 || fs > MAX_ACCEL_FS_PARAM) +		return -EINVAL; +	if (fs == st->chip_config.accel_fs) +		return 0; +	if (INV_MPU3050 == st->chip_type) +		result = st->slave_accel->set_fs(st, fs); +	else +		result = inv_i2c_single_write(st, reg->accel_config, +				(fs << ACCEL_CONFIG_FSR_SHIFT)); +	if (result) +		return result; + +	st->chip_config.accel_fs = fs; + +	return 0; +} + +static int inv_set_offset_reg(struct inv_mpu_state *st, int reg, int val) +{ +	int result; +	u8 d; + +	d = ((val >> 8) & 0xff); +	result = inv_i2c_single_write(st, reg, d); +	if (result) +		return result; + +	d = (val & 0xff); +	result = inv_i2c_single_write(st, reg + 1, d); + +	return result; +} + +int inv_reset_offset_reg(struct inv_mpu_state *st, bool en) +{ +	const u8 *ch; +	int i, result; +	s16 gyro[3], accel[3]; + +	if (en) { +		for (i = 0; i < 3; i++) { +			gyro[i] = st->rom_gyro_offset[i]; +			accel[i] = st->rom_accel_offset[i]; +		} +	} else { +		for (i = 0; i < 3; i++) { +			gyro[i] = st->rom_gyro_offset[i] + +						st->input_gyro_offset[i]; +			accel[i] = st->rom_accel_offset[i] + +					(st->input_accel_offset[i] << 1); +		} +	} +	if (INV_MPU6050 == st->chip_type) +		ch = reg_6050_accel_offset; +	else +		ch = reg_6500_accel_offset; + +	for (i = 0; i < 3; i++) { +		result = inv_set_offset_reg(st, reg_gyro_offset[i], gyro[i]); +		if (result) +			return result; +		result = inv_set_offset_reg(st, ch[i], accel[i]); +		if (result) +			return result; +	} + +	return 0; +} +/* + *  inv_fifo_rate_store() - Set fifo rate. + */ +static int inv_fifo_rate_store(struct inv_mpu_state *st, int fifo_rate) +{ +	if ((fifo_rate < MIN_FIFO_RATE) || (fifo_rate > MAX_FIFO_RATE)) +		return -EINVAL; +	if (fifo_rate == st->chip_config.fifo_rate) +		return 0; + +	st->irq_dur_ns = NSEC_PER_SEC / fifo_rate; +	st->chip_config.fifo_rate = fifo_rate; + +	return 0; +} + +/* + *  inv_reg_dump_show() - Register dump for testing. + */ +static ssize_t inv_reg_dump_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	int ii; +	char data; +	ssize_t bytes_printed = 0; +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); + +	mutex_lock(&indio_dev->mlock); +	for (ii = 0; ii < st->hw->num_reg; ii++) { +		/* don't read fifo r/w register */ +		if (ii == st->reg.fifo_r_w) +			data = 0; +		else +			inv_i2c_read(st, ii, 1, &data); +		bytes_printed += sprintf(buf + bytes_printed, "%#2x: %#2x\n", +					 ii, data); +	} +	mutex_unlock(&indio_dev->mlock); + +	return bytes_printed; +} + +int write_be32_key_to_mem(struct inv_mpu_state *st, +					u32 data, int key) +{ +	cpu_to_be32s(&data); +	return mem_w_key(key, sizeof(data), (u8 *)&data); +} + +int inv_write_2bytes(struct inv_mpu_state *st, int k, int data) +{ +	u8 d[2]; + +	if (data < 0 || data > USHRT_MAX) +		return -EINVAL; + +	d[0] = (u8)((data >> 8) & 0xff); +	d[1] = (u8)(data & 0xff); + +	return mem_w_key(k, ARRAY_SIZE(d), d); +} + +static ssize_t _dmp_bias_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int result, data, tmp; + +	if (!st->chip_config.firmware_loaded) +		return -EINVAL; + +	if (!st->chip_config.enable) { +		result = st->set_power_state(st, true); +		if (result) +			return result; +	} + +	result = kstrtoint(buf, 10, &data); +	if (result) +		goto dmp_bias_store_fail; +	switch (this_attr->address) { +	case ATTR_DMP_ACCEL_X_DMP_BIAS: +		tmp = st->input_accel_dmp_bias[0]; +		st->input_accel_dmp_bias[0] = data; +		result = inv_set_accel_bias_dmp(st); +		if (result) +			st->input_accel_dmp_bias[0] = tmp; +		break; +	case ATTR_DMP_ACCEL_Y_DMP_BIAS: +		tmp = st->input_accel_dmp_bias[1]; +		st->input_accel_dmp_bias[1] = data; +		result = inv_set_accel_bias_dmp(st); +		if (result) +			st->input_accel_dmp_bias[1] = tmp; +		break; +	case ATTR_DMP_ACCEL_Z_DMP_BIAS: +		tmp = st->input_accel_dmp_bias[2]; +		st->input_accel_dmp_bias[2] = data; +		result = inv_set_accel_bias_dmp(st); +		if (result) +			st->input_accel_dmp_bias[2] = tmp; +		break; +	case ATTR_DMP_GYRO_X_DMP_BIAS: +		result = write_be32_key_to_mem(st, data, +					KEY_CFG_EXT_GYRO_BIAS_X); +		if (result) +			goto dmp_bias_store_fail; +		st->input_gyro_dmp_bias[0] = data; +		break; +	case ATTR_DMP_GYRO_Y_DMP_BIAS: +		result = write_be32_key_to_mem(st, data, +					KEY_CFG_EXT_GYRO_BIAS_Y); +		if (result) +			goto dmp_bias_store_fail; +		st->input_gyro_dmp_bias[1] = data; +		break; +	case ATTR_DMP_GYRO_Z_DMP_BIAS: +		result = write_be32_key_to_mem(st, data, +					KEY_CFG_EXT_GYRO_BIAS_Z); +		if (result) +			goto dmp_bias_store_fail; +		st->input_gyro_dmp_bias[2] = data; +		break; +	default: +		break; +	} + +dmp_bias_store_fail: +	if (!st->chip_config.enable) +		result |= st->set_power_state(st, false); +	if (result) +		return result; + +	return count; +} + +static ssize_t inv_dmp_bias_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	int result; + +	mutex_lock(&indio_dev->mlock); +	result = _dmp_bias_store(dev, attr, buf, count); +	mutex_unlock(&indio_dev->mlock); + +	return result; +} + +static ssize_t _dmp_attr_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int result, data; + +	if (st->chip_config.enable) +		return -EBUSY; +	if (this_attr->address <= ATTR_DMP_DISPLAY_ORIENTATION_ON) { +		if (!st->chip_config.firmware_loaded) +			return -EINVAL; +		result = st->set_power_state(st, true); +		if (result) +			return result; +	} + +	result = kstrtoint(buf, 10, &data); +	if (result) +		goto dmp_attr_store_fail; +	switch (this_attr->address) { +	case ATTR_DMP_PED_INT_ON: +		result = inv_enable_pedometer_interrupt(st, !!data); +		if (result) +			goto dmp_attr_store_fail; +		st->ped.int_on = !!data; +		break; +	case ATTR_DMP_PED_ON: +	{ +		result = inv_enable_pedometer(st, !!data); +		if (result) +			goto dmp_attr_store_fail; +		st->ped.on = !!data; +		break; +	} +	case ATTR_DMP_PED_STEP_THRESH: +	{ +		result = inv_write_2bytes(st, KEY_D_PEDSTD_SB, data); +		if (result) +			goto dmp_attr_store_fail; +		st->ped.step_thresh = data; +		break; +	} +	case ATTR_DMP_PED_INT_THRESH: +	{ +		result = inv_write_2bytes(st, KEY_D_PEDSTD_SB2, data); +		if (result) +			goto dmp_attr_store_fail; +		st->ped.int_thresh = data; +		break; +	} +	case ATTR_DMP_SMD_ENABLE: +		result = inv_write_2bytes(st, KEY_SMD_ENABLE, !!data); +		if (result) +			goto dmp_attr_store_fail; +		st->chip_config.smd_enable = !!data; +		break; +	case ATTR_DMP_SMD_THLD: +		if (data < 0 || data > SHRT_MAX) +			goto dmp_attr_store_fail; +		result = write_be32_key_to_mem(st, data << 16, +						KEY_SMD_ACCEL_THLD); +		if (result) +			goto dmp_attr_store_fail; +		st->smd.threshold = data; +		break; +	case ATTR_DMP_SMD_DELAY_THLD: +		if (data < 0 || data > INT_MAX / MPU_DEFAULT_DMP_FREQ) +			goto dmp_attr_store_fail; +		result = write_be32_key_to_mem(st, data * MPU_DEFAULT_DMP_FREQ, +						KEY_SMD_DELAY_THLD); +		if (result) +			goto dmp_attr_store_fail; +		st->smd.delay = data; +		break; +	case ATTR_DMP_SMD_DELAY_THLD2: +		if (data < 0 || data > INT_MAX / MPU_DEFAULT_DMP_FREQ) +			goto dmp_attr_store_fail; +		result = write_be32_key_to_mem(st, data * MPU_DEFAULT_DMP_FREQ, +						KEY_SMD_DELAY2_THLD); +		if (result) +			goto dmp_attr_store_fail; +		st->smd.delay2 = data; +		break; +	case ATTR_DMP_TAP_ON: +		result = inv_enable_tap_dmp(st, !!data); +		if (result) +			goto dmp_attr_store_fail; +		st->tap.on = !!data; +		break; +	case ATTR_DMP_TAP_THRESHOLD: +		if (data < 0 || data > USHRT_MAX) { +			result = -EINVAL; +			goto dmp_attr_store_fail; +		} +		result = inv_set_tap_threshold_dmp(st, data); +		if (result) +			goto dmp_attr_store_fail; +		st->tap.thresh = data; +		break; +	case ATTR_DMP_TAP_MIN_COUNT: +		if (data < 0 || data > USHRT_MAX) { +			result = -EINVAL; +			goto dmp_attr_store_fail; +		} +		result = inv_set_min_taps_dmp(st, data); +		if (result) +			goto dmp_attr_store_fail; +		st->tap.min_count = data; +		break; +	case ATTR_DMP_TAP_TIME: +		if (data < 0 || data > USHRT_MAX) { +			result = -EINVAL; +			goto dmp_attr_store_fail; +		} +		result = inv_set_tap_time_dmp(st, data); +		if (result) +			goto dmp_attr_store_fail; +		st->tap.time = data; +		break; +	case ATTR_DMP_DISPLAY_ORIENTATION_ON: +		result = inv_set_display_orient_interrupt_dmp(st, !!data); +		if (result) +			goto dmp_attr_store_fail; +		st->chip_config.display_orient_on = !!data; +		break; +	/* from here, power of chip is not turned on */ +	case ATTR_DMP_ON: +		st->chip_config.dmp_on = !!data; +		break; +	case ATTR_DMP_INT_ON: +		st->chip_config.dmp_int_on = !!data; +		break; +	case ATTR_DMP_EVENT_INT_ON: +		st->chip_config.dmp_event_int_on = !!data; +		break; +	case ATTR_DMP_STEP_INDICATOR_ON: +		st->chip_config.step_indicator_on = !!data; +		break; +	case ATTR_DMP_BATCHMODE_TIMEOUT: +		if (data < 0 || data > INT_MAX) { +			result = -EINVAL; +			goto dmp_attr_store_fail; +		} +		st->batch.timeout = data; +		break; +	case ATTR_DMP_BATCHMODE_WAKE_FIFO_FULL: +		st->batch.wake_fifo_on = !!data; +		st->batch.overflow_on = 0; +		break; +	case ATTR_DMP_SIX_Q_ON: +		st->sensor[SENSOR_SIXQ].on = !!data; +		break; +	case ATTR_DMP_SIX_Q_RATE: +		if (data > MPU_DEFAULT_DMP_FREQ || data < 0) { +			result = -EINVAL; +			goto dmp_attr_store_fail; +		} +		st->sensor[SENSOR_SIXQ].rate = data; +		st->sensor[SENSOR_SIXQ].dur = MPU_DEFAULT_DMP_FREQ / data; +		st->sensor[SENSOR_SIXQ].dur *= DMP_INTERVAL_INIT; +		break; +	case ATTR_DMP_LPQ_ON: +		st->sensor[SENSOR_LPQ].on = !!data; +		break; +	case ATTR_DMP_LPQ_RATE: +		if (data > MPU_DEFAULT_DMP_FREQ || data < 0) { +			result = -EINVAL; +			goto dmp_attr_store_fail; +		} +		st->sensor[SENSOR_LPQ].rate = data; +		st->sensor[SENSOR_LPQ].dur = MPU_DEFAULT_DMP_FREQ / data; +		st->sensor[SENSOR_LPQ].dur *= DMP_INTERVAL_INIT; +		break; +	case ATTR_DMP_PED_Q_ON: +		st->sensor[SENSOR_PEDQ].on = !!data; +		break; +	case ATTR_DMP_PED_Q_RATE: +		if (data > MPU_DEFAULT_DMP_FREQ || data < 0) { +			result = -EINVAL; +			goto dmp_attr_store_fail; +		} +		st->sensor[SENSOR_PEDQ].rate = data; +		st->sensor[SENSOR_PEDQ].dur = MPU_DEFAULT_DMP_FREQ / data; +		st->sensor[SENSOR_PEDQ].dur *= DMP_INTERVAL_INIT; +		break; +	case ATTR_DMP_STEP_DETECTOR_ON: +		st->sensor[SENSOR_STEP].on = !!data; +		break; +#ifdef CONFIG_INV_TESTING +	case ATTR_DEBUG_SMD_ENABLE_TESTP1: +	{ +		u8 d[] = {0x42}; +		result = st->set_power_state(st, true); +		if (result) +			goto dmp_attr_store_fail; +		result = mem_w_key(KEY_SMD_ENABLE_TESTPT1, ARRAY_SIZE(d), d); +		if (result) +			goto dmp_attr_store_fail; +	} +		break; +	case ATTR_DEBUG_SMD_ENABLE_TESTP2: +	{ +		u8 d[] = {0x42}; +		result = st->set_power_state(st, true); +		if (result) +			goto dmp_attr_store_fail; +		result = mem_w_key(KEY_SMD_ENABLE_TESTPT2, ARRAY_SIZE(d), d); +		if (result) +			goto dmp_attr_store_fail; +	} +		break; +#endif +	default: +		result = -EINVAL; +		goto dmp_attr_store_fail; +	} + +dmp_attr_store_fail: +	if (this_attr->address <= ATTR_DMP_DISPLAY_ORIENTATION_ON) +		result |= st->set_power_state(st, false); +	if (result) +		return result; + +	return count; +} + +/* + * inv_dmp_attr_store() -  calling this function will store current + *                        dmp parameter settings + */ +static ssize_t inv_dmp_attr_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	int result; + +	mutex_lock(&indio_dev->mlock); +	result = _dmp_attr_store(dev, attr, buf, count); +	mutex_unlock(&indio_dev->mlock); + +	return result; +} + +static ssize_t inv_attr64_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int result; +	u64 tmp; +	u32 ped; + +	mutex_lock(&indio_dev->mlock); +	if (!st->chip_config.enable || !st->chip_config.dmp_on) { +		mutex_unlock(&indio_dev->mlock); +		return -EINVAL; +	} +	result = 0; +	switch (this_attr->address) { +	case ATTR_DMP_PEDOMETER_STEPS: +		result = inv_get_pedometer_steps(st, &ped); +		result |= inv_read_pedometer_counter(st); +		tmp = st->ped.step + ped; +		break; +	case ATTR_DMP_PEDOMETER_TIME: +		result = inv_get_pedometer_time(st, &ped); +		tmp = st->ped.time + ped; +		break; +	case ATTR_DMP_PEDOMETER_COUNTER: +		tmp = st->ped.last_step_time; +		break; +	default: +		result = -EINVAL; +		break; +	} +	mutex_unlock(&indio_dev->mlock); +	if (result) +		return -EINVAL; +	return sprintf(buf, "%lld\n", tmp); +} + +static ssize_t inv_attr64_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int result; +	u64 data; + +	mutex_lock(&indio_dev->mlock); +	if (st->chip_config.enable || (!st->chip_config.firmware_loaded)) { +		mutex_unlock(&indio_dev->mlock); +		return -EINVAL; +	} +	result = st->set_power_state(st, true); +	if (result) { +		mutex_unlock(&indio_dev->mlock); +		return result; +	} +	result = kstrtoull(buf, 10, &data); +	if (result) +		goto attr64_store_fail; +	switch (this_attr->address) { +	case ATTR_DMP_PEDOMETER_STEPS: +		result = write_be32_key_to_mem(st, 0, KEY_D_PEDSTD_STEPCTR); +		if (result) +			goto attr64_store_fail; +		st->ped.step = data; +		break; +	case ATTR_DMP_PEDOMETER_TIME: +		result = write_be32_key_to_mem(st, 0, KEY_D_PEDSTD_TIMECTR); +		if (result) +			goto attr64_store_fail; +		st->ped.time = data; +		break; +	default: +		result = -EINVAL; +		break; +	} +attr64_store_fail: +	mutex_unlock(&indio_dev->mlock); +	result = st->set_power_state(st, false); +	if (result) +		return result; + +	return count; +} +/* + * inv_attr_show() -  calling this function will show current + *                        dmp parameters. + */ +static ssize_t inv_attr_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int result, axis; +	s8 *m; + +	switch (this_attr->address) { +	case ATTR_GYRO_SCALE: +	{ +		const s16 gyro_scale[] = {250, 500, 1000, 2000}; + +		return sprintf(buf, "%d\n", gyro_scale[st->chip_config.fsr]); +	} +	case ATTR_ACCEL_SCALE: +	{ +		const s16 accel_scale[] = {2, 4, 8, 16}; +		return sprintf(buf, "%d\n", +					accel_scale[st->chip_config.accel_fs] * +					st->chip_info.multi); +	} +	case ATTR_COMPASS_SCALE: +		st->slave_compass->get_scale(st, &result); + +		return sprintf(buf, "%d\n", result); +	case ATTR_ACCEL_X_CALIBBIAS: +	case ATTR_ACCEL_Y_CALIBBIAS: +	case ATTR_ACCEL_Z_CALIBBIAS: +		axis = this_attr->address - ATTR_ACCEL_X_CALIBBIAS; +		return sprintf(buf, "%d\n", st->accel_bias[axis] * +						st->chip_info.multi); +	case ATTR_GYRO_X_CALIBBIAS: +	case ATTR_GYRO_Y_CALIBBIAS: +	case ATTR_GYRO_Z_CALIBBIAS: +		axis = this_attr->address - ATTR_GYRO_X_CALIBBIAS; +		return sprintf(buf, "%d\n", st->gyro_bias[axis]); +	case ATTR_SELF_TEST_GYRO_SCALE: +		return sprintf(buf, "%d\n", SELF_TEST_GYRO_FULL_SCALE); +	case ATTR_SELF_TEST_ACCEL_SCALE: +		if (INV_MPU6500 == st->chip_type) +			return sprintf(buf, "%d\n", SELF_TEST_ACCEL_6500_SCALE); +		else +			return sprintf(buf, "%d\n", SELF_TEST_ACCEL_FULL_SCALE); +	case ATTR_GYRO_X_OFFSET: +	case ATTR_GYRO_Y_OFFSET: +	case ATTR_GYRO_Z_OFFSET: +		axis = this_attr->address - ATTR_GYRO_X_OFFSET; +		return sprintf(buf, "%d\n", st->input_gyro_offset[axis]); +	case ATTR_ACCEL_X_OFFSET: +	case ATTR_ACCEL_Y_OFFSET: +	case ATTR_ACCEL_Z_OFFSET: +		axis = this_attr->address - ATTR_ACCEL_X_OFFSET; +		return sprintf(buf, "%d\n", st->input_accel_offset[axis]); +	case ATTR_DMP_ACCEL_X_DMP_BIAS: +		return sprintf(buf, "%d\n", st->input_accel_dmp_bias[0]); +	case ATTR_DMP_ACCEL_Y_DMP_BIAS: +		return sprintf(buf, "%d\n", st->input_accel_dmp_bias[1]); +	case ATTR_DMP_ACCEL_Z_DMP_BIAS: +		return sprintf(buf, "%d\n", st->input_accel_dmp_bias[2]); +	case ATTR_DMP_GYRO_X_DMP_BIAS: +		return sprintf(buf, "%d\n", st->input_gyro_dmp_bias[0]); +	case ATTR_DMP_GYRO_Y_DMP_BIAS: +		return sprintf(buf, "%d\n", st->input_gyro_dmp_bias[1]); +	case ATTR_DMP_GYRO_Z_DMP_BIAS: +		return sprintf(buf, "%d\n", st->input_gyro_dmp_bias[2]); +	case ATTR_DMP_PED_INT_ON: +		return sprintf(buf, "%d\n", st->ped.int_on); +	case ATTR_DMP_PED_ON: +		return sprintf(buf, "%d\n", st->ped.on); +	case ATTR_DMP_PED_STEP_THRESH: +		return sprintf(buf, "%d\n", st->ped.step_thresh); +	case ATTR_DMP_PED_INT_THRESH: +		return sprintf(buf, "%d\n", st->ped.int_thresh); +	case ATTR_DMP_SMD_ENABLE: +		return sprintf(buf, "%d\n", st->chip_config.smd_enable); +	case ATTR_DMP_SMD_THLD: +		return sprintf(buf, "%d\n", st->smd.threshold); +	case ATTR_DMP_SMD_DELAY_THLD: +		return sprintf(buf, "%d\n", st->smd.delay); +	case ATTR_DMP_SMD_DELAY_THLD2: +		return sprintf(buf, "%d\n", st->smd.delay2); +	case ATTR_DMP_TAP_ON: +		return sprintf(buf, "%d\n", st->tap.on); +	case ATTR_DMP_TAP_THRESHOLD: +		return sprintf(buf, "%d\n", st->tap.thresh); +	case ATTR_DMP_TAP_MIN_COUNT: +		return sprintf(buf, "%d\n", st->tap.min_count); +	case ATTR_DMP_TAP_TIME: +		return sprintf(buf, "%d\n", st->tap.time); +	case ATTR_DMP_DISPLAY_ORIENTATION_ON: +		return sprintf(buf, "%d\n", +			st->chip_config.display_orient_on); +	case ATTR_DMP_ON: +		return sprintf(buf, "%d\n", st->chip_config.dmp_on); +	case ATTR_DMP_INT_ON: +		return sprintf(buf, "%d\n", st->chip_config.dmp_int_on); +	case ATTR_DMP_EVENT_INT_ON: +		return sprintf(buf, "%d\n", st->chip_config.dmp_event_int_on); +	case ATTR_DMP_STEP_INDICATOR_ON: +		return sprintf(buf, "%d\n", st->chip_config.step_indicator_on); +	case ATTR_DMP_BATCHMODE_TIMEOUT: +		return sprintf(buf, "%d\n", +				st->batch.timeout); +	case ATTR_DMP_BATCHMODE_WAKE_FIFO_FULL: +		return sprintf(buf, "%d\n", +				st->batch.wake_fifo_on); +	case ATTR_DMP_SIX_Q_ON: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_SIXQ].on); +	case ATTR_DMP_SIX_Q_RATE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_SIXQ].rate); +	case ATTR_DMP_LPQ_ON: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_LPQ].on); +	case ATTR_DMP_LPQ_RATE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_LPQ].rate); +	case ATTR_DMP_PED_Q_ON: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_PEDQ].on); +	case ATTR_DMP_PED_Q_RATE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_PEDQ].rate); +	case ATTR_DMP_STEP_DETECTOR_ON: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_STEP].on); +	case ATTR_MOTION_LPA_ON: +		return sprintf(buf, "%d\n", st->mot_int.mot_on); +	case ATTR_MOTION_LPA_FREQ:{ +		const char *f[] = {"1.25", "5", "20", "40"}; +		return sprintf(buf, "%s\n", f[st->chip_config.lpa_freq]); +	} +	case ATTR_MOTION_LPA_THRESHOLD: +		return sprintf(buf, "%d\n", st->mot_int.mot_thr); + +	case ATTR_SELF_TEST_SAMPLES: +		return sprintf(buf, "%d\n", st->self_test.samples); +	case ATTR_SELF_TEST_THRESHOLD: +		return sprintf(buf, "%d\n", st->self_test.threshold); +	case ATTR_GYRO_ENABLE: +		return sprintf(buf, "%d\n", st->chip_config.gyro_enable); +	case ATTR_GYRO_FIFO_ENABLE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_GYRO].on); +	case ATTR_GYRO_RATE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_GYRO].rate); +	case ATTR_ACCEL_ENABLE: +		return sprintf(buf, "%d\n", st->chip_config.accel_enable); +	case ATTR_ACCEL_FIFO_ENABLE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_ACCEL].on); +	case ATTR_ACCEL_RATE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_ACCEL].rate); +	case ATTR_COMPASS_ENABLE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_COMPASS].on); +	case ATTR_COMPASS_RATE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_COMPASS].rate); +	case ATTR_PRESSURE_ENABLE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_PRESSURE].on); +	case ATTR_PRESSURE_RATE: +		return sprintf(buf, "%d\n", st->sensor[SENSOR_PRESSURE].rate); +	case ATTR_POWER_STATE: +		return sprintf(buf, "%d\n", !fake_asleep); +	case ATTR_FIRMWARE_LOADED: +		return sprintf(buf, "%d\n", st->chip_config.firmware_loaded); +	case ATTR_SAMPLING_FREQ: +		return sprintf(buf, "%d\n", st->chip_config.fifo_rate); +	case ATTR_SELF_TEST: +		mutex_lock(&indio_dev->mlock); +		if (st->chip_config.enable) { +			mutex_unlock(&indio_dev->mlock); +			return -EBUSY; +		} +		if (INV_MPU3050 == st->chip_type) +			result = 1; +		else +			result = inv_hw_self_test(st); +		mutex_unlock(&indio_dev->mlock); +		return sprintf(buf, "%d\n", result); +	case ATTR_GYRO_MATRIX: +		m = st->plat_data.orientation; +		return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n", +			m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]); +	case ATTR_ACCEL_MATRIX: +		if (st->plat_data.sec_slave_type == +						SECONDARY_SLAVE_TYPE_ACCEL) +			m = +			st->plat_data.secondary_orientation; +		else +			m = st->plat_data.orientation; +		return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n", +			m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]); +	case ATTR_COMPASS_MATRIX: +		if (st->plat_data.sec_slave_type == +				SECONDARY_SLAVE_TYPE_COMPASS) +			m = +			st->plat_data.secondary_orientation; +		else +			return -ENODEV; +		return sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d\n", +			m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]); +	case ATTR_SECONDARY_NAME: +	{ +		const char *n[] = {"NULL", "AK8975", "AK8972", "AK8963", +					"BMA250", "MLX90399", "AK09911"}; +		switch (st->plat_data.sec_slave_id) { +		case COMPASS_ID_AK8975: +			return sprintf(buf, "%s\n", n[1]); +		case COMPASS_ID_AK8972: +			return sprintf(buf, "%s\n", n[2]); +		case COMPASS_ID_AK8963: +			return sprintf(buf, "%s\n", n[3]); +		case ACCEL_ID_BMA250: +			return sprintf(buf, "%s\n", n[4]); +		case COMPASS_ID_MLX90399: +			return sprintf(buf, "%s\n", n[5]); +		case COMPASS_ID_AK09911: +			return sprintf(buf, "%s\n", n[6]); +		default: +			return sprintf(buf, "%s\n", n[0]); +		} +	} +#ifdef CONFIG_INV_TESTING +	case ATTR_REG_WRITE: +		return sprintf(buf, "1\n"); +	case ATTR_COMPASS_SENS: +	{ +		/* these 2 conditions should never be met, since the +		   'compass_sens' sysfs entry should be hidden if the compass +		   is not an AKM */ +		if (st->plat_data.sec_slave_type != +					SECONDARY_SLAVE_TYPE_COMPASS) +			return -ENODEV; +		if (st->plat_data.sec_slave_id != COMPASS_ID_AK8975 && +		    st->plat_data.sec_slave_id != COMPASS_ID_AK8972 && +		    st->plat_data.sec_slave_id != COMPASS_ID_AK8963) +			return -ENODEV; +		m = st->chip_info.compass_sens; +		return sprintf(buf, "%d,%d,%d\n", m[0], m[1], m[2]); +	} +	case ATTR_DEBUG_SMD_EXE_STATE: +	{ +		u8 d[2]; + +		result = st->set_power_state(st, true); +		mpu_memory_read(st, st->i2c_addr, +				inv_dmp_get_address(KEY_SMD_EXE_STATE), 2, d); +		return sprintf(buf, "%d\n", (short)be16_to_cpup((__be16 *)(d))); +	} +	case ATTR_DEBUG_SMD_DELAY_CNTR: +	{ +		u8 d[4]; + +		result = st->set_power_state(st, true); +		mpu_memory_read(st, st->i2c_addr, +				inv_dmp_get_address(KEY_SMD_DELAY_CNTR), 4, d); +		return sprintf(buf, "%d\n", (int)be32_to_cpup((__be32 *)(d))); +	} +#endif +	default: +		return -EPERM; +	} +} + +/* + * inv_dmp_display_orient_show() -  calling this function will + *			show orientation This event must use poll. + */ +static ssize_t inv_dmp_display_orient_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct inv_mpu_state *st = iio_priv(dev_get_drvdata(dev)); + +	return sprintf(buf, "%d\n", st->display_orient_data); +} + +/* + * inv_accel_motion_show() -  calling this function showes motion interrupt. + *                         This event must use poll. + */ +static ssize_t inv_accel_motion_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	return sprintf(buf, "1\n"); +} + +/* + * inv_smd_show() -  calling this function showes smd interrupt. + *                         This event must use poll. + */ +static ssize_t inv_smd_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	return sprintf(buf, "1\n"); +} + +/* + * inv_ped_show() -  calling this function showes pedometer interrupt. + *                         This event must use poll. + */ +static ssize_t inv_ped_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	return sprintf(buf, "1\n"); +} + +/* + * inv_dmp_tap_show() -  calling this function will show tap + *                         This event must use poll. + */ +static ssize_t inv_dmp_tap_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct inv_mpu_state *st = iio_priv(dev_get_drvdata(dev)); +	return sprintf(buf, "%d\n", st->tap_data); +} + +/* + *  inv_temperature_show() - Read temperature data directly from registers. + */ +static ssize_t inv_temperature_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ + +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct inv_reg_map_s *reg; +	int result, cur_scale, cur_off; +	short temp; +	long scale_t; +	u8 data[2]; +	const long scale[] = {3834792L, 3158064L, 3340827L}; +	const long offset[] = {5383314L, 2394184L, 1376256L}; + +	reg = &st->reg; +	mutex_lock(&indio_dev->mlock); +	if (!st->chip_config.enable) +		result = st->set_power_state(st, true); +	else +		result = 0; +	if (result) { +		mutex_unlock(&indio_dev->mlock); +		return result; +	} +	result = inv_i2c_read(st, reg->temperature, 2, data); +	if (!st->chip_config.enable) +		result |= st->set_power_state(st, false); +	mutex_unlock(&indio_dev->mlock); +	if (result) { +		pr_err("Could not read temperature register.\n"); +		return result; +	} +	temp = (signed short)(be16_to_cpup((short *)&data[0])); +	switch (st->chip_type) { +	case INV_MPU3050: +		cur_scale = scale[0]; +		cur_off   = offset[0]; +		break; +	case INV_MPU6050: +		cur_scale = scale[1]; +		cur_off   = offset[1]; +		break; +	case INV_MPU6500: +		cur_scale = scale[2]; +		cur_off   = offset[2]; +		break; +	default: +		return -EINVAL; +	}; +	scale_t = cur_off + +		inv_q30_mult((int)temp << MPU_TEMP_SHIFT, cur_scale); + +	INV_I2C_INC_TEMPREAD(1); + +	return sprintf(buf, "%ld %lld\n", scale_t, get_time_ns()); +} + +static ssize_t inv_flush_batch_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	int result; +	bool has_data; + +	mutex_lock(&indio_dev->mlock); +	result = inv_flush_batch_data(indio_dev, &has_data); +	mutex_unlock(&indio_dev->mlock); +	if (result) +		return sprintf(buf, "%d\n", result); +	else +		return sprintf(buf, "%d\n", has_data); +} + +/* + * inv_firmware_loaded() -  calling this function will change + *                        firmware load + */ +static int inv_firmware_loaded(struct inv_mpu_state *st, int data) +{ +	if (data) +		return -EINVAL; +	st->chip_config.firmware_loaded = 0; + +	return 0; +} + +static int inv_switch_gyro_engine(struct inv_mpu_state *st, bool en) +{ +	return inv_switch_engine(st, en, BIT_PWR_GYRO_STBY); +} + +static int inv_switch_accel_engine(struct inv_mpu_state *st, bool en) +{ +	return inv_switch_engine(st, en, BIT_PWR_ACCEL_STBY); +} + +static ssize_t _attr_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int data; +	u8 d, axis; +	int result; + +	result = 0; +	if (st->chip_config.enable) +		return -EBUSY; +	if (this_attr->address <= ATTR_MOTION_LPA_THRESHOLD) { +		result = st->set_power_state(st, true); +		if (result) +			return result; +	} + +	/* check the input and validate it's format */ +	switch (this_attr->address) { +#ifdef CONFIG_INV_TESTING +	/* these inputs are strings */ +	case ATTR_COMPASS_MATRIX: +	case ATTR_COMPASS_SENS: +		break; +#endif +	/* these inputs are integers */ +	default: +		result = kstrtoint(buf, 10, &data); +		if (result) +			goto attr_store_fail; +		break; +	} + +	switch (this_attr->address) { +	case ATTR_GYRO_X_OFFSET: +	case ATTR_GYRO_Y_OFFSET: +	case ATTR_GYRO_Z_OFFSET: +		if ((data > MPU_MAX_G_OFFSET_VALUE) || +				(data < MPU_MIN_G_OFFSET_VALUE)) +			return -EINVAL; +		axis = this_attr->address - ATTR_GYRO_X_OFFSET; +		result = inv_set_offset_reg(st, +				reg_gyro_offset[axis], +				st->rom_gyro_offset[axis] + data); + +		if (result) +			goto attr_store_fail; +		st->input_gyro_offset[axis] = data; +		break; +	case ATTR_ACCEL_X_OFFSET: +	case ATTR_ACCEL_Y_OFFSET: +	case ATTR_ACCEL_Z_OFFSET: +	{ +		const u8 *ch; + +		if ((data > MPU_MAX_A_OFFSET_VALUE) || +			(data < MPU_MIN_A_OFFSET_VALUE)) +			return -EINVAL; + +		axis = this_attr->address - ATTR_ACCEL_X_OFFSET; +		if (INV_MPU6050 == st->chip_type) +			ch = reg_6050_accel_offset; +		else +			ch = reg_6500_accel_offset; + +		result = inv_set_offset_reg(st, ch[axis], +			st->rom_accel_offset[axis] + (data << 1)); +		if (result) +			goto attr_store_fail; +		st->input_accel_offset[axis] = data; +		break; +	} +	case ATTR_GYRO_SCALE: +		result = inv_write_fsr(st, data); +		break; +	case ATTR_ACCEL_SCALE: +		result = inv_write_accel_fs(st, data); +		break; +	case ATTR_COMPASS_SCALE: +		result = st->slave_compass->set_scale(st, data); +		break; +	case ATTR_MOTION_LPA_ON: +		if (INV_MPU6500 == st->chip_type) { +			if (data) +				/* enable and put in MPU6500 mode */ +				d = BIT_ACCEL_INTEL_ENABLE +					| BIT_ACCEL_INTEL_MODE; +			else +				d = 0; +			result = inv_i2c_single_write(st, +						REG_6500_ACCEL_INTEL_CTRL, d); +			if (result) +				goto attr_store_fail; +		} +		st->mot_int.mot_on = !!data; +		st->chip_config.lpa_mode = !!data; +		break; +	case ATTR_MOTION_LPA_FREQ: +		result = inv_lpa_freq(st, data); +		break; +	case ATTR_MOTION_LPA_THRESHOLD: +		if ((data > MPU6XXX_MAX_MOTION_THRESH) || (data < 0)) { +			result = -EINVAL; +			goto attr_store_fail; +		} +		if (INV_MPU6500 == st->chip_type) { +			d = (u8)(data >> MPU6500_MOTION_THRESH_SHIFT); +			data = (d << MPU6500_MOTION_THRESH_SHIFT); +		} else { +			d = (u8)(data >> MPU6050_MOTION_THRESH_SHIFT); +			data = (d << MPU6050_MOTION_THRESH_SHIFT); +		} + +		result = inv_i2c_single_write(st, REG_ACCEL_MOT_THR, d); +		if (result) +			goto attr_store_fail; +		st->mot_int.mot_thr = data; +		break; +	/* from now on, power is not turned on */ +	case ATTR_SELF_TEST_SAMPLES: +		if (data > ST_MAX_SAMPLES || data < 0) { +			result = -EINVAL; +			goto attr_store_fail; +		} +		st->self_test.samples = data; +		break; +	case ATTR_SELF_TEST_THRESHOLD: +		if (data > ST_MAX_THRESHOLD || data < 0) { +			result = -EINVAL; +			goto attr_store_fail; +		} +		st->self_test.threshold = data; +	case ATTR_GYRO_ENABLE: +		st->chip_config.gyro_enable = !!data; +		break; +	case ATTR_GYRO_FIFO_ENABLE: +		st->sensor[SENSOR_GYRO].on = !!data; +		break; +	case ATTR_GYRO_RATE: +		st->sensor[SENSOR_GYRO].rate = data; +		st->sensor[SENSOR_GYRO].dur  = MPU_DEFAULT_DMP_FREQ / data; +		st->sensor[SENSOR_GYRO].dur  *= DMP_INTERVAL_INIT; +		break; +	case ATTR_ACCEL_ENABLE: +		st->chip_config.accel_enable = !!data; +		break; +	case ATTR_ACCEL_FIFO_ENABLE: +		st->sensor[SENSOR_ACCEL].on = !!data; +		break; +	case ATTR_ACCEL_RATE: +		st->sensor[SENSOR_ACCEL].rate = data; +		st->sensor[SENSOR_ACCEL].dur  = MPU_DEFAULT_DMP_FREQ / data; +		st->sensor[SENSOR_ACCEL].dur  *= DMP_INTERVAL_INIT; +		break; +	case ATTR_COMPASS_ENABLE: +		st->sensor[SENSOR_COMPASS].on = !!data; +		break; +	case ATTR_COMPASS_RATE: +		if (data <= 0) { +			result = -EINVAL; +			goto attr_store_fail; +		} +		if ((MSEC_PER_SEC / st->slave_compass->rate_scale) < data) +			data = MSEC_PER_SEC / st->slave_compass->rate_scale; + +		st->sensor[SENSOR_COMPASS].rate = data; +		st->sensor[SENSOR_COMPASS].dur  = MPU_DEFAULT_DMP_FREQ / data; +		st->sensor[SENSOR_COMPASS].dur  *= DMP_INTERVAL_INIT; +		break; +	case ATTR_PRESSURE_ENABLE: +		st->sensor[SENSOR_PRESSURE].on = !!data; +		break; +	case ATTR_PRESSURE_RATE: +		if (data <= 0) { +			result = -EINVAL; +			goto attr_store_fail; +		} +		if ((MSEC_PER_SEC / st->slave_pressure->rate_scale) < data) +			data = MSEC_PER_SEC / st->slave_pressure->rate_scale; + +		st->sensor[SENSOR_PRESSURE].rate = data; +		st->sensor[SENSOR_PRESSURE].dur  = MPU_DEFAULT_DMP_FREQ / data; +		st->sensor[SENSOR_PRESSURE].dur  *= DMP_INTERVAL_INIT; +		break; +	case ATTR_POWER_STATE: +		fake_asleep = !data; +		break; +	case ATTR_FIRMWARE_LOADED: +		result = inv_firmware_loaded(st, data); +		break; +	case ATTR_SAMPLING_FREQ: +		result = inv_fifo_rate_store(st, data); +		break; +#ifdef CONFIG_INV_TESTING +	case ATTR_COMPASS_MATRIX: +	{ +		char *str; +		__s8 m[9]; +		d = 0; +		if (st->plat_data.sec_slave_type == SECONDARY_SLAVE_TYPE_NONE) +			return -ENODEV; +		while ((str = strsep((char **)&buf, ","))) { +			if (d >= 9) { +				result = -EINVAL; +				goto attr_store_fail; +			} +			result = kstrtoint(str, 10, &data); +			if (result) +				goto attr_store_fail; +			if (data < -1 || data > 1) { +				result = -EINVAL; +				goto attr_store_fail; +			} +			m[d] = data; +			d++; +		} +		if (d < 9) { +			result = -EINVAL; +			goto attr_store_fail; +		} +		memcpy(st->plat_data.secondary_orientation, m, sizeof(m)); +		pr_debug(KERN_INFO +			 "compass_matrix: %d,%d,%d,%d,%d,%d,%d,%d,%d\n", +			 m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]); +		break; +	} +	case ATTR_COMPASS_SENS: +	{ +		char *str; +		__s8 s[3]; +		d = 0; +		/* these 2 conditions should never be met, since the +		   'compass_sens' sysfs entry should be hidden if the compass +		   is not an AKM */ +		if (st->plat_data.sec_slave_type == SECONDARY_SLAVE_TYPE_NONE) +			return -ENODEV; +		if (st->plat_data.sec_slave_id != COMPASS_ID_AK8975 && +		    st->plat_data.sec_slave_id != COMPASS_ID_AK8972 && +		    st->plat_data.sec_slave_id != COMPASS_ID_AK8963) +			return -ENODEV; +		/* read the input data, expecting 3 comma separated values */ +		while ((str = strsep((char **)&buf, ","))) { +			if (d >= 3) { +				result = -EINVAL; +				goto attr_store_fail; +			} +			result = kstrtoint(str, 10, &data); +			if (result) +				goto attr_store_fail; +			if (data < 0 || data > 255) { +				result = -EINVAL; +				goto attr_store_fail; +			} +			s[d] = data; +			d++; +		} +		if (d < 3) { +			result = -EINVAL; +			goto attr_store_fail; +		} +		/* store the new compass sensitivity adjustment */ +		memcpy(st->chip_info.compass_sens, s, sizeof(s)); +		pr_debug(KERN_INFO +			 "compass_sens: %d,%d,%d\n", s[0], s[1], s[2]); +		break; +	} +#endif +	default: +		result = -EINVAL; +		goto attr_store_fail; +	}; + +attr_store_fail: +	if (this_attr->address <= ATTR_MOTION_LPA_THRESHOLD) +		result |= st->set_power_state(st, false); +	if (result) +		return result; + +	return count; +} + +/* + * inv_attr_store() -  calling this function will store current + *                        non-dmp parameter settings + */ +static ssize_t inv_attr_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	int result; + +	mutex_lock(&indio_dev->mlock); +	result = _attr_store(dev, attr, buf, count); +	mutex_unlock(&indio_dev->mlock); + +	return result; +} + +static ssize_t inv_master_enable_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	int data; +	int result; + +	result = kstrtoint(buf, 10, &data); +	if (result) +		return result; + +	mutex_lock(&indio_dev->mlock); +	if (st->chip_config.enable == (!!data)) { +		result = count; +		goto end_enable; +	} +	if (!!data) { +		result = st->set_power_state(st, true); +		if (result) +			goto end_enable; +	} +	result = set_inv_enable(indio_dev, !!data); +	if (result) +		goto end_enable; +	if (!data) { +		result = st->set_power_state(st, false); +		if (result) +			goto end_enable; +	} +	result = count; + +end_enable: +	mutex_unlock(&indio_dev->mlock); + +	return result; +} + +static ssize_t inv_master_enable_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct inv_mpu_state *st = iio_priv(dev_get_drvdata(dev)); + +	return sprintf(buf, "%d\n", st->chip_config.enable); +} + +#ifdef CONFIG_INV_TESTING +static ssize_t inv_test_suspend_resume_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	int data; +	int result; + +	result = kstrtoint(buf, 10, &data); +	if (result) +		return result; +	if (data) +		inv_mpu_suspend(dev); +	else +		inv_mpu_resume(dev); +	suspend_state = !!data; + +	return count; +} + +static ssize_t inv_test_suspend_resume_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ + +	return sprintf(buf, "%d\n", suspend_state); +} + +/* + * inv_reg_write_store() - register write command for testing. + *                         Format: WSRRDD, where RR is the register in hex, + *                                         and DD is the data in hex. + */ +static ssize_t inv_reg_write_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	u32 result; +	u8 wreg, wval; +	int temp; +	char local_buf[10]; + +	if ((buf[0] != 'W' && buf[0] != 'w') || +	    (buf[1] != 'S' && buf[1] != 's')) +		return -EINVAL; +	if (strlen(buf) < 6) +		return -EINVAL; + +	strncpy(local_buf, buf, 7); +	local_buf[6] = 0; +	result = sscanf(&local_buf[4], "%x", &temp); +	if (result == 0) +		return -EINVAL; +	wval = temp; +	local_buf[4] = 0; +	sscanf(&local_buf[2], "%x", &temp); +	if (result == 0) +		return -EINVAL; +	wreg = temp; + +	result = inv_i2c_single_write(st, wreg, wval); +	if (result) +		return result; + +	return count; +} + +static ssize_t inv_test_store(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	int data; +	u8 *m; +	int result; + +	if (st->chip_config.enable) +		return -EBUSY; +	result = kstrtoint(buf, 10, &data); +	if (result) +		return -EINVAL; + +	result = st->set_power_state(st, true); +	if (result) +		return result; + +	switch (this_attr->address) { +	case ATTR_DEBUG_ACCEL_COUNTER: +	{ +		u8 D[6] = {0xf2, 0xb0, 0x80, 0xc0, 0xc8, 0xc2}; +		u8 E[6] = {0xf3, 0xb1, 0x88, 0xc0, 0xc0, 0xc0}; + +		if (data) +			m = E; +		else +			m = D; +		result = mem_w_key(KEY_TEST_01, ARRAY_SIZE(D), m); +		data_out_control.accel = !!data; +		break; +	} +	case ATTR_DEBUG_GYRO_COUNTER: +	{ +		u8 D[6] = {0xf2, 0xb0, 0x80, 0xc4, 0xcc, 0xc6}; +		u8 E[6] = {0xf3, 0xb1, 0x88, 0xc0, 0xc0, 0xc0}; + +		if (data) +			m = E; +		else +			m = D; +		result = mem_w_key(KEY_TEST_02, ARRAY_SIZE(D), m); +		data_out_control.gyro = !!data; +		break; +	} +	case ATTR_DEBUG_COMPASS_COUNTER: +	{ +		u8 D[6] = {0xf2, 0xb0, 0x81, 0xc0, 0xc8, 0xc2}; +		u8 E[6] = {0xf3, 0xb1, 0x88, 0xc0, 0xc0, 0xc0}; + +		if (data) +			m = E; +		else +			m = D; +		result = mem_w_key(KEY_TEST_03, ARRAY_SIZE(D), m); +		data_out_control.compass = !!data; +		break; +	} +	case ATTR_DEBUG_PRESSURE_COUNTER: +	{ +		u8 D[6] = {0xf2, 0xb0, 0x81, 0xc4, 0xcc, 0xc6}; +		u8 E[6] = {0xf3, 0xb1, 0x88, 0xc0, 0xc0, 0xc0}; + +		if (data) +			m = E; +		else +			m = D; +		result = mem_w_key(KEY_TEST_04, ARRAY_SIZE(D), m); +		data_out_control.pressure = !!data; +		break; +	} +	case ATTR_DEBUG_LPQ_COUNTER: +	{ +		u8 D[6] = {0xf1, 0xb1, 0x83, 0xc2, 0xc4, 0xc6}; +		u8 E[6] = {0xf1, 0xb1, 0x88, 0xc0, 0xc0, 0xc0}; + +		if (data) +			m = E; +		else +			m = D; +		result = mem_w_key(KEY_TEST_05, ARRAY_SIZE(D), m); +		data_out_control.LPQ = !!data; +		break; +	} +	case ATTR_DEBUG_SIXQ_COUNTER: +	{ +		u8 D[6] = {0xf1, 0xb1, 0x89, 0xc2, 0xc4, 0xc6}; +		u8 E[6] = {0xf1, 0xb1, 0x88, 0xc0, 0xc0, 0xc0}; + +		if (data) +			m = E; +		else +			m = D; +		result = mem_w_key(KEY_TEST_06, ARRAY_SIZE(D), m); +		data_out_control.SIXQ = !!data; +		break; +	} +	case ATTR_DEBUG_PEDQ_COUNTER: +	{ +		u8 D[6] = {0xf2, 0xf2, 0x88, 0xc2, 0xc4, 0xc6}; +		u8 E[6] = {0xf3, 0xb1, 0x88, 0xc0, 0xc0, 0xc0}; + +		if (data) +			m = E; +		else +			m = D; +		result = mem_w_key(KEY_TEST_07, ARRAY_SIZE(D), m); +		data_out_control.PEDQ = !!data; +		break; +	} +	default: +		return -EINVAL; +	} + +	return count; +} + +static ssize_t inv_test_show(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + +	switch (this_attr->address) { +	case ATTR_DEBUG_ACCEL_COUNTER: +		return sprintf(buf, "%d\n", data_out_control.accel); +	case ATTR_DEBUG_GYRO_COUNTER: +		return sprintf(buf, "%d\n", data_out_control.gyro); +	case ATTR_DEBUG_COMPASS_COUNTER: +		return sprintf(buf, "%d\n", data_out_control.compass); +	case ATTR_DEBUG_PRESSURE_COUNTER: +		return sprintf(buf, "%d\n", data_out_control.pressure); +	case ATTR_DEBUG_LPQ_COUNTER: +		return sprintf(buf, "%d\n", data_out_control.LPQ); +	case ATTR_DEBUG_SIXQ_COUNTER: +		return sprintf(buf, "%d\n", data_out_control.SIXQ); +	case ATTR_DEBUG_PEDQ_COUNTER: +		return sprintf(buf, "%d\n", data_out_control.PEDQ); +	default: +		return -EINVAL; +	} +} + +#endif /* CONFIG_INV_TESTING */ + +static const struct iio_chan_spec inv_mpu_channels[] = { +	IIO_CHAN_SOFT_TIMESTAMP(INV_MPU_SCAN_TIMESTAMP), +}; + +/*constant IIO attribute */ +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 20 50 100 200 500"); + +/* special sysfs */ +static DEVICE_ATTR(reg_dump, S_IRUGO, inv_reg_dump_show, NULL); +static DEVICE_ATTR(temperature, S_IRUGO, inv_temperature_show, NULL); + +/* event based sysfs, needs poll to read */ +static DEVICE_ATTR(event_tap, S_IRUGO, inv_dmp_tap_show, NULL); +static DEVICE_ATTR(event_display_orientation, S_IRUGO, +	inv_dmp_display_orient_show, NULL); +static DEVICE_ATTR(event_accel_motion, S_IRUGO, inv_accel_motion_show, NULL); +static DEVICE_ATTR(event_smd, S_IRUGO, inv_smd_show, NULL); +static DEVICE_ATTR(event_pedometer, S_IRUGO, inv_ped_show, NULL); + +/* master enable method */ +static DEVICE_ATTR(master_enable, S_IRUGO | S_IWUSR, inv_master_enable_show, +					inv_master_enable_store); + +/* special run time sysfs entry, read only */ +static DEVICE_ATTR(flush_batch, S_IRUGO, inv_flush_batch_show, NULL); + +/* DMP sysfs with power on/off */ +static IIO_DEVICE_ATTR(in_accel_x_dmp_bias, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_bias_store, ATTR_DMP_ACCEL_X_DMP_BIAS); +static IIO_DEVICE_ATTR(in_accel_y_dmp_bias, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_bias_store, ATTR_DMP_ACCEL_Y_DMP_BIAS); +static IIO_DEVICE_ATTR(in_accel_z_dmp_bias, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_bias_store, ATTR_DMP_ACCEL_Z_DMP_BIAS); + +static IIO_DEVICE_ATTR(in_anglvel_x_dmp_bias, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_bias_store, ATTR_DMP_GYRO_X_DMP_BIAS); +static IIO_DEVICE_ATTR(in_anglvel_y_dmp_bias, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_bias_store, ATTR_DMP_GYRO_Y_DMP_BIAS); +static IIO_DEVICE_ATTR(in_anglvel_z_dmp_bias, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_bias_store, ATTR_DMP_GYRO_Z_DMP_BIAS); + +static IIO_DEVICE_ATTR(pedometer_int_on, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_attr_store, ATTR_DMP_PED_INT_ON); +static IIO_DEVICE_ATTR(pedometer_on, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_attr_store, ATTR_DMP_PED_ON); +static IIO_DEVICE_ATTR(pedometer_step_thresh, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_attr_store, ATTR_DMP_PED_STEP_THRESH); +static IIO_DEVICE_ATTR(pedometer_int_thresh, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_attr_store, ATTR_DMP_PED_INT_THRESH); + +static IIO_DEVICE_ATTR(smd_enable, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_attr_store, ATTR_DMP_SMD_ENABLE); +static IIO_DEVICE_ATTR(smd_threshold, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_attr_store, ATTR_DMP_SMD_THLD); +static IIO_DEVICE_ATTR(smd_delay_threshold, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_attr_store, ATTR_DMP_SMD_DELAY_THLD); +static IIO_DEVICE_ATTR(smd_delay_threshold2, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_attr_store, ATTR_DMP_SMD_DELAY_THLD2); + +static IIO_DEVICE_ATTR(pedometer_steps, S_IRUGO | S_IWUSR, inv_attr64_show, +	inv_attr64_store, ATTR_DMP_PEDOMETER_STEPS); +static IIO_DEVICE_ATTR(pedometer_time, S_IRUGO | S_IWUSR, inv_attr64_show, +	inv_attr64_store, ATTR_DMP_PEDOMETER_TIME); +static IIO_DEVICE_ATTR(pedometer_counter, S_IRUGO | S_IWUSR, inv_attr64_show, +	NULL, ATTR_DMP_PEDOMETER_COUNTER); + +static IIO_DEVICE_ATTR(tap_on, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_attr_store, ATTR_DMP_TAP_ON); +static IIO_DEVICE_ATTR(tap_threshold, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_attr_store, ATTR_DMP_TAP_THRESHOLD); +static IIO_DEVICE_ATTR(tap_min_count, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_attr_store, ATTR_DMP_TAP_MIN_COUNT); +static IIO_DEVICE_ATTR(tap_time, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_attr_store, ATTR_DMP_TAP_TIME); +static IIO_DEVICE_ATTR(display_orientation_on, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_attr_store, ATTR_DMP_DISPLAY_ORIENTATION_ON); + +/* DMP sysfs without power on/off */ +static IIO_DEVICE_ATTR(dmp_on, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_ON); +static IIO_DEVICE_ATTR(dmp_int_on, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_INT_ON); +static IIO_DEVICE_ATTR(dmp_event_int_on, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_EVENT_INT_ON); +static IIO_DEVICE_ATTR(step_indicator_on, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_STEP_INDICATOR_ON); +static IIO_DEVICE_ATTR(batchmode_timeout, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_attr_store, ATTR_DMP_BATCHMODE_TIMEOUT); +static IIO_DEVICE_ATTR(batchmode_wake_fifo_full_on, S_IRUGO | S_IWUSR, +	inv_attr_show, inv_dmp_attr_store, ATTR_DMP_BATCHMODE_WAKE_FIFO_FULL); + +static IIO_DEVICE_ATTR(six_axes_q_on, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_SIX_Q_ON); +static IIO_DEVICE_ATTR(six_axes_q_rate, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_SIX_Q_RATE); + +static IIO_DEVICE_ATTR(three_axes_q_on, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_LPQ_ON); +static IIO_DEVICE_ATTR(three_axes_q_rate, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_LPQ_RATE); + +static IIO_DEVICE_ATTR(ped_q_on, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_PED_Q_ON); +static IIO_DEVICE_ATTR(ped_q_rate, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_PED_Q_RATE); + +static IIO_DEVICE_ATTR(step_detector_on, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_dmp_attr_store, ATTR_DMP_STEP_DETECTOR_ON); + +/* non DMP sysfs with power on/off */ +static IIO_DEVICE_ATTR(motion_lpa_on, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_MOTION_LPA_ON); +static IIO_DEVICE_ATTR(motion_lpa_freq, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_MOTION_LPA_FREQ); +static IIO_DEVICE_ATTR(motion_lpa_threshold, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_MOTION_LPA_THRESHOLD); + +static IIO_DEVICE_ATTR(in_accel_scale, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_ACCEL_SCALE); +static IIO_DEVICE_ATTR(in_anglvel_scale, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_GYRO_SCALE); +static IIO_DEVICE_ATTR(in_magn_scale, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_COMPASS_SCALE); + +static IIO_DEVICE_ATTR(in_anglvel_x_offset, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_GYRO_X_OFFSET); +static IIO_DEVICE_ATTR(in_anglvel_y_offset, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_GYRO_Y_OFFSET); +static IIO_DEVICE_ATTR(in_anglvel_z_offset, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_GYRO_Z_OFFSET); + +static IIO_DEVICE_ATTR(in_accel_x_offset, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_ACCEL_X_OFFSET); +static IIO_DEVICE_ATTR(in_accel_y_offset, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_ACCEL_Y_OFFSET); +static IIO_DEVICE_ATTR(in_accel_z_offset, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_ACCEL_Z_OFFSET); + +/* non DMP sysfs without power on/off */ +static IIO_DEVICE_ATTR(self_test_samples, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_SELF_TEST_SAMPLES); +static IIO_DEVICE_ATTR(self_test_threshold, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_SELF_TEST_THRESHOLD); + +static IIO_DEVICE_ATTR(gyro_enable, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_GYRO_ENABLE); +static IIO_DEVICE_ATTR(gyro_fifo_enable, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_GYRO_FIFO_ENABLE); +static IIO_DEVICE_ATTR(gyro_rate, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_GYRO_RATE); + +static IIO_DEVICE_ATTR(accel_enable, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_ACCEL_ENABLE); +static IIO_DEVICE_ATTR(accel_fifo_enable, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_ACCEL_FIFO_ENABLE); +static IIO_DEVICE_ATTR(accel_rate, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_ACCEL_RATE); + +static IIO_DEVICE_ATTR(compass_enable, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_COMPASS_ENABLE); +static IIO_DEVICE_ATTR(compass_rate, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_COMPASS_RATE); + +static IIO_DEVICE_ATTR(pressure_enable, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_PRESSURE_ENABLE); +static IIO_DEVICE_ATTR(pressure_rate, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_PRESSURE_RATE); + +static IIO_DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_POWER_STATE); +static IIO_DEVICE_ATTR(firmware_loaded, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_FIRMWARE_LOADED); +static IIO_DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_SAMPLING_FREQ); + +/* show method only sysfs but with power on/off */ +static IIO_DEVICE_ATTR(self_test, S_IRUGO, inv_attr_show, NULL, +	ATTR_SELF_TEST); + +/* show method only sysfs */ +static IIO_DEVICE_ATTR(in_accel_x_calibbias, S_IRUGO, inv_attr_show, +	NULL, ATTR_ACCEL_X_CALIBBIAS); +static IIO_DEVICE_ATTR(in_accel_y_calibbias, S_IRUGO, inv_attr_show, +	NULL, ATTR_ACCEL_Y_CALIBBIAS); +static IIO_DEVICE_ATTR(in_accel_z_calibbias, S_IRUGO, inv_attr_show, +	NULL, ATTR_ACCEL_Z_CALIBBIAS); + +static IIO_DEVICE_ATTR(in_anglvel_x_calibbias, S_IRUGO, +		inv_attr_show, NULL, ATTR_GYRO_X_CALIBBIAS); +static IIO_DEVICE_ATTR(in_anglvel_y_calibbias, S_IRUGO, +		inv_attr_show, NULL, ATTR_GYRO_Y_CALIBBIAS); +static IIO_DEVICE_ATTR(in_anglvel_z_calibbias, S_IRUGO, +		inv_attr_show, NULL, ATTR_GYRO_Z_CALIBBIAS); + +static IIO_DEVICE_ATTR(in_anglvel_self_test_scale, S_IRUGO, +		inv_attr_show, NULL, ATTR_SELF_TEST_GYRO_SCALE); +static IIO_DEVICE_ATTR(in_accel_self_test_scale, S_IRUGO, +		inv_attr_show, NULL, ATTR_SELF_TEST_ACCEL_SCALE); + +static IIO_DEVICE_ATTR(gyro_matrix, S_IRUGO, inv_attr_show, NULL, +	ATTR_GYRO_MATRIX); +static IIO_DEVICE_ATTR(accel_matrix, S_IRUGO, inv_attr_show, NULL, +	ATTR_ACCEL_MATRIX); +#ifdef CONFIG_INV_TESTING /* read/write in test mode */ +static IIO_DEVICE_ATTR(compass_matrix, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_COMPASS_MATRIX); +static IIO_DEVICE_ATTR(compass_sens, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_attr_store, ATTR_COMPASS_SENS); +#else +static IIO_DEVICE_ATTR(compass_matrix, S_IRUGO, inv_attr_show, NULL, +	ATTR_COMPASS_MATRIX); +#endif +static IIO_DEVICE_ATTR(secondary_name, S_IRUGO, inv_attr_show, NULL, +	ATTR_SECONDARY_NAME); + +#ifdef CONFIG_INV_TESTING +static IIO_DEVICE_ATTR(reg_write, S_IRUGO | S_IWUSR, inv_attr_show, +	inv_reg_write_store, ATTR_REG_WRITE); +/* smd debug related sysfs */ +static IIO_DEVICE_ATTR(debug_smd_enable_testp1, S_IWUSR, NULL, +	inv_dmp_attr_store, ATTR_DEBUG_SMD_ENABLE_TESTP1); +static IIO_DEVICE_ATTR(debug_smd_enable_testp2, S_IWUSR, NULL, +	inv_dmp_attr_store, ATTR_DEBUG_SMD_ENABLE_TESTP2); +static IIO_DEVICE_ATTR(debug_smd_exe_state, S_IRUGO, inv_attr_show, +	NULL, ATTR_DEBUG_SMD_EXE_STATE); +static IIO_DEVICE_ATTR(debug_smd_delay_cntr, S_IRUGO, inv_attr_show, +	NULL, ATTR_DEBUG_SMD_DELAY_CNTR); +static DEVICE_ATTR(test_suspend_resume, S_IRUGO | S_IWUSR, +		inv_test_suspend_resume_show, inv_test_suspend_resume_store); + +static IIO_DEVICE_ATTR(test_gyro_counter, S_IRUGO | S_IWUSR, inv_test_show, +	inv_test_store, ATTR_DEBUG_GYRO_COUNTER); +static IIO_DEVICE_ATTR(test_accel_counter, S_IRUGO | S_IWUSR, inv_test_show, +	inv_test_store, ATTR_DEBUG_ACCEL_COUNTER); +static IIO_DEVICE_ATTR(test_compass_counter, S_IRUGO | S_IWUSR, inv_test_show, +	inv_test_store, ATTR_DEBUG_COMPASS_COUNTER); +static IIO_DEVICE_ATTR(test_pressure_counter, S_IRUGO | S_IWUSR, inv_test_show, +	inv_test_store, ATTR_DEBUG_PRESSURE_COUNTER); +static IIO_DEVICE_ATTR(test_LPQ_counter, S_IRUGO | S_IWUSR, inv_test_show, +	inv_test_store, ATTR_DEBUG_LPQ_COUNTER); +static IIO_DEVICE_ATTR(test_SIXQ_counter, S_IRUGO | S_IWUSR, inv_test_show, +	inv_test_store, ATTR_DEBUG_SIXQ_COUNTER); +static IIO_DEVICE_ATTR(test_PEDQ_counter, S_IRUGO | S_IWUSR, inv_test_show, +	inv_test_store, ATTR_DEBUG_PEDQ_COUNTER); +#endif + +static const struct attribute *inv_gyro_attributes[] = { +	&iio_const_attr_sampling_frequency_available.dev_attr.attr, +	&dev_attr_reg_dump.attr, +	&dev_attr_temperature.attr, +	&dev_attr_master_enable.attr, +	&iio_dev_attr_in_anglvel_scale.dev_attr.attr, +	&iio_dev_attr_in_anglvel_x_calibbias.dev_attr.attr, +	&iio_dev_attr_in_anglvel_y_calibbias.dev_attr.attr, +	&iio_dev_attr_in_anglvel_z_calibbias.dev_attr.attr, +	&iio_dev_attr_in_anglvel_x_offset.dev_attr.attr, +	&iio_dev_attr_in_anglvel_y_offset.dev_attr.attr, +	&iio_dev_attr_in_anglvel_z_offset.dev_attr.attr, +	&iio_dev_attr_in_anglvel_self_test_scale.dev_attr.attr, +	&iio_dev_attr_self_test_samples.dev_attr.attr, +	&iio_dev_attr_self_test_threshold.dev_attr.attr, +	&iio_dev_attr_gyro_enable.dev_attr.attr, +	&iio_dev_attr_gyro_fifo_enable.dev_attr.attr, +	&iio_dev_attr_gyro_rate.dev_attr.attr, +	&iio_dev_attr_power_state.dev_attr.attr, +	&iio_dev_attr_sampling_frequency.dev_attr.attr, +	&iio_dev_attr_self_test.dev_attr.attr, +	&iio_dev_attr_gyro_matrix.dev_attr.attr, +	&iio_dev_attr_secondary_name.dev_attr.attr, +#ifdef CONFIG_INV_TESTING +	&iio_dev_attr_reg_write.dev_attr.attr, +	&iio_dev_attr_debug_smd_enable_testp1.dev_attr.attr, +	&iio_dev_attr_debug_smd_enable_testp2.dev_attr.attr, +	&iio_dev_attr_debug_smd_exe_state.dev_attr.attr, +	&iio_dev_attr_debug_smd_delay_cntr.dev_attr.attr, +	&dev_attr_test_suspend_resume.attr, +	&iio_dev_attr_test_gyro_counter.dev_attr.attr, +	&iio_dev_attr_test_accel_counter.dev_attr.attr, +	&iio_dev_attr_test_compass_counter.dev_attr.attr, +	&iio_dev_attr_test_pressure_counter.dev_attr.attr, +	&iio_dev_attr_test_LPQ_counter.dev_attr.attr, +	&iio_dev_attr_test_SIXQ_counter.dev_attr.attr, +	&iio_dev_attr_test_PEDQ_counter.dev_attr.attr, +#endif +}; + +static const struct attribute *inv_mpu6xxx_attributes[] = { +	&dev_attr_event_accel_motion.attr, +	&dev_attr_event_smd.attr, +	&dev_attr_event_pedometer.attr, +	&dev_attr_flush_batch.attr, +	&iio_dev_attr_in_accel_scale.dev_attr.attr, +	&iio_dev_attr_in_accel_x_calibbias.dev_attr.attr, +	&iio_dev_attr_in_accel_y_calibbias.dev_attr.attr, +	&iio_dev_attr_in_accel_z_calibbias.dev_attr.attr, +	&iio_dev_attr_in_accel_self_test_scale.dev_attr.attr, +	&iio_dev_attr_in_accel_x_offset.dev_attr.attr, +	&iio_dev_attr_in_accel_y_offset.dev_attr.attr, +	&iio_dev_attr_in_accel_z_offset.dev_attr.attr, +	&iio_dev_attr_in_accel_x_dmp_bias.dev_attr.attr, +	&iio_dev_attr_in_accel_y_dmp_bias.dev_attr.attr, +	&iio_dev_attr_in_accel_z_dmp_bias.dev_attr.attr, +	&iio_dev_attr_in_anglvel_x_dmp_bias.dev_attr.attr, +	&iio_dev_attr_in_anglvel_y_dmp_bias.dev_attr.attr, +	&iio_dev_attr_in_anglvel_z_dmp_bias.dev_attr.attr, +	&iio_dev_attr_pedometer_int_on.dev_attr.attr, +	&iio_dev_attr_pedometer_on.dev_attr.attr, +	&iio_dev_attr_pedometer_steps.dev_attr.attr, +	&iio_dev_attr_pedometer_time.dev_attr.attr, +	&iio_dev_attr_pedometer_counter.dev_attr.attr, +	&iio_dev_attr_pedometer_step_thresh.dev_attr.attr, +	&iio_dev_attr_pedometer_int_thresh.dev_attr.attr, +	&iio_dev_attr_smd_enable.dev_attr.attr, +	&iio_dev_attr_smd_threshold.dev_attr.attr, +	&iio_dev_attr_smd_delay_threshold.dev_attr.attr, +	&iio_dev_attr_smd_delay_threshold2.dev_attr.attr, +	&iio_dev_attr_dmp_on.dev_attr.attr, +	&iio_dev_attr_dmp_int_on.dev_attr.attr, +	&iio_dev_attr_dmp_event_int_on.dev_attr.attr, +	&iio_dev_attr_step_indicator_on.dev_attr.attr, +	&iio_dev_attr_batchmode_timeout.dev_attr.attr, +	&iio_dev_attr_batchmode_wake_fifo_full_on.dev_attr.attr, +	&iio_dev_attr_six_axes_q_on.dev_attr.attr, +	&iio_dev_attr_six_axes_q_rate.dev_attr.attr, +	&iio_dev_attr_three_axes_q_on.dev_attr.attr, +	&iio_dev_attr_three_axes_q_rate.dev_attr.attr, +	&iio_dev_attr_ped_q_on.dev_attr.attr, +	&iio_dev_attr_ped_q_rate.dev_attr.attr, +	&iio_dev_attr_step_detector_on.dev_attr.attr, +	&iio_dev_attr_accel_enable.dev_attr.attr, +	&iio_dev_attr_accel_fifo_enable.dev_attr.attr, +	&iio_dev_attr_accel_rate.dev_attr.attr, +	&iio_dev_attr_firmware_loaded.dev_attr.attr, +	&iio_dev_attr_accel_matrix.dev_attr.attr, +}; + +static const struct attribute *inv_mpu6500_attributes[] = { +	&iio_dev_attr_motion_lpa_on.dev_attr.attr, +	&iio_dev_attr_motion_lpa_freq.dev_attr.attr, +	&iio_dev_attr_motion_lpa_threshold.dev_attr.attr, +}; + +static const struct attribute *inv_tap_attributes[] = { +	&dev_attr_event_tap.attr, +	&iio_dev_attr_tap_on.dev_attr.attr, +	&iio_dev_attr_tap_threshold.dev_attr.attr, +	&iio_dev_attr_tap_min_count.dev_attr.attr, +	&iio_dev_attr_tap_time.dev_attr.attr, +}; + +static const struct attribute *inv_display_orient_attributes[] = { +	&dev_attr_event_display_orientation.attr, +	&iio_dev_attr_display_orientation_on.dev_attr.attr, +}; + +static const struct attribute *inv_compass_attributes[] = { +	&iio_dev_attr_in_magn_scale.dev_attr.attr, +	&iio_dev_attr_compass_enable.dev_attr.attr, +	&iio_dev_attr_compass_rate.dev_attr.attr, +	&iio_dev_attr_compass_matrix.dev_attr.attr, +}; + +static const struct attribute *inv_akxxxx_attributes[] = { +#ifdef CONFIG_INV_TESTING +	&iio_dev_attr_compass_sens.dev_attr.attr, +#endif +}; + +static const struct attribute *inv_pressure_attributes[] = { +	&iio_dev_attr_pressure_enable.dev_attr.attr, +	&iio_dev_attr_pressure_rate.dev_attr.attr, +}; + +static const struct attribute *inv_mpu3050_attributes[] = { +	&iio_dev_attr_in_accel_scale.dev_attr.attr, +	&iio_dev_attr_accel_enable.dev_attr.attr, +	&iio_dev_attr_accel_fifo_enable.dev_attr.attr, +	&iio_dev_attr_accel_matrix.dev_attr.attr, +}; + +static struct attribute *inv_attributes[ +	ARRAY_SIZE(inv_gyro_attributes) + +	ARRAY_SIZE(inv_mpu6xxx_attributes) + +	ARRAY_SIZE(inv_mpu6500_attributes) + +	ARRAY_SIZE(inv_compass_attributes) + +	ARRAY_SIZE(inv_akxxxx_attributes) + +	ARRAY_SIZE(inv_pressure_attributes) + +	ARRAY_SIZE(inv_tap_attributes) + +	ARRAY_SIZE(inv_display_orient_attributes) + +	1 +]; + +static const struct attribute_group inv_attribute_group = { +	.name = "mpu", +	.attrs = inv_attributes +}; + +static const struct iio_info mpu_info = { +	.driver_module = THIS_MODULE, +	.attrs = &inv_attribute_group, +}; + +static void inv_setup_func_ptr(struct inv_mpu_state *st) +{ +	if (st->chip_type == INV_MPU3050) { +		st->set_power_state    = set_power_mpu3050; +		st->switch_gyro_engine = inv_switch_3050_gyro_engine; +		st->switch_accel_engine = inv_switch_3050_accel_engine; +		st->init_config        = inv_init_config_mpu3050; +		st->setup_reg          = inv_setup_reg_mpu3050; +	} else { +		st->set_power_state    = set_power_itg; +		st->switch_gyro_engine = inv_switch_gyro_engine; +		st->switch_accel_engine = inv_switch_accel_engine; +		st->init_config        = inv_init_config; +		st->setup_reg          = inv_setup_reg; +	} +} + +static int inv_detect_6xxx(struct inv_mpu_state *st) +{ +	int result; +	u8 d; + +	result = inv_i2c_read(st, REG_WHOAMI, 1, &d); +	if (result) +		return result; +	if ((d == MPU6500_ID) || (d == MPU6515_ID)) { +		st->chip_type = INV_MPU6500; +		strcpy(st->name, "mpu6500"); +	} else { +		strcpy(st->name, "mpu6050"); +	} + +	return 0; +} + +static int inv_setup_vddio(struct inv_mpu_state *st) +{ +	int result; +	u8 data[1]; + +	if (INV_MPU6050 == st->chip_type) { +		result = inv_i2c_read(st, REG_YGOFFS_TC, 1, data); +		if (result) +			return result; +		data[0] &= ~BIT_I2C_MST_VDDIO; +		if (st->plat_data.level_shifter) +			data[0] |= BIT_I2C_MST_VDDIO; +		/*set up VDDIO register */ +		result = inv_i2c_single_write(st, REG_YGOFFS_TC, data[0]); +		if (result) +			return result; +	} + +	return 0; +} + +/* + *  inv_check_chip_type() - check and setup chip type. + */ +static int inv_check_chip_type(struct inv_mpu_state *st, +		const struct i2c_device_id *id) +{ +	struct inv_reg_map_s *reg; +	int result; +	int t_ind; +	struct inv_chip_config_s *conf; +	struct mpu_platform_data *plat; + +	conf = &st->chip_config; +	plat = &st->plat_data; +	if (!strcmp(id->name, "itg3500")) { +		st->chip_type = INV_ITG3500; +	} else if (!strcmp(id->name, "mpu3050")) { +		st->chip_type = INV_MPU3050; +	} else if (!strcmp(id->name, "mpu6050")) { +		st->chip_type = INV_MPU6050; +	} else if (!strcmp(id->name, "mpu9150")) { +		st->chip_type = INV_MPU6050; +		plat->sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS; +		plat->sec_slave_id = COMPASS_ID_AK8975; +	} else if (!strcmp(id->name, "mpu6500")) { +		st->chip_type = INV_MPU6500; +	} else if (!strcmp(id->name, "mpu9250")) { +		st->chip_type = INV_MPU6500; +		plat->sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS; +		plat->sec_slave_id = COMPASS_ID_AK8963; +	} else if (!strcmp(id->name, "mpu6xxx")) { +		st->chip_type = INV_MPU6050; +	} else if (!strcmp(id->name, "mpu9350")) { +		st->chip_type = INV_MPU6500; +		plat->sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS; +		plat->sec_slave_id = COMPASS_ID_MLX90399; +		/* block use of MPU9350 in this build +		   since it's not production ready */ +		pr_err("MPU9350 support is not officially available yet.\n"); +		return -EPERM; +	} else if (!strcmp(id->name, "mpu6515")) { +		st->chip_type = INV_MPU6500; +	} else { +		return -EPERM; +	} +	inv_setup_func_ptr(st); +	st->hw  = &hw_info[st->chip_type]; +	reg = &st->reg; +	st->setup_reg(reg); +	/* reset to make sure previous state are not there */ +	result = inv_i2c_single_write(st, reg->pwr_mgmt_1, BIT_H_RESET); +	if (result) +		return result; +	msleep(POWER_UP_TIME); +	/* toggle power state */ +	result = st->set_power_state(st, false); +	if (result) +		return result; + +	result = st->set_power_state(st, true); +	if (result) +		return result; + +	if (!strcmp(id->name, "mpu6xxx")) { +		/* for MPU6500, reading register need more time */ +		msleep(POWER_UP_TIME); +		result = inv_detect_6xxx(st); +		if (result) +			return result; +	} + +	if ((plat->sec_slave_type != SECONDARY_SLAVE_TYPE_NONE) || +		(plat->aux_slave_type != SECONDARY_SLAVE_TYPE_NONE)) { +		result = inv_setup_vddio(st); +		if (result) +			return result; +	} + +	switch (st->chip_type) { +	case INV_ITG3500: +		break; +	case INV_MPU6050: +	case INV_MPU6500: +		if (SECONDARY_SLAVE_TYPE_COMPASS == plat->sec_slave_type) +			conf->has_compass = 1; +		else +			conf->has_compass = 0; +		if (SECONDARY_SLAVE_TYPE_PRESSURE == plat->aux_slave_type) +			conf->has_pressure = 1; +		else +			conf->has_pressure = 0; + +		break; +	case INV_MPU3050: +		if (SECONDARY_SLAVE_TYPE_ACCEL == plat->sec_slave_type) { +			if (ACCEL_ID_BMA250 == plat->sec_slave_id) +				inv_register_mpu3050_slave(st); +		} +		break; +	default: +		result = st->set_power_state(st, false); +		return -ENODEV; +	} +	if (conf->has_compass && conf->has_pressure && +			(COMPASS_ID_MLX90399 == plat->sec_slave_id)) { +		pr_err("MLX90399 can't share slaves with others\n"); +		return -EINVAL; +	} +	switch (st->chip_type) { +	case INV_MPU6050: +		result = inv_get_silicon_rev_mpu6050(st); +		break; +	case INV_MPU6500: +		result = inv_get_silicon_rev_mpu6500(st); +		break; +	default: +		result = 0; +		break; +	} +	if (result) { +		pr_err("read silicon rev error\n"); +		st->set_power_state(st, false); +		return result; +	} + +	/* turn off the gyro engine after OTP reading */ +	result = st->switch_gyro_engine(st, false); +	if (result) +		return result; +	result = st->switch_accel_engine(st, false); +	if (result) +		return result; + +	if (conf->has_compass) { +		result = inv_mpu_setup_compass_slave(st); +		if (result) { +			pr_err("compass setup failed\n"); +			st->set_power_state(st, false); +			return result; +		} +	} +	if (conf->has_pressure) { +		result = inv_mpu_setup_pressure_slave(st); +		if (result) { +			pr_err("pressure setup failed\n"); +			st->set_power_state(st, false); +			return result; +		} +	} + +	t_ind = 0; +	memcpy(&inv_attributes[t_ind], inv_gyro_attributes, +		sizeof(inv_gyro_attributes)); +	t_ind += ARRAY_SIZE(inv_gyro_attributes); + +	if (INV_MPU3050 == st->chip_type && st->slave_accel != NULL) { +		memcpy(&inv_attributes[t_ind], inv_mpu3050_attributes, +		       sizeof(inv_mpu3050_attributes)); +		t_ind += ARRAY_SIZE(inv_mpu3050_attributes); +		inv_attributes[t_ind] = NULL; +		return 0; +	} + +	/* all MPU6xxx based parts */ +	if ((INV_MPU6050 == st->chip_type) || (INV_MPU6500 == st->chip_type)) { +		memcpy(&inv_attributes[t_ind], inv_mpu6xxx_attributes, +		       sizeof(inv_mpu6xxx_attributes)); +		t_ind += ARRAY_SIZE(inv_mpu6xxx_attributes); +	} + +	/* MPU6500 only */ +	if (INV_MPU6500 == st->chip_type) { +		memcpy(&inv_attributes[t_ind], inv_mpu6500_attributes, +		       sizeof(inv_mpu6500_attributes)); +		t_ind += ARRAY_SIZE(inv_mpu6500_attributes); +	} + +	if (conf->has_compass) { +		memcpy(&inv_attributes[t_ind], inv_compass_attributes, +		       sizeof(inv_compass_attributes)); +		t_ind += ARRAY_SIZE(inv_compass_attributes); + +		/* AKM only */ +		if (st->plat_data.sec_slave_id == COMPASS_ID_AK8975 || +		    st->plat_data.sec_slave_id == COMPASS_ID_AK8972 || +		    st->plat_data.sec_slave_id == COMPASS_ID_AK09911 || +		    st->plat_data.sec_slave_id == COMPASS_ID_AK8963) { +			memcpy(&inv_attributes[t_ind], inv_akxxxx_attributes, +			       sizeof(inv_akxxxx_attributes)); +			t_ind += ARRAY_SIZE(inv_akxxxx_attributes); +		} +	} + +	if (conf->has_pressure) { +		memcpy(&inv_attributes[t_ind], inv_pressure_attributes, +		       sizeof(inv_pressure_attributes)); +		t_ind += ARRAY_SIZE(inv_pressure_attributes); +	} + +	inv_attributes[t_ind] = NULL; + +	return 0; +} + +/* + *  inv_create_dmp_sysfs() - create binary sysfs dmp entry. + */ +static const struct bin_attribute dmp_firmware = { +	.attr = { +		.name = "dmp_firmware", +		.mode = S_IRUGO | S_IWUSR +	}, +	.size = DMP_IMAGE_SIZE + 32, +	.read = inv_dmp_firmware_read, +	.write = inv_dmp_firmware_write, +}; + +static const struct bin_attribute six_q_value = { +	.attr = { +		.name = "six_axes_q_value", +		.mode = S_IWUSR +	}, +	.size = QUATERNION_BYTES, +	.read = NULL, +	.write = inv_six_q_write, +}; + +static int inv_create_dmp_sysfs(struct iio_dev *ind) +{ +	int result; + +	result = sysfs_create_bin_file(&ind->dev.kobj, &dmp_firmware); +	if (result) +		return result; +	result = sysfs_create_bin_file(&ind->dev.kobj, &six_q_value); + +	return result; +} + +/* + *  inv_mpu_probe() - probe function. + */ +static int inv_mpu_probe(struct i2c_client *client, +	const struct i2c_device_id *id) +{ +	struct inv_mpu_state *st; +	struct iio_dev *indio_dev; +	int result; + +#ifdef CONFIG_DTS_INV_MPU_IIO +	enable_irq_wake(client->irq); +#endif + +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { +		result = -ENOSYS; +		pr_err("I2c function error\n"); +		goto out_no_free; +	} +	indio_dev = iio_device_alloc(sizeof(*st)); +	if (indio_dev == NULL) { +		pr_err("memory allocation failed\n"); +		result =  -ENOMEM; +		goto out_no_free; +	} +	st = iio_priv(indio_dev); +	st->client = client; +	st->sl_handle = client->adapter; +	st->i2c_addr = client->addr; +#ifdef CONFIG_DTS_INV_MPU_IIO +	result = invensense_mpu_parse_dt(&client->dev, &st->plat_data); +	if (result) +		goto out_free; + +	/*Power on device.*/ +	if (st->plat_data.power_on) { +		result = st->plat_data.power_on(&st->plat_data); +		if (result < 0) { +			dev_err(&client->dev, +					"power_on failed: %d\n", result); +			return result; +		} +	} + +msleep(100); +#else +	/* power is turned on inside check chip type */ +	st->plat_data = +	*(struct mpu_platform_data *)dev_get_platdata(&client->dev); +#endif +	result = inv_check_chip_type(st, id); +	if (result) +		goto out_free; + +	result = st->init_config(indio_dev); +	if (result) { +		dev_err(&client->adapter->dev, +			"Could not initialize device.\n"); +		goto out_free; +	} +	/* Make state variables available to all _show and _store functions. */ +	i2c_set_clientdata(client, indio_dev); +	indio_dev->dev.parent = &client->dev; +	if (!strcmp(id->name, "mpu6xxx")) +		indio_dev->name = st->name; +	else +		indio_dev->name = id->name; +	indio_dev->channels = inv_mpu_channels; +	indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels); + +	indio_dev->info = &mpu_info; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->currentmode = INDIO_DIRECT_MODE; + +	result = inv_mpu_configure_ring(indio_dev); +	if (result) { +		pr_err("configure ring buffer fail\n"); +		goto out_free; +	} +	result = iio_buffer_register(indio_dev, indio_dev->channels, +					indio_dev->num_channels); +	if (result) { +		pr_err("ring buffer register fail\n"); +		goto out_unreg_ring; +	} +	st->irq = client->irq; +	result = inv_mpu_probe_trigger(indio_dev); +	if (result) { +		pr_err("trigger probe fail\n"); +		goto out_remove_ring; +	} + +	/* Tell the i2c counter, we have an IRQ */ +	INV_I2C_SETIRQ(IRQ_MPU, client->irq); + +	result = iio_device_register(indio_dev); +	if (result) { +		pr_err("IIO device register fail\n"); +		goto out_remove_trigger; +	} + +	if (INV_MPU6050 == st->chip_type || +		INV_MPU6500 == st->chip_type) { +		result = inv_create_dmp_sysfs(indio_dev); +		if (result) { +			pr_err("create dmp sysfs failed\n"); +			goto out_unreg_iio; +		} +	} +	INIT_KFIFO(st->timestamps); +	spin_lock_init(&st->time_stamp_lock); +	mutex_init(&st->suspend_resume_lock); +	result = st->set_power_state(st, false); +	if (result) { +		dev_err(&client->adapter->dev, +			"%s could not be turned off.\n", st->hw->name); +		goto out_unreg_iio; +	} +	inv_init_sensor_struct(st); +	dev_info(&client->dev, "%s is ready to go!\n", +					indio_dev->name); + +	return 0; +out_unreg_iio: +	iio_device_unregister(indio_dev); +out_remove_trigger: +	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) +		inv_mpu_remove_trigger(indio_dev); +out_remove_ring: +	iio_buffer_unregister(indio_dev); +out_unreg_ring: +	inv_mpu_unconfigure_ring(indio_dev); +out_free: +	iio_device_free(indio_dev); +out_no_free: +	dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); +	return -EIO; +} + +static void inv_mpu_shutdown(struct i2c_client *client) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(client); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct inv_reg_map_s *reg; +	int result; + +	reg = &st->reg; +	mutex_lock(&indio_dev->mlock); +	dev_dbg(&client->adapter->dev, "Shutting down %s...\n", st->hw->name); + +	/* reset to make sure previous state are not there */ +	result = inv_i2c_single_write(st, reg->pwr_mgmt_1, BIT_H_RESET); +	if (result) +		dev_err(&client->adapter->dev, "Failed to reset %s\n", +			st->hw->name); +	msleep(POWER_UP_TIME); +	/* turn off power to ensure gyro engine is off */ +	result = st->set_power_state(st, false); +	if (result) +		dev_err(&client->adapter->dev, "Failed to turn off %s\n", +			st->hw->name); +	mutex_unlock(&indio_dev->mlock); +} + +/* + *  inv_mpu_remove() - remove function. + */ +static int inv_mpu_remove(struct i2c_client *client) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(client); +	struct inv_mpu_state *st = iio_priv(indio_dev); + +	kfifo_free(&st->timestamps); +	iio_device_unregister(indio_dev); +	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) +		inv_mpu_remove_trigger(indio_dev); +	iio_buffer_unregister(indio_dev); +	inv_mpu_unconfigure_ring(indio_dev); +	iio_device_free(indio_dev); + +	dev_info(&client->adapter->dev, "inv-mpu-iio module removed.\n"); + +	return 0; +} + +static int inv_setup_suspend_batchmode(struct iio_dev *indio_dev, bool suspend) +{ +	struct inv_mpu_state *st = iio_priv(indio_dev); +	int result; + +	if (st->chip_config.dmp_on && +		st->chip_config.enable && +		st->batch.on && +		(!st->chip_config.dmp_event_int_on)) { +		if (!st->batch.wake_fifo_on) { +			st->batch.overflow_on = suspend; +			result = inv_i2c_single_write(st, +					st->reg.user_ctrl, 0); +			if (result) +				return result; +			result = inv_batchmode_setup(st); +			if (result) +				return result; +			result = inv_reset_fifo(indio_dev); +			if (result) +				return result; +		} +		/* turn off data interrupt in suspend mode;turn on resume */ +		result = inv_set_interrupt_on_gesture_event(st, suspend); +		if (result) +			return result; +	} + +	return 0; +} + +#ifdef CONFIG_PM + +/*************************************************************************** + * olio_set_lpa_off - turn off low power acc mode + * + * This function turns off the LPA mode, and resets (not yet! 20150331) the + * acc settings to what they were before we went into suspend mode.  + */ + +int olio_set_lpa_off (struct inv_mpu_state * st) { +  u8 off = 0; +  u8 regstate; +  int result; +  int length = 1; + +  printk("%s olio_set_lpa_off.\n", st->hw->name); + +  /* Set the motion lpa to off */ + +  result = inv_i2c_single_write(st, REG_6500_ACCEL_INTEL_CTRL, off); +  if (result) goto i2c_error; + +  st->mot_int.mot_on = off; +  st->chip_config.lpa_mode = off; + +  result = inv_i2c_read (st, REG_PWR_MGMT_1, length, ®state); +  if (result) goto i2c_error; + +  regstate &= (~BIT_SLEEP & ~BIT_CYCLE & ~0x10); + +  result = inv_i2c_single_write(st, REG_PWR_MGMT_1,  BIT_CYCLE); +  if (result) goto i2c_error; +   +  return result; + +i2c_error: +  printk ("%s:%s: Olio: Error, i2c related...\n",  +            __FILE__, __FUNCTION__); +  return result; +} + + +/*************************************************************************** + * olio_set_lpa_on - turn on low power acc mode + * + * This function turns on the LPA mode. Lots of settings here, all taken + * from Invensense docs for the 6515 chip. + */ + +int olio_set_lpa_on (struct inv_mpu_state * st) { +  int result; +  u8 regstate; +  int length = 1; + +  int freq; +  u8 thres; + +  /* OLIO_FREQ is 0 -> 3.  +   * thres is 0-255 - 128 seems OK. Steps of 4. +   */ + +  printk("%s olio_set_lpa_on.\n", st->hw->name); + +  freq  = OLIO_DEF_FREQ; +  thres = OLIO_DEF_THRES; +     +  /* In PWR_MGMT_1 (0x6B) make CYCLE =0, SLEEP = 0 and STANDBY = 0 */ + +  result = inv_i2c_read (st, REG_PWR_MGMT_1, length, ®state); +  if (result) goto i2c_error; + +  regstate &= (~BIT_SLEEP & ~BIT_CYCLE & ~0x10); +  result = inv_i2c_single_write(st, REG_PWR_MGMT_1, regstate); +  if (result) goto i2c_error; + +  /* In PWR_MGMT_2 (0x6C) set DIS_XA, DIS_YA, DIS_ZA = 0 and DIS_XG, DIS_YG, DIS_ZG = 1 */ + +  result = inv_i2c_single_write(st, REG_PWR_MGMT_2, 0x07); +  if (result) goto i2c_error; + +  /* In ACCEL_CONFIG 2 (0x1D) set ACCEL_FCHOICE_B = 0 and A_DLPFCFG[2:0]=1(b001) */ + +  regstate = 0; +  result = inv_i2c_read (st, REG_6500_ACCEL_CONFIG2, length, ®state); +  if (result) goto i2c_error; +  +  regstate &= 0xF1; /* We're setting last four bits to 0001. Everything else we don't care. */ +  result = inv_i2c_single_write (st, REG_6500_ACCEL_CONFIG2, regstate); +  if (result) goto i2c_error; + +  /* Set motion interrupts only */ + +  st->mot_int.mot_on = 1; +  st->chip_config.lpa_mode = 1; + +  result = inv_i2c_single_write (st, REG_INT_ENABLE, 0x40); +  if (result) goto i2c_error; + +  /* Enable accel hw intelligence */ + +  result = inv_i2c_single_write(st, REG_6500_ACCEL_INTEL_CTRL,  +                                BIT_ACCEL_INTEL_ENABLE | BIT_ACCEL_INTEL_MODE); +  if (result) goto i2c_error; +   +  /* Set the threshold */ + +  result = inv_i2c_single_write(st, REG_ACCEL_MOT_THR, thres); +  if (result) goto i2c_error; + +  st->mot_int.mot_thr = thres; + +  /* Set the frequency of acc checks */ + +  inv_lpa_freq(st, freq); +  +  /* Set the motion lpa to on */ +   +  result = inv_i2c_single_write(st, REG_PWR_MGMT_1, BIT_CYCLE); +  if (result) goto i2c_error; +   +  return 0; + +i2c_error:   +  printk ("%s:%s: Olio: Error, i2c related...\n",  +            __FILE__, __FUNCTION__); +  return result; +} + + +static int inv_mpu_resume(struct device *dev) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	int result; + +	pr_debug("%s inv_mpu_resume\n", st->hw->name); +	mutex_lock(&indio_dev->mlock); + +	result = 0; +	if (st->chip_config.dmp_on && st->chip_config.enable) { +      pr_debug("%s dmp_on and enabled\n", st->hw->name); +		result = st->set_power_state(st, true); +		result |= inv_read_time_and_ticks(st, true); +		if (st->ped.int_on) +			result |= inv_enable_pedometer_interrupt(st, true); +		if (st->chip_config.display_orient_on) +			result |= inv_set_display_orient_interrupt_dmp(st, +								true); +		result |= inv_setup_suspend_batchmode(indio_dev, false); +	} else if (st->chip_config.enable) { +		result = st->set_power_state(st, true); +	}  + +    /* TODO: OLIO, mfj: Here we need to turn the low-power motion +     * detection off, since the watch is now awake.  +     */  +    /* result = olio_set_lpa_off (st); */ + +	mutex_unlock(&indio_dev->mlock); +	mutex_unlock(&st->suspend_resume_lock); + +	return result; +} + +static int inv_mpu_suspend(struct device *dev) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); +	struct inv_mpu_state *st = iio_priv(indio_dev); +	int result; + +	pr_debug("%s inv_mpu_suspend\n", st->hw->name); +	mutex_lock(&indio_dev->mlock); + +	result = 0; +	if (st->chip_config.dmp_on && st->chip_config.enable) { +      pr_debug("%s dmp_on and enabled\n", st->hw->name); +		/* turn off pedometer interrupt during suspend */ +		if (st->ped.int_on) +			result |= inv_enable_pedometer_interrupt(st, false); +		/* turn off orientation interrupt during suspend */ +		if (st->chip_config.display_orient_on) +			result |= inv_set_display_orient_interrupt_dmp(st, +								false); +		/* setup batch mode related during suspend */ +		result = inv_setup_suspend_batchmode(indio_dev, true); +		/* only in DMP non-batch data mode, turn off the power */ +		if ((!st->batch.on) && (!st->chip_config.smd_enable) && +					(!st->ped.on)) +			result |= st->set_power_state(st, false); +	} else if (st->chip_config.enable) { +		/* in non DMP case, just turn off the power */ +		result |= st->set_power_state(st, false); +	} + +    /* OLIO, mfj: Here we need to set the low-power motion +     * detection, for waking the watch up. +     */ +     +    /* result |= olio_set_lpa_on (st); */ + +	mutex_unlock(&indio_dev->mlock); +	mutex_lock(&st->suspend_resume_lock); + +	return result; +} +static const struct dev_pm_ops inv_mpu_pmops = { +	SET_SYSTEM_SLEEP_PM_OPS(inv_mpu_suspend, inv_mpu_resume) +}; +#define INV_MPU_PMOPS (&inv_mpu_pmops) +#else +#define INV_MPU_PMOPS NULL +#endif /* CONFIG_PM */ + +static const u16 normal_i2c[] = { I2C_CLIENT_END }; +/* device id table is used to identify what device can be + * supported by this driver + */ +static const struct i2c_device_id inv_mpu_id[] = { +	{"itg3500", INV_ITG3500}, +	{"mpu3050", INV_MPU3050}, +	{"mpu6050", INV_MPU6050}, +	{"mpu9150", INV_MPU9150}, +	{"mpu6500", INV_MPU6500}, +	{"mpu9250", INV_MPU9250}, +	{"mpu6xxx", INV_MPU6XXX}, +	{"mpu9350", INV_MPU9350}, +	{"mpu6515", INV_MPU6515}, +	{} +}; + +MODULE_DEVICE_TABLE(i2c, inv_mpu_id); + +static struct i2c_driver inv_mpu_driver = { +	.class = I2C_CLASS_HWMON, +	.probe		=	inv_mpu_probe, +	.remove		=	inv_mpu_remove, +	.shutdown	=	inv_mpu_shutdown, +	.id_table	=	inv_mpu_id, +	.driver = { +		.owner	=	THIS_MODULE, +		.name	=	"inv-mpu-iio", +		.pm     =       INV_MPU_PMOPS, +	}, +	.address_list = normal_i2c, +}; + +static int __init inv_mpu_init(void) +{ +	int result = i2c_add_driver(&inv_mpu_driver); +	if (result) { +		pr_err("failed\n"); +		return result; +	} +	return 0; +} + +static void __exit inv_mpu_exit(void) +{ +	i2c_del_driver(&inv_mpu_driver); +} + +module_init(inv_mpu_init); +module_exit(inv_mpu_exit); + +MODULE_AUTHOR("Invensense Corporation"); +MODULE_DESCRIPTION("Invensense device driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("inv-mpu-iio"); + diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_dts.c b/drivers/staging/iio/imu/inv_mpu/inv_mpu_dts.c new file mode 100644 index 00000000000..276d5bcfea7 --- /dev/null +++ b/drivers/staging/iio/imu/inv_mpu/inv_mpu_dts.c @@ -0,0 +1,270 @@ +#include <linux/err.h> +#include <linux/of_gpio.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regulator/consumer.h> + +#include <linux/i2c.h> +#include <linux/mpu.h> +#include "inv_mpu_dts.h" + +int inv_mpu_power_on(struct mpu_platform_data *pdata) +{ +	int err ; + +	err = regulator_enable(pdata->vdd_ana); +	err = regulator_enable(pdata->vdd_i2c); +	pr_debug(KERN_INFO "inv_mpu_power_on call"); + +	return err ; + +} + +int inv_mpu_power_off(struct mpu_platform_data *pdata) +{ +	int err ; + +	err = regulator_disable(pdata->vdd_ana); +	err = regulator_disable(pdata->vdd_i2c); +	pr_debug(KERN_INFO "inv_mpu_power_off call"); + +	return err; +} + +int inv_parse_orientation_matrix(struct device *dev, s8 *orient) +{ +	int rc, i; +	struct device_node *np = dev->of_node; +	u32 temp_val, temp_val2; + +	for (i = 0; i < 9; i++) +		orient[i] = 0; + +	/* parsing axis x orientation matrix*/ +	rc = of_property_read_u32(np, "axis_map_x", &temp_val); +	if (rc) { +		dev_err(dev, "Unable to read axis_map_x\n"); +		return rc; +	} +	rc = of_property_read_u32(np, "negate_x", &temp_val2); +	if (rc) { +		dev_err(dev, "Unable to read negate_x\n"); +		return rc; +	} +	if (temp_val2) +		orient[temp_val] = -1; +	else +		orient[temp_val] = 1; + +	/* parsing axis y orientation matrix*/ +	rc = of_property_read_u32(np, "axis_map_y", &temp_val); +	if (rc) { +		dev_err(dev, "Unable to read axis_map_y\n"); +		return rc; +	} +	rc = of_property_read_u32(np, "negate_y", &temp_val2); +	if (rc) { +		dev_err(dev, "Unable to read negate_y\n"); +		return rc; +	} +	if (temp_val2) +		orient[3 + temp_val] = -1; +	else +		orient[3 + temp_val] = 1; + +	/* parsing axis z orientation matrix*/ +	rc = of_property_read_u32(np, "axis_map_z", &temp_val); +	if (rc) { +		dev_err(dev, "Unable to read axis_map_z\n"); +		return rc; +	} +	rc = of_property_read_u32(np, "negate_z", &temp_val2); +	if (rc) { +		dev_err(dev, "Unable to read negate_z\n"); +		return rc; +	} +	if (temp_val2) +		orient[6 + temp_val] = -1; +	else +		orient[6 + temp_val] = 1; + +	return 0; +} + +int inv_parse_secondary_orientation_matrix(struct device *dev, +							s8 *orient) +{ +	int rc, i; +	struct device_node *np = dev->of_node; +	u32 temp_val, temp_val2; + +	for (i = 0; i < 9; i++) +		orient[i] = 0; + +	/* parsing axis x orientation matrix*/ +	rc = of_property_read_u32(np, "inven,secondary_axis_map_x", &temp_val); +	if (rc) { +		dev_err(dev, "Unable to read secondary axis_map_x\n"); +		return rc; +	} +	rc = of_property_read_u32(np, "inven,secondary_negate_x", &temp_val2); +	if (rc) { +		dev_err(dev, "Unable to read secondary negate_x\n"); +		return rc; +	} +	if (temp_val2) +		orient[temp_val] = -1; +	else +		orient[temp_val] = 1; + +	/* parsing axis y orientation matrix*/ +	rc = of_property_read_u32(np, "inven,secondary_axis_map_y", &temp_val); +	if (rc) { +		dev_err(dev, "Unable to read secondary axis_map_y\n"); +		return rc; +	} +	rc = of_property_read_u32(np, "inven,secondary_negate_y", &temp_val2); +	if (rc) { +		dev_err(dev, "Unable to read secondary negate_y\n"); +		return rc; +	} +	if (temp_val2) +		orient[3 + temp_val] = -1; +	else +		orient[3 + temp_val] = 1; + +	/* parsing axis z orientation matrix*/ +	rc = of_property_read_u32(np, "inven,secondary_axis_map_z", &temp_val); +	if (rc) { +		dev_err(dev, "Unable to read secondary axis_map_z\n"); +		return rc; +	} +	rc = of_property_read_u32(np, "inven,secondary_negate_z", &temp_val2); +	if (rc) { +		dev_err(dev, "Unable to read secondary negate_z\n"); +		return rc; +	} +	if (temp_val2) +		orient[6 + temp_val] = -1; +	else +		orient[6 + temp_val] = 1; + +	return 0; +} + +int inv_parse_secondary(struct device *dev, struct mpu_platform_data *pdata) +{ +	int rc; +	struct device_node *np = dev->of_node; +	u32 temp_val; +	const char *name; + +	if (of_property_read_string(np, "inven,secondary_type", &name)) { +		dev_err(dev, "Missing secondary type.\n"); +		return -EINVAL; +	} +	if (!strcmp(name, "compass")) { +		pdata->sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS; +	} else if (!strcmp(name, "none")) { +		pdata->sec_slave_type = SECONDARY_SLAVE_TYPE_NONE; +		return 0; +	} else { +		return -EINVAL; +	} + +	if (of_property_read_string(np, "inven,secondary_name", &name)) { +		dev_err(dev, "Missing secondary name.\n"); +		return -EINVAL; +	} +	if (!strcmp(name, "ak8963")) +		pdata->sec_slave_id = COMPASS_ID_AK8963; +	else if (!strcmp(name, "ak8975")) +		pdata->sec_slave_id = COMPASS_ID_AK8975; +	else if (!strcmp(name, "ak8972")) +		pdata->sec_slave_id = COMPASS_ID_AK8972; +	else +		return -EINVAL; +	rc = of_property_read_u32(np, "inven,secondary_reg", &temp_val); +	if (rc) { +		dev_err(dev, "Unable to read secondary register\n"); +		return rc; +	} +	pdata->secondary_i2c_addr = temp_val; +	rc = inv_parse_secondary_orientation_matrix(dev, +						pdata->secondary_orientation); + +	return rc; +} + +int inv_parse_aux(struct device *dev, struct mpu_platform_data *pdata) +{ +	int rc; +	struct device_node *np = dev->of_node; +	u32 temp_val; +	const char *name; + +	if (of_property_read_string(np, "inven,aux_type", &name)) { +		dev_err(dev, "Missing aux type.\n"); +		return -EINVAL; +	} +	if (!strcmp(name, "pressure")) { +		pdata->aux_slave_type = SECONDARY_SLAVE_TYPE_PRESSURE; +	} else if (!strcmp(name, "none")) { +		pdata->aux_slave_type = SECONDARY_SLAVE_TYPE_NONE; +		return 0; +	} else { +		return -EINVAL; +	} + +	if (of_property_read_string(np, "inven,aux_name", &name)) { +		dev_err(dev, "Missing aux name.\n"); +		return -EINVAL; +	} +	if (!strcmp(name, "bmp280")) +		pdata->aux_slave_id = PRESSURE_ID_BMP280; +	else +		return -EINVAL; + +	rc = of_property_read_u32(np, "inven,aux_reg", &temp_val); +	if (rc) { +		dev_err(dev, "Unable to read aux register\n"); +		return rc; +	} +	pdata->aux_i2c_addr = temp_val; + +	return 0; +} + +int invensense_mpu_parse_dt(struct device *dev, struct mpu_platform_data *pdata) +{ +	int rc; +	pr_debug("Invensense MPU parse_dt started.\n"); + +	rc = inv_parse_orientation_matrix(dev, pdata->orientation); +	if (rc) +		return rc; + +	rc = inv_parse_secondary(dev, pdata); +	if (rc) +		return rc; + +	inv_parse_aux(dev, pdata); + +	pdata->vdd_ana = regulator_get(dev, "inven,vdd_ana"); +	if (IS_ERR(pdata->vdd_ana)) { +		rc = PTR_ERR(pdata->vdd_ana); +		dev_err(dev, "Regulator get failed vdd_ana-supply rc=%d\n", rc); +		return rc; +	} +	pdata->vdd_i2c = regulator_get(dev, "inven,vcc_i2c"); +	if (IS_ERR(pdata->vdd_i2c)) { +		rc = PTR_ERR(pdata->vdd_i2c); +		dev_err(dev, "Regulator get failed vcc-i2c-supply rc=%d\n", rc); +		return rc; +	} +	pdata->power_on = inv_mpu_power_on; +	pdata->power_off = inv_mpu_power_off; +	pr_debug("Invensense MPU parse_dt complete.\n"); +	return rc; +} + diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_dts.h b/drivers/staging/iio/imu/inv_mpu/inv_mpu_dts.h new file mode 100644 index 00000000000..151ac74ea05 --- /dev/null +++ b/drivers/staging/iio/imu/inv_mpu/inv_mpu_dts.h @@ -0,0 +1,30 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ + +#ifndef _INV_MPU_DTS_H_ +#define _INV_MPU_DTS_H_ + +#include <linux/i2c.h> +#include <linux/mpu.h> + +int inv_mpu_power_on(struct mpu_platform_data *pdata); +int inv_mpu_power_off(struct mpu_platform_data *pdata); +int inv_parse_orientation_matrix(struct device *dev, s8 *orient); +int inv_parse_secondary_orientation_matrix(struct device *dev, +							s8 *orient); +int inv_parse_secondary(struct device *dev, struct mpu_platform_data *pdata); +int inv_parse_aux(struct device *dev, struct mpu_platform_data *pdata); +int invensense_mpu_parse_dt(struct device *dev, +			    struct mpu_platform_data *pdata); + +#endif  /* #ifndef _INV_MPU_DTS_H_ */ diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_iio.h b/drivers/staging/iio/imu/inv_mpu/inv_mpu_iio.h new file mode 100644 index 00000000000..d0da84c54af --- /dev/null +++ b/drivers/staging/iio/imu/inv_mpu/inv_mpu_iio.h @@ -0,0 +1,1068 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ + +#ifndef _INV_MPU_IIO_H_ +#define _INV_MPU_IIO_H_ + +#include <linux/i2c.h> +#include <linux/kfifo.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> +#include <linux/mpu.h> + +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> + +#include "dmpKey.h" + +/*register and associated bit definition*/ +#define REG_3050_FIFO_EN         0x12 +#define BITS_3050_ACCEL_OUT      0x0E + +#define REG_3050_AUX_VDDIO       0x13 +#define BIT_3050_VDDIO           0x04 + +#define REG_3050_SLAVE_ADDR      0x14 +#define REG_3050_SAMPLE_RATE_DIV 0x15 +#define REG_3050_LPF             0x16 +#define REG_3050_INT_ENABLE      0x17 +#define REG_3050_AUX_BST_ADDR    0x18 +#define REG_3050_INT_STATUS      0x1A +#define REG_3050_TEMPERATURE     0x1B +#define REG_3050_RAW_GYRO        0x1D +#define REG_3050_AUX_XOUT_H      0x23 +#define REG_3050_FIFO_COUNT_H    0x3A +#define REG_3050_FIFO_R_W        0x3C + +#define REG_3050_USER_CTRL       0x3D +#define BIT_3050_AUX_IF_EN       0x20 +#define BIT_3050_FIFO_RST        0x02 + +#define REG_3050_PWR_MGMT_1      0x3E +#define BITS_3050_POWER1         0x30 +#define BITS_3050_POWER2         0x10 +#define BITS_3050_GYRO_STANDBY   0x38 + +#define REG_3500_OTP            0x0 + +#define REG_YGOFFS_TC           0x1 +#define BIT_I2C_MST_VDDIO       0x80 + +#define REG_XA_OFFS_H           0x6 +#define REG_XA_OFFS_L_TC        0x7 +#define REG_PRODUCT_ID          0xC +#define REG_ST_GCT_X            0xD +#define REG_XG_OFFS_USRH        0x13 +#define REG_SAMPLE_RATE_DIV     0x19 +#define REG_CONFIG              0x1A + +#define REG_GYRO_CONFIG         0x1B +#define BITS_SELF_TEST_EN       0xE0 + +#define REG_ACCEL_CONFIG        0x1C +#define REG_ACCEL_MOT_THR       0x1F +#define REG_ACCEL_MOT_DUR       0x20 + +#define REG_FIFO_EN             0x23 +#define BIT_ACCEL_OUT           0x08 +#define BITS_GYRO_OUT           0x70 + +#define REG_I2C_MST_CTRL        0x24 +#define BIT_WAIT_FOR_ES         0x40 + +#define REG_I2C_SLV0_ADDR       0x25 +#define REG_I2C_SLV0_REG        0x26 +#define REG_I2C_SLV0_CTRL       0x27 +#define REG_I2C_SLV1_ADDR       0x28 +#define REG_I2C_SLV1_REG        0x29 +#define REG_I2C_SLV1_CTRL       0x2A +#define REG_I2C_SLV2_ADDR       0x2B +#define REG_I2C_SLV2_REG        0x2C +#define REG_I2C_SLV2_CTRL       0x2D +#define REG_I2C_SLV3_ADDR       0x2E +#define REG_I2C_SLV3_REG        0x2F +#define REG_I2C_SLV3_CTRL       0x30 +#define REG_I2C_SLV4_CTRL       0x34 + +#define INV_MPU_BIT_SLV_EN      0x80 +#define INV_MPU_BIT_BYTE_SW     0x40 +#define INV_MPU_BIT_REG_DIS     0x20 +#define INV_MPU_BIT_I2C_READ    0x80 + +#define REG_INT_PIN_CFG         0x37 +#define BIT_BYPASS_EN           0x2 + +#define REG_INT_ENABLE          0x38 +#define BIT_DATA_RDY_EN         0x01 +#define BIT_DMP_INT_EN          0x02 +#define BIT_ZMOT_EN             0x20 +#define BIT_MOT_EN              0x40 +#define BIT_6500_WOM_EN         0x40 + +#define REG_DMP_INT_STATUS      0x39 +#define BIT_DMP_INT_CI          0x01 + +#define REG_INT_STATUS          0x3A +#define BIT_MOT_INT             0x40 +#define BIT_ZMOT_INT            0x20 + +#define REG_RAW_ACCEL           0x3B +#define REG_TEMPERATURE         0x41 +#define REG_EXT_SENS_DATA_00    0x49 +#define REG_EXT_SENS_DATA_08    0x51 + +#define REG_ACCEL_INTEL_STATUS  0x61 + +#define INV_MPU_REG_I2C_SLV0_DO         0x63 +#define INV_MPU_REG_I2C_SLV1_DO         0x64 +#define INV_MPU_REG_I2C_SLV2_DO         0x65 +#define INV_MPU_REG_I2C_SLV3_DO         0x66 + +#define REG_I2C_MST_DELAY_CTRL  0x67 +#define BIT_SLV0_DLY_EN                 0x01 +#define BIT_SLV1_DLY_EN                 0x02 +#define BIT_SLV2_DLY_EN                 0x04 +#define BIT_SLV3_DLY_EN                 0x08 + +#define REG_USER_CTRL           0x6A +#define BIT_FIFO_RST                    0x04 +#define BIT_DMP_RST                     0x08 +#define BIT_I2C_MST_EN                  0x20 +#define BIT_FIFO_EN                     0x40 +#define BIT_DMP_EN                      0x80 + +#define REG_PWR_MGMT_1          0x6B +#define BIT_H_RESET                     0x80 +#define BIT_SLEEP                       0x40 +#define BIT_CYCLE                       0x20 +#define BIT_CLK_MASK                    0x7 + +#define REG_PWR_MGMT_2          0x6C +#define BIT_PWR_ACCEL_STBY              0x38 +#define BIT_PWR_GYRO_STBY               0x07 +#define BIT_LPA_FREQ                    0xC0 + +#define REG_BANK_SEL            0x6D +#define REG_MEM_START_ADDR      0x6E +#define REG_MEM_RW              0x6F +#define REG_PRGM_STRT_ADDRH     0x70 +#define REG_FIFO_COUNT_H        0x72 +#define REG_FIFO_R_W            0x74 +#define REG_WHOAMI              0x75 + +#define REG_6500_XG_ST_DATA     0x0 +#define REG_6500_XA_ST_DATA     0xD +#define REG_6500_XA_OFFS_H      0x77 +#define REG_6500_YA_OFFS_H      0x7A +#define REG_6500_ZA_OFFS_H      0x7D +#define REG_6500_ACCEL_CONFIG2  0x1D +#define BIT_ACCEL_FCHOCIE_B              0x08 +#define BIT_FIFO_SIZE_1K                 0x40 + +#define REG_6500_LP_ACCEL_ODR   0x1E +#define REG_6500_ACCEL_WOM_THR  0x1F + +#define REG_6500_ACCEL_INTEL_CTRL 0x69 +#define BIT_ACCEL_INTEL_ENABLE          0x80 +#define BIT_ACCEL_INTEL_MODE            0x40 + +/* data definitions */ +#define DMP_START_ADDR           0x400 +#define DMP_MASK_TAP             0x3f +#define DMP_MASK_DIS_ORIEN       0xC0 +#define DMP_DIS_ORIEN_SHIFT      6 + +#define BYTES_FOR_DMP            8 +#define BYTES_FOR_EVENTS         4 +#define QUATERNION_BYTES         16 +#define BYTES_PER_SENSOR         6 +#define MPU3050_FOOTER_SIZE      2 +#define FIFO_COUNT_BYTE          2 +#define FIFO_THRESHOLD           800 +#define FIFO_SIZE                800 +#define HARDWARE_FIFO_SIZE       1024 +#define MAX_READ_SIZE            64 +#define POWER_UP_TIME            100 +#define SENSOR_UP_TIME           30 +#define REG_UP_TIME              5 +#define INV_MPU_SAMPLE_RATE_CHANGE_STABLE 50 +#define MPU_MEM_BANK_SIZE        256 +#define SELF_TEST_GYRO_FULL_SCALE 250 +#define SELF_TEST_ACCEL_FULL_SCALE 8 +#define SELF_TEST_ACCEL_6500_SCALE 2 + +/* data header defines */ +#define PRESSURE_HDR             0x8000 +#define ACCEL_HDR                0x4000 +#define GYRO_HDR                 0x2000 +#define COMPASS_HDR              0x1000 +#define LPQUAT_HDR               0x0800 +#define SIXQUAT_HDR              0x0400 +#define PEDQUAT_HDR              0x0200 +#define STEP_DETECTOR_HDR        0x0100 +#define STEP_INDICATOR_MASK      0xf + +#define MAX_BYTES_PER_SAMPLE     80 +#define MAX_HW_FIFO_BYTES        (BYTES_PER_SENSOR * 2) +#define IIO_BUFFER_BYTES         8 +#define HEADERED_NORMAL_BYTES    8 +#define HEADERED_Q_BYTES         16 + +#define MPU6XXX_MAX_MOTION_THRESH (255*4) +#define MPU6050_MOTION_THRESH_SHIFT 5 +#define MPU6500_MOTION_THRESH_SHIFT 2 +#define MPU6050_MOTION_DUR_DEFAULT  1 +#define MPU6050_ID               0x68 +#define MPU6050_MAX_MOTION_DUR   255 +#define MPU_TEMP_SHIFT           16 +#define LPA_FREQ_SHIFT           6 +#define COMPASS_RATE_SCALE       10 +#define MAX_GYRO_FS_PARAM        3 +#define MAX_ACCEL_FS_PARAM        3 +#define MAX_LPA_FREQ_PARAM       3 +#define MPU_MAX_A_OFFSET_VALUE     16383 +#define MPU_MIN_A_OFFSET_VALUE     -16384 +#define MPU_MAX_G_OFFSET_VALUE     32767 +#define MPU_MIN_G_OFFSET_VALUE     -32767 +#define MPU6XXX_MAX_MPU_MEM      (256 * 12) + +#define INIT_MOT_DUR             128 +#define INIT_MOT_THR             128 +#define INIT_ZMOT_DUR            128 +#define INIT_ZMOT_THR            128 +#define INIT_ST_SAMPLES          200 +#define INIT_ST_MPU6050_SAMPLES  600 +#define INIT_ST_THRESHOLD        50 +#define INIT_PED_INT_THRESH      2 +#define INIT_PED_THRESH          7 +#define ST_THRESHOLD_MULTIPLIER  10 +#define ST_MAX_SAMPLES           500 +#define ST_MAX_THRESHOLD         100 +#define DMP_INTERVAL_INIT       (5 * NSEC_PER_MSEC) +#define DMP_INTERVAL_MIN_ADJ    (50 * NSEC_PER_USEC) + +/*---- MPU6500 ----*/ +#define MPU6500_ID               0x70      /* unique WHOAMI */ +#define MPU6515_ID               0x74      /* unique WHOAMI */ +#define MPU6500_PRODUCT_REVISION 1 +#define MPU6500_MEM_REV_ADDR     0x16 +#define INV_MPU_REV_MASK         0x0F +#define MPU6500_REV              2 +#define MPU_DMP_LOAD_START       0x20 + +/*---- MPU9250 ----*/ +#define MPU9250_ID               0x71      /* unique WHOAMI */ + +/* ----MPU9350 ----*/ +#define MPU9350_ID               0x72 + +#define THREE_AXIS               3 +#define GYRO_CONFIG_FSR_SHIFT    3 +#define ACCEL_CONFIG_FSR_SHIFT    3 +#define GYRO_DPS_SCALE           250 +#define MEM_ADDR_PROD_REV        0x6 +#define SOFT_PROD_VER_BYTES      5 +#define CRC_FIRMWARE_SEED        0 +#define SELF_TEST_SUCCESS        1 +#define MS_PER_DMP_TICK          20 +#define DMP_IMAGE_SIZE           2463 + +/* init parameters */ +#define INIT_FIFO_RATE           50 +#define INIT_DMP_OUTPUT_RATE     25 +#define INIT_DUR_TIME           (NSEC_PER_SEC / INIT_FIFO_RATE) +#define INIT_TAP_THRESHOLD       100 +#define INIT_TAP_TIME            100 +#define INIT_TAP_MIN_COUNT       2 +#define INIT_SAMPLE_DIVIDER      4 +#define MPU_INIT_SMD_DELAY_THLD  3 +#define MPU_INIT_SMD_DELAY2_THLD 1 +#define MPU_INIT_SMD_THLD        1500 +#define MPU_DEFAULT_DMP_FREQ     200 +#define MPL_PROD_KEY(ver, rev)  (ver * 100 + rev) +#define NUM_OF_PROD_REVS (ARRAY_SIZE(prod_rev_map)) +/*---- MPU6050 Silicon Revisions ----*/ +#define MPU_SILICON_REV_A2                    1       /* MPU6050A2 Device */ +#define MPU_SILICON_REV_B1                    2       /* MPU6050B1 Device */ + +#define BIT_PRFTCH_EN                         0x40 +#define BIT_CFG_USER_BANK                     0x20 +#define BITS_MEM_SEL                          0x1f + +#define TIME_STAMP_TOR                        5 +#define MAX_CATCH_UP                          5 +#define DEFAULT_ACCEL_TRIM                    16384 +#define DEFAULT_GYRO_TRIM                     131 +#define MAX_FIFO_RATE                         1000 +#define MAX_DMP_OUTPUT_RATE                   200 +#define MIN_FIFO_RATE                         4 +#define ONE_K_HZ                              1000 +#define NS_PER_MS_SHIFT                       20 +#define END_MARKER                            0x0010 +#define EMPTY_MARKER                          0x0020 + +/*tap related defines */ +#define INV_TAP                               0x08 +#define INV_NUM_TAP_AXES                      3 + +#define INV_TAP_AXIS_X_POS                    0x20 +#define INV_TAP_AXIS_X_NEG                    0x10 +#define INV_TAP_AXIS_Y_POS                    0x08 +#define INV_TAP_AXIS_Y_NEG                    0x04 +#define INV_TAP_AXIS_Z_POS                    0x02 +#define INV_TAP_AXIS_Z_NEG                    0x01 +#define INV_TAP_ALL_DIRECTIONS                0x3f + +#define INV_TAP_AXIS_X                        0x1 +#define INV_TAP_AXIS_Y                        0x2 +#define INV_TAP_AXIS_Z                        0x4 + +#define INV_TAP_AXIS_ALL               \ +		(INV_TAP_AXIS_X            |   \ +		INV_TAP_AXIS_Y             |   \ +		INV_TAP_AXIS_Z) + +#define INT_SRC_TAP             0x01 +#define INT_SRC_DISPLAY_ORIENT  0x08 +#define INT_SRC_SHAKE           0x10 + +#define INV_X_AXIS_INDEX                  0x00 +#define INV_Y_AXIS_INDEX                  0x01 +#define INV_Z_AXIS_INDEX                  0x02 + +#define INV_ELEMENT_1                     0x0001 +#define INV_ELEMENT_2                     0x0002 +#define INV_ELEMENT_3                     0x0004 +#define INV_ELEMENT_4                     0x0008 +#define INV_ELEMENT_5                     0x0010 +#define INV_ELEMENT_6                     0x0020 +#define INV_ELEMENT_7                     0x0040 +#define INV_ELEMENT_8                     0x0080 +#define INV_ALL                           0xFFFF +#define INV_ELEMENT_MASK                  0x00FF +#define INV_GYRO_ACC_MASK                 0x007E +#define INV_ACCEL_MASK                    0x70 +#define INV_GYRO_MASK                     0xE + +struct inv_mpu_state; + +/** + *  struct inv_reg_map_s - Notable slave registers. + *  @sample_rate_div:	Divider applied to gyro output rate. + *  @lpf:		Configures internal LPF. + *  @bank_sel:		Selects between memory banks. + *  @user_ctrl:		Enables/resets the FIFO. + *  @fifo_en:		Determines which data will appear in FIFO. + *  @gyro_config:	gyro config register. + *  @accel_config:	accel config register + *  @fifo_count_h:	Upper byte of FIFO count. + *  @fifo_r_w:		FIFO register. + *  @raw_accel		Address of first accel register. + *  @temperature	temperature register + *  @int_enable:	Interrupt enable register. + *  @int_status:	Interrupt flags. + *  @pwr_mgmt_1:	Controls chip's power state and clock source. + *  @pwr_mgmt_2:	Controls power state of individual sensors. + *  @mem_start_addr:	Address of first memory read. + *  @mem_r_w:		Access to memory. + *  @prgm_strt_addrh	firmware program start address register + */ +struct inv_reg_map_s { +	u8 sample_rate_div; +	u8 lpf; +	u8 bank_sel; +	u8 user_ctrl; +	u8 fifo_en; +	u8 gyro_config; +	u8 accel_config; +	u8 fifo_count_h; +	u8 fifo_r_w; +	u8 raw_accel; +	u8 temperature; +	u8 int_enable; +	u8 int_status; +	u8 pwr_mgmt_1; +	u8 pwr_mgmt_2; +	u8 mem_start_addr; +	u8 mem_r_w; +	u8 prgm_strt_addrh; +}; + +/* device enum */ +enum inv_devices { +	INV_ITG3500, +	INV_MPU3050, +	INV_MPU6050, +	INV_MPU9150, +	INV_MPU6500, +	INV_MPU9250, +	INV_MPU6XXX, +	INV_MPU9350, +	INV_MPU6515, +	INV_NUM_PARTS +}; + +/** + *  struct inv_hw_s - Other important hardware information. + *  @num_reg:	Number of registers on device. + *  @name:      name of the chip + */ +struct inv_hw_s { +	u8 num_reg; +	u8 *name; +}; + +/* enum for sensor */ +enum INV_SENSORS { +	SENSOR_GYRO = 0, +	SENSOR_ACCEL, +	SENSOR_COMPASS, +	SENSOR_PRESSURE, +	SENSOR_STEP, +	SENSOR_PEDQ, +	SENSOR_SIXQ, +	SENSOR_LPQ, +	SENSOR_NUM_MAX, +	SENSOR_INVALID, +}; + +/** + *  struct inv_sensor - information for each sensor. + *  @ts: this sensors timestamp. + *  @dur: duration between samples in ns. + *  @rate:  sensor data rate. + *  @counter: dmp tick counter corresponding to rate. + *  @on:    sensor on/off. + *  @sample_size: number of bytes for the sensor. + *  @send_data: function pointer to send data or not. + *  @set_rate: funcition pointer to set data rate. + */ +struct inv_sensor { +	u64 ts; +	int dur; +	int rate; +	int counter; +	bool on; +	u8 sample_size; +	int (*send_data)(struct inv_mpu_state *st, bool on); +	int (*set_rate)(struct inv_mpu_state *st); +}; + +/** + *  struct inv_batch - information batchmode. + *  @on: derived variable for batch mode. + *  @overflow_on: overflow mode for batchmode. + *  @wake_fifo_on: overflow for suspend mode. + *  @counter: counter for batch mode. + *  @timeout: nominal timeout value for batchmode in milliseconds. + *  @min_rate: minimum sensor rate that is turned on. + */ +struct inv_batch { +	bool on; +	bool overflow_on; +	bool wake_fifo_on; +	u32 counter; +	u32 timeout; +	u32 min_rate; +}; + +/** + *  struct inv_chip_config_s - Cached chip configuration data. + *  @fsr:		Full scale range. + *  @lpf:		Digital low pass filter frequency. + *  @accel_fs:		accel full scale range. + *  @has_footer:	MPU3050 specific work around. + *  @has_compass:	has compass or not. + *  @has_pressure:      has pressure sensor or not. + *  @enable:		master enable to enable output + *  @accel_enable:	enable accel functionality + *  @gyro_enable:	enable gyro functionality + *  @is_asleep:		1 if chip is powered down. + *  @dmp_on:		dmp is on/off. + *  @dmp_int_on:        dmp interrupt on/off. + *  @step_indicator_on: step indicate bit added to the sensor or not. + *  @dmp_event_int_on:  dmp event interrupt on/off. + *  @firmware_loaded:	flag indicate firmware loaded or not. + *  @lpa_mod:		low power mode. + *  @display_orient_on:	display orientation on/off. + *  @normal_compass_measure: discard first compass data after reset. + *  @normal_pressure_measure: discard first pressure data after reset. + *  @smd_enable: disable/enable SMD function. + *  @adjust_time: flag to indicate whether adjust chip clock or not. + *  @smd_triggered: smd is triggered. + *  @lpa_freq:		low power frequency + *  @prog_start_addr:	firmware program start address. + *  @fifo_rate:		current FIFO update rate. + *  @bytes_per_datum: number of bytes for 1 sample data. + */ +struct inv_chip_config_s { +	u32 fsr:2; +	u32 lpf:3; +	u32 accel_fs:2; +	u32 has_footer:1; +	u32 has_compass:1; +	u32 has_pressure:1; +	u32 enable:1; +	u32 accel_enable:1; +	u32 gyro_enable:1; +	u32 is_asleep:1; +	u32 dmp_on:1; +	u32 dmp_int_on:1; +	u32 dmp_event_int_on:1; +	u32 step_indicator_on:1; +	u32 firmware_loaded:1; +	u32 lpa_mode:1; +	u32 display_orient_on:1; +	u32 normal_compass_measure:1; +	u32 normal_pressure_measure:1; +	u32 smd_enable:1; +	u32 adjust_time:1; +	u32 smd_triggered:1; +	u16 lpa_freq; +	u16 prog_start_addr; +	u16 fifo_rate; +	u16 bytes_per_datum; +}; + +/** + *  struct inv_chip_info_s - Chip related information. + *  @product_id:	Product id. + *  @product_revision:	Product revision. + *  @silicon_revision:	Silicon revision. + *  @software_revision:	software revision. + *  @multi:		accel specific multiplier. + *  @compass_sens:	compass sensitivity. + *  @gyro_sens_trim:	Gyro sensitivity trim factor. + *  @accel_sens_trim:    accel sensitivity trim factor. + */ +struct inv_chip_info_s { +	u8 product_id; +	u8 product_revision; +	u8 silicon_revision; +	u8 software_revision; +	u8 multi; +	u8 compass_sens[3]; +	u32 gyro_sens_trim; +	u32 accel_sens_trim; +}; + +/** + *  struct inv_tap structure to store tap data. + *  @min_count:  minimum taps counted. + *  @thresh:    tap threshold. + *  @time:	tap time. + *  @on: tap on/off. + */ +struct inv_tap { +	u16 min_count; +	u16 thresh; +	u16 time; +	bool on; +}; + +/** + *  struct accel_mot_int_s structure to store motion interrupt data + *  @mot_thr:    motion threshold. + *  @mot_dur:    motion duration. + *  @mot_on:     flag to indicate motion detection on; + */ +struct accel_mot_int { +	u16 mot_thr; +	u32 mot_dur; +	u8 mot_on:1; +}; + +/** + * struct self_test_setting - self test settables from sysfs + * samples: number of samples used in self test. + * threshold: threshold fail/pass criterion in self test. + *            This value is in the percentage multiplied by 100. + *            So 14% would be 14. + */ +struct self_test_setting { +	u16 samples; +	u16 threshold; +}; + +/** + * struct inv_smd significant motion detection structure. + * @threshold: accel threshold for motion detection. + * @delay: delay time to confirm 2nd motion. + * @delay2: delay window parameter. + */ +struct inv_smd { +	u32 threshold; +	u32 delay; +	u32 delay2; +}; + +/** + * struct inv_ped pedometer related data structure. + * @step: steps taken. + * @time: time taken during the period. + * @last_step_time: last time the step is taken. + * @step_thresh: step threshold to show steps. + * @int_thresh: step threshold to generate interrupt. + * @int_on:   pedometer interrupt enable/disable. + * @on:  pedometer on/off. + */ +struct inv_ped { +	u64 step; +	u64 time; +	u64 last_step_time; +	u16 step_thresh; +	u16 int_thresh; +	bool int_on; +	bool on; +}; + +struct inv_mpu_slave; +/** + *  struct inv_mpu_state - Driver state variables. + *  @chip_config:	Cached attribute information. + *  @chip_info:		Chip information from read-only registers. + *  @trig;              iio trigger. + *  @tap:               tap data structure. + *  @smd:               SMD data structure. + *  @ped:               pedometer data structure. + *  @batch:             batchmode data structure. + *  @reg:		Map of important registers. + *  @self_test:         self test settings. + *  @hw:		Other hardware-specific information. + *  @chip_type:		chip type. + *  @time_stamp_lock:	spin lock to time stamp. + *  @suspend_resume_lock: mutex lock for suspend/resume. + *  @client:		i2c client handle. + *  @plat_data:		platform data. + *  @slave_accel:       mpu slave handle for accelerometer(MPU3050 only). + *  @slave_compass:	mpu slave handle for magnetometer. + *  @slave_pressure:	mpu slave handle for pressure sensor. + *  (*set_power_state)(struct inv_mpu_state *, int on): function ptr + *  (*switch_gyro_engine)(struct inv_mpu_state *, int on): function ptr + *  (*switch_accel_engine)(struct inv_mpu_state *, int on): function ptr + *  (*init_config)(struct iio_dev *indio_dev): function ptr + *  (*setup_reg)(struct inv_reg_map_s *reg): function ptr + *  @timestamps:        kfifo queue to store time stamp. + *  @irq:               irq number store. + *  @accel_bias:        accel bias store. + *  @gyro_bias:         gyro bias store. + *  @input_gyro_offset[3]: gyro offset from sysfs. + *  @input_accel_offset[3]: accel offset from sysfs. + *  @input_accel_dmp_bias[3]: accel bias for dmp. + *  @input_gyro_dmp_bias[3];: gyro bias for dmp. + *  @rom_gyro_bias[3]: gyro bias from sysfs. + *  @rom_accel_bias[3]: accel bias from sysfs. + *  @fifo_data[6]: fifo data storage. + *  @i2c_addr:          i2c address. + *  @sample_divider:    sample divider for dmp. + *  @fifo_divider:      fifo divider for dmp. + *  @display_orient_data:display orient data. + *  @tap_data:          tap data. + *  @bytes_per_sec: bytes per seconds when in DMP mode. + *  @left_over[HEADERED_Q_BYTES]: left over bytes storage. + *  @left_over_size: left over size. + *  @sensor{SENSOR_NUM_MAX]: sensor individual properties. + *  @fifo_count: current fifo_count; + *  @dmp_counter: dmp_counter; + *  @dmp_ticks: DMP ticks past since the previous read. + *  @sl_handle:         Handle to I2C port. + *  @irq_dur_ns:        duration between each irq. + *  @ts_counter:        time stamp counter. + *  @dmp_interval:      dmp interval. nomial value is 5 ms. + *  @dmp_interval_accum: dmp interval accumlater. + *  @diff_accumulater:  accumlator for the difference of nominal and actual. + *  @last_ts:           last time stamp. + *  @step_detector_base_ts: base time stamp for step detector calculation. + *  @prev_ts:           previous time stamp. + *  @pedometer_step:    pedometer steps stored in driver. + *  @pedometer_time:    pedometer time stored in driver. + *  @last_run_time: last time the post ISR runs. + *  @name: name for distiguish MPU6050 and MPU6500 in MPU6XXX. + *  @secondary_name: name for the slave device in the secondary I2C. + */ +struct inv_mpu_state { +#define TIMESTAMP_FIFO_SIZE 64 +	struct inv_chip_config_s chip_config; +	struct inv_chip_info_s chip_info; +	struct iio_trigger  *trig; +	struct inv_tap tap; +	struct inv_smd smd; +	struct inv_ped ped; +	struct inv_batch batch; +	struct inv_reg_map_s reg; +	struct self_test_setting self_test; +	const struct inv_hw_s *hw; +	enum   inv_devices chip_type; +	spinlock_t time_stamp_lock; +	struct mutex suspend_resume_lock; +	struct i2c_client *client; +	struct mpu_platform_data plat_data; +	struct inv_mpu_slave *slave_accel; +	struct inv_mpu_slave *slave_compass; +	struct inv_mpu_slave *slave_pressure; +	struct accel_mot_int mot_int; +	int (*set_power_state)(struct inv_mpu_state *, bool on); +	int (*switch_gyro_engine)(struct inv_mpu_state *, bool on); +	int (*switch_accel_engine)(struct inv_mpu_state *, bool on); +	int (*init_config)(struct iio_dev *indio_dev); +	void (*setup_reg)(struct inv_reg_map_s *reg); +	DECLARE_KFIFO(timestamps, u64, TIMESTAMP_FIFO_SIZE); +	short irq; +	int accel_bias[3]; +	int gyro_bias[3]; +	s16 input_gyro_offset[3]; +	s16 input_accel_offset[3]; +	int input_accel_dmp_bias[3]; +	int input_gyro_dmp_bias[3]; +	s16 rom_gyro_offset[3]; +	s16 rom_accel_offset[3]; +	u8 fifo_data[6]; +	u8 i2c_addr; +	u8 sample_divider; +	u8 fifo_divider; +	u8 display_orient_data; +	u8 tap_data; +	u16 bytes_per_sec; +	u8 left_over[HEADERED_Q_BYTES]; +	u32 left_over_size; +	struct inv_sensor sensor[SENSOR_NUM_MAX]; +	u32 fifo_count; +	u32 dmp_counter; +	u32 dmp_ticks; +	void *sl_handle; +	u32 irq_dur_ns; +	u32 ts_counter; +	u32 dmp_interval; +	s32 dmp_interval_accum; +	s64 diff_accumulater; +	u64 last_ts; +	u64 step_detector_base_ts; +	u64 prev_ts; +	u64 last_run_time; +	u8 name[20]; +	u8 secondary_name[20]; +}; + +/* produces an unique identifier for each device based on the +   combination of product version and product revision */ +struct prod_rev_map_t { +	u16 mpl_product_key; +	u8 silicon_rev; +	u16 gyro_trim; +	u16 accel_trim; +}; + +/** + *  struct inv_mpu_slave - MPU slave structure. + *  @st_upper:  compass self test upper limit. + *  @st_lower:  compass self test lower limit. + *  @scale: compass scale. + *  @rate_scale: decide how fast a compass can read. + *  @min_read_time: minimum time between each reading. + *  @self_test: self test method of the slave. + *  @set_scale: set scale of slave + *  @get_scale: read scale back of the slave. + *  @suspend:		suspend operation. + *  @resume:		resume operation. + *  @setup:		setup chip. initialization. + *  @combine_data:	combine raw data into meaningful data. + *  @read_data:        read external sensor and output + *  @get_mode:		get current chip mode. + *  @set_lpf:            set low pass filter. + *  @set_fs:             set full scale + *  @prev_ts: last time it is read. + */ +struct inv_mpu_slave { +	const short *st_upper; +	const short *st_lower; +	int scale; +	int rate_scale; +	int min_read_time; +	int (*self_test)(struct inv_mpu_state *); +	int (*set_scale)(struct inv_mpu_state *, int scale); +	int (*get_scale)(struct inv_mpu_state *, int *val); +	int (*suspend)(struct inv_mpu_state *); +	int (*resume)(struct inv_mpu_state *); +	int (*setup)(struct inv_mpu_state *); +	int (*combine_data)(u8 *in, short *out); +	int (*read_data)(struct inv_mpu_state *, short *out); +	int (*get_mode)(void); +	int (*set_lpf)(struct inv_mpu_state *, int rate); +	int (*set_fs)(struct inv_mpu_state *, int fs); +	u64 prev_ts; +}; + +/* scan element definition */ +enum inv_mpu_scan { +	INV_MPU_SCAN_QUAT_R = 0, +	INV_MPU_SCAN_QUAT_X, +	INV_MPU_SCAN_QUAT_Y, +	INV_MPU_SCAN_QUAT_Z, +	INV_MPU_SCAN_ACCEL_X, +	INV_MPU_SCAN_ACCEL_Y, +	INV_MPU_SCAN_ACCEL_Z, +	INV_MPU_SCAN_GYRO_X, +	INV_MPU_SCAN_GYRO_Y, +	INV_MPU_SCAN_GYRO_Z, +	INV_MPU_SCAN_MAGN_X, +	INV_MPU_SCAN_MAGN_Y, +	INV_MPU_SCAN_MAGN_Z, +	INV_MPU_SCAN_TIMESTAMP, +}; + +enum inv_filter_e { +	INV_FILTER_256HZ_NOLPF2 = 0, +	INV_FILTER_188HZ, +	INV_FILTER_98HZ, +	INV_FILTER_42HZ, +	INV_FILTER_20HZ, +	INV_FILTER_10HZ, +	INV_FILTER_5HZ, +	INV_FILTER_2100HZ_NOLPF, +	NUM_FILTER +}; + +enum inv_slave_mode { +	INV_MODE_SUSPEND, +	INV_MODE_NORMAL, +}; + +/*==== MPU6050B1 MEMORY ====*/ +enum MPU_MEMORY_BANKS { +	MEM_RAM_BANK_0 = 0, +	MEM_RAM_BANK_1, +	MEM_RAM_BANK_2, +	MEM_RAM_BANK_3, +	MEM_RAM_BANK_4, +	MEM_RAM_BANK_5, +	MEM_RAM_BANK_6, +	MEM_RAM_BANK_7, +	MEM_RAM_BANK_8, +	MEM_RAM_BANK_9, +	MEM_RAM_BANK_10, +	MEM_RAM_BANK_11, +	MPU_MEM_NUM_RAM_BANKS, +	MPU_MEM_OTP_BANK_0 = 16 +}; + +/* IIO attribute address */ +enum MPU_IIO_ATTR_ADDR { +	ATTR_DMP_GYRO_X_DMP_BIAS, +	ATTR_DMP_GYRO_Y_DMP_BIAS, +	ATTR_DMP_GYRO_Z_DMP_BIAS, +	ATTR_DMP_ACCEL_X_DMP_BIAS, +	ATTR_DMP_ACCEL_Y_DMP_BIAS, +	ATTR_DMP_ACCEL_Z_DMP_BIAS, +	ATTR_DMP_PED_INT_ON, +	ATTR_DMP_PED_STEP_THRESH, +	ATTR_DMP_PED_INT_THRESH, +	ATTR_DMP_PED_ON, +	ATTR_DMP_SMD_ENABLE, +	ATTR_DMP_SMD_THLD, +	ATTR_DMP_SMD_DELAY_THLD, +	ATTR_DMP_SMD_DELAY_THLD2, +	ATTR_DMP_PEDOMETER_STEPS, +	ATTR_DMP_PEDOMETER_TIME, +	ATTR_DMP_PEDOMETER_COUNTER, +	ATTR_DMP_TAP_ON, +	ATTR_DMP_TAP_THRESHOLD, +	ATTR_DMP_TAP_MIN_COUNT, +	ATTR_DMP_TAP_TIME, +	ATTR_DMP_DISPLAY_ORIENTATION_ON, +/* *****above this line, are DMP features, power needs on/off */ +/* *****below this line, are DMP features, no power needed */ +	ATTR_DMP_ON, +	ATTR_DMP_INT_ON, +	ATTR_DMP_EVENT_INT_ON, +	ATTR_DMP_STEP_INDICATOR_ON, +	ATTR_DMP_BATCHMODE_TIMEOUT, +	ATTR_DMP_BATCHMODE_WAKE_FIFO_FULL, +	ATTR_DMP_SIX_Q_ON, +	ATTR_DMP_SIX_Q_RATE, +	ATTR_DMP_LPQ_ON, +	ATTR_DMP_LPQ_RATE, +	ATTR_DMP_PED_Q_ON, +	ATTR_DMP_PED_Q_RATE, +	ATTR_DMP_STEP_DETECTOR_ON, +	ATTR_DMP_STEP_DETECTOR_RATE, +/*  *****above this line, it is all DMP related features */ +/*  *****below this line, it is all non-DMP related features */ +	ATTR_GYRO_SCALE, +	ATTR_ACCEL_SCALE, +	ATTR_COMPASS_SCALE, +	ATTR_GYRO_X_OFFSET, +	ATTR_GYRO_Y_OFFSET, +	ATTR_GYRO_Z_OFFSET, +	ATTR_ACCEL_X_OFFSET, +	ATTR_ACCEL_Y_OFFSET, +	ATTR_ACCEL_Z_OFFSET, +	ATTR_MOTION_LPA_ON, +	ATTR_MOTION_LPA_FREQ, +	ATTR_MOTION_LPA_THRESHOLD, +/*  *****above this line, it is non-DMP, power needs on/off */ +/*  *****below this line, it is non-DMP, no needs to on/off power */ +	ATTR_SELF_TEST_SAMPLES, +	ATTR_SELF_TEST_THRESHOLD, +	ATTR_GYRO_ENABLE, +	ATTR_GYRO_FIFO_ENABLE, +	ATTR_GYRO_RATE, +	ATTR_ACCEL_ENABLE, +	ATTR_ACCEL_FIFO_ENABLE, +	ATTR_ACCEL_RATE, +	ATTR_COMPASS_ENABLE, +	ATTR_COMPASS_RATE, +	ATTR_PRESSURE_ENABLE, +	ATTR_PRESSURE_RATE, +	ATTR_POWER_STATE, /* this is fake sysfs for compatibility */ +	ATTR_FIRMWARE_LOADED, +	ATTR_SAMPLING_FREQ, +/*  *****below this line, it is attributes only has show methods */ +	ATTR_SELF_TEST, /* this has show-only methods needs power on/off */ +	ATTR_GYRO_X_CALIBBIAS, +	ATTR_GYRO_Y_CALIBBIAS, +	ATTR_GYRO_Z_CALIBBIAS, +	ATTR_ACCEL_X_CALIBBIAS, +	ATTR_ACCEL_Y_CALIBBIAS, +	ATTR_ACCEL_Z_CALIBBIAS, +	ATTR_SELF_TEST_GYRO_SCALE, +	ATTR_SELF_TEST_ACCEL_SCALE, +	ATTR_GYRO_MATRIX, +	ATTR_ACCEL_MATRIX, +	ATTR_COMPASS_MATRIX, +	ATTR_SECONDARY_NAME, +#ifdef CONFIG_INV_TESTING +	ATTR_COMPASS_SENS, +	ATTR_I2C_COUNTERS, +	ATTR_REG_WRITE, +	ATTR_DEBUG_SMD_ENABLE_TESTP1, +	ATTR_DEBUG_SMD_ENABLE_TESTP2, +	ATTR_DEBUG_SMD_EXE_STATE, +	ATTR_DEBUG_SMD_DELAY_CNTR, +	ATTR_DEBUG_GYRO_COUNTER, +	ATTR_DEBUG_ACCEL_COUNTER, +	ATTR_DEBUG_COMPASS_COUNTER, +	ATTR_DEBUG_PRESSURE_COUNTER, +	ATTR_DEBUG_LPQ_COUNTER, +	ATTR_DEBUG_SIXQ_COUNTER, +	ATTR_DEBUG_PEDQ_COUNTER, +#endif +}; + +enum inv_accel_fs_e { +	INV_FS_02G = 0, +	INV_FS_04G, +	INV_FS_08G, +	INV_FS_16G, +	NUM_ACCEL_FSR +}; + +enum inv_fsr_e { +	INV_FSR_250DPS = 0, +	INV_FSR_500DPS, +	INV_FSR_1000DPS, +	INV_FSR_2000DPS, +	NUM_FSR +}; + +enum inv_clock_sel_e { +	INV_CLK_INTERNAL = 0, +	INV_CLK_PLL, +	NUM_CLK +}; + +ssize_t inv_dmp_firmware_write(struct file *fp, struct kobject *kobj, +	struct bin_attribute *attr, char *buf, loff_t pos, size_t size); +ssize_t inv_dmp_firmware_read(struct file *filp, +				struct kobject *kobj, +				struct bin_attribute *bin_attr, +				char *buf, loff_t off, size_t count); +ssize_t inv_six_q_write(struct file *fp, struct kobject *kobj, +	struct bin_attribute *attr, char *buf, loff_t pos, size_t size); + +int inv_mpu_configure_ring(struct iio_dev *indio_dev); +int inv_mpu_probe_trigger(struct iio_dev *indio_dev); +void inv_mpu_unconfigure_ring(struct iio_dev *indio_dev); +void inv_mpu_remove_trigger(struct iio_dev *indio_dev); +int inv_init_config_mpu3050(struct iio_dev *indio_dev); +int inv_get_silicon_rev_mpu6050(struct inv_mpu_state *st); +int inv_get_silicon_rev_mpu6500(struct inv_mpu_state *st); +int set_3050_bypass(struct inv_mpu_state *st, bool enable); +int inv_register_mpu3050_slave(struct inv_mpu_state *st); +void inv_setup_reg_mpu3050(struct inv_reg_map_s *reg); +int inv_switch_3050_gyro_engine(struct inv_mpu_state *st, bool en); +int inv_switch_3050_accel_engine(struct inv_mpu_state *st, bool en); +int set_power_mpu3050(struct inv_mpu_state *st, bool power_on); +int inv_set_interrupt_on_gesture_event(struct inv_mpu_state *st, bool on); +int inv_set_display_orient_interrupt_dmp(struct inv_mpu_state *st, bool on); +u16 inv_dmp_get_address(u16 key); +int inv_q30_mult(int a, int b); +int inv_set_tap_threshold_dmp(struct inv_mpu_state *st, u16 threshold); +int inv_write_2bytes(struct inv_mpu_state *st, int k, int data); +int inv_set_min_taps_dmp(struct inv_mpu_state *st, u16 min_taps); +int  inv_set_tap_time_dmp(struct inv_mpu_state *st, u16 time); +int inv_enable_tap_dmp(struct inv_mpu_state *st, bool on); +int inv_i2c_read_base(struct inv_mpu_state *st, u16 i2c_addr, +	u8 reg, u16 length, u8 *data); +int inv_i2c_single_write_base(struct inv_mpu_state *st, +	u16 i2c_addr, u8 reg, u8 data); +int inv_hw_self_test(struct inv_mpu_state *st); +s64 get_time_ns(void); +int write_be32_key_to_mem(struct inv_mpu_state *st, +					u32 data, int key); +int inv_set_accel_bias_dmp(struct inv_mpu_state *st); +int inv_mpu_setup_compass_slave(struct inv_mpu_state *st); +int inv_mpu_setup_pressure_slave(struct inv_mpu_state *st); +int mpu_memory_write(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr, +		     u32 len, u8 const *data); +int mpu_memory_read(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr, +		    u32 len, u8 *data); +int mpu_memory_write_unaligned(struct inv_mpu_state *st, u16 key, int len, +							u8 const *d); +int inv_quaternion_on(struct inv_mpu_state *st, +				 struct iio_buffer *ring, bool en); +int inv_enable_pedometer_interrupt(struct inv_mpu_state *st, bool en); +int inv_read_pedometer_counter(struct inv_mpu_state *st); +int inv_enable_pedometer(struct inv_mpu_state *st, bool en); +int inv_get_pedometer_steps(struct inv_mpu_state *st, u32 *steps); +int inv_get_pedometer_time(struct inv_mpu_state *st, u32 *time); +int inv_reset_fifo(struct iio_dev *indio_dev); +int inv_reset_offset_reg(struct inv_mpu_state *st, bool en); +int inv_batchmode_setup(struct inv_mpu_state *st); +void inv_init_sensor_struct(struct inv_mpu_state *st); +int inv_read_time_and_ticks(struct inv_mpu_state *st, bool resume); +int inv_flush_batch_data(struct iio_dev *indio_dev, bool *has_data); +int set_inv_enable(struct iio_dev *indio_dev, bool enable); +/* used to print i2c data using pr_debug */ +char *wr_pr_debug_begin(u8 const *data, u32 len, char *string); +char *wr_pr_debug_end(char *string); + +#define mem_w(a, b, c) \ +	mpu_memory_write(st, st->i2c_addr, a, b, c) +#define mem_w_key(key, b, c) mpu_memory_write_unaligned(st, key, b, c) +#define inv_i2c_read(st, reg, len, data) \ +	inv_i2c_read_base(st, st->i2c_addr, reg, len, data) +#define inv_i2c_single_write(st, reg, data) \ +	inv_i2c_single_write_base(st, st->i2c_addr, reg, data) +#define inv_secondary_read(reg, len, data) \ +	inv_i2c_read_base(st, st->plat_data.secondary_i2c_addr, reg, len, data) +#define inv_secondary_write(reg, data) \ +	inv_i2c_single_write_base(st, st->plat_data.secondary_i2c_addr, \ +		reg, data) +#define inv_aux_read(reg, len, data) \ +	inv_i2c_read_base(st, st->plat_data.aux_i2c_addr, reg, len, data) +#define inv_aux_write(reg, data) \ +	inv_i2c_single_write_base(st, st->plat_data.aux_i2c_addr, \ +		reg, data) + +#endif  /* #ifndef _INV_MPU_IIO_H_ */ + diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_misc.c b/drivers/staging/iio/imu/inv_mpu/inv_mpu_misc.c new file mode 100644 index 00000000000..c327c75b1ab --- /dev/null +++ b/drivers/staging/iio/imu/inv_mpu/inv_mpu_misc.c @@ -0,0 +1,2028 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/crc32.h> + +#include "inv_mpu_iio.h" +#include "inv_test/inv_counters.h" + +/* DMP defines */ +#define DMP_ORIENTATION_TIME            500 +#define DMP_ORIENTATION_ANGLE           60 +#define DMP_DEFAULT_FIFO_RATE           200 +#define DMP_TAP_SCALE                   (767603923 / 5) +#define DMP_MULTI_SHIFT                 30 +#define DMP_MULTI_TAP_TIME              500 +#define DMP_SHAKE_REJECT_THRESH         100 +#define DMP_SHAKE_REJECT_TIME           10 +#define DMP_SHAKE_REJECT_TIMEOUT        10 +#define DMP_ANGLE_SCALE                 15 +#define DMP_PRECISION                   1000 +#define DMP_MAX_DIVIDER                 4 +#define DMP_MAX_MIN_TAPS                4 +#define DMP_IMAGE_CRC_VALUE             0x972aae92 + +/*--- Test parameters defaults --- */ +#define DEF_OLDEST_SUPP_PROD_REV        8 +#define DEF_OLDEST_SUPP_SW_REV          2 + +/* sample rate */ +#define DEF_SELFTEST_SAMPLE_RATE        0 +/* full scale setting dps */ +#define DEF_SELFTEST_GYRO_FS            (0 << 3) +#define DEF_SELFTEST_ACCEL_FS           (2 << 3) +#define DEF_SELFTEST_GYRO_SENS          (32768 / 250) +/* wait time before collecting data */ +#define DEF_GYRO_WAIT_TIME              10 +#define DEF_ST_STABLE_TIME              20 +#define DEF_ST_6500_STABLE_TIME         20 +#define DEF_GYRO_SCALE                  131 +#define DEF_ST_PRECISION                1000 +#define DEF_ST_ACCEL_FS_MG              8000UL +#define DEF_ST_SCALE                    (1L << 15) +#define DEF_ST_TRY_TIMES                2 +#define DEF_ST_COMPASS_RESULT_SHIFT     2 +#define DEF_ST_ACCEL_RESULT_SHIFT       1 +#define DEF_ST_OTP0_THRESH              60 +#define DEF_ST_ABS_THRESH               20 +#define DEF_ST_TOR                      2 + +#define X                               0 +#define Y                               1 +#define Z                               2 +/*---- MPU6050 notable product revisions ----*/ +#define MPU_PRODUCT_KEY_B1_E1_5         105 +#define MPU_PRODUCT_KEY_B2_F1           431 +/* accelerometer Hw self test min and max bias shift (mg) */ +#define DEF_ACCEL_ST_SHIFT_MIN          300 +#define DEF_ACCEL_ST_SHIFT_MAX          950 + +#define DEF_ACCEL_ST_SHIFT_DELTA        500 +#define DEF_GYRO_CT_SHIFT_DELTA         500 +/* gyroscope Coriolis self test min and max bias shift (dps) */ +#define DEF_GYRO_CT_SHIFT_MIN           10 +#define DEF_GYRO_CT_SHIFT_MAX           105 + +/*---- MPU6500 Self Test Pass/Fail Criteria ----*/ +/* Gyro Offset Max Value (dps) */ +#define DEF_GYRO_OFFSET_MAX             20 +/* Gyro Self Test Absolute Limits ST_AL (dps) */ +#define DEF_GYRO_ST_AL                  60 +/* Accel Self Test Absolute Limits ST_AL (mg) */ +#define DEF_ACCEL_ST_AL_MIN             225 +#define DEF_ACCEL_ST_AL_MAX             675 +#define DEF_6500_ACCEL_ST_SHIFT_DELTA   500 +#define DEF_6500_GYRO_CT_SHIFT_DELTA    500 +#define DEF_ST_MPU6500_ACCEL_LPF        2 +#define DEF_ST_6500_ACCEL_FS_MG         2000UL +#define DEF_SELFTEST_6500_ACCEL_FS      (0 << 3) + +/* Note: The ST_AL values are only used when ST_OTP = 0, + * i.e no factory self test values for reference + */ + +/* NOTE: product entries are in chronological order */ +static const struct prod_rev_map_t prod_rev_map[] = { +	/* prod_ver = 0 */ +	{MPL_PROD_KEY(0,   1), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,   2), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,   3), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,   4), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,   5), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,   6), MPU_SILICON_REV_A2, 131, 16384}, +	/* prod_ver = 1 */ +	{MPL_PROD_KEY(0,   7), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,   8), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,   9), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,  10), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,  11), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,  12), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,  13), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,  14), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,  15), MPU_SILICON_REV_A2, 131, 16384}, +	{MPL_PROD_KEY(0,  27), MPU_SILICON_REV_A2, 131, 16384}, +	/* prod_ver = 1 */ +	{MPL_PROD_KEY(1,  16), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,  17), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,  18), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,  19), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,  20), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,  28), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,   1), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,   2), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,   3), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,   4), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,   5), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(1,   6), MPU_SILICON_REV_B1, 131, 16384}, +	/* prod_ver = 2 */ +	{MPL_PROD_KEY(2,   7), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(2,   8), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(2,   9), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(2,  10), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(2,  11), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(2,  12), MPU_SILICON_REV_B1, 131, 16384}, +	{MPL_PROD_KEY(2,  29), MPU_SILICON_REV_B1, 131, 16384}, +	/* prod_ver = 3 */ +	{MPL_PROD_KEY(3,  30), MPU_SILICON_REV_B1, 131, 16384}, +	/* prod_ver = 4 */ +	{MPL_PROD_KEY(4,  31), MPU_SILICON_REV_B1, 131,  8192}, +	{MPL_PROD_KEY(4,   1), MPU_SILICON_REV_B1, 131,  8192}, +	{MPL_PROD_KEY(4,   3), MPU_SILICON_REV_B1, 131,  8192}, +	/* prod_ver = 5 */ +	{MPL_PROD_KEY(5,   3), MPU_SILICON_REV_B1, 131, 16384}, +	/* prod_ver = 6 */ +	{MPL_PROD_KEY(6,  19), MPU_SILICON_REV_B1, 131, 16384}, +	/* prod_ver = 7 */ +	{MPL_PROD_KEY(7,  19), MPU_SILICON_REV_B1, 131, 16384}, +	/* prod_ver = 8 */ +	{MPL_PROD_KEY(8,  19), MPU_SILICON_REV_B1, 131, 16384}, +	/* prod_ver = 9 */ +	{MPL_PROD_KEY(9,  19), MPU_SILICON_REV_B1, 131, 16384}, +	/* prod_ver = 10 */ +	{MPL_PROD_KEY(10, 19), MPU_SILICON_REV_B1, 131, 16384} +}; + +/* +*   List of product software revisions +* +*   NOTE : +*   software revision 0 falls back to the old detection method +*   based off the product version and product revision per the +*   table above +*/ +static const struct prod_rev_map_t sw_rev_map[] = { +	{0,		     0,   0,     0}, +	{1, MPU_SILICON_REV_B1, 131,  8192},	/* rev C */ +	{2, MPU_SILICON_REV_B1, 131, 16384}	/* rev D */ +}; + +static const u16 mpu_6500_st_tb[256] = { +	2620, 2646, 2672, 2699, 2726, 2753, 2781, 2808, +	2837, 2865, 2894, 2923, 2952, 2981, 3011, 3041, +	3072, 3102, 3133, 3165, 3196, 3228, 3261, 3293, +	3326, 3359, 3393, 3427, 3461, 3496, 3531, 3566, +	3602, 3638, 3674, 3711, 3748, 3786, 3823, 3862, +	3900, 3939, 3979, 4019, 4059, 4099, 4140, 4182, +	4224, 4266, 4308, 4352, 4395, 4439, 4483, 4528, +	4574, 4619, 4665, 4712, 4759, 4807, 4855, 4903, +	4953, 5002, 5052, 5103, 5154, 5205, 5257, 5310, +	5363, 5417, 5471, 5525, 5581, 5636, 5693, 5750, +	5807, 5865, 5924, 5983, 6043, 6104, 6165, 6226, +	6289, 6351, 6415, 6479, 6544, 6609, 6675, 6742, +	6810, 6878, 6946, 7016, 7086, 7157, 7229, 7301, +	7374, 7448, 7522, 7597, 7673, 7750, 7828, 7906, +	7985, 8065, 8145, 8227, 8309, 8392, 8476, 8561, +	8647, 8733, 8820, 8909, 8998, 9088, 9178, 9270, +	9363, 9457, 9551, 9647, 9743, 9841, 9939, 10038, +	10139, 10240, 10343, 10446, 10550, 10656, 10763, 10870, +	10979, 11089, 11200, 11312, 11425, 11539, 11654, 11771, +	11889, 12008, 12128, 12249, 12371, 12495, 12620, 12746, +	12874, 13002, 13132, 13264, 13396, 13530, 13666, 13802, +	13940, 14080, 14221, 14363, 14506, 14652, 14798, 14946, +	15096, 15247, 15399, 15553, 15709, 15866, 16024, 16184, +	16346, 16510, 16675, 16842, 17010, 17180, 17352, 17526, +	17701, 17878, 18057, 18237, 18420, 18604, 18790, 18978, +	19167, 19359, 19553, 19748, 19946, 20145, 20347, 20550, +	20756, 20963, 21173, 21385, 21598, 21814, 22033, 22253, +	22475, 22700, 22927, 23156, 23388, 23622, 23858, 24097, +	24338, 24581, 24827, 25075, 25326, 25579, 25835, 26093, +	26354, 26618, 26884, 27153, 27424, 27699, 27976, 28255, +	28538, 28823, 29112, 29403, 29697, 29994, 30294, 30597, +	30903, 31212, 31524, 31839, 32157, 32479, 32804 +}; + +static const int accel_st_tb[31] = { +	340, 351, 363, 375, 388, 401, 414, 428, +	443, 458, 473, 489, 506, 523, 541, 559, +	578, 597, 617, 638, 660, 682, 705, 729, +	753, 779, 805, 832, 860, 889, 919 +}; + +static const int gyro_6050_st_tb[31] = { +	3275, 3425, 3583, 3748, 3920, 4100, 4289, 4486, +	4693, 4909, 5134, 5371, 5618, 5876, 6146, 6429, +	6725, 7034, 7358, 7696, 8050, 8421, 8808, 9213, +	9637, 10080, 10544, 11029, 11537, 12067, 12622 +}; + +static const int gyro_3500_st_tb[255] = { +	2620, 2646, 2672, 2699, 2726, 2753, 2781, 2808, +	2837, 2865, 2894, 2923, 2952, 2981, 3011, 3041, +	3072, 3102, 3133, 3165, 3196, 3228, 3261, 3293, +	3326, 3359, 3393, 3427, 3461, 3496, 3531, 3566, +	3602, 3638, 3674, 3711, 3748, 3786, 3823, 3862, +	3900, 3939, 3979, 4019, 4059, 4099, 4140, 4182, +	4224, 4266, 4308, 4352, 4395, 4439, 4483, 4528, +	4574, 4619, 4665, 4712, 4759, 4807, 4855, 4903, +	4953, 5002, 5052, 5103, 5154, 5205, 5257, 5310, +	5363, 5417, 5471, 5525, 5581, 5636, 5693, 5750, +	5807, 5865, 5924, 5983, 6043, 6104, 6165, 6226, +	6289, 6351, 6415, 6479, 6544, 6609, 6675, 6742, +	6810, 6878, 6946, 7016, 7086, 7157, 7229, 7301, +	7374, 7448, 7522, 7597, 7673, 7750, 7828, 7906, +	7985, 8065, 8145, 8227, 8309, 8392, 8476, 8561, +	8647, 8733, 8820, 8909, 8998, 9088, 9178, 9270, +	9363, 9457, 9551, 9647, 9743, 9841, 9939, 10038, +	10139, 10240, 10343, 10446, 10550, 10656, 10763, 10870, +	10979, 11089, 11200, 11312, 11425, 11539, 11654, 11771, +	11889, 12008, 12128, 12249, 12371, 12495, 12620, 12746, +	12874, 13002, 13132, 13264, 13396, 13530, 13666, 13802, +	13940, 14080, 14221, 14363, 14506, 14652, 14798, 14946, +	15096, 15247, 15399, 15553, 15709, 15866, 16024, 16184, +	16346, 16510, 16675, 16842, 17010, 17180, 17352, 17526, +	17701, 17878, 18057, 18237, 18420, 18604, 18790, 18978, +	19167, 19359, 19553, 19748, 19946, 20145, 20347, 20550, +	20756, 20963, 21173, 21385, 21598, 21814, 22033, 22253, +	22475, 22700, 22927, 23156, 23388, 23622, 23858, 24097, +	24338, 24581, 24827, 25075, 25326, 25579, 25835, 26093, +	26354, 26618, 26884, 27153, 27424, 27699, 27976, 28255, +	28538, 28823, 29112, 29403, 29697, 29994, 30294, 30597, +	30903, 31212, 31524, 31839, 32157, 32479, 32804 +}; + +char *wr_pr_debug_begin(u8 const *data, u32 len, char *string) +{ +	int ii; +	string = kmalloc(len * 2 + 1, GFP_KERNEL); +	for (ii = 0; ii < len; ii++) +		sprintf(&string[ii * 2], "%02X", data[ii]); +	string[len * 2] = 0; +	return string; +} + +char *wr_pr_debug_end(char *string) +{ +	kfree(string); +	return ""; +} + +int mpu_memory_write(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr, +		     u32 len, u8 const *data) +{ +	u8 bank[2]; +	u8 addr[2]; +	u8 buf[513]; + +	struct i2c_msg msgs[3]; +	int res; + +	if (!data || !st) +		return -EINVAL; + +	if (len >= (sizeof(buf) - 1)) +		return -ENOMEM; + +	bank[0] = REG_BANK_SEL; +	bank[1] = mem_addr >> 8; + +	addr[0] = REG_MEM_START_ADDR; +	addr[1] = mem_addr & 0xFF; + +	buf[0] = REG_MEM_RW; +	memcpy(buf + 1, data, len); + +	/* write message */ +	msgs[0].addr = mpu_addr; +	msgs[0].flags = 0; +	msgs[0].buf = bank; +	msgs[0].len = sizeof(bank); + +	msgs[1].addr = mpu_addr; +	msgs[1].flags = 0; +	msgs[1].buf = addr; +	msgs[1].len = sizeof(addr); + +	msgs[2].addr = mpu_addr; +	msgs[2].flags = 0; +	msgs[2].buf = (u8 *)buf; +	msgs[2].len = len + 1; + +	INV_I2C_INC_MPUWRITE(3 + 3 + (2 + len)); +#ifdef CONFIG_DYNAMIC_DEBUG +	{ +		char *write = 0; +		pr_debug("%s WM%02X%02X%02X%s%s - %d\n", st->hw->name, +			 mpu_addr, bank[1], addr[1], +			 wr_pr_debug_begin(data, len, write), +			 wr_pr_debug_end(write), +			 len); +	} +#endif + +	res = i2c_transfer(st->sl_handle, msgs, 3); +	if (res != 3) { +		if (res >= 0) +			res = -EIO; +		return res; +	} else { +		return 0; +	} +} + +int mpu_memory_read(struct inv_mpu_state *st, u8 mpu_addr, u16 mem_addr, +		    u32 len, u8 *data) +{ +	u8 bank[2]; +	u8 addr[2]; +	u8 buf; + +	struct i2c_msg msgs[4]; +	int res; + +	if (!data || !st) +		return -EINVAL; + +	bank[0] = REG_BANK_SEL; +	bank[1] = mem_addr >> 8; + +	addr[0] = REG_MEM_START_ADDR; +	addr[1] = mem_addr & 0xFF; + +	buf = REG_MEM_RW; + +	/* write message */ +	msgs[0].addr = mpu_addr; +	msgs[0].flags = 0; +	msgs[0].buf = bank; +	msgs[0].len = sizeof(bank); + +	msgs[1].addr = mpu_addr; +	msgs[1].flags = 0; +	msgs[1].buf = addr; +	msgs[1].len = sizeof(addr); + +	msgs[2].addr = mpu_addr; +	msgs[2].flags = 0; +	msgs[2].buf = &buf; +	msgs[2].len = 1; + +	msgs[3].addr = mpu_addr; +	msgs[3].flags = I2C_M_RD; +	msgs[3].buf = data; +	msgs[3].len = len; + +	res = i2c_transfer(st->sl_handle, msgs, 4); +	if (res != 4) { +		if (res >= 0) +			res = -EIO; +	} else +		res = 0; + +	INV_I2C_INC_MPUWRITE(3 + 3 + 3); +	INV_I2C_INC_MPUREAD(len); +#ifdef CONFIG_DYNAMIC_DEBUG +	{ +		char *read = 0; +		pr_debug("%s RM%02X%02X%02X%02X - %s%s\n", st->hw->name, +			 mpu_addr, bank[1], addr[1], len, +			 wr_pr_debug_begin(data, len, read), +			 wr_pr_debug_end(read)); +	} +#endif + +	return res; +} + +int mpu_memory_write_unaligned(struct inv_mpu_state *st, u16 key, int len, +								u8 const *d) +{ +	u32 addr; +	int start, end; +	int len1, len2; +	int result = 0; + +	if (len > MPU_MEM_BANK_SIZE) +		return -EINVAL; +	addr = inv_dmp_get_address(key); +	if (addr > MPU6XXX_MAX_MPU_MEM) +		return -EINVAL; + +	start = (addr >> 8); +	end   = ((addr + len - 1) >> 8); +	if (start == end) { +		result = mpu_memory_write(st, st->i2c_addr, addr, len, d); +	} else { +		end <<= 8; +		len1 = end - addr; +		len2 = len - len1; +		result = mpu_memory_write(st, st->i2c_addr, addr, len1, d); +		result |= mpu_memory_write(st, st->i2c_addr, end, len2, +								d + len1); +	} + +	return result; +} + +/** + *  index_of_key()- Inverse lookup of the index of an MPL product key . + *  @key: the MPL product indentifier also referred to as 'key'. + */ +static short index_of_key(u16 key) +{ +	int i; +	for (i = 0; i < NUM_OF_PROD_REVS; i++) +		if (prod_rev_map[i].mpl_product_key == key) +			return (short)i; +	return -EINVAL; +} + +int inv_get_silicon_rev_mpu6500(struct inv_mpu_state *st) +{ +	struct inv_chip_info_s *chip_info = &st->chip_info; +	int result; +	u8 whoami, sw_rev; + +	result = inv_i2c_read(st, REG_WHOAMI, 1, &whoami); +	if (result) +		return result; +	if (whoami != MPU6500_ID && whoami != MPU9250_ID && +			whoami != MPU9350_ID && whoami != MPU6515_ID) +		return -EINVAL; + +	/*memory read need more time after power up */ +	msleep(POWER_UP_TIME); +	result = mpu_memory_read(st, st->i2c_addr, +			MPU6500_MEM_REV_ADDR, 1, &sw_rev); +	sw_rev &= INV_MPU_REV_MASK; +	if (result) +		return result; +	if (sw_rev != 0) +		return -EINVAL; +	/* these values are place holders and not real values */ +	chip_info->product_id = MPU6500_PRODUCT_REVISION; +	chip_info->product_revision = MPU6500_PRODUCT_REVISION; +	chip_info->silicon_revision = MPU6500_PRODUCT_REVISION; +	chip_info->software_revision = sw_rev; +	chip_info->gyro_sens_trim = DEFAULT_GYRO_TRIM; +	chip_info->accel_sens_trim = DEFAULT_ACCEL_TRIM; +	chip_info->multi = 1; + +	return 0; +} + +int inv_get_silicon_rev_mpu6050(struct inv_mpu_state *st) +{ +	int result; +	struct inv_reg_map_s *reg; +	u8 prod_ver = 0x00, prod_rev = 0x00; +	struct prod_rev_map_t *p_rev; +	u8 bank = +	    (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0); +	u16 mem_addr = ((bank << 8) | MEM_ADDR_PROD_REV); +	u16 key; +	u8 regs[5]; +	u16 sw_rev; +	short index; +	struct inv_chip_info_s *chip_info = &st->chip_info; +	reg = &st->reg; + +	result = inv_i2c_read(st, REG_PRODUCT_ID, 1, &prod_ver); +	if (result) +		return result; +	prod_ver &= 0xf; +	/*memory read need more time after power up */ +	msleep(POWER_UP_TIME); +	result = mpu_memory_read(st, st->i2c_addr, mem_addr, 1, &prod_rev); +	if (result) +		return result; +	prod_rev >>= 2; +	/* clean the prefetch and cfg user bank bits */ +	result = inv_i2c_single_write(st, reg->bank_sel, 0); +	if (result) +		return result; +	/* get the software-product version, read from XA_OFFS_L */ +	result = inv_i2c_read(st, REG_XA_OFFS_L_TC, +				SOFT_PROD_VER_BYTES, regs); +	if (result) +		return result; + +	sw_rev = (regs[4] & 0x01) << 2 |	/* 0x0b, bit 0 */ +		 (regs[2] & 0x01) << 1 |	/* 0x09, bit 0 */ +		 (regs[0] & 0x01);		/* 0x07, bit 0 */ +	/* if 0, use the product key to determine the type of part */ +	if (sw_rev == 0) { +		key = MPL_PROD_KEY(prod_ver, prod_rev); +		if (key == 0) +			return -EINVAL; +		index = index_of_key(key); +		if (index < 0 || index >= NUM_OF_PROD_REVS) +			return -EINVAL; +		/* check MPL is compiled for this device */ +		if (prod_rev_map[index].silicon_rev != MPU_SILICON_REV_B1) +			return -EINVAL; +		p_rev = (struct prod_rev_map_t *)&prod_rev_map[index]; +	/* if valid, use the software product key */ +	} else if (sw_rev < ARRAY_SIZE(sw_rev_map)) { +		p_rev = (struct prod_rev_map_t *)&sw_rev_map[sw_rev]; +	} else { +		return -EINVAL; +	} +	chip_info->product_id = prod_ver; +	chip_info->product_revision = prod_rev; +	chip_info->silicon_revision = p_rev->silicon_rev; +	chip_info->software_revision = sw_rev; +	chip_info->gyro_sens_trim = p_rev->gyro_trim; +	chip_info->accel_sens_trim = p_rev->accel_trim; +	if (chip_info->accel_sens_trim == 0) +		chip_info->accel_sens_trim = DEFAULT_ACCEL_TRIM; +	chip_info->multi = DEFAULT_ACCEL_TRIM / chip_info->accel_sens_trim; +	if (chip_info->multi != 1) +		pr_info("multi is %d\n", chip_info->multi); +	return result; +} + +/** + *  read_accel_hw_self_test_prod_shift()- read the accelerometer hardware + *                                         self-test bias shift calculated + *                                         during final production test and + *                                         stored in chip non-volatile memory. + *  @st:  main data structure. + *  @st_prod:   A pointer to an array of 3 elements to hold the values + *              for production hardware self-test bias shifts returned to the + *              user. + *  @accel_sens: accel sensitivity. + */ +static int read_accel_hw_self_test_prod_shift(struct inv_mpu_state *st, +					int *st_prod, int *accel_sens) +{ +	u8 regs[4]; +	u8 shift_code[3]; +	int result, i; + +	for (i = 0; i < 3; i++) +		st_prod[i] = 0; + +	result = inv_i2c_read(st, REG_ST_GCT_X, ARRAY_SIZE(regs), regs); +	if (result) +		return result; +	if ((0 == regs[0])  && (0 == regs[1]) && +	    (0 == regs[2]) && (0 == regs[3])) +		return -EINVAL; +	shift_code[X] = ((regs[0] & 0xE0) >> 3) | ((regs[3] & 0x30) >> 4); +	shift_code[Y] = ((regs[1] & 0xE0) >> 3) | ((regs[3] & 0x0C) >> 2); +	shift_code[Z] = ((regs[2] & 0xE0) >> 3) |  (regs[3] & 0x03); +	for (i = 0; i < 3; i++) +		if (shift_code[i] != 0) +			st_prod[i] = accel_sens[i] * +					accel_st_tb[shift_code[i] - 1]; + +	return 0; +} + +/** +* inv_check_accel_self_test()- check accel self test. this function returns +*                              zero as success. A non-zero return value +*                              indicates failure in self test. +*  @*st: main data structure. +*  @*reg_avg: average value of normal test. +*  @*st_avg:  average value of self test +*/ +static int inv_check_accel_self_test(struct inv_mpu_state *st, +						int *reg_avg, int *st_avg){ +	int gravity, j, ret_val; +	int tmp; +	int st_shift_prod[THREE_AXIS], st_shift_cust[THREE_AXIS]; +	int st_shift_ratio[THREE_AXIS]; +	int accel_sens[THREE_AXIS]; + +	if (st->chip_info.software_revision < DEF_OLDEST_SUPP_SW_REV && +	    st->chip_info.product_revision < DEF_OLDEST_SUPP_PROD_REV) +		return 0; +	ret_val = 0; +	tmp = DEF_ST_SCALE * DEF_ST_PRECISION / DEF_ST_ACCEL_FS_MG; +	for (j = 0; j < 3; j++) +		accel_sens[j] = tmp; + +	if (MPL_PROD_KEY(st->chip_info.product_id, +			 st->chip_info.product_revision) == +	    MPU_PRODUCT_KEY_B1_E1_5) { +		/* half sensitivity Z accelerometer parts */ +		accel_sens[Z] /= 2; +	} else { +		/* half sensitivity X, Y, Z accelerometer parts */ +		accel_sens[X] /= st->chip_info.multi; +		accel_sens[Y] /= st->chip_info.multi; +		accel_sens[Z] /= st->chip_info.multi; +	} +	gravity = accel_sens[Z]; +	ret_val = read_accel_hw_self_test_prod_shift(st, st_shift_prod, +							accel_sens); +	if (ret_val) +		return ret_val; + +	for (j = 0; j < 3; j++) { +		st_shift_cust[j] = abs(reg_avg[j] - st_avg[j]); +		if (st_shift_prod[j]) { +			tmp = st_shift_prod[j] / DEF_ST_PRECISION; +			st_shift_ratio[j] = abs(st_shift_cust[j] / tmp +				- DEF_ST_PRECISION); +			if (st_shift_ratio[j] > DEF_ACCEL_ST_SHIFT_DELTA) +				ret_val = 1; +		} else { +			if (st_shift_cust[j] < +				DEF_ACCEL_ST_SHIFT_MIN * gravity) +				ret_val = 1; +			if (st_shift_cust[j] > +				DEF_ACCEL_ST_SHIFT_MAX * gravity) +				ret_val = 1; +		} +	} + +	return ret_val; +} + +/** +* inv_check_3500_gyro_self_test() check gyro self test. this function returns +*                                 zero as success. A non-zero return value +*                                 indicates failure in self test. +*  @*st: main data structure. +*  @*reg_avg: average value of normal test. +*  @*st_avg:  average value of self test +*/ + +static int inv_check_3500_gyro_self_test(struct inv_mpu_state *st, +						int *reg_avg, int *st_avg){ +	int result; +	int gst[3], ret_val; +	int gst_otp[3], i; +	u8 st_code[THREE_AXIS]; +	ret_val = 0; + +	for (i = 0; i < 3; i++) +		gst[i] = st_avg[i] - reg_avg[i]; +	result = inv_i2c_read(st, REG_3500_OTP, THREE_AXIS, st_code); +	if (result) +		return result; +	gst_otp[0] = 0; +	gst_otp[1] = 0; +	gst_otp[2] = 0; +	for (i = 0; i < 3; i++) { +		if (st_code[i] != 0) +			gst_otp[i] = gyro_3500_st_tb[st_code[i] - 1]; +	} +	/* check self test value passing criterion. Using the DEF_ST_TOR +	 * for certain degree of tolerance */ +	for (i = 0; i < 3; i++) { +		if (gst_otp[i] == 0) { +			if (abs(gst[i]) * DEF_ST_TOR < DEF_ST_OTP0_THRESH * +							DEF_ST_PRECISION * +							DEF_GYRO_SCALE) +				ret_val |= (1 << i); +		} else { +			if (abs(gst[i]/gst_otp[i] - DEF_ST_PRECISION) > +					DEF_GYRO_CT_SHIFT_DELTA) +				ret_val |= (1 << i); +		} +	} +	/* check for absolute value passing criterion. Using DEF_ST_TOR +	 * for certain degree of tolerance */ +	for (i = 0; i < 3; i++) { +		if (abs(reg_avg[i]) > DEF_ST_TOR * DEF_ST_ABS_THRESH * +		    DEF_ST_PRECISION * DEF_GYRO_SCALE) +			ret_val |= (1 << i); +	} + +	return ret_val; +} + +/** +* inv_check_6050_gyro_self_test() - check 6050 gyro self test. this function +*                                   returns zero as success. A non-zero return +*                                   value indicates failure in self test. +*  @*st: main data structure. +*  @*reg_avg: average value of normal test. +*  @*st_avg:  average value of self test +*/ +static int inv_check_6050_gyro_self_test(struct inv_mpu_state *st, +						int *reg_avg, int *st_avg){ +	int result; +	int ret_val; +	int st_shift_prod[3], st_shift_cust[3], st_shift_ratio[3], i; +	u8 regs[3]; + +	if (st->chip_info.software_revision < DEF_OLDEST_SUPP_SW_REV && +	    st->chip_info.product_revision < DEF_OLDEST_SUPP_PROD_REV) +		return 0; + +	ret_val = 0; +	result = inv_i2c_read(st, REG_ST_GCT_X, 3, regs); +	if (result) +		return result; +	regs[X] &= 0x1f; +	regs[Y] &= 0x1f; +	regs[Z] &= 0x1f; +	for (i = 0; i < 3; i++) { +		if (regs[i] != 0) +			st_shift_prod[i] = gyro_6050_st_tb[regs[i] - 1]; +		else +			st_shift_prod[i] = 0; +	} +	st_shift_prod[1] = -st_shift_prod[1]; + +	for (i = 0; i < 3; i++) { +		st_shift_cust[i] =  st_avg[i] - reg_avg[i]; +		if (st_shift_prod[i]) { +			st_shift_ratio[i] = abs(st_shift_cust[i] / +				st_shift_prod[i] - DEF_ST_PRECISION); +			if (st_shift_ratio[i] > DEF_GYRO_CT_SHIFT_DELTA) +				ret_val = 1; +		} else { +			if (st_shift_cust[i] < DEF_ST_PRECISION * +				DEF_GYRO_CT_SHIFT_MIN * DEF_SELFTEST_GYRO_SENS) +				ret_val = 1; +			if (st_shift_cust[i] > DEF_ST_PRECISION * +				DEF_GYRO_CT_SHIFT_MAX * DEF_SELFTEST_GYRO_SENS) +				ret_val = 1; +		} +	} +	/* check for absolute value passing criterion. Using DEF_ST_TOR +	 * for certain degree of tolerance */ +	for (i = 0; i < 3; i++) +		if (abs(reg_avg[i]) > DEF_ST_TOR * DEF_ST_ABS_THRESH * +		    DEF_ST_PRECISION * DEF_GYRO_SCALE) +			ret_val = 1; + +	return ret_val; +} + +/** +* inv_check_6500_gyro_self_test() - check 6500 gyro self test. this function +*                                   returns zero as success. A non-zero return +*                                   value indicates failure in self test. +*  @*st: main data structure. +*  @*reg_avg: average value of normal test. +*  @*st_avg:  average value of self test +*/ +static int inv_check_6500_gyro_self_test(struct inv_mpu_state *st, +						int *reg_avg, int *st_avg) { +	u8 regs[3]; +	int ret_val, result; +	int otp_value_zero = 0; +	int st_shift_prod[3], st_shift_cust[3], i; + +	ret_val = 0; +	result = inv_i2c_read(st, REG_6500_XG_ST_DATA, 3, regs); +	if (result) +		return result; +	pr_debug("%s self_test gyro shift_code - %02x %02x %02x\n", +		 st->hw->name, regs[0], regs[1], regs[2]); + +	for (i = 0; i < 3; i++) { +		if (regs[i] != 0) { +			st_shift_prod[i] = mpu_6500_st_tb[regs[i] - 1]; +		} else { +			st_shift_prod[i] = 0; +			otp_value_zero = 1; +		} +	} +	pr_debug("%s self_test gyro st_shift_prod - %+d %+d %+d\n", +		 st->hw->name, st_shift_prod[0], st_shift_prod[1], +		 st_shift_prod[2]); + +	for (i = 0; i < 3; i++) { +		st_shift_cust[i] = st_avg[i] - reg_avg[i]; +		if (!otp_value_zero) { +			/* Self Test Pass/Fail Criteria A */ +			if (st_shift_cust[i] < DEF_6500_GYRO_CT_SHIFT_DELTA +						* st_shift_prod[i]) +					ret_val = 1; +		} else { +			/* Self Test Pass/Fail Criteria B */ +			if (st_shift_cust[i] < DEF_GYRO_ST_AL * +						DEF_SELFTEST_GYRO_SENS * +						DEF_ST_PRECISION) +				ret_val = 1; +		} +	} +	pr_debug("%s self_test gyro st_shift_cust - %+d %+d %+d\n", +		 st->hw->name, st_shift_cust[0], st_shift_cust[1], +		 st_shift_cust[2]); + +	if (ret_val == 0) { +		/* Self Test Pass/Fail Criteria C */ +		for (i = 0; i < 3; i++) +			if (abs(reg_avg[i]) > DEF_GYRO_OFFSET_MAX * +						DEF_SELFTEST_GYRO_SENS * +						DEF_ST_PRECISION) +				ret_val = 1; +	} + +	return ret_val; +} + +/** +* inv_check_6500_accel_self_test() - check 6500 accel self test. this function +*                                   returns zero as success. A non-zero return +*                                   value indicates failure in self test. +*  @*st: main data structure. +*  @*reg_avg: average value of normal test. +*  @*st_avg:  average value of self test +*/ +static int inv_check_6500_accel_self_test(struct inv_mpu_state *st, +						int *reg_avg, int *st_avg) { +	int ret_val, result; +	int st_shift_prod[3], st_shift_cust[3], st_shift_ratio[3], i; +	u8 regs[3]; +	int otp_value_zero = 0; + +#define ACCEL_ST_AL_MIN ((DEF_ACCEL_ST_AL_MIN * DEF_ST_SCALE \ +				 / DEF_ST_6500_ACCEL_FS_MG) * DEF_ST_PRECISION) +#define ACCEL_ST_AL_MAX ((DEF_ACCEL_ST_AL_MAX * DEF_ST_SCALE \ +				 / DEF_ST_6500_ACCEL_FS_MG) * DEF_ST_PRECISION) + +	ret_val = 0; +	result = inv_i2c_read(st, REG_6500_XA_ST_DATA, 3, regs); +	if (result) +		return result; +	pr_debug("%s self_test accel shift_code - %02x %02x %02x\n", +		 st->hw->name, regs[0], regs[1], regs[2]); + +	for (i = 0; i < 3; i++) { +		if (regs[i] != 0) { +			st_shift_prod[i] = mpu_6500_st_tb[regs[i] - 1]; +		} else { +			st_shift_prod[i] = 0; +			otp_value_zero = 1; +		} +	} +	pr_debug("%s self_test accel st_shift_prod - %+d %+d %+d\n", +		 st->hw->name, st_shift_prod[0], st_shift_prod[1], +		 st_shift_prod[2]); + +	if (!otp_value_zero) { +		/* Self Test Pass/Fail Criteria A */ +		for (i = 0; i < 3; i++) { +			st_shift_cust[i] = st_avg[i] - reg_avg[i]; +			st_shift_ratio[i] = abs(st_shift_cust[i] / +					st_shift_prod[i] - DEF_ST_PRECISION); +			if (st_shift_ratio[i] > DEF_6500_ACCEL_ST_SHIFT_DELTA) +				ret_val = 1; +		} +	} else { +		/* Self Test Pass/Fail Criteria B */ +		for (i = 0; i < 3; i++) { +			st_shift_cust[i] = abs(st_avg[i] - reg_avg[i]); +			if (st_shift_cust[i] < ACCEL_ST_AL_MIN || +					st_shift_cust[i] > ACCEL_ST_AL_MAX) +				ret_val = 1; +		} +	} +	pr_debug("%s self_test accel st_shift_cust - %+d %+d %+d\n", +		 st->hw->name, st_shift_cust[0], st_shift_cust[1], +		 st_shift_cust[2]); + +	return ret_val; +} + +/* + *  inv_do_test() - do the actual test of self testing + */ +static int inv_do_test(struct inv_mpu_state *st, int self_test_flag, +		int *gyro_result, int *accel_result) +{ +	struct inv_reg_map_s *reg; +	int result, i, j, packet_size; +	u8 data[BYTES_PER_SENSOR * 2], d; +	bool has_accel; +	int fifo_count, packet_count, ind, s; + +	reg = &st->reg; +	has_accel = (st->chip_type != INV_ITG3500); +	if (has_accel) +		packet_size = BYTES_PER_SENSOR * 2; +	else +		packet_size = BYTES_PER_SENSOR; + +	result = inv_i2c_single_write(st, reg->int_enable, 0); +	if (result) +		return result; +	/* disable the sensor output to FIFO */ +	result = inv_i2c_single_write(st, reg->fifo_en, 0); +	if (result) +		return result; +	/* disable fifo reading */ +	result = inv_i2c_single_write(st, reg->user_ctrl, 0); +	if (result) +		return result; +	/* clear FIFO */ +	result = inv_i2c_single_write(st, reg->user_ctrl, BIT_FIFO_RST); +	if (result) +		return result; +	/* setup parameters */ +	result = inv_i2c_single_write(st, reg->lpf, INV_FILTER_98HZ); +	if (result) +		return result; + +	if (INV_MPU6500 == st->chip_type) { +		/* config accel LPF register for MPU6500 */ +		result = inv_i2c_single_write(st, REG_6500_ACCEL_CONFIG2, +						DEF_ST_MPU6500_ACCEL_LPF | +						BIT_FIFO_SIZE_1K); +		if (result) +			return result; +	} + +	result = inv_i2c_single_write(st, reg->sample_rate_div, +			DEF_SELFTEST_SAMPLE_RATE); +	if (result) +		return result; +	/* wait for the sampling rate change to stabilize */ +	mdelay(INV_MPU_SAMPLE_RATE_CHANGE_STABLE); +	result = inv_i2c_single_write(st, reg->gyro_config, +		self_test_flag | DEF_SELFTEST_GYRO_FS); +	if (result) +		return result; +	if (has_accel) { +		if (INV_MPU6500 == st->chip_type) +			d = DEF_SELFTEST_6500_ACCEL_FS; +		else +			d = DEF_SELFTEST_ACCEL_FS; +		d |= self_test_flag; +		result = inv_i2c_single_write(st, reg->accel_config, d); +		if (result) +			return result; +	} +	/* wait for the output to get stable */ +	if (self_test_flag) { +		if (INV_MPU6500 == st->chip_type) +			msleep(DEF_ST_6500_STABLE_TIME); +		else +			msleep(DEF_ST_STABLE_TIME); +	} + +	/* enable FIFO reading */ +	result = inv_i2c_single_write(st, reg->user_ctrl, BIT_FIFO_EN); +	if (result) +		return result; +	/* enable sensor output to FIFO */ +	if (has_accel) +		d = BITS_GYRO_OUT | BIT_ACCEL_OUT; +	else +		d = BITS_GYRO_OUT; +	for (i = 0; i < THREE_AXIS; i++) { +		gyro_result[i] = 0; +		accel_result[i] = 0; +	} +	s = 0; +	while (s < st->self_test.samples) { +		result = inv_i2c_single_write(st, reg->fifo_en, d); +		if (result) +			return result; +		mdelay(DEF_GYRO_WAIT_TIME); +		result = inv_i2c_single_write(st, reg->fifo_en, 0); +		if (result) +			return result; + +		result = inv_i2c_read(st, reg->fifo_count_h, +					FIFO_COUNT_BYTE, data); +		if (result) +			return result; +		fifo_count = be16_to_cpup((__be16 *)(&data[0])); +		pr_debug("%s self_test fifo_count - %d\n", +			 st->hw->name, fifo_count); +		packet_count = fifo_count / packet_size; +		i = 0; +		while ((i < packet_count) && (s < st->self_test.samples)) { +			short vals[3]; +			result = inv_i2c_read(st, reg->fifo_r_w, +				packet_size, data); +			if (result) +				return result; +			ind = 0; +			if (has_accel) { +				for (j = 0; j < THREE_AXIS; j++) { +					vals[j] = (short)be16_to_cpup( +					    (__be16 *)(&data[ind + 2 * j])); +					accel_result[j] += vals[j]; +				} +				ind += BYTES_PER_SENSOR; +				pr_debug( +				    "%s self_test accel data - %d %+d %+d %+d", +				    st->hw->name, s, vals[0], vals[1], vals[2]); +			} + +			for (j = 0; j < THREE_AXIS; j++) { +				vals[j] = (short)be16_to_cpup( +					(__be16 *)(&data[ind + 2 * j])); +				gyro_result[j] += vals[j]; +			} +			pr_debug("%s self_test gyro data - %d %+d %+d %+d", +				 st->hw->name, s, vals[0], vals[1], vals[2]); + +			s++; +			i++; +		} +	} + +	if (has_accel) { +		for (j = 0; j < THREE_AXIS; j++) { +			accel_result[j] = accel_result[j] / s; +			accel_result[j] *= DEF_ST_PRECISION; +		} +	} +	for (j = 0; j < THREE_AXIS; j++) { +		gyro_result[j] = gyro_result[j] / s; +		gyro_result[j] *= DEF_ST_PRECISION; +	} + +	return 0; +} + +/* + *  inv_recover_setting() recover the old settings after everything is done + */ +static void inv_recover_setting(struct inv_mpu_state *st) +{ +	struct inv_reg_map_s *reg; +	int data; + +	reg = &st->reg; +	inv_i2c_single_write(st, reg->gyro_config, +			     st->chip_config.fsr << GYRO_CONFIG_FSR_SHIFT); +	inv_i2c_single_write(st, reg->lpf, st->chip_config.lpf); +	data = ONE_K_HZ/st->chip_config.fifo_rate - 1; +	inv_i2c_single_write(st, reg->sample_rate_div, data); +	/* wait for the sampling rate change to stabilize */ +	mdelay(INV_MPU_SAMPLE_RATE_CHANGE_STABLE); +	if (INV_ITG3500 != st->chip_type) { +		inv_i2c_single_write(st, reg->accel_config, +				     (st->chip_config.accel_fs << +				     ACCEL_CONFIG_FSR_SHIFT)); +	} +	inv_reset_offset_reg(st, false); +	st->switch_gyro_engine(st, false); +	st->switch_accel_engine(st, false); +	st->set_power_state(st, false); +} + + +static int inv_power_up_self_test(struct inv_mpu_state *st) +{ +	int result; + +	result = st->set_power_state(st, true); +	if (result) +		return result; +	result = st->switch_accel_engine(st, true); +	if (result) +		return result; +	result = st->switch_gyro_engine(st, true); +	if (result) +		return result; + +	return 0; +} + +/* + *  inv_hw_self_test() - main function to do hardware self test + */ +int inv_hw_self_test(struct inv_mpu_state *st) +{ +	int result; +	int gyro_bias_st[THREE_AXIS], gyro_bias_regular[THREE_AXIS]; +	int accel_bias_st[THREE_AXIS], accel_bias_regular[THREE_AXIS]; +	int test_times, i; +	char compass_result, accel_result, gyro_result; + +	result = inv_power_up_self_test(st); +	if (result) +		return result; +	result = inv_reset_offset_reg(st, true); +	if (result) +		return result; +	compass_result = 0; +	accel_result = 0; +	gyro_result = 0; +	test_times = DEF_ST_TRY_TIMES; +	while (test_times > 0) { +		result = inv_do_test(st, 0, gyro_bias_regular, +			accel_bias_regular); +		if (result == -EAGAIN) +			test_times--; +		else +			test_times = 0; +	} +	if (result) +		goto test_fail; +	pr_debug("%s self_test accel bias_regular - %+d %+d %+d\n", +		 st->hw->name, accel_bias_regular[0], +		 accel_bias_regular[1], accel_bias_regular[2]); +	pr_debug("%s self_test gyro bias_regular - %+d %+d %+d\n", +		 st->hw->name, gyro_bias_regular[0], gyro_bias_regular[1], +		 gyro_bias_regular[2]); + +	for (i = 0; i < 3; i++) { +		st->gyro_bias[i] = gyro_bias_regular[i]; +		st->accel_bias[i] = accel_bias_regular[i]; +	} + +	test_times = DEF_ST_TRY_TIMES; +	while (test_times > 0) { +		result = inv_do_test(st, BITS_SELF_TEST_EN, gyro_bias_st, +					accel_bias_st); +		if (result == -EAGAIN) +			test_times--; +		else +			break; +	} +	if (result) +		goto test_fail; +	pr_debug("%s self_test accel bias_st - %+d %+d %+d\n", +		 st->hw->name, accel_bias_st[0], accel_bias_st[1], +		 accel_bias_st[2]); +	pr_debug("%s self_test gyro bias_st - %+d %+d %+d\n", +		 st->hw->name, gyro_bias_st[0], gyro_bias_st[1], +		 gyro_bias_st[2]); + +	if (st->chip_type == INV_ITG3500) { +		gyro_result = !inv_check_3500_gyro_self_test(st, +			gyro_bias_regular, gyro_bias_st); +	} else { +		if (st->chip_config.has_compass) +			compass_result = !st->slave_compass->self_test(st); + +		 if (INV_MPU6050 == st->chip_type) { +			accel_result = !inv_check_accel_self_test(st, +				accel_bias_regular, accel_bias_st); +			gyro_result = !inv_check_6050_gyro_self_test(st, +				gyro_bias_regular, gyro_bias_st); +		} else if (INV_MPU6500 == st->chip_type) { +			accel_result = !inv_check_6500_accel_self_test(st, +				accel_bias_regular, accel_bias_st); +			gyro_result = !inv_check_6500_gyro_self_test(st, +				gyro_bias_regular, gyro_bias_st); +		} +	} + +test_fail: +	inv_recover_setting(st); + +	return (compass_result << DEF_ST_COMPASS_RESULT_SHIFT) | +		(accel_result << DEF_ST_ACCEL_RESULT_SHIFT) | gyro_result; +} + +static int inv_load_firmware(struct inv_mpu_state *st, +	u8 *data, int size) +{ +	int bank, write_size; +	int result; +	u16 memaddr; + +	/* first bank start at MPU_DMP_LOAD_START */ +	write_size = MPU_MEM_BANK_SIZE - MPU_DMP_LOAD_START; +	memaddr = MPU_DMP_LOAD_START; +	result = mem_w(memaddr, write_size, data); +	if (result) +		return result; +	size -= write_size; +	data += write_size; + +	/* Write and verify memory */ +	for (bank = 1; size > 0; bank++, size -= write_size, +				data += write_size) { +		if (size > MPU_MEM_BANK_SIZE) +			write_size = MPU_MEM_BANK_SIZE; +		else +			write_size = size; + +		memaddr = ((bank << 8) | 0x00); + +		result = mem_w(memaddr, write_size, data); +		if (result) +			return result; +	} +	return 0; +} + +static int inv_verify_firmware(struct inv_mpu_state *st, +	u8 *data, int size) +{ +	int bank, write_size; +	int result; +	u16 memaddr; +	u8 firmware[MPU_MEM_BANK_SIZE]; + +	/* Write and verify memory */ +	write_size = MPU_MEM_BANK_SIZE - MPU_DMP_LOAD_START; +	size -= write_size; +	data += write_size; +	for (bank = 1; size > 0; bank++, +		size -= write_size, +		data += write_size) { +		if (size > MPU_MEM_BANK_SIZE) +			write_size = MPU_MEM_BANK_SIZE; +		else +			write_size = size; + +		memaddr = ((bank << 8) | 0x00); +		result = mpu_memory_read(st, +			st->i2c_addr, memaddr, write_size, firmware); +		if (result) +			return result; +		if (0 != memcmp(firmware, data, write_size)) +			return -EINVAL; +	} +	return 0; +} + +int inv_enable_pedometer_interrupt(struct inv_mpu_state *st, bool en) +{ +	u8 reg[3]; + +	if (en) { +		reg[0] = 0xf4; +		reg[1] = 0x44; +		reg[2] = 0xf1; + +	} else { +		reg[0] = 0xf1; +		reg[1] = 0xf1; +		reg[2] = 0xf1; +	} + +	return mem_w_key(KEY_CFG_PED_INT, ARRAY_SIZE(reg), reg); +} + +int inv_read_pedometer_counter(struct inv_mpu_state *st) +{ +	int result; +	u8 d[4]; +	u32 last_step_counter, curr_counter; + +	result = mpu_memory_read(st, st->i2c_addr, +			inv_dmp_get_address(KEY_D_STPDET_TIMESTAMP), 4, d); +	if (result) +		return result; +	last_step_counter = (u32)be32_to_cpup((__be32 *)(d)); + +	result = mpu_memory_read(st, st->i2c_addr, +			inv_dmp_get_address(KEY_DMP_RUN_CNTR), 4, d); +	if (result) +		return result; +	curr_counter = (u32)be32_to_cpup((__be32 *)(d)); +	if (0 != last_step_counter) +		st->ped.last_step_time = get_time_ns() - +			((u64)(curr_counter - last_step_counter)) * +			DMP_INTERVAL_INIT; + +	return 0; +} + +int inv_enable_pedometer(struct inv_mpu_state *st, bool en) +{ +	u8 d[1]; + +	if (en) +		d[0] = 0xf1; +	else +		d[0] = 0xff; + +	return mem_w_key(KEY_CFG_PED_ENABLE, ARRAY_SIZE(d), d); +} + +int inv_get_pedometer_steps(struct inv_mpu_state *st, u32 *steps) +{ +	u8 d[4]; +	int result; + +	result = mpu_memory_read(st, st->i2c_addr, +			inv_dmp_get_address(KEY_D_PEDSTD_STEPCTR), 4, d); +	*steps = (u32)be32_to_cpup((__be32 *)(d)); + +	return result; +} + +int inv_get_pedometer_time(struct inv_mpu_state *st, u32 *time) +{ +	u8 d[4]; +	int result; + +	result = mpu_memory_read(st, st->i2c_addr, +			inv_dmp_get_address(KEY_D_PEDSTD_TIMECTR), 4, d); +	*time = (u32)be32_to_cpup((__be32 *)(d)); + +	return result; +} + +int inv_set_display_orient_interrupt_dmp(struct inv_mpu_state *st, bool on) +{ +	int r; +	u8  rn[] = {0xf4, 0x41}; +	u8  rf[] = {0xd8, 0xd8}; + +	if (on) +		r = mem_w_key(KEY_CFG_DISPLAY_ORIENT_INT, ARRAY_SIZE(rn), rn); +	else +		r = mem_w_key(KEY_CFG_DISPLAY_ORIENT_INT, ARRAY_SIZE(rf), rf); + +	return r; +} + +static int inv_set_tap_interrupt_dmp(struct inv_mpu_state *st, u8 on) +{ +	int result; +	u16 d; + +	if (on) +		d = 192; +	else +		d = 128; + +	result = inv_write_2bytes(st, KEY_DMP_TAP_GATE, d); + +	return result; +} + +/* + * inv_set_tap_threshold_dmp(): + * Sets the tap threshold in the dmp + * Simultaneously sets secondary tap threshold to help correct the tap + * direction for soft taps. + */ +int inv_set_tap_threshold_dmp(struct inv_mpu_state *st, u16 threshold) +{ +	int result; +	int sampleDivider; +	int scaledThreshold; +	u32 dmpThreshold; +	u8 sample_div; +	const u32  accel_sens = (0x20000000 / 0x00010000); + +	if (threshold > (1 << 15)) +		return -EINVAL; +	sample_div = st->sample_divider; + +	sampleDivider = (1 + sample_div); +	/* Scale factor corresponds linearly using +	* 0  : 0 +	* 25 : 0.0250  g/ms +	* 50 : 0.0500  g/ms +	* 100: 1.0000  g/ms +	* 200: 2.0000  g/ms +	* 400: 4.0000  g/ms +	* 800: 8.0000  g/ms +	*/ +	/*multiply by 1000 to avoid floating point 1000/1000*/ +	scaledThreshold = threshold; +	/* Convert to per sample */ +	scaledThreshold *= sampleDivider; + +	/* Scale to DMP 16 bit value */ +	if (accel_sens != 0) +		dmpThreshold = (u32)(scaledThreshold * accel_sens); +	else +		return -EINVAL; +	dmpThreshold = dmpThreshold / DMP_PRECISION; +	result = inv_write_2bytes(st, KEY_DMP_TAP_THR_Z, dmpThreshold); +	if (result) +		return result; +	result = inv_write_2bytes(st, KEY_DMP_TAP_PREV_JERK_Z, +						dmpThreshold * 3 / 4); + +	return result; +} + + +/* + * inv_set_min_taps_dmp(): + * Indicates the minimum number of consecutive taps required + * before the DMP will generate an interrupt. + */ +int inv_set_min_taps_dmp(struct inv_mpu_state *st, u16 min_taps) +{ +	u8 result; + +	/* check if any spurious bit other the ones expected are set */ +	if ((min_taps > DMP_MAX_MIN_TAPS) || (min_taps < 1)) +		return -EINVAL; + +	/* DMP tap count is zero-based. So single-tap is 0. +	   Furthermore, DMP code checks for tap_count > min_taps. +	   So we have to do minus 2 here. +	   For example, if the user expects any single tap will generate an +	   interrupt, (s)he will call inv_set_min_taps_dmp(1). +	   When DMP gets a single tap, tap_count = 0. To get +	   tap_count > min_taps, we have to decrement min_taps by 2 to -1. */ +	result = inv_write_2bytes(st, KEY_DMP_TAP_MIN_TAPS, (u16)(min_taps-2)); + +	return result; +} + +/* + * inv_set_tap_time_dmp(): + * Determines how long after a tap the DMP requires before + * another tap can be registered. + */ +int  inv_set_tap_time_dmp(struct inv_mpu_state *st, u16 time) +{ +	int result; +	u16 dmpTime; +	u8 sampleDivider; + +	sampleDivider = st->sample_divider; +	sampleDivider++; + +	/* 60 ms minimum time added */ +	dmpTime = ((time) / sampleDivider); +	result = inv_write_2bytes(st, KEY_DMP_TAPW_MIN, dmpTime); + +	return result; +} + +/* + * inv_set_multiple_tap_time_dmp(): + * Determines how close together consecutive taps must occur + * to be considered double/triple taps. + */ +static int inv_set_multiple_tap_time_dmp(struct inv_mpu_state *st, u32 time) +{ +	int result; +	u16 dmpTime; +	u8 sampleDivider; + +	sampleDivider = st->sample_divider; +	sampleDivider++; + +	/* 60 ms minimum time added */ +	dmpTime = ((time) / sampleDivider); +	result = inv_write_2bytes(st, KEY_DMP_TAP_NEXT_TAP_THRES, dmpTime); + +	return result; +} + +int inv_q30_mult(int a, int b) +{ +	u64 temp; +	int result; + +	temp = (u64)a * b; +	result = (int)(temp >> DMP_MULTI_SHIFT); + +	return result; +} + +static u16 inv_row_2_scale(const s8 *row) +{ +	u16 b; + +	if (row[0] > 0) +		b = 0; +	else if (row[0] < 0) +		b = 4; +	else if (row[1] > 0) +		b = 1; +	else if (row[1] < 0) +		b = 5; +	else if (row[2] > 0) +		b = 2; +	else if (row[2] < 0) +		b = 6; +	else +		b = 7; + +	return b; +} + +/** Converts an orientation matrix made up of 0,+1,and -1 to a scalar +*	representation. +* @param[in] mtx Orientation matrix to convert to a scalar. +* @return Description of orientation matrix. The lowest 2 bits (0 and 1) +* represent the column the one is on for the +* first row, with the bit number 2 being the sign. The next 2 bits +* (3 and 4) represent +* the column the one is on for the second row with bit number 5 being +* the sign. +* The next 2 bits (6 and 7) represent the column the one is on for the +* third row with +* bit number 8 being the sign. In binary the identity matrix would therefor +* be: 010_001_000 or 0x88 in hex. +*/ +static u16 inv_orientation_matrix_to_scaler(const signed char *mtx) +{ + +	u16 scalar; +	scalar = inv_row_2_scale(mtx); +	scalar |= inv_row_2_scale(mtx + 3) << 3; +	scalar |= inv_row_2_scale(mtx + 6) << 6; + +	return scalar; +} + +static int inv_gyro_dmp_cal(struct inv_mpu_state *st) +{ +	int inv_gyro_orient; +	u8 regs[3]; +	int result; + +	u8 tmpD = DINA4C; +	u8 tmpE = DINACD; +	u8 tmpF = DINA6C; + +	inv_gyro_orient = +		inv_orientation_matrix_to_scaler(st->plat_data.orientation); + +	if ((inv_gyro_orient & 3) == 0) +		regs[0] = tmpD; +	else if ((inv_gyro_orient & 3) == 1) +		regs[0] = tmpE; +	else if ((inv_gyro_orient & 3) == 2) +		regs[0] = tmpF; +	if ((inv_gyro_orient & 0x18) == 0) +		regs[1] = tmpD; +	else if ((inv_gyro_orient & 0x18) == 0x8) +		regs[1] = tmpE; +	else if ((inv_gyro_orient & 0x18) == 0x10) +		regs[1] = tmpF; +	if ((inv_gyro_orient & 0xc0) == 0) +		regs[2] = tmpD; +	else if ((inv_gyro_orient & 0xc0) == 0x40) +		regs[2] = tmpE; +	else if ((inv_gyro_orient & 0xc0) == 0x80) +		regs[2] = tmpF; + +	result = mem_w_key(KEY_FCFG_1, ARRAY_SIZE(regs), regs); +	if (result) +		return result; + +	if (inv_gyro_orient & 4) +		regs[0] = DINA36 | 1; +	else +		regs[0] = DINA36; +	if (inv_gyro_orient & 0x20) +		regs[1] = DINA56 | 1; +	else +		regs[1] = DINA56; +	if (inv_gyro_orient & 0x100) +		regs[2] = DINA76 | 1; +	else +		regs[2] = DINA76; +	result = mem_w_key(KEY_FCFG_3, ARRAY_SIZE(regs), regs); + +	return result; +} + +static int inv_accel_dmp_cal(struct inv_mpu_state *st) +{ +	int inv_accel_orient; +	int result; +	u8 regs[3]; +	const u8 tmp[3] = { DINA0C, DINAC9, DINA2C }; +	inv_accel_orient = +		inv_orientation_matrix_to_scaler(st->plat_data.orientation); + +	regs[0] = tmp[inv_accel_orient & 3]; +	regs[1] = tmp[(inv_accel_orient >> 3) & 3]; +	regs[2] = tmp[(inv_accel_orient >> 6) & 3]; +	result = mem_w_key(KEY_FCFG_2, ARRAY_SIZE(regs), regs); +	if (result) +		return result; + +	regs[0] = DINA26; +	regs[1] = DINA46; +	regs[2] = DINA66; +	if (inv_accel_orient & 4) +		regs[0] |= 1; +	if (inv_accel_orient & 0x20) +		regs[1] |= 1; +	if (inv_accel_orient & 0x100) +		regs[2] |= 1; +	result = mem_w_key(KEY_FCFG_7, ARRAY_SIZE(regs), regs); + +	return result; +} + +int inv_set_accel_bias_dmp(struct inv_mpu_state *st) +{ +	int inv_accel_orient, result, i, accel_bias_body[3], out[3]; +	int tmp[] = {1, 1, 1}; +	int mask[] = {4, 0x20, 0x100}; +	int accel_sf = 0x20000000;/* 536870912 */ +	u8 *regs; + +	inv_accel_orient = +		inv_orientation_matrix_to_scaler(st->plat_data.orientation); + +	for (i = 0; i < 3; i++) +		if (inv_accel_orient & mask[i]) +			tmp[i] = -1; + +	for (i = 0; i < 3; i++) +		accel_bias_body[i] = +			st->input_accel_dmp_bias[(inv_accel_orient >> +			(i * 3)) & 3] * tmp[i]; +	for (i = 0; i < 3; i++) +		accel_bias_body[i] = inv_q30_mult(accel_sf, +					accel_bias_body[i]); +	for (i = 0; i < 3; i++) +		out[i] = cpu_to_be32p(&accel_bias_body[i]); +	regs = (u8 *)out; +	result = mem_w_key(KEY_D_ACCEL_BIAS, sizeof(out), regs); + +	return result; +} + +/* + * inv_set_gyro_sf_dmp(): + * The gyro threshold, in dps, above which taps will be rejected. + */ +static int inv_set_gyro_sf_dmp(struct inv_mpu_state *st) +{ +	int result; +	u8 sampleDivider; +	u32 gyro_sf; +	const u32 gyro_sens = 0x03e80000; + +	sampleDivider = st->sample_divider; +	gyro_sf = inv_q30_mult(gyro_sens, +			(int)(DMP_TAP_SCALE * (sampleDivider + 1))); +	result = write_be32_key_to_mem(st, gyro_sf, KEY_D_0_104); + +	return result; +} + +/* + * inv_set_shake_reject_thresh_dmp(): + * The gyro threshold, in dps, above which taps will be rejected. + */ +static int inv_set_shake_reject_thresh_dmp(struct inv_mpu_state *st, +						int thresh) +{ +	int result; +	u8 sampleDivider; +	int thresh_scaled; +	u32 gyro_sf; +	const u32 gyro_sens = 0x03e80000; + +	sampleDivider = st->sample_divider; +	gyro_sf = inv_q30_mult(gyro_sens, (int)(DMP_TAP_SCALE * +			(sampleDivider + 1))); +	/* We're in units of DPS, convert it back to chip units*/ +	/*split the operation to aviod overflow of integer*/ +	thresh_scaled = gyro_sens / (1L << 16); +	thresh_scaled = thresh_scaled / thresh; +	thresh_scaled = gyro_sf / thresh_scaled; +	result = write_be32_key_to_mem(st, thresh_scaled, +						KEY_DMP_TAP_SHAKE_REJECT); + +	return result; +} + +/* + * inv_set_shake_reject_time_dmp(): + * How long a gyro axis must remain above its threshold + * before taps are rejected. + */ +static int inv_set_shake_reject_time_dmp(struct inv_mpu_state *st, +						u32 time) +{ +	int result; +	u16 dmpTime; +	u8 sampleDivider; + +	sampleDivider = st->sample_divider; +	sampleDivider++; + +	/* 60 ms minimum time added */ +	dmpTime = ((time) / sampleDivider); +	result = inv_write_2bytes(st, KEY_DMP_TAP_SHAKE_COUNT_MAX, dmpTime); + +	return result; +} + +/* + * inv_set_shake_reject_timeout_dmp(): + * How long the gyros must remain below their threshold, + * after taps have been rejected, before taps can be detected again. + */ +static int inv_set_shake_reject_timeout_dmp(struct inv_mpu_state *st, +						u32 time) +{ +	int result; +	u16 dmpTime; +	u8 sampleDivider; + +	sampleDivider = st->sample_divider; +	sampleDivider++; + +	/* 60 ms minimum time added */ +	dmpTime = ((time) / sampleDivider); +	result = inv_write_2bytes(st, KEY_DMP_TAP_SHAKE_TIMEOUT_MAX, dmpTime); + +	return result; +} + +int inv_set_interrupt_on_gesture_event(struct inv_mpu_state *st, bool on) +{ +	u8 r; +	const u8 rn[] = {0xA3}; +	const u8 rf[] = {0xFE}; + +	if (on) +		r = mem_w_key(KEY_CFG_FIFO_INT, ARRAY_SIZE(rn), rn); +	else +		r = mem_w_key(KEY_CFG_FIFO_INT, ARRAY_SIZE(rf), rf); + +	return r; +} + +/* + * inv_enable_tap_dmp() -  calling this function will enable/disable tap + *                         function. + */ +int inv_enable_tap_dmp(struct inv_mpu_state *st, bool on) +{ +	int result; + +	result = inv_set_tap_interrupt_dmp(st, on); +	if (result) +		return result; +	result = inv_set_tap_threshold_dmp(st, st->tap.thresh); +	if (result) +		return result; + +	result = inv_set_min_taps_dmp(st, st->tap.min_count); +	if (result) +		return result; + +	result = inv_set_tap_time_dmp(st, st->tap.time); +	if (result) +		return result; + +	result = inv_set_multiple_tap_time_dmp(st, DMP_MULTI_TAP_TIME); +	if (result) +		return result; + +	result = inv_set_gyro_sf_dmp(st); +	if (result) +		return result; + +	result = inv_set_shake_reject_thresh_dmp(st, DMP_SHAKE_REJECT_THRESH); +	if (result) +		return result; + +	result = inv_set_shake_reject_time_dmp(st, DMP_SHAKE_REJECT_TIME); +	if (result) +		return result; + +	result = inv_set_shake_reject_timeout_dmp(st, +						  DMP_SHAKE_REJECT_TIMEOUT); +	return result; +} + +static int inv_dry_run_dmp(struct inv_mpu_state *st) +{ +	int result; +	struct inv_reg_map_s *reg; + +	reg = &st->reg; +	result = st->switch_gyro_engine(st, true); +	if (result) +		return result; +	result = inv_i2c_single_write(st, reg->user_ctrl, BIT_DMP_EN); +	if (result) +		return result; +	msleep(400); +	result = inv_i2c_single_write(st, reg->user_ctrl, 0); +	if (result) +		return result; +	result = st->switch_gyro_engine(st, false); +	if (result) +		return result; + +	return 0; +} + +static void inv_test_reset(struct inv_mpu_state *st) +{ +	int result, ii; +	u8 d[0x80]; + +	if (INV_MPU6500 != st->chip_type) +		return; + +	for (ii = 3; ii < 0x80; ii++) { +		/* don't read fifo r/w register */ +		if (ii != st->reg.fifo_r_w) +			inv_i2c_read(st, ii, 1, &d[ii]); +	} +	result = inv_i2c_single_write(st, st->reg.pwr_mgmt_1, BIT_H_RESET); +	if (result) +		return; +	msleep(POWER_UP_TIME); + +	for (ii = 3; ii < 0x80; ii++) { +		/* don't write certain registers */ +		if ((ii != st->reg.fifo_r_w) && +		    (ii != st->reg.mem_r_w) && +		    (ii != st->reg.mem_start_addr) && +		    (ii != st->reg.fifo_count_h) && +		    ii != (st->reg.fifo_count_h + 1)) +			result = inv_i2c_single_write(st, ii, d[ii]); +	} +} + +/* + * inv_dmp_firmware_write() -  calling this function will load the firmware. + *                        This is the write function of file "dmp_firmware". + */ +ssize_t inv_dmp_firmware_write(struct file *fp, struct kobject *kobj, +	struct bin_attribute *attr, +	char *buf, loff_t pos, size_t size) +{ +	u8 *firmware; +	int result; +	struct inv_reg_map_s *reg; +	struct iio_dev *indio_dev; +	struct inv_mpu_state *st; + +	indio_dev = dev_get_drvdata(container_of(kobj, struct device, kobj)); +	st = iio_priv(indio_dev); + +	if (st->chip_config.firmware_loaded) +		return -EINVAL; +	if (st->chip_config.enable) +		return -EBUSY; + +	reg = &st->reg; +	if (DMP_IMAGE_SIZE != size) { +		pr_err("wrong DMP image size - expected %d, actual %d\n", +			DMP_IMAGE_SIZE, size); +		return -EINVAL; +	} + +	firmware = kmalloc(size, GFP_KERNEL); +	if (!firmware) +		return -ENOMEM; + +	mutex_lock(&indio_dev->mlock); + +	memcpy(firmware, buf, size); +	result = crc32(CRC_FIRMWARE_SEED, firmware, size); +	if (DMP_IMAGE_CRC_VALUE != result) { +		pr_err("firmware CRC error - 0x%08x vs 0x%08x\n", +			result, DMP_IMAGE_CRC_VALUE); +		result = -EINVAL; +		goto firmware_write_fail; +	} + +	result = st->set_power_state(st, true); +	if (result) +		goto firmware_write_fail; +	inv_test_reset(st); + +	result = inv_load_firmware(st, firmware, size); +	if (result) +		goto firmware_write_fail; + +	result = inv_verify_firmware(st, firmware, size); +	if (result) +		goto firmware_write_fail; + +	result = inv_i2c_single_write(st, reg->prgm_strt_addrh, +	st->chip_config.prog_start_addr >> 8); +	if (result) +		goto firmware_write_fail; +	result = inv_i2c_single_write(st, reg->prgm_strt_addrh + 1, +	st->chip_config.prog_start_addr & 0xff); +	if (result) +		goto firmware_write_fail; + +	result = inv_gyro_dmp_cal(st); +	if (result) +		goto firmware_write_fail; +	result = inv_accel_dmp_cal(st); +	if (result) +		goto firmware_write_fail; +	result = inv_dry_run_dmp(st); +	if (result) +		goto firmware_write_fail; + +	st->chip_config.firmware_loaded = 1; + +firmware_write_fail: +	result |= st->set_power_state(st, false); +	mutex_unlock(&indio_dev->mlock); +	kfree(firmware); +	if (result) +		return result; + +	return size; +} + +ssize_t inv_dmp_firmware_read(struct file *filp, +				struct kobject *kobj, +				struct bin_attribute *bin_attr, +				char *buf, loff_t off, size_t count) +{ +	int bank, write_size, size, data, result; +	u16 memaddr; +	struct iio_dev *indio_dev; +	struct inv_mpu_state *st; + +	size = count; +	indio_dev = dev_get_drvdata(container_of(kobj, struct device, kobj)); +	st = iio_priv(indio_dev); + +	data = 0; +	mutex_lock(&indio_dev->mlock); +	if (!st->chip_config.enable) { +		result = st->set_power_state(st, true); +		if (result) { +			mutex_unlock(&indio_dev->mlock); +			return result; +		} +	} +	for (bank = 0; size > 0; bank++, size -= write_size, +					data += write_size) { +		if (size > MPU_MEM_BANK_SIZE) +			write_size = MPU_MEM_BANK_SIZE; +		else +			write_size = size; + +		memaddr = (bank << 8); +		result = mpu_memory_read(st, +			st->i2c_addr, memaddr, write_size, &buf[data]); +		if (result) { +			mutex_unlock(&indio_dev->mlock); +			return result; +		} +	} +	if (!st->chip_config.enable) +		result = st->set_power_state(st, false); +	mutex_unlock(&indio_dev->mlock); +	if (result) +		return result; + +	return count; +} + +ssize_t inv_six_q_write(struct file *fp, struct kobject *kobj, +	struct bin_attribute *attr, char *buf, loff_t pos, size_t size) +{ +	u8 q[QUATERNION_BYTES]; +	struct inv_reg_map_s *reg; +	struct iio_dev *indio_dev; +	struct inv_mpu_state *st; +	int result; + +	indio_dev = dev_get_drvdata(container_of(kobj, struct device, kobj)); +	st = iio_priv(indio_dev); + +	mutex_lock(&indio_dev->mlock); + +	if (!st->chip_config.firmware_loaded) { +		mutex_unlock(&indio_dev->mlock); +		return -EINVAL; +	} +	if (st->chip_config.enable) { +		mutex_unlock(&indio_dev->mlock); +		return -EBUSY; +	} +	reg = &st->reg; +	if (QUATERNION_BYTES != size) { +		pr_err("wrong quaternion size=%d, should=%d\n", size, +							QUATERNION_BYTES); +		mutex_unlock(&indio_dev->mlock); +		return -EINVAL; +	} + +	memcpy(q, buf, size); +	result = st->set_power_state(st, true); +	if (result) +		goto firmware_write_fail; +	result = mem_w_key(KEY_DMP_Q0, QUATERNION_BYTES, q); + +firmware_write_fail: +	result |= st->set_power_state(st, false); +	mutex_unlock(&indio_dev->mlock); +	if (result) +		return result; + +	return size; +} + diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_ring.c b/drivers/staging/iio/imu/inv_mpu/inv_mpu_ring.c new file mode 100644 index 00000000000..8b0f27cd1a3 --- /dev/null +++ b/drivers/staging/iio/imu/inv_mpu/inv_mpu_ring.c @@ -0,0 +1,1868 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> + +#include <linux/iio/iio.h> +#include <linux/iio/kfifo_buf.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/sysfs.h> + +#include "inv_mpu_iio.h" + +static u8 fifo_data[HARDWARE_FIFO_SIZE + HEADERED_Q_BYTES]; +static int inv_process_batchmode(struct inv_mpu_state *st); + +static int inv_push_marker_to_buffer(struct inv_mpu_state *st, u16 hdr) +{ +	struct iio_dev *indio_dev = iio_priv_to_dev(st); +	u8 buf[IIO_BUFFER_BYTES]; + +	memcpy(buf, &hdr, sizeof(hdr)); +	iio_push_to_buffers(indio_dev, buf); + +	return 0; +} + +static int inv_push_8bytes_buffer(struct inv_mpu_state *st, u16 hdr, +							u64 t, s16 *d) +{ +	struct iio_dev *indio_dev = iio_priv_to_dev(st); +	u8 buf[IIO_BUFFER_BYTES]; +	int i; + +	memcpy(buf, &hdr, sizeof(hdr)); +	for (i = 0; i < 3; i++) +		memcpy(&buf[2 + i * 2], &d[i], sizeof(d[i])); +	iio_push_to_buffers(indio_dev, buf); +	memcpy(buf, &t, sizeof(t)); +	iio_push_to_buffers(indio_dev, buf); + +	return 0; +} + +static int inv_push_16bytes_buffer(struct inv_mpu_state *st, u16 hdr, u64 t, +									int *q) +{ +	struct iio_dev *indio_dev = iio_priv_to_dev(st); +	u8 buf[IIO_BUFFER_BYTES]; +	int i; + +	memcpy(buf, &hdr, sizeof(hdr)); +	memcpy(buf + 4, &q[0], sizeof(q[0])); +	iio_push_to_buffers(indio_dev, buf); +	for (i = 0; i < 2; i++) +		memcpy(buf + 4 * i, &q[i + 1], sizeof(q[i])); +	iio_push_to_buffers(indio_dev, buf); +	memcpy(buf, &t, sizeof(t)); +	iio_push_to_buffers(indio_dev, buf); + +	return 0; +} + +static int inv_send_pressure_data(struct inv_mpu_state *st) +{ +	short sen[3]; +	struct inv_chip_config_s *conf; +	struct inv_mpu_slave *slave; +	u64 curr_ts; +	int result; + +	conf = &st->chip_config; +	slave = st->slave_pressure; +	if (!st->sensor[SENSOR_PRESSURE].on) +		return 0; +	if (conf->dmp_on && conf->dmp_event_int_on) +		return 0; +	if (!conf->normal_pressure_measure) { +		conf->normal_pressure_measure = 1; +		return 0; +	} +	curr_ts = get_time_ns(); +	if (curr_ts - slave->prev_ts > slave->min_read_time) { +		result = slave->read_data(st, sen); +		if (!result) +			inv_push_8bytes_buffer(st, PRESSURE_HDR, +						st->last_ts, sen); +		slave->prev_ts = curr_ts; +	} + +	return 0; +} + +static int inv_send_compass_data(struct inv_mpu_state *st) +{ +	short sen[3]; +	struct inv_chip_config_s *conf; +	struct inv_mpu_slave *slave; +	u64 curr_ts; +	int result; + +	conf = &st->chip_config; +	slave = st->slave_compass; +	if (!st->sensor[SENSOR_COMPASS].on) +		return 0; +	if (conf->dmp_on && conf->dmp_event_int_on) +		return 0; +	if (!conf->normal_compass_measure) { +		conf->normal_compass_measure = 1; +		return 0; +	} +	curr_ts = get_time_ns(); +	if (curr_ts - slave->prev_ts > slave->min_read_time) { +		result = slave->read_data(st, sen); +		if (!result) +			inv_push_8bytes_buffer(st, COMPASS_HDR, +						st->last_ts, sen); +		slave->prev_ts = curr_ts; +	} + +	return 0; +} + +static int inv_batchmode_calc(struct inv_mpu_state *st) +{ +	int b, timeout; +	int rate_dur; + +	rate_dur = MSEC_PER_SEC / st->batch.min_rate; +	if (st->batch.timeout < rate_dur) +		st->batch.timeout = rate_dur; +	b = st->batch.timeout * st->bytes_per_sec; +	if ((b > (FIFO_SIZE * ONE_K_HZ)) && (!st->batch.overflow_on)) +		timeout = FIFO_SIZE * ONE_K_HZ / st->bytes_per_sec; +	else +		timeout = st->batch.timeout; + +	st->batch.counter = timeout / 5; +	if (timeout) +		st->batch.on = true; + +	return 0; +} + +int inv_batchmode_setup(struct inv_mpu_state *st) +{ +	int r; + +	r = inv_write_2bytes(st, KEY_BM_NUMWORD_TOFILL, 0); +	if (r) +		return r; +	r = write_be32_key_to_mem(st, 0, KEY_BM_BATCH_CNTR); +	if (r) +		return r; + +	if (st->chip_config.dmp_on && (st->batch.timeout > 0) && +			(st->chip_config.dmp_event_int_on == 0)) { +		r = inv_batchmode_calc(st); +		if (r) +			return r; +	} + +	if (st->batch.on) { +		r = write_be32_key_to_mem(st, st->batch.counter, +						KEY_BM_BATCH_THLD); +		if (r) +			return r; +	} +	r = inv_write_2bytes(st, KEY_BM_ENABLE, st->batch.on); + +	return r; +} + +/** + *  reset_fifo_mpu3050() - Reset FIFO related registers + *  @indio_dev:	Device driver instance. + */ +static int reset_fifo_mpu3050(struct iio_dev *indio_dev) +{ +	struct inv_reg_map_s *reg; +	int result; +	u8 val, user_ctrl; +	struct inv_mpu_state  *st = iio_priv(indio_dev); +	reg = &st->reg; + +	/* disable interrupt */ +	result = inv_i2c_single_write(st, reg->int_enable, +				st->plat_data.int_config); +	if (result) +		return result; +	/* disable the sensor output to FIFO */ +	result = inv_i2c_single_write(st, reg->fifo_en, 0); +	if (result) +		goto reset_fifo_fail; +	result = inv_i2c_read(st, reg->user_ctrl, 1, &user_ctrl); +	if (result) +		goto reset_fifo_fail; +	/* disable fifo reading */ +	user_ctrl &= ~BIT_FIFO_EN; +	st->chip_config.has_footer = 0; +	/* reset fifo */ +	val = (BIT_3050_FIFO_RST | user_ctrl); +	result = inv_i2c_single_write(st, reg->user_ctrl, val); +	if (result) +		goto reset_fifo_fail; +	if (st->chip_config.dmp_on) { +		/* enable interrupt when DMP is done */ +		result = inv_i2c_single_write(st, reg->int_enable, +				st->plat_data.int_config | BIT_DMP_INT_EN); +		if (result) +			return result; + +		result = inv_i2c_single_write(st, reg->user_ctrl, +			BIT_FIFO_EN|user_ctrl); +		if (result) +			return result; +	} else { +		/* enable interrupt */ +		if (st->sensor[SENSOR_ACCEL].on || +		    st->sensor[SENSOR_GYRO].on) { +			result = inv_i2c_single_write(st, reg->int_enable, +				st->plat_data.int_config | BIT_DATA_RDY_EN); +			if (result) +				return result; +		} +		/* enable FIFO reading and I2C master interface*/ +		result = inv_i2c_single_write(st, reg->user_ctrl, +			BIT_FIFO_EN | user_ctrl); +		if (result) +			return result; +		/* enable sensor output to FIFO and FIFO footer*/ +		val = 1; +		if (st->sensor[SENSOR_ACCEL].on) +			val |= BITS_3050_ACCEL_OUT; +		if (st->sensor[SENSOR_GYRO].on) +			val |= BITS_GYRO_OUT; +		result = inv_i2c_single_write(st, reg->fifo_en, val); +		if (result) +			return result; +	} + +	return 0; +reset_fifo_fail: +	if (st->chip_config.dmp_on) +		val = BIT_DMP_INT_EN; +	else +		val = BIT_DATA_RDY_EN; +	inv_i2c_single_write(st, reg->int_enable, +			     st->plat_data.int_config | val); +	pr_err("reset fifo failed\n"); + +	return result; +} + +/* + *  inv_set_lpf() - set low pass filer based on fifo rate. + */ +static int inv_set_lpf(struct inv_mpu_state *st, int rate) +{ +	const short hz[] = {188, 98, 42, 20, 10, 5}; +	const int   d[] = {INV_FILTER_188HZ, INV_FILTER_98HZ, +			INV_FILTER_42HZ, INV_FILTER_20HZ, +			INV_FILTER_10HZ, INV_FILTER_5HZ}; +	int i, h, data, result; +	struct inv_reg_map_s *reg; + +	reg = &st->reg; +	h = (rate >> 1); +	i = 0; +	while ((h < hz[i]) && (i < ARRAY_SIZE(d) - 1)) +		i++; +	data = d[i]; +	if (INV_MPU3050 == st->chip_type) { +		if (st->slave_accel != NULL) { +			result = st->slave_accel->set_lpf(st, rate); +			if (result) +				return result; +		} +		result = inv_i2c_single_write(st, reg->lpf, data | +			(st->chip_config.fsr << GYRO_CONFIG_FSR_SHIFT)); +	} else { +		result = inv_i2c_single_write(st, reg->lpf, data); +	} +	if (result) +		return result; +	st->chip_config.lpf = data; + +	return 0; +} + +/* + *  set_fifo_rate_reg() - Set fifo rate in hardware register + */ +static int set_fifo_rate_reg(struct inv_mpu_state *st) +{ +	u8 data; +	u16 fifo_rate; +	int result; +	struct inv_reg_map_s *reg; + +	reg = &st->reg; +	fifo_rate = st->chip_config.fifo_rate; +	data = ONE_K_HZ / fifo_rate - 1; +	result = inv_i2c_single_write(st, reg->sample_rate_div, data); +	if (result) +		return result; +	result = inv_set_lpf(st, fifo_rate); +	if (result) +		return result; +	/* wait for the sampling rate change to stabilize */ +	mdelay(INV_MPU_SAMPLE_RATE_CHANGE_STABLE); + +	return 0; +} + +/* + *  inv_lpa_mode() - store current low power mode settings + */ +static int inv_lpa_mode(struct inv_mpu_state *st, int lpa_mode) +{ +	unsigned long result; +	u8 d; +	struct inv_reg_map_s *reg; + +	reg = &st->reg; +	result = inv_i2c_read(st, reg->pwr_mgmt_1, 1, &d); +	if (result) +		return result; +	if (lpa_mode) +		d |= BIT_CYCLE; +	else +		d &= ~BIT_CYCLE; + +	result = inv_i2c_single_write(st, reg->pwr_mgmt_1, d); +	if (result) +		return result; +	if (INV_MPU6500 == st->chip_type) { +		d = BIT_FIFO_SIZE_1K; +		if (lpa_mode) +			d |= BIT_ACCEL_FCHOCIE_B; +		result = inv_i2c_single_write(st, REG_6500_ACCEL_CONFIG2, d); +		if (result) +			return result; +	} + +	return 0; +} + +static int inv_saturate_secondary_counter(struct inv_mpu_state *st) +{ +	int result; +	struct inv_reg_map_s *reg; + +#define COUNT_SATURATE_TIME_MS 32 +	reg = &st->reg; +	/* set sampling to 1KHz */ +	result = inv_i2c_single_write(st, reg->sample_rate_div, 0); +	if (result) +		return result; +	result = inv_i2c_single_write(st, REG_I2C_MST_DELAY_CTRL, +							BIT_SLV0_DLY_EN); +	if (result) +		return result; +	result = inv_i2c_single_write(st, REG_I2C_SLV4_CTRL, 0); +	if (result) +		return result; +	result = inv_i2c_single_write(st, reg->user_ctrl, BIT_I2C_MST_EN); +	if (result) +		return result; +	msleep(COUNT_SATURATE_TIME_MS); + +	return 0; +} +static int inv_set_master_delay(struct inv_mpu_state *st) +{ +	int d, result, rate; +	u8 delay; + +	if ((!st->sensor[SENSOR_COMPASS].on) && +		(!st->sensor[SENSOR_PRESSURE].on)) +		return 0; + +	delay = 0; +	d = 0; +	if (st->sensor[SENSOR_COMPASS].on) { +		switch (st->plat_data.sec_slave_id) { +		case COMPASS_ID_AK8975: +		case COMPASS_ID_AK8972: +		case COMPASS_ID_AK8963: +		case COMPASS_ID_AK09911: +			delay = (BIT_SLV0_DLY_EN | BIT_SLV1_DLY_EN); +			break; +		case COMPASS_ID_MLX90399: +			delay = (BIT_SLV0_DLY_EN | +				BIT_SLV1_DLY_EN | +				BIT_SLV2_DLY_EN | +				BIT_SLV3_DLY_EN); +			break; +		default: +			return -EINVAL; +		} +		d = max(d, st->slave_compass->rate_scale); +	} +	if (st->sensor[SENSOR_PRESSURE].on) { +		/* read fake data when compass is disabled for DMP read */ +		if ((!st->sensor[SENSOR_COMPASS].on) && st->chip_config.dmp_on) +			delay |= BIT_SLV0_DLY_EN; +		switch (st->plat_data.aux_slave_id) { +		case PRESSURE_ID_BMP280: +			delay |= (BIT_SLV2_DLY_EN | BIT_SLV3_DLY_EN); +			break; +		default: +			return -EINVAL; +		} +		d = max(d, st->slave_pressure->rate_scale); +	} + +	d = d * st->chip_config.fifo_rate / ONE_K_HZ; +	if (st->chip_config.dmp_on) { +		rate = 0; +		if (st->sensor[SENSOR_PRESSURE].on) +			rate = max(rate, st->sensor[SENSOR_PRESSURE].rate); +		if (st->sensor[SENSOR_COMPASS].on) +			rate = max(rate, st->sensor[SENSOR_COMPASS].rate); +		if (rate == 0) +			return -EINVAL; +		d = max(d, st->chip_config.fifo_rate / rate); +	} + +	if (d > 0) +		d -= 1; +	if (d > 0x1F) +		d = 0x1F; + +	/* I2C_MST_DLY is set to slow down secondary I2C */ +	if (0 == d) +		delay = 0; +	if (delay) { +		result = inv_saturate_secondary_counter(st); +		if (result) +			return result; +	} +	result = inv_i2c_single_write(st, REG_I2C_MST_DELAY_CTRL, delay); +	if (result) +		return result; + +	return inv_i2c_single_write(st, REG_I2C_SLV4_CTRL, d); +} + +/* + *  reset_fifo_itg() - Reset FIFO related registers. + */ +static int reset_fifo_itg(struct iio_dev *indio_dev) +{ +	struct inv_reg_map_s *reg; +	int result, i; +	u8 val, int_word; +	struct inv_mpu_state  *st = iio_priv(indio_dev); + +	reg = &st->reg; +	if (st->chip_config.lpa_mode) { +		result = inv_lpa_mode(st, 0); +		if (result) { +			pr_err("reset lpa mode failed\n"); +			return result; +		} +	} +	/* disable interrupt */ +	result = inv_i2c_single_write(st, reg->int_enable, 0); +	if (result) { +		pr_err("int_enable write failed\n"); +		return result; +	} +	/* disable the sensor output to FIFO */ +	result = inv_i2c_single_write(st, reg->fifo_en, 0); +	if (result) +		goto reset_fifo_fail; +	/* disable fifo reading */ +	result = inv_i2c_single_write(st, reg->user_ctrl, 0); +	if (result) +		goto reset_fifo_fail; +	int_word = 0; + +	/* MPU6500's BIT_6500_WOM_EN is the same as BIT_MOT_EN */ +	if (st->mot_int.mot_on) +		int_word |= BIT_MOT_EN; + +	if (st->chip_config.dmp_on) { +		val = (BIT_FIFO_RST | BIT_DMP_RST); +		result = inv_i2c_single_write(st, reg->user_ctrl, val); +		if (result) +			goto reset_fifo_fail; +		if (st->chip_config.dmp_int_on) { +			int_word |= BIT_DMP_INT_EN; +			result = inv_i2c_single_write(st, reg->int_enable, +							int_word); +			if (result) +				return result; +		} +		val = (BIT_DMP_EN | BIT_FIFO_EN); +		if ((st->sensor[SENSOR_COMPASS].on || +			st->sensor[SENSOR_PRESSURE].on) && +			(!st->chip_config.dmp_event_int_on)) +			val |= BIT_I2C_MST_EN; +		result = inv_i2c_single_write(st, reg->user_ctrl, val); +		if (result) +			goto reset_fifo_fail; +	} else { +		/* reset FIFO and possibly reset I2C*/ +		val = BIT_FIFO_RST; +		result = inv_i2c_single_write(st, reg->user_ctrl, val); +		if (result) +			goto reset_fifo_fail; +		/* enable interrupt */ +		if (st->sensor[SENSOR_ACCEL].on || +				    st->sensor[SENSOR_GYRO].on || +				    st->sensor[SENSOR_COMPASS].on || +				    st->sensor[SENSOR_PRESSURE].on) +			int_word |= BIT_DATA_RDY_EN; + +		result = inv_i2c_single_write(st, reg->int_enable, int_word); +		if (result) +			return result; +		/* enable FIFO reading and I2C master interface*/ +		val = BIT_FIFO_EN; +		if (st->sensor[SENSOR_COMPASS].on || +		    st->sensor[SENSOR_PRESSURE].on) +			val |= BIT_I2C_MST_EN; +		result = inv_i2c_single_write(st, reg->user_ctrl, val); +		if (result) +			goto reset_fifo_fail; +		/* enable sensor output to FIFO */ +		val = 0; +		if (st->sensor[SENSOR_GYRO].on) +			val |= BITS_GYRO_OUT; +		if (st->sensor[SENSOR_ACCEL].on) +			val |= BIT_ACCEL_OUT; +		result = inv_i2c_single_write(st, reg->fifo_en, val); +		if (result) +			goto reset_fifo_fail; +	} +	st->last_ts = get_time_ns(); +	st->prev_ts = st->last_ts; +	st->last_run_time = st->last_ts; +	if (st->sensor[SENSOR_COMPASS].on) +		st->slave_compass->prev_ts = st->last_ts; +	if (st->sensor[SENSOR_PRESSURE].on) +		st->slave_pressure->prev_ts = st->last_ts; + +	st->dmp_interval = DMP_INTERVAL_INIT; +	st->ts_counter = 0; +	st->diff_accumulater = 0; +	st->dmp_interval_accum = 0; +	st->step_detector_base_ts = st->last_ts; +	st->chip_config.normal_compass_measure = 0; +	st->chip_config.normal_pressure_measure = 0; +	st->left_over_size = 0; +	for (i = 0; i < SENSOR_NUM_MAX; i++) +		st->sensor[i].ts = st->last_ts; + +	result = inv_lpa_mode(st, st->chip_config.lpa_mode); +	if (result) +		goto reset_fifo_fail; + +	return 0; + +reset_fifo_fail: +	if (st->chip_config.dmp_on) +		val = BIT_DMP_INT_EN; +	else +		val = BIT_DATA_RDY_EN; +	inv_i2c_single_write(st, reg->int_enable, val); +	pr_err("reset fifo failed\n"); + +	return result; +} + +/** + *  inv_clear_kfifo() - clear time stamp fifo + *  @st:	Device driver instance. + */ +static void inv_clear_kfifo(struct inv_mpu_state *st) +{ +	unsigned long flags; + +	spin_lock_irqsave(&st->time_stamp_lock, flags); +	kfifo_reset(&st->timestamps); +	spin_unlock_irqrestore(&st->time_stamp_lock, flags); +} + +/* + *  inv_reset_fifo() - Reset FIFO related registers. + */ +int inv_reset_fifo(struct iio_dev *indio_dev) +{ +	struct inv_mpu_state *st = iio_priv(indio_dev); + +	inv_clear_kfifo(st); +	if (INV_MPU3050 == st->chip_type) +		return reset_fifo_mpu3050(indio_dev); +	else +		return reset_fifo_itg(indio_dev); +} + +static int inv_send_gyro_data(struct inv_mpu_state *st, bool on) +{ +	u8 rn[] = {0xa3, 0xa3}; +	u8 rf[] = {0xf4, 0x12}; +	int result; +	u8 *r; + +	if (on) +		r = rn; +	else +		r = rf; +	result = mem_w_key(KEY_CFG_OUT_GYRO, ARRAY_SIZE(rf), r); + +	return result; +} + +static int inv_send_accel_data(struct inv_mpu_state *st, bool on) +{ +	u8 rn[] = {0xa3, 0xa3}; +	u8 rf[] = {0xf4, 0x12}; +	int result; +	u8 *r; + +	if (on) +		r = rn; +	else +		r = rf; +	result = mem_w_key(KEY_CFG_OUT_ACCL, ARRAY_SIZE(rf), r); + +	return result; +} + +static int inv_send_three_q_data(struct inv_mpu_state *st, bool on) +{ +	u8 rn[] = {0xa3, 0xa3}; +	u8 rf[] = {0xf4, 0x13}; +	int result; +	u8 *r; + +	if (on) +		r = rn; +	else +		r = rf; +	result = mem_w_key(KEY_CFG_OUT_3QUAT, ARRAY_SIZE(rf), r); + +	return result; +} + +static int inv_send_six_q_data(struct inv_mpu_state *st, bool on) +{ +	u8 rn[] = {0xa3, 0xa3}; +	u8 rf[] = {0xf4, 0x13}; +	int result; +	u8 *r; + +	if (on) +		r = rn; +	else +		r = rf; +	result = mem_w_key(KEY_CFG_OUT_6QUAT, ARRAY_SIZE(rf), r); + +	return result; +} + +static int inv_send_ped_q_data(struct inv_mpu_state *st, bool on) +{ +	u8 rn[] = {0xa3, 0xa3}; +	u8 rf[] = {0xf4, 0x12}; +	u8 *r; +	int result; + +	if (on) +		r = rn; +	else +		r = rf; +	result = mem_w_key(KEY_CFG_OUT_PQUAT, ARRAY_SIZE(rn), r); + +	return result; +} + +static int inv_add_step_indicator(struct inv_mpu_state *st, bool on) +{ +	u8 rn[] = {0xf3, 0xf3}; +	u8 rf[] = {0xf4, 0x03}; +	int result; +	u8 *r; + +	if (on) +		r = rn; +	else +		r = rf; +	result = mem_w_key(KEY_CFG_PEDSTEP_DET, ARRAY_SIZE(rn), r); + +	return result; +} + +static int inv_send_compass_dmp_data(struct inv_mpu_state *st, bool on) +{ +	u8 rn[] = {0xa3, 0xa3}; +	u8 rf[] = {0xf4, 0x12}; +	u8 *r; +	int result; + +	if (on) +		r = rn; +	else +		r = rf; +	result = mem_w_key(KEY_CFG_OUT_CPASS, ARRAY_SIZE(rf), r); + +	return result; +} + +static int inv_send_pressure_dmp_data(struct inv_mpu_state *st, bool on) +{ +	u8 rn[] = {0xa3, 0xa3}; +	u8 rf[] = {0xf4, 0x12}; +	u8 *r; +	int result; + +	if (on) +		r = rn; +	else +		r = rf; +	result = mem_w_key(KEY_CFG_OUT_PRESS, ARRAY_SIZE(rf), r); + +	return result; +} + +static int inv_send_step_detector(struct inv_mpu_state *st, bool on) +{ +	u8 rn[] = {0xa3, 0xa3}; +	u8 rf[] = {0xf4, 0x0e}; +	u8 *r; +	int result; + +	if (on) +		r = rn; +	else +		r = rf; +	result = mem_w_key(KEY_CFG_OUT_STEPDET, ARRAY_SIZE(rf), r); + +	return result; +} + +static int inv_set_rate(struct inv_mpu_state *st, int k, int k_ct, int rate) +{ +	int v, result; + +	v = MPU_DEFAULT_DMP_FREQ / rate - 1; +	result = inv_write_2bytes(st, k, v); +	if (result) +		return result; +	result = inv_write_2bytes(st, k_ct, 0); + +	return result; +} + +static int inv_set_gyro_rate(struct inv_mpu_state *st) +{ +	int result; + +	result = inv_set_rate(st, KEY_CFG_GYRO_ODR, KEY_ODR_CNTR_GYRO, +					st->sensor[SENSOR_GYRO].rate); + +	return result; +} + +static int inv_set_accel_rate(struct inv_mpu_state *st) +{ +	int result; + +	result = inv_set_rate(st, KEY_CFG_ACCL_ODR, KEY_ODR_CNTR_ACCL, +					st->sensor[SENSOR_ACCEL].rate); + +	return result; +} + +static int inv_set_compass_rate(struct inv_mpu_state *st) +{ +	int result; + +	result = inv_set_rate(st, KEY_CFG_CPASS_ODR, KEY_ODR_CNTR_CPASS, +					st->sensor[SENSOR_COMPASS].rate); + +	return result; +} + +static int inv_set_pressure_rate(struct inv_mpu_state *st) +{ +	int result; + +	result = inv_set_rate(st, KEY_CFG_PRESS_ODR, KEY_ODR_CNTR_PRESS, +					st->sensor[SENSOR_PRESSURE].rate); + +	return result; +} + +static int inv_set_step_detector(struct inv_mpu_state *st) +{ +	return 0; +} + + +static int inv_set_lpq_rate(struct inv_mpu_state *st) +{ +	int result; + +	result = inv_set_rate(st, KEY_CFG_3QUAT_ODR, KEY_ODR_CNTR_3QUAT, +					st->sensor[SENSOR_LPQ].rate); + +	return result; +} + +static int inv_set_sixq_rate(struct inv_mpu_state *st) +{ +	int result; + +	result = inv_set_rate(st, KEY_CFG_6QUAT_ODR, KEY_ODR_CNTR_6QUAT, +					st->sensor[SENSOR_SIXQ].rate); + +	return result; +} + +static int inv_set_pedq_rate(struct inv_mpu_state *st) +{ +	int result; + +	result = inv_set_rate(st, KEY_CFG_PQUAT6_ODR, KEY_ODR_CNTR_PQUAT, +					st->sensor[SENSOR_PEDQ].rate); + +	return result; +} + + +static int inv_set_dmp_sysfs(struct inv_mpu_state *st) +{ +	int result, i, s; +	u8 d[] = {0, 0, 0, 0}; + +	result = inv_set_interrupt_on_gesture_event(st, +				st->chip_config.dmp_event_int_on); +	if (result) +		return result; + +	if (st->chip_config.dmp_event_int_on) { +		for (i = 0; i < SENSOR_NUM_MAX; i++) { +			result = st->sensor[i].send_data(st, false); +			if (result) +				return result; +		} +	} else { +		s = 0; +		st->batch.min_rate = MAX_DMP_OUTPUT_RATE; +		for (i = 0; i < SENSOR_NUM_MAX; i++) { +			result = st->sensor[i].send_data(st, st->sensor[i].on); +			if (result) +				return result; +			if (st->sensor[i].on) { +				if (0 == st->sensor[i].rate) +					return -EINVAL; +				if (st->sensor[i].rate < st->batch.min_rate) +					st->batch.min_rate = st->sensor[i].rate; +				s += st->sensor[i].rate * +						st->sensor[i].sample_size; + +				result = st->sensor[i].set_rate(st); +				if (result) +					return result; +				st->sensor[i].counter = MPU_DEFAULT_DMP_FREQ / +							st->sensor[i].rate; +			} +		} +		st->bytes_per_sec = s; +		if (st->sensor[SENSOR_STEP].on) +			result = inv_add_step_indicator(st, true); +		else +			result = inv_add_step_indicator(st, +					st->chip_config.step_indicator_on); +		if (result) +			return result; +	} +	result = inv_batchmode_setup(st); +	if (result) +		return result; + +	st->dmp_counter = 0; +	result = mem_w_key(KEY_DMP_RUN_CNTR, ARRAY_SIZE(d), d); +	if (result) +		return result; +	result = mem_w_key(KEY_D_STPDET_TIMESTAMP, ARRAY_SIZE(d), d); + +	return result; +} + +static void inv_get_data_count(struct inv_mpu_state *st) +{ +	struct inv_chip_config_s *c; +	int b, i; + +	c = &st->chip_config; +	b = 0; +	if (st->chip_config.dmp_on) { +		for (i = 0; i < SENSOR_NUM_MAX; i++) { +			if (st->sensor[i].on) +				b += st->sensor[i].sample_size; +		} +	} else { +		if (st->sensor[SENSOR_ACCEL].on) +			b += BYTES_PER_SENSOR; +		if (st->sensor[SENSOR_GYRO].on) +			b += BYTES_PER_SENSOR; +	} +	c->bytes_per_datum = b; + +	return; +} +/* + *  set_inv_enable() - main enable/disable function. + */ +int set_inv_enable(struct iio_dev *indio_dev, bool enable) +{ +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct inv_reg_map_s *reg; +	u8 data[2]; +	int result; + +	reg = &st->reg; +	if (enable) { +		if (st->chip_config.dmp_on && +				(!st->chip_config.firmware_loaded)) +			return -EINVAL; +		st->batch.on = false; + +		inv_get_data_count(st); +		result = inv_set_master_delay(st); +		if (result) +			return result; + +		result = set_fifo_rate_reg(st); +		if (result) +			return result; +		if (st->chip_config.dmp_on) { +			result = inv_set_dmp_sysfs(st); +			if (result) +				return result; +		} + +		if (st->chip_config.gyro_enable) { +			result = st->switch_gyro_engine(st, true); +			if (result) +				return result; +		} +		if (st->chip_config.accel_enable) { +			result = st->switch_accel_engine(st, true); +			if (result) +				return result; +		} +		if (st->sensor[SENSOR_COMPASS].on) { +			result = st->slave_compass->resume(st); +			if (result) +				return result; +		} +		if (st->sensor[SENSOR_PRESSURE].on) { +			result = st->slave_pressure->resume(st); +			if (result) +				return result; +		} +		result = inv_reset_fifo(indio_dev); +		if (result) +			return result; +	} else { +		if ((INV_MPU3050 != st->chip_type) +			&& st->chip_config.lpa_mode) { +			/* if the chip is in low power mode, +				register write/read could fail */ +			result = inv_lpa_mode(st, 0); +			if (result) +				return result; +		} +		result = inv_i2c_single_write(st, reg->fifo_en, 0); +		if (result) +			return result; +		if (st->chip_config.dmp_on) { +			result = inv_read_time_and_ticks(st, false); +			if (result) +				return result; +			result = inv_i2c_read(st, reg->fifo_count_h, +						FIFO_COUNT_BYTE, data); +			if (result) +				return result; +			st->fifo_count = be16_to_cpup((__be16 *)(data)); +			if (st->fifo_count) { +				result = inv_process_batchmode(st); +				if (result) +					return result; +			} +		} +		inv_push_marker_to_buffer(st, END_MARKER); +		/* disable fifo reading */ +		if (INV_MPU3050 != st->chip_type) { +			result = inv_i2c_single_write(st, reg->int_enable, 0); +			if (result) +				return result; +			result = inv_i2c_single_write(st, reg->user_ctrl, 0); +		} else { +			result = inv_i2c_single_write(st, reg->int_enable, +				st->plat_data.int_config); +		} +		if (result) +			return result; +		/* turn off the gyro/accel engine during disable phase */ +		result = st->switch_gyro_engine(st, false); +		if (result) +			return result; +		result = st->switch_accel_engine(st, false); +		if (result) +			return result; +		if (st->sensor[SENSOR_COMPASS].on) { +			result = st->slave_compass->suspend(st); +			if (result) +				return result; +		} +		if (st->sensor[SENSOR_PRESSURE].on) { +			result = st->slave_pressure->suspend(st); +			if (result) +				return result; +		} +	} +	st->chip_config.enable = enable; + +	return 0; +} + +/* + *  inv_irq_handler() - Cache a timestamp at each data ready interrupt. + */ +static irqreturn_t inv_irq_handler(int irq, void *dev_id) +{ +	struct inv_mpu_state *st = (struct inv_mpu_state *)dev_id; +	u64 ts; + +	if (!st->chip_config.dmp_on) { +		ts = get_time_ns(); +		kfifo_in_spinlocked(&st->timestamps, &ts, 1, +						&st->time_stamp_lock); +	} + +	return IRQ_WAKE_THREAD; +} + +static void inv_report_data_3050(struct iio_dev *indio_dev, s64 t, +			int has_footer, u8 *data) +{ +	struct inv_mpu_state *st = iio_priv(indio_dev); +	int ind, i; +	short s[THREE_AXIS]; + +	ind = 0; +	if (has_footer) +		ind += 2; + +	if (st->sensor[SENSOR_GYRO].on) { +		for (i = 0; i < 3; i++) +			s[i] = be16_to_cpup((__be16 *)(&data[ind + i * 2])); + +		inv_push_8bytes_buffer(st, GYRO_HDR, t, s); +		ind += BYTES_PER_SENSOR; +	} +	if (st->sensor[SENSOR_ACCEL].on) { +		st->slave_accel->combine_data(&data[ind], s); +		inv_push_8bytes_buffer(st, ACCEL_HDR, t, s); +	} +} + +/* + *  inv_read_fifo_mpu3050() - Transfer data from FIFO to ring buffer for + *                            mpu3050. + */ +irqreturn_t inv_read_fifo_mpu3050(int irq, void *dev_id) +{ + +	struct inv_mpu_state *st = (struct inv_mpu_state *)dev_id; +	struct iio_dev *indio_dev = iio_priv_to_dev(st); +	int bytes_per_datum; +	u8 data[64]; +	int result; +	short fifo_count, byte_read; +	s64 timestamp; +	struct inv_reg_map_s *reg; + +	reg = &st->reg; +	mutex_lock(&st->suspend_resume_lock); +	mutex_lock(&indio_dev->mlock); +	if (st->chip_config.dmp_on) +		bytes_per_datum = HEADERED_NORMAL_BYTES; +	else +		bytes_per_datum = (st->sensor[SENSOR_ACCEL].on + +			st->sensor[SENSOR_GYRO].on) * BYTES_PER_SENSOR; +	if (st->chip_config.has_footer) +		byte_read = bytes_per_datum + MPU3050_FOOTER_SIZE; +	else +		byte_read = bytes_per_datum; + +	fifo_count = 0; +	if (byte_read != 0) { +		result = inv_i2c_read(st, reg->fifo_count_h, +				FIFO_COUNT_BYTE, data); +		if (result) +			goto end_session; +		fifo_count = be16_to_cpup((__be16 *)(&data[0])); +		if (fifo_count < byte_read) +			goto end_session; +		if (fifo_count & 1) +			goto flush_fifo; +		if (fifo_count > FIFO_THRESHOLD) +			goto flush_fifo; +		/* Timestamp mismatch. */ +		if (kfifo_len(&st->timestamps) < +			fifo_count / byte_read) +			goto flush_fifo; +		if (kfifo_len(&st->timestamps) > +			fifo_count / byte_read + TIME_STAMP_TOR) { +			if (st->chip_config.dmp_on) { +				result = kfifo_out(&st->timestamps, +				×tamp, 1); +				if (result != 1) +					goto flush_fifo; +			} else { +				goto flush_fifo; +			} +		} +	} +	while ((bytes_per_datum != 0) && (fifo_count >= byte_read)) { +		result = inv_i2c_read(st, reg->fifo_r_w, byte_read, data); +		if (result) +			goto flush_fifo; + +		result = kfifo_out(&st->timestamps, ×tamp, 1); +		if (result != 1) +			goto flush_fifo; +		inv_report_data_3050(indio_dev, timestamp, +				     st->chip_config.has_footer, data); +		fifo_count -= byte_read; +		if (st->chip_config.has_footer == 0) { +			st->chip_config.has_footer = 1; +			byte_read = bytes_per_datum + MPU3050_FOOTER_SIZE; +		} +	} + +end_session: +	mutex_unlock(&indio_dev->mlock); +	mutex_unlock(&st->suspend_resume_lock); + +	return IRQ_HANDLED; + +flush_fifo: +	/* Flush HW and SW FIFOs. */ +	inv_reset_fifo(indio_dev); +	inv_clear_kfifo(st); +	mutex_unlock(&indio_dev->mlock); +	mutex_unlock(&st->suspend_resume_lock); + +	return IRQ_HANDLED; +} + +static int inv_report_gyro_accel(struct iio_dev *indio_dev, +					u8 *data, s64 t) +{ +	struct inv_mpu_state *st = iio_priv(indio_dev); +	short s[THREE_AXIS]; +	int ind; +	int i; + +	ind = 0; +	if (st->sensor[SENSOR_ACCEL].on) { +		for (i = 0; i < 3; i++) +			s[i] = be16_to_cpup((__be16 *)(&data[ind + i * 2])); +		inv_push_8bytes_buffer(st, ACCEL_HDR, t, s); +		ind += BYTES_PER_SENSOR; +	} + +	if (st->sensor[SENSOR_GYRO].on) { +		for (i = 0; i < 3; i++) +			s[i] = be16_to_cpup((__be16 *)(&data[ind + i * 2])); +		inv_push_8bytes_buffer(st, GYRO_HDR, t, s); +		ind += BYTES_PER_SENSOR; +	} + +	return 0; +} + +static void inv_process_motion(struct inv_mpu_state *st) +{ +	struct iio_dev *indio_dev = iio_priv_to_dev(st); +	int result; +	u8 data[1]; + +	/* motion interrupt */ +	result = inv_i2c_read(st, REG_INT_STATUS, 1, data); +	if (result) +		return; + +	if (data[0] & BIT_MOT_INT) +		sysfs_notify(&indio_dev->dev.kobj, NULL, "event_accel_motion"); +} + +static int inv_get_timestamp(struct inv_mpu_state *st, int count) +{ +	u32 *dur; +	u32 thresh; +	s32 diff, result, counter; +	u64 ts; + +	/* goal of algorithm is to estimate the true frequency of the chip */ +	if (st->chip_config.dmp_on && st->chip_config.dmp_event_int_on) +		return 0; +	dur = &st->irq_dur_ns; +	counter = 1; +	thresh = min((u32)((*dur) >> 2), (u32)(10 * NSEC_PER_MSEC)); +	while (kfifo_len(&st->timestamps) >= count) { +		result = kfifo_out(&st->timestamps, &ts, 1); +		if (result != 1) +			return -EINVAL; +		/* first time since reset fifo, just take it */ +		if (!st->ts_counter) { +			st->last_ts = ts; +			st->prev_ts = ts; +			st->ts_counter++; +			return 0; +		} +		diff = (s32)(ts - st->prev_ts); +		st->prev_ts = ts; +		if (abs(diff - (*dur)) < thresh) { +			st->diff_accumulater >>= 1; +			if (*dur > diff) +				st->diff_accumulater -= (((*dur) - diff) >> 7); +			else +				st->diff_accumulater += ((diff - (*dur)) >> 7); +			*dur += st->diff_accumulater; +		} +	} +	ts = *dur; +	ts *= counter; +	st->last_ts += ts; + +	return 0; +} + +static int inv_process_dmp_interrupt(struct inv_mpu_state *st) +{ +	int r; +	u8 d[1]; +	struct iio_dev *indio_dev = iio_priv_to_dev(st); + +#define DMP_INT_SMD             0x04 +#define DMP_INT_PED             0x08 + +	if ((!st->chip_config.smd_enable) && +			(!st->ped.int_on) && +			(!st->tap.on)) +		return 0; + +	r = inv_i2c_read(st, REG_DMP_INT_STATUS, 1, d); +	if (r) +		return r; +	if (d[0] & DMP_INT_SMD) { +		sysfs_notify(&indio_dev->dev.kobj, NULL, "event_smd"); +		st->chip_config.smd_enable = false; +		st->chip_config.smd_triggered = true; +	} +	if (d[0] & DMP_INT_PED) +		sysfs_notify(&indio_dev->dev.kobj, NULL, "event_pedometer"); + +	return 0; +} + +static int inv_get_shift2(int count) +{ +	int i; + +	if (1 == count) +		return 13; +	if (count > 2000) +		return 2; +	i = 13; +	while (count > 0) { +		count >>= 1; +		i--; +	} + +	return i; +} + +static void inv_adjust_sensor_ts(struct inv_mpu_state *st, int sensor_ind) +{ +	s64 diff; +	int i, rate_adj, s3, delta, total_count; + +	if (!st->chip_config.adjust_time) +		return; +#define MAX_DIFF 0x7fffffff +	total_count = st->dmp_ticks; +	if (0 == total_count) +		total_count = 1; + +	diff = (st->last_ts - st->prev_ts) - (u64)(st->dmp_interval) * +								total_count; +	if (diff > MAX_DIFF) +		diff = MAX_DIFF; +	if (diff < -MAX_DIFF) +		diff = -MAX_DIFF; +	s3 = 4; +	rate_adj = (int)diff; +	rate_adj /= total_count; +	delta = min(abs(rate_adj) >> inv_get_shift2(total_count), +						DMP_INTERVAL_MIN_ADJ); +	if (rate_adj < 0) +		delta = -delta; +	st->dmp_interval_accum >>= 1; +	st->dmp_interval_accum += delta; +	st->dmp_interval += st->dmp_interval_accum; +	for (i = 0; i < SENSOR_NUM_MAX; i++) +		if (st->sensor[i].on) +			st->sensor[i].dur = st->dmp_interval * +						st->sensor[i].counter; + +	st->prev_ts = st->last_ts; +} + +static void inv_reset_ts(struct inv_mpu_state *st, u64 curr_ts) +{ +	u32 dur, i; + +	dur = USEC_PER_SEC / st->bytes_per_sec; +	dur *= 1024; +	curr_ts -= ((u64)dur * NSEC_PER_USEC); +	for (i = 0; i < SENSOR_NUM_MAX; i++) +		st->sensor[i].ts = curr_ts; +} + +static void inv_push_step_indicator(struct inv_mpu_state *st, int sensor_ind, +							int steps) +{ +	int dur, i; +	s16 sen[3]; +	u64 base; +#define STEP_INDICATOR_HEADER 0x0001 + +	dur = st->sensor[sensor_ind].dur / steps; +	base = st->sensor[sensor_ind].ts; + +	for (i = 1; i < steps; i++) +		inv_push_8bytes_buffer(st, STEP_INDICATOR_HEADER, +						base + i * dur, sen); +} + +static int inv_parse_header(u16 hdr) +{ +	int sensor_ind; + +	switch (hdr) { +	case ACCEL_HDR: +		sensor_ind = SENSOR_ACCEL; +		break; +	case GYRO_HDR: +		sensor_ind = SENSOR_GYRO; +		break; +	case PEDQUAT_HDR: +		sensor_ind = SENSOR_PEDQ; +		break; +	case LPQUAT_HDR: +		sensor_ind = SENSOR_LPQ; +		break; +	case SIXQUAT_HDR: +		sensor_ind = SENSOR_SIXQ; +		break; +	case COMPASS_HDR: +		sensor_ind = SENSOR_COMPASS; +		break; +	case PRESSURE_HDR: +		sensor_ind = SENSOR_PRESSURE; +		break; +	case STEP_DETECTOR_HDR: +		sensor_ind = SENSOR_STEP; +		break; +	default: +		sensor_ind = SENSOR_INVALID; +		break; +	} + +	return sensor_ind; +} + +static int inv_process_batchmode(struct inv_mpu_state *st) +{ +	int i, target_bytes, tmp, res, counter; +	int sensor_ind, q[3]; +	u8 *dptr, *d; +	u16 hdr, steps; +	s16 sen[3]; +	u64 t; +	bool done_flag; + +	if (1024 == st->fifo_count) { +		inv_reset_ts(st, st->last_ts); +		st->left_over_size = 0; +	} +	d = fifo_data; +	if (st->left_over_size > 0) { +		dptr = d + st->left_over_size; +		memcpy(d, st->left_over, st->left_over_size); +	} else { +		dptr = d; +	} +	target_bytes = st->fifo_count; +	while (target_bytes > 0) { +		if (target_bytes < MAX_READ_SIZE) +			tmp = target_bytes; +		else +			tmp = MAX_READ_SIZE; +		res = inv_i2c_read(st, st->reg.fifo_r_w, tmp, dptr); +		if (res < 0) +			return res; +		dptr += tmp; +		target_bytes -= tmp; +	} +	dptr = d; +	done_flag = false; +	target_bytes = st->fifo_count + st->left_over_size; +	counter = 0; +	while ((dptr - d <= target_bytes - HEADERED_NORMAL_BYTES) && +							(!done_flag)) { +		hdr = (u16)be16_to_cpup((__be16 *)(dptr)); +		steps = (hdr & STEP_INDICATOR_MASK); +		hdr &= (~STEP_INDICATOR_MASK); +		sensor_ind = inv_parse_header(hdr); +		/* incomplete packet */ +		if (target_bytes - (dptr - d) < +					st->sensor[sensor_ind].sample_size) { +			done_flag = true; +			continue; +		} +		/* error packet */ +		if ((sensor_ind == SENSOR_INVALID) || +				(!st->sensor[sensor_ind].on)) { +			dptr += HEADERED_NORMAL_BYTES; +			continue; +		} +		if (sensor_ind == SENSOR_STEP) { +			tmp = (int)be32_to_cpup((__be32 *)(dptr + 4)); +			t = st->step_detector_base_ts + +					(u64)tmp * 5 * NSEC_PER_MSEC; +			inv_push_8bytes_buffer(st, hdr, t, sen); +			dptr += HEADERED_NORMAL_BYTES; +			continue; +		} +		if (steps > 1) +			inv_push_step_indicator(st, sensor_ind, steps); +		st->sensor[sensor_ind].ts += (u64)st->sensor[sensor_ind].dur; +		t = st->sensor[sensor_ind].ts; +		if (sensor_ind == SENSOR_COMPASS) { +			if (!st->chip_config.normal_compass_measure) { +				st->chip_config.normal_compass_measure = 1; +				dptr += HEADERED_NORMAL_BYTES; +				continue; +			} +			for (i = 0; i < 6; i++) +				st->fifo_data[i] = dptr[i + 2]; +			res = st->slave_compass->read_data(st, sen); +			if (!res) +				inv_push_8bytes_buffer(st, hdr | +							(!!steps), t, sen); + +			dptr += HEADERED_NORMAL_BYTES; +			continue; +		} +		if (sensor_ind == SENSOR_PRESSURE) { +			if (!st->chip_config.normal_pressure_measure) { +				st->chip_config.normal_pressure_measure = 1; +				dptr += HEADERED_NORMAL_BYTES; +				continue; +			} +			for (i = 0; i < 6; i++) +				st->fifo_data[i] = dptr[i + 2]; +			res = st->slave_pressure->read_data(st, sen); +			if (!res) +				inv_push_8bytes_buffer(st, hdr | +							(!!steps), t, sen); + +			dptr += HEADERED_NORMAL_BYTES; +			continue; +		} +		if (st->sensor[sensor_ind].sample_size == HEADERED_Q_BYTES) { +			for (i = 0; i < 3; i++) +				q[i] = (int)be32_to_cpup((__be32 *)(dptr + 4 +							+ i * 4)); +			inv_push_16bytes_buffer(st, hdr | (!!steps), t, q); +		} else { +			for (i = 0; i < 3; i++) +				sen[i] = (short)be16_to_cpup((__be16 *)(dptr + +							2 + i * 2)); +			inv_push_8bytes_buffer(st, hdr | (!!steps), t, sen); +		} +		dptr += st->sensor[sensor_ind].sample_size; +	} +	inv_adjust_sensor_ts(st, sensor_ind); +	st->left_over_size = target_bytes - (dptr - d); + +	if (st->left_over_size) +		memcpy(st->left_over, dptr, st->left_over_size); + +	return 0; +} + +int inv_read_time_and_ticks(struct inv_mpu_state *st, bool resume) +{ +	int result; +	u32 counter; +	u8 data[4]; + +#define MIN_TICK_READING_TIME NSEC_PER_SEC +	st->last_ts = get_time_ns(); +	if ((st->last_ts - st->prev_ts < MIN_TICK_READING_TIME) && +							(!resume)) { +		st->chip_config.adjust_time = false; +		return 0; +	} +	result = mpu_memory_read(st, st->i2c_addr, +			inv_dmp_get_address(KEY_DMP_RUN_CNTR), 4, data); +	if (result) +		return result; + +	counter = be32_to_cpup((__be32 *)(data)); +	if (resume) { +		st->dmp_counter = counter; +		st->prev_ts = st->last_ts; + +		return 0; +	} +	if (counter > st->dmp_counter) +		st->dmp_ticks = counter - st->dmp_counter; +	else +		st->dmp_ticks = 0xffffffff - st->dmp_counter + counter + 1; +	st->dmp_counter = counter; +	st->chip_config.adjust_time = true; + +	return 0; +} + +/* + *  inv_read_fifo() - Transfer data from FIFO to ring buffer. + */ +irqreturn_t inv_read_fifo(int irq, void *dev_id) +{ + +	struct inv_mpu_state *st = (struct inv_mpu_state *)dev_id; +	struct iio_dev *indio_dev = iio_priv_to_dev(st); +	int result, bpm; +	u8 data[MAX_HW_FIFO_BYTES]; +	u16 fifo_count; +	struct inv_reg_map_s *reg; +	u64 pts1; + +#define DMP_MIN_RUN_TIME (37 * NSEC_PER_MSEC) +	mutex_lock(&st->suspend_resume_lock); +	mutex_lock(&indio_dev->mlock); +	if (st->chip_config.dmp_on) { +		pts1 = get_time_ns(); +		result = inv_process_dmp_interrupt(st); +		if (result || st->chip_config.dmp_event_int_on) +			goto end_session; +		if (!st->chip_config.smd_triggered) { +			if (pts1 - st->last_run_time < DMP_MIN_RUN_TIME) +				goto end_session; +			else +				st->last_run_time = pts1; +		} else { +			st->chip_config.smd_triggered = false; +		} +	} + +	if (!(iio_buffer_enabled(indio_dev)) || (!st->chip_config.enable)) +		goto end_session; + +	reg = &st->reg; +	if (!(st->sensor[SENSOR_ACCEL].on | +		st->sensor[SENSOR_GYRO].on | +		st->sensor[SENSOR_COMPASS].on | +		st->sensor[SENSOR_PRESSURE].on | +		st->chip_config.dmp_on | +		st->mot_int.mot_on)) +		goto end_session; +	if (st->chip_config.lpa_mode) { +		result = inv_i2c_read(st, reg->raw_accel, +						BYTES_PER_SENSOR, data); +		if (result) +			goto end_session; +		inv_report_gyro_accel(indio_dev, data, get_time_ns()); +		if (st->mot_int.mot_on) +			inv_process_motion(st); + +		goto end_session; +	} + +	if (st->chip_config.dmp_on) { +		result = inv_read_time_and_ticks(st, false); +		if (result) +			goto end_session; +	} +	bpm = st->chip_config.bytes_per_datum; +	fifo_count = 0; +	if (bpm) { +		result = inv_i2c_read(st, reg->fifo_count_h, FIFO_COUNT_BYTE, +									data); +		if (result) +			goto end_session; +		fifo_count = be16_to_cpup((__be16 *)(data)); +		/* fifo count can't be odd number */ +		if (fifo_count & 1) +			goto flush_fifo; +		if (fifo_count == 0) +			goto end_session; +		st->fifo_count = fifo_count; +	} + +	if (st->chip_config.dmp_on) { +		result = inv_process_batchmode(st); +	} else { +		if (fifo_count >  FIFO_THRESHOLD) +			goto flush_fifo; +		if (bpm) { +			while (fifo_count >= bpm) { +				result = inv_i2c_read(st, reg->fifo_r_w, bpm, +									data); +				if (result) +					goto flush_fifo; +				result = inv_get_timestamp(st, +							fifo_count / bpm); +				if (result) +					goto flush_fifo; +				inv_report_gyro_accel(indio_dev, data, +								st->last_ts); +				fifo_count -= bpm; +			} +		} else { +			result = inv_get_timestamp(st, 1); +			if (result) +				goto flush_fifo; +		} +		inv_send_compass_data(st); +		inv_send_pressure_data(st); +	} +end_session: +	mutex_unlock(&indio_dev->mlock); +	mutex_unlock(&st->suspend_resume_lock); + +	return IRQ_HANDLED; +flush_fifo: +	/* Flush HW and SW FIFOs. */ +	inv_reset_fifo(indio_dev); +	inv_clear_kfifo(st); +	mutex_unlock(&indio_dev->mlock); +	mutex_unlock(&st->suspend_resume_lock); + +	return IRQ_HANDLED; +} + +void inv_mpu_unconfigure_ring(struct iio_dev *indio_dev) +{ +	struct inv_mpu_state *st = iio_priv(indio_dev); +	free_irq(st->client->irq, st); +	iio_kfifo_free(indio_dev->buffer); +}; + +static int inv_predisable(struct iio_dev *indio_dev) +{ +	struct inv_mpu_state  *st = iio_priv(indio_dev); +	int result; + +	if (st->chip_config.enable) { +		result = set_inv_enable(indio_dev, false); +		if (result) +			return result; +		result = st->set_power_state(st, false); +		if (result) +			return result; +	} + +	return 0; +} + +static int inv_check_conflict_sysfs(struct iio_dev *indio_dev) +{ +	struct inv_mpu_state  *st = iio_priv(indio_dev); + +	if (st->chip_config.lpa_mode) { +		/* dmp cannot run with low power mode on */ +		st->chip_config.dmp_on = 0; +		st->chip_config.gyro_enable = false; +		st->sensor[SENSOR_GYRO].on = false; +		st->sensor[SENSOR_COMPASS].on = false; +	} +	if (st->sensor[SENSOR_GYRO].on && +		(!st->chip_config.gyro_enable)) { +		st->chip_config.gyro_enable = true; +	} +	if (st->sensor[SENSOR_ACCEL].on && +		(!st->chip_config.accel_enable)) { +		st->chip_config.accel_enable = true; +	} + +	return 0; +} + +static int inv_preenable(struct iio_dev *indio_dev) +{ +	int result; + +	result = inv_check_conflict_sysfs(indio_dev); +	if (result) +		return result; +	result = iio_sw_buffer_preenable(indio_dev); + +	return result; +} + +void inv_init_sensor_struct(struct inv_mpu_state *st) +{ +	int i; + +	for (i = 0; i < SENSOR_NUM_MAX; i++) { +		if (i < SENSOR_SIXQ) +			st->sensor[i].sample_size = +					HEADERED_NORMAL_BYTES; +		else +			st->sensor[i].sample_size = HEADERED_Q_BYTES; +		if (i == SENSOR_STEP) { +			st->sensor[i].rate = 1; +			st->sensor[i].dur = NSEC_PER_SEC; +		} else { +			st->sensor[i].rate = INIT_DMP_OUTPUT_RATE; +			st->sensor[i].dur  = NSEC_PER_SEC / +						INIT_DMP_OUTPUT_RATE; +		} +	} + +	st->sensor[SENSOR_ACCEL].send_data     = inv_send_accel_data; +	st->sensor[SENSOR_GYRO].send_data      = inv_send_gyro_data; +	st->sensor[SENSOR_COMPASS].send_data   = inv_send_compass_dmp_data; +	st->sensor[SENSOR_PRESSURE].send_data  = inv_send_pressure_dmp_data; +	st->sensor[SENSOR_STEP].send_data      = inv_send_step_detector; +	st->sensor[SENSOR_PEDQ].send_data      = inv_send_ped_q_data; +	st->sensor[SENSOR_SIXQ].send_data      = inv_send_six_q_data; +	st->sensor[SENSOR_LPQ].send_data       = inv_send_three_q_data; + +	st->sensor[SENSOR_ACCEL].set_rate     = inv_set_accel_rate; +	st->sensor[SENSOR_GYRO].set_rate      = inv_set_gyro_rate; +	st->sensor[SENSOR_COMPASS].set_rate   = inv_set_compass_rate; +	st->sensor[SENSOR_PRESSURE].set_rate  = inv_set_pressure_rate; +	st->sensor[SENSOR_STEP].set_rate      = inv_set_step_detector; +	st->sensor[SENSOR_PEDQ].set_rate      = inv_set_pedq_rate; +	st->sensor[SENSOR_SIXQ].set_rate      = inv_set_sixq_rate; +	st->sensor[SENSOR_LPQ].set_rate       = inv_set_lpq_rate; +} + +int inv_flush_batch_data(struct iio_dev *indio_dev, bool *has_data) +{ +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct inv_reg_map_s *reg; +	u8 data[4]; +	int result; + +	reg = &st->reg; +	if (!(iio_buffer_enabled(indio_dev)) || (!st->chip_config.enable)) +		return -EINVAL; + +	if (st->batch.on) { +		result = inv_read_time_and_ticks(st, false); +		if (result) +			return result; +		result = inv_i2c_read(st, reg->fifo_count_h, +					FIFO_COUNT_BYTE, data); +		if (result) +			return result; +		st->fifo_count = be16_to_cpup((__be16 *)(data)); +		if (st->fifo_count) { +			result = inv_process_batchmode(st); +			if (result) +				return result; +			*has_data = !!st->fifo_count; +			inv_push_marker_to_buffer(st, END_MARKER); +			result = write_be32_key_to_mem(st, 0, +						KEY_BM_BATCH_CNTR); +			return result; +		} +	} +	inv_push_marker_to_buffer(st, EMPTY_MARKER); + +	return 0; +} + +static const struct iio_buffer_setup_ops inv_mpu_ring_setup_ops = { +	.preenable = &inv_preenable, +	.predisable = &inv_predisable, +}; + +int inv_mpu_configure_ring(struct iio_dev *indio_dev) +{ +	int ret; +	struct inv_mpu_state *st = iio_priv(indio_dev); +	struct iio_buffer *ring; + +	ring = iio_kfifo_allocate(indio_dev); +	if (!ring) +		return -ENOMEM; +	indio_dev->buffer = ring; +	/* setup ring buffer */ +	ring->scan_timestamp = true; +	indio_dev->setup_ops = &inv_mpu_ring_setup_ops; +	/*scan count double count timestamp. should subtract 1. but +	number of channels still includes timestamp*/ +	if (INV_MPU3050 == st->chip_type) +		ret = request_threaded_irq(st->client->irq, inv_irq_handler, +			inv_read_fifo_mpu3050, +			IRQF_TRIGGER_RISING | IRQF_SHARED, "inv_irq", st); +	else +		ret = request_threaded_irq(st->client->irq, inv_irq_handler, +			inv_read_fifo, +			IRQF_TRIGGER_RISING | IRQF_SHARED, "inv_irq", st); +	if (ret) +		goto error_iio_sw_rb_free; + +	indio_dev->modes |= INDIO_BUFFER_TRIGGERED; + +	return 0; +error_iio_sw_rb_free: +	iio_kfifo_free(indio_dev->buffer); + +	return ret; +} + diff --git a/drivers/staging/iio/imu/inv_mpu/inv_mpu_trigger.c b/drivers/staging/iio/imu/inv_mpu/inv_mpu_trigger.c new file mode 100644 index 00000000000..78b04a6ad86 --- /dev/null +++ b/drivers/staging/iio/imu/inv_mpu/inv_mpu_trigger.c @@ -0,0 +1,80 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +* GNU General Public License for more details. +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> + +#include "inv_mpu_iio.h" + +/* + * inv_mpu_data_rdy_trigger_set_state() set data ready interrupt state + */ +static int inv_mpu_data_rdy_trigger_set_state(struct iio_trigger *trig, +						bool state) +{ +	return 0; +} + +static const struct iio_trigger_ops inv_mpu_trigger_ops = { +	.owner = THIS_MODULE, +	.set_trigger_state = &inv_mpu_data_rdy_trigger_set_state, +}; + +int inv_mpu_probe_trigger(struct iio_dev *indio_dev) +{ +	int ret; +	struct inv_mpu_state *st = iio_priv(indio_dev); + +	st->trig = iio_trigger_alloc("%s-dev%d", +					indio_dev->name, +					indio_dev->id); +	if (st->trig == NULL) +		return -ENOMEM; +	st->trig->dev.parent = &st->client->dev; +	/* st->trig->private_data = indio_dev; Linux 3.4 --mfj */ +	st->trig->ops = &inv_mpu_trigger_ops; +	ret = iio_trigger_register(st->trig); + +	if (ret) { +		iio_trigger_free(st->trig); +		return -EPERM; +	} +	indio_dev->trig = st->trig; + +	return 0; +} + +void inv_mpu_remove_trigger(struct iio_dev *indio_dev) +{ +	struct inv_mpu_state *st = iio_priv(indio_dev); + +	iio_trigger_unregister(st->trig); +	iio_trigger_free(st->trig); +} + diff --git a/drivers/staging/iio/imu/inv_mpu/inv_slave_bma250.c b/drivers/staging/iio/imu/inv_mpu/inv_slave_bma250.c new file mode 100644 index 00000000000..99ae702e67a --- /dev/null +++ b/drivers/staging/iio/imu/inv_mpu/inv_slave_bma250.c @@ -0,0 +1,315 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +* GNU General Public License for more details. +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> + +#include "inv_mpu_iio.h" +#define BMA250_CHIP_ID			3 +#define BMA250_RANGE_SET		0 +#define BMA250_BW_SET			4 + +/* range and bandwidth */ +#define BMA250_RANGE_2G                 3 +#define BMA250_RANGE_4G                 5 +#define BMA250_RANGE_8G                 8 +#define BMA250_RANGE_16G                12 +#define BMA250_RANGE_MAX                4 +#define BMA250_RANGE_MASK               0xF0 + +#define BMA250_BW_7_81HZ        0x08 +#define BMA250_BW_15_63HZ       0x09 +#define BMA250_BW_31_25HZ       0x0A +#define BMA250_BW_62_50HZ       0x0B +#define BMA250_BW_125HZ         0x0C +#define BMA250_BW_250HZ         0x0D +#define BMA250_BW_500HZ         0x0E +#define BMA250_BW_1000HZ        0x0F +#define BMA250_MAX_BW_SIZE      8 +#define BMA250_BW_REG_MASK      0xE0 + +/*      register definitions */ +#define BMA250_X_AXIS_LSB_REG                   0x02 +#define BMA250_RANGE_SEL_REG                    0x0F +#define BMA250_BW_SEL_REG                       0x10 +#define BMA250_MODE_CTRL_REG                    0x11 + +/* mode settings */ +#define BMA250_MODE_NORMAL     0 +#define BMA250_MODE_LOWPOWER   1 +#define BMA250_MODE_SUSPEND    2 +#define BMA250_MODE_MAX        3 +#define BMA250_MODE_MASK       0x3F +#define BMA250_BIT_SUSPEND     0x80 +#define BMA250_BIT_LP          0x40 + +struct bma_property { +	int range; +	int bandwidth; +	int mode; +}; + +static struct bma_property bma_static_property = { +	.range = BMA250_RANGE_SET, +	.bandwidth = BMA250_BW_SET, +	.mode = BMA250_MODE_SUSPEND +}; + +static int bma250_set_bandwidth(struct inv_mpu_state *st, u8 bw) +{ +	int res; +	u8 data; +	int bandwidth; +	switch (bw) { +	case 0: +		bandwidth = BMA250_BW_7_81HZ; +		break; +	case 1: +		bandwidth = BMA250_BW_15_63HZ; +		break; +	case 2: +		bandwidth = BMA250_BW_31_25HZ; +		break; +	case 3: +		bandwidth = BMA250_BW_62_50HZ; +		break; +	case 4: +		bandwidth = BMA250_BW_125HZ; +		break; +	case 5: +		bandwidth = BMA250_BW_250HZ; +		break; +	case 6: +		bandwidth = BMA250_BW_500HZ; +		break; +	case 7: +		bandwidth = BMA250_BW_1000HZ; +		break; +	default: +		return -EINVAL; +	} +	res = inv_secondary_read(BMA250_BW_SEL_REG, 1, &data); +	if (res) +		return res; +	data &= BMA250_BW_REG_MASK; +	data |= bandwidth; +	res = inv_secondary_write(BMA250_BW_SEL_REG, data); +	return res; +} + +static int bma250_set_range(struct inv_mpu_state *st, u8 range) +{ +	int res; +	u8 orig, data; +	switch (range) { +	case 0: +		data  = BMA250_RANGE_2G; +		break; +	case 1: +		data  = BMA250_RANGE_4G; +		break; +	case 2: +		data  = BMA250_RANGE_8G; +		break; +	case 3: +		data  = BMA250_RANGE_16G; +		break; +	default: +		return -EINVAL; +	} +	res = inv_secondary_read(BMA250_RANGE_SEL_REG, 1, &orig); +	if (res) +		return res; +	orig &= BMA250_RANGE_MASK; +	data |= orig; +	res = inv_secondary_write(BMA250_RANGE_SEL_REG, data); +	if (res) +		return res; +	bma_static_property.range = range; + +	return 0; +} + +static int setup_slave_bma250(struct inv_mpu_state *st) +{ +	int result; +	u8 data[2]; +	result = set_3050_bypass(st, true); +	if (result) +		return result; +	/*read secondary i2c ID register */ +	result = inv_secondary_read(0, 1, data); +	if (result) +		return result; +	if (BMA250_CHIP_ID != data[0]) +		return -EINVAL; +	result = set_3050_bypass(st, false); +	if (result) +		return result; +	/*AUX(accel), slave address is set inside set_3050_bypass*/ +	/* bma250 x axis LSB register address is 2 */ +	result = inv_i2c_single_write(st, REG_3050_AUX_BST_ADDR, +					BMA250_X_AXIS_LSB_REG); + +	return result; +} + +static int bma250_set_mode(struct inv_mpu_state *st, u8 mode) +{ +	int res; +	u8 data; + +	res = inv_secondary_read(BMA250_MODE_CTRL_REG, 1, &data); +	if (res) +		return res; +	data &= BMA250_MODE_MASK; +	switch (mode) { +	case BMA250_MODE_NORMAL: +		break; +	case BMA250_MODE_LOWPOWER: +		data |= BMA250_BIT_LP; +		break; +	case BMA250_MODE_SUSPEND: +		data |= BMA250_BIT_SUSPEND; +		break; +	default: +		return -EINVAL; +	} +	res = inv_secondary_write(BMA250_MODE_CTRL_REG, data); +	if (res) +		return res; +	bma_static_property.mode = mode; + +	return 0; +} + +static int suspend_slave_bma250(struct inv_mpu_state *st) +{ +	int result; +	if (bma_static_property.mode == BMA250_MODE_SUSPEND) +		return 0; +	/*set to bypass mode */ +	result = set_3050_bypass(st, true); +	if (result) +		return result; +	bma250_set_mode(st, BMA250_MODE_SUSPEND); +	/* no need to recover to non-bypass mode because we need it now */ + +	return 0; +} + +static int resume_slave_bma250(struct inv_mpu_state *st) +{ +	int result; +	if (bma_static_property.mode == BMA250_MODE_NORMAL) +		return 0; +	/*set to bypass mode */ +	result = set_3050_bypass(st, true); +	if (result) +		return result; +	result = bma250_set_mode(st, BMA250_MODE_NORMAL); +	/* recover bypass mode */ +	result |= set_3050_bypass(st, false); + +	return result ? (-EINVAL) : 0; +} + +static int combine_data_slave_bma250(u8 *in, short *out) +{ +	out[0] = le16_to_cpup((__le16 *)(&in[0])); +	out[1] = le16_to_cpup((__le16 *)(&in[2])); +	out[2] = le16_to_cpup((__le16 *)(&in[4])); + +	return 0; +} + +static int get_mode_slave_bma250(void) +{ +	switch (bma_static_property.mode) { +	case BMA250_MODE_SUSPEND: +		return INV_MODE_SUSPEND; +	case BMA250_MODE_NORMAL: +		return INV_MODE_NORMAL; +	default: +		return -EINVAL; +	} +} + +/** + *  set_lpf_bma250() - set lpf value + */ + +static int set_lpf_bma250(struct inv_mpu_state *st, int rate) +{ +	const short hz[] = {1000, 500, 250, 125, 62, 31, 15, 7}; +	const int   d[] = {7, 6, 5, 4, 3, 2, 1, 0}; +	int i, h, data, result; +	h = (rate >> 1); +	i = 0; +	while ((h < hz[i]) && (i < ARRAY_SIZE(hz) - 1)) +		i++; +	data = d[i]; + +	result = set_3050_bypass(st, true); +	if (result) +		return result; +	result = bma250_set_bandwidth(st, (u8) data); +	result |= set_3050_bypass(st, false); + +	return result ? (-EINVAL) : 0; +} +/** + *  set_fs_bma250() - set range value + */ + +static int set_fs_bma250(struct inv_mpu_state *st, int fs) +{ +	int result; +	result = set_3050_bypass(st, true); +	if (result) +		return result; +	result = bma250_set_range(st, (u8) fs); +	result |= set_3050_bypass(st, false); + +	return result ? (-EINVAL) : 0; +} + +static struct inv_mpu_slave slave_bma250 = { +	.suspend = suspend_slave_bma250, +	.resume  = resume_slave_bma250, +	.setup   = setup_slave_bma250, +	.combine_data = combine_data_slave_bma250, +	.get_mode = get_mode_slave_bma250, +	.set_lpf = set_lpf_bma250, +	.set_fs  = set_fs_bma250 +}; + +int inv_register_mpu3050_slave(struct inv_mpu_state *st) +{ +	st->slave_accel = &slave_bma250; + +	return 0; +} + diff --git a/drivers/staging/iio/imu/inv_mpu/inv_slave_compass.c b/drivers/staging/iio/imu/inv_mpu/inv_slave_compass.c new file mode 100644 index 00000000000..f7d7ff20b17 --- /dev/null +++ b/drivers/staging/iio/imu/inv_mpu/inv_slave_compass.c @@ -0,0 +1,852 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> + +#include "inv_mpu_iio.h" +/* AKM definitions */ +#define REG_AKM_ID               0x00 +#define REG_AKM_INFO             0x01 +#define REG_AKM_STATUS           0x02 +#define REG_AKM_MEASURE_DATA     0x03 +#define REG_AKM_MODE             0x0A +#define REG_AKM_ST_CTRL          0x0C +#define REG_AKM_SENSITIVITY      0x10 +#define REG_AKM8963_CNTL1        0x0A + +/* AK09911 register definition */ +#define REG_AK09911_DMP_READ    0x10 +#define REG_AK09911_STATUS1     0x10 +#define REG_AK09911_CNTL2       0x31 +#define REG_AK09911_SENSITIVITY 0x60 + +#define DATA_AKM_ID              0x48 +#define DATA_AKM_MODE_PD	 0x00 +#define DATA_AKM_MODE_SM	 0x01 +#define DATA_AKM_MODE_ST	 0x08 +#define DATA_AKM_MODE_FR	 0x0F +#define DATA_AK09911_MODE_FR     0x1F +#define DATA_AKM_SELF_TEST       0x40 +#define DATA_AKM_DRDY            0x01 +#define DATA_AKM8963_BIT         0x10 +#define DATA_AKM_STAT_MASK       0x0C + +#define DATA_AKM8975_SCALE       (9830 * (1L << 15)) +#define DATA_AKM8972_SCALE       (19661 * (1L << 15)) +#define DATA_AKM8963_SCALE0      (19661 * (1L << 15)) +#define DATA_AKM8963_SCALE1      (4915 * (1L << 15)) +#define DATA_AK09911_SCALE       (19661 * (1L << 15)) +#define DATA_MLX_SCALE           (4915 * (1L << 15)) +#define DATA_MLX_SCALE_EMPIRICAL (26214 * (1L << 15)) + +#define DATA_AKM8963_SCALE_SHIFT      4 +#define DATA_AKM_BYTES_DMP  10 +#define DATA_AKM_BYTES      8 +#define DATA_AKM_MIN_READ_TIME            (9 * NSEC_PER_MSEC) + +#define DEF_ST_COMPASS_WAIT_MIN     (10 * 1000) +#define DEF_ST_COMPASS_WAIT_MAX     (15 * 1000) +#define DEF_ST_COMPASS_TRY_TIMES    10 +#define DEF_ST_COMPASS_8963_SHIFT   2 +#define X                           0 +#define Y                           1 +#define Z                           2 + +/* milliseconds between each access */ +#define AKM_RATE_SCALE       10 +#define MLX_RATE_SCALE       50 + +/* MLX90399 compass definition */ +#define DATA_MLX_CMD_READ_MEASURE         0x4F +#define DATA_MLX_CMD_SINGLE_MEASURE       0x3F +#define DATA_MLX_READ_DATA_BYTES          9 +#define DATA_MLX_STATUS_DATA              3 +#define DATA_MLX_MIN_READ_TIME            (95 * NSEC_PER_MSEC) + +static const short AKM8975_ST_Lower[3] = {-100, -100, -1000}; +static const short AKM8975_ST_Upper[3] = {100, 100, -300}; + +static const short AKM8972_ST_Lower[3] = {-50, -50, -500}; +static const short AKM8972_ST_Upper[3] = {50, 50, -100}; + +static const short AKM8963_ST_Lower[3] = {-200, -200, -3200}; +static const short AKM8963_ST_Upper[3] = {200, 200, -800}; + +/* + *  inv_setup_compass_akm() - Configure akm series compass. + */ +static int inv_setup_compass_akm(struct inv_mpu_state *st) +{ +	int result; +	u8 data[4]; +	u8 sens, mode, cmd; + +	/* set to bypass mode */ +	result = inv_i2c_single_write(st, REG_INT_PIN_CFG, +				st->plat_data.int_config | BIT_BYPASS_EN); +	if (result) +		return result; +	/* read secondary i2c ID register */ +	result = inv_secondary_read(REG_AKM_ID, 1, data); +	if (result) +		return result; +	if (data[0] != DATA_AKM_ID) +		return -ENXIO; +	/* set AKM to Fuse ROM access mode */ +	if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id) { +		mode = REG_AK09911_CNTL2; +		sens = REG_AK09911_SENSITIVITY; +		cmd = DATA_AK09911_MODE_FR; +	} else { +		mode = REG_AKM_MODE; +		sens = REG_AKM_SENSITIVITY; +		cmd = DATA_AKM_MODE_FR; +	} + +	result = inv_secondary_write(mode, cmd); +	if (result) +		return result; +	result = inv_secondary_read(sens, THREE_AXIS, +						st->chip_info.compass_sens); +	if (result) +		return result; +	/* revert to power down mode */ +	result = inv_secondary_write(mode, DATA_AKM_MODE_PD); +	if (result) +		return result; +	pr_debug("%s senx=%d, seny=%d, senz=%d\n", +		 st->hw->name, +		 st->chip_info.compass_sens[0], +		 st->chip_info.compass_sens[1], +		 st->chip_info.compass_sens[2]); +	/* restore to non-bypass mode */ +	result = inv_i2c_single_write(st, REG_INT_PIN_CFG, +			st->plat_data.int_config); +	if (result) +		return result; + +	/* setup master mode and master clock and ES bit */ +	result = inv_i2c_single_write(st, REG_I2C_MST_CTRL, BIT_WAIT_FOR_ES); +	if (result) +		return result; +	/* slave 1 is used for AKM mode change only */ +	result = inv_i2c_single_write(st, REG_I2C_SLV1_ADDR, +		st->plat_data.secondary_i2c_addr); +	if (result) +		return result; +	/* AKM mode register address */ +	result = inv_i2c_single_write(st, REG_I2C_SLV1_REG, mode); +	if (result) +		return result; +	/* output data for slave 1 is fixed, single measure mode */ +	st->slave_compass->scale = 1; +	if (COMPASS_ID_AK8975 == st->plat_data.sec_slave_id) { +		st->slave_compass->st_upper = AKM8975_ST_Upper; +		st->slave_compass->st_lower = AKM8975_ST_Lower; +		data[0] = DATA_AKM_MODE_SM; +	} else if (COMPASS_ID_AK8972 == st->plat_data.sec_slave_id) { +		st->slave_compass->st_upper = AKM8972_ST_Upper; +		st->slave_compass->st_lower = AKM8972_ST_Lower; +		data[0] = DATA_AKM_MODE_SM; +	} else if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id) { +		st->slave_compass->st_upper = AKM8963_ST_Upper; +		st->slave_compass->st_lower = AKM8963_ST_Lower; +		data[0] = DATA_AKM_MODE_SM | +			(st->slave_compass->scale << DATA_AKM8963_SCALE_SHIFT); +	}  else if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id) { +		st->slave_compass->st_upper = AKM8963_ST_Upper; +		st->slave_compass->st_lower = AKM8963_ST_Lower; +		data[0] = DATA_AKM_MODE_SM; +	} else { +		return -EINVAL; +	} + +	result = inv_i2c_single_write(st, INV_MPU_REG_I2C_SLV1_DO, data[0]); + +	return result; +} + +static int inv_akm_read_data(struct inv_mpu_state *st, short *o) +{ +	int result, shift; +	int i; +	u8 d[DATA_AKM_BYTES]; +	u8 *sens; + +	sens = st->chip_info.compass_sens; +	result = 0; +	if (st->chip_config.dmp_on && +			(COMPASS_ID_AK09911 != st->plat_data.sec_slave_id)) { +		for (i = 0; i < 6; i++) +			d[1 + i] = st->fifo_data[i]; +	} else { +		result = inv_i2c_read(st, REG_EXT_SENS_DATA_00, +						DATA_AKM_BYTES, d); +		if ((DATA_AKM_DRDY != d[0]) || result) +			result = -EINVAL; +	} +	if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id) +		shift = 7; +	else +		shift = 8; +	for (i = 0; i < 3; i++) { +		o[i] = (short)((d[i * 2 + 2] << 8) | d[i * 2 + 1]); +		o[i] = (short)(((int)o[i] * (sens[i] + 128)) >> shift); +	} + +	return result; +} + +static int inv_mlx_read_data(struct inv_mpu_state *st, short *o) +{ +	int result; +	int i, z; +	u8 d[DATA_MLX_READ_DATA_BYTES]; + +	result = inv_i2c_read(st, REG_EXT_SENS_DATA_00, +			     DATA_MLX_READ_DATA_BYTES, d); +	if ((!(d[0] & ~DATA_MLX_STATUS_DATA)) && (!result)) { +		for (i = 0; i < 3; i++) +			o[i] = (short)((d[i * 2 + 3] << 8) + d[i * 2 + 4]); +	} else { +		for (i = 0; i < 3; i++) +			o[i] = 0; +	} +	z = o[2]; +	/* axis sensitivity conversion. Z axis has different sensitiviy from +	   x and y */ +	z *= 26; +	z /= 15; +	o[2] = z; + +	return 0; +} + +static int inv_check_akm_self_test(struct inv_mpu_state *st) +{ +	int result; +	u8 data[6], mode; +	u8 counter, cntl; +	short x, y, z; +	u8 *sens; +	sens = st->chip_info.compass_sens; + +	/* set to bypass mode */ +	result = inv_i2c_single_write(st, REG_INT_PIN_CFG, +				st->plat_data.int_config | BIT_BYPASS_EN); +	if (result) { +		result = inv_i2c_single_write(st, REG_INT_PIN_CFG, +				st->plat_data.int_config); +		return result; +	} +	if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id) +		mode = REG_AK09911_CNTL2; +	else +		mode = REG_AKM_MODE; +	/* set to power down mode */ +	result = inv_secondary_write(mode, DATA_AKM_MODE_PD); +	if (result) +		goto AKM_fail; + +	/* write 1 to ASTC register */ +	result = inv_secondary_write(REG_AKM_ST_CTRL, DATA_AKM_SELF_TEST); +	if (result) +		goto AKM_fail; +	/* set self test mode */ +	result = inv_secondary_write(mode, DATA_AKM_MODE_ST); +	if (result) +		goto AKM_fail; +	counter = DEF_ST_COMPASS_TRY_TIMES; +	while (counter > 0) { +		usleep_range(DEF_ST_COMPASS_WAIT_MIN, DEF_ST_COMPASS_WAIT_MAX); +		result = inv_secondary_read(REG_AKM_STATUS, 1, data); +		if (result) +			goto AKM_fail; +		if ((data[0] & DATA_AKM_DRDY) == 0) +			counter--; +		else +			counter = 0; +	} +	if ((data[0] & DATA_AKM_DRDY) == 0) { +		result = -EINVAL; +		goto AKM_fail; +	} +	result = inv_secondary_read(REG_AKM_MEASURE_DATA, +					BYTES_PER_SENSOR, data); +	if (result) +		goto AKM_fail; + +	x = le16_to_cpup((__le16 *)(&data[0])); +	y = le16_to_cpup((__le16 *)(&data[2])); +	z = le16_to_cpup((__le16 *)(&data[4])); +	x = ((x * (sens[0] + 128)) >> 8); +	y = ((y * (sens[1] + 128)) >> 8); +	z = ((z * (sens[2] + 128)) >> 8); +	if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id) { +		result = inv_secondary_read(REG_AKM8963_CNTL1, 1, &cntl); +		if (result) +			goto AKM_fail; +		if (0 == (cntl & DATA_AKM8963_BIT)) { +			x <<= DEF_ST_COMPASS_8963_SHIFT; +			y <<= DEF_ST_COMPASS_8963_SHIFT; +			z <<= DEF_ST_COMPASS_8963_SHIFT; +		} +	} +	result = -EINVAL; +	if (x > st->slave_compass->st_upper[X] || +					x < st->slave_compass->st_lower[X]) +		goto AKM_fail; +	if (y > st->slave_compass->st_upper[Y] || +					y < st->slave_compass->st_lower[Y]) +		goto AKM_fail; +	if (z > st->slave_compass->st_upper[Z] || +					z < st->slave_compass->st_lower[Z]) +		goto AKM_fail; +	result = 0; +AKM_fail: +	/*write 0 to ASTC register */ +	result |= inv_secondary_write(REG_AKM_ST_CTRL, 0); +	/*set to power down mode */ +	result |= inv_secondary_write(mode, DATA_AKM_MODE_PD); +	/*restore to non-bypass mode */ +	result |= inv_i2c_single_write(st, REG_INT_PIN_CFG, +			st->plat_data.int_config); +	return result; +} + +/* + *  inv_write_akm_scale() - Configure the akm scale range. + */ +static int inv_write_akm_scale(struct inv_mpu_state *st, int data) +{ +	char d, en; +	int result; + +	if (COMPASS_ID_AK8963 != st->plat_data.sec_slave_id) +		return 0; +	en = !!data; +	if (st->slave_compass->scale == en) +		return 0; +	d = (DATA_AKM_MODE_SM | (en << DATA_AKM8963_SCALE_SHIFT)); +	result = inv_i2c_single_write(st, INV_MPU_REG_I2C_SLV1_DO, d); +	if (result) +		return result; +	st->slave_compass->scale = en; + +	return 0; +} + +/* + *  inv_read_akm_scale() - show AKM scale. + */ +static int inv_read_akm_scale(struct inv_mpu_state *st, int *scale) +{ +	if (COMPASS_ID_AK8975 == st->plat_data.sec_slave_id) +		*scale = DATA_AKM8975_SCALE; +	else if (COMPASS_ID_AK8972 == st->plat_data.sec_slave_id) +		*scale = DATA_AKM8972_SCALE; +	else if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id) +		if (st->slave_compass->scale) +			*scale = DATA_AKM8963_SCALE1; +		else +			*scale = DATA_AKM8963_SCALE0; +	else if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id) +		*scale = DATA_AK09911_SCALE; +	else +		return -EINVAL; + +	return IIO_VAL_INT; +} + +static int inv_suspend_akm(struct inv_mpu_state *st) +{ +	int result; + +	/* slave 0 is disabled */ +	result = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, 0); +	if (result) +		return result; +	/* slave 1 is disabled */ +	result = inv_i2c_single_write(st, REG_I2C_SLV1_CTRL, 0); + +	return result; +} + +static int inv_resume_akm(struct inv_mpu_state *st) +{ +	int result; +	u8 reg_addr, bytes; + +	/* slave 0 is used to read data from compass */ +	/*read mode */ +	result = inv_i2c_single_write(st, REG_I2C_SLV0_ADDR, +					INV_MPU_BIT_I2C_READ | +					st->plat_data.secondary_i2c_addr); +	if (result) +		return result; +	/* AKM status register address is 1 */ +	if (COMPASS_ID_AK09911 == st->plat_data.sec_slave_id) { +		if (st->chip_config.dmp_on) { +			reg_addr = REG_AK09911_DMP_READ; +			bytes = DATA_AKM_BYTES_DMP; +		} else { +			reg_addr = REG_AK09911_STATUS1; +			bytes = DATA_AKM_BYTES; +		} +	} else { +		if (st->chip_config.dmp_on) { +			reg_addr = REG_AKM_INFO; +			bytes = DATA_AKM_BYTES_DMP; +		} else { +			reg_addr = REG_AKM_STATUS; +			bytes = DATA_AKM_BYTES; +		} +	} +	result = inv_i2c_single_write(st, REG_I2C_SLV0_REG, reg_addr); +	if (result) +		return result; + +	/* slave 0 is enabled, read 10 or 8 bytes from here */ +	result = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, +						INV_MPU_BIT_SLV_EN | bytes); +	if (result) +		return result; +	/* slave 1 is enabled, write byte length is 1 */ +	result = inv_i2c_single_write(st, REG_I2C_SLV1_CTRL, +						INV_MPU_BIT_SLV_EN | 1); + +	return result; +} + +/* + *  inv_write_mlx_scale() - Configure the mlx90399 scale range. + */ +static int inv_write_mlx_scale(struct inv_mpu_state *st, int data) +{ +	st->slave_compass->scale = data; +	return 0; +} + +/* + *  inv_read_mlx_scale() - show mlx90399 scale. + */ +static int inv_read_mlx_scale(struct inv_mpu_state *st, int *scale) +{ +	*scale = st->slave_compass->scale; +	return IIO_VAL_INT; +} + +static int inv_i2c_read_mlx(struct inv_mpu_state *st, u16 i2c_addr, +			    u16 length, u8 *data) +{ +	struct i2c_msg msgs[1]; +	int res; + +	if (!data) +		return -EINVAL; + +	msgs[0].addr = i2c_addr; +	msgs[0].flags = I2C_M_RD; +	msgs[0].buf = data; +	msgs[0].len = length; + +	res = i2c_transfer(st->sl_handle, msgs, 1); + +	if (res < 1) { +		if (res >= 0) +			res = -EIO; +	} else +		res = 0; + +	return res; +} + +static int inv_i2c_write_mlx(struct inv_mpu_state *st, +	u16 i2c_addr, u8 data) +{ +	u8 tmp[1]; +	struct i2c_msg msg; +	int res; + +	tmp[0] = data; +	msg.addr = i2c_addr; +	msg.flags = 0;	/* write */ +	msg.buf = tmp; +	msg.len = 1; + +	res = i2c_transfer(st->sl_handle, &msg, 1); +	if (res < 1) { +		if (res == 0) +			res = -EIO; +		return res; +	} else +		return 0; +} + +static int inv_i2c_read_reg_mlx(struct inv_mpu_state *st, +	u16 i2c_addr, u8 reg, u16 *val) +{ +	u8 tmp[10]; +	struct i2c_msg msg; +	int res; + +	tmp[0] = 0x50; +	tmp[1] = (reg << 2); +	msg.addr = i2c_addr; +	msg.flags = 0;	/* write */ +	msg.buf = tmp; +	msg.len = 2; + +	res = i2c_transfer(st->sl_handle, &msg, 1); +	if (res < 1) { +		if (res == 0) +			res = -EIO; +		return res; +	} +	res = inv_i2c_read_mlx(st, i2c_addr, 10, tmp); +	if (res) +		return res; +	*val = ((tmp[1] << 8) | tmp[2]); + +	return res; +} + +static int inv_i2c_write_mlx_reg(struct inv_mpu_state *st, +	u16 i2c_addr, int reg, u16 d) +{ +	u8 tmp[10]; +	struct i2c_msg msg; +	int res; + +	/* write register command, writing volatile memory */ +	tmp[0] = 0x60; +	tmp[1] = ((d >> 8) & 0xff); +	tmp[2] = (d & 0xff); +	tmp[3] = (reg << 2); +	msg.addr = i2c_addr; +	msg.flags = 0;	/* write */ +	msg.buf = tmp; +	msg.len = 4; + +	res = i2c_transfer(st->sl_handle, &msg, 1); +	if (res < 1) { +		if (res == 0) +			res = -EIO; +		return res; +	} +	/* read status */ +	res = inv_i2c_read_mlx(st, i2c_addr, 10, tmp); + +	return res; +} + +static int inv_write_mlx_cmd(struct inv_mpu_state *st, u8 cmd) +{ +	int result; +	u8 d[10]; +	int addr; + +	addr = st->plat_data.secondary_i2c_addr; +	result = inv_i2c_write_mlx(st, addr, cmd); +	if (result) +		return result; +	/* read back status byte */ +	result = inv_i2c_read_mlx(st, addr, 10, d); + +	return result; +} + +static int inv_read_mlx_z_axis(struct inv_mpu_state *st, s16 *z) +{ +	int result; +	u8 d[10]; +	int addr; + +	addr = st->plat_data.secondary_i2c_addr; + +	/* measure z axis */ +	result = inv_write_mlx_cmd(st, 0x39); +	if (result) +		return result; +	msleep(100); +	/* read z axis */ +	result = inv_i2c_write_mlx(st, addr, 0x49); +	if (result) +		return result; +	/* read back status byte */ +	result = inv_i2c_read_mlx(st, addr, 10, d); +	if (result) +		return result; +	if ((d[0] & 0x3) == 1) +		*z = (short)((d[3] << 8) + d[4]); +	else +		return -EINVAL; + +	return 0; +} + +static int inv_write_mlx_reg(struct inv_mpu_state *st) +{ +	int result; +	int addr; +	u16 r_val; + +	addr = st->plat_data.secondary_i2c_addr; + +	/* write register 0. +	   set GAIN_SEL as 7; +	   set HALL_CONF as 0xC. */ +	result = inv_i2c_write_mlx_reg(st, addr, 0, 0x7c); +	if (result) +		return result; +	/* write register 2. +	   set resolution is zero for all axes; +	   set DIGI filter as 6. +	   set OSR as 0. +	   set OSR2 as 0. */ +	result = inv_i2c_write_mlx_reg(st, addr, 2, 0x18); +	if (result) +		return result; +	/* read register 1 */ +	result = inv_i2c_read_reg_mlx(st, addr, 1, &r_val); +	if (result) +		return result; +	/* enable temp comp */ +	r_val |= 0x400; +	result = inv_i2c_write_mlx_reg(st, addr, 1, r_val); +	/* the value should be kept in the volatile memory */ + +	return result; +} + +static int inv_check_mlx_self_test(struct inv_mpu_state *st) +{ +	int result; +	int addr; +	s16 meas_ref, meas_coil; +	u16 diff, r_val; + +	/* set to bypass mode */ +	result = inv_i2c_single_write(st, REG_INT_PIN_CFG, +				st->plat_data.int_config | BIT_BYPASS_EN); +	if (result) { +		result = inv_i2c_single_write(st, REG_INT_PIN_CFG, +				st->plat_data.int_config); +		return result; +	} + +	addr = st->plat_data.secondary_i2c_addr; + +	/* fake read to flush the previous data */ +	result = inv_read_mlx_z_axis(st, &meas_ref); + +	result = inv_read_mlx_z_axis(st, &meas_ref); +	if (result) +		return result; + +	/* read register 1 */ +	result = inv_i2c_read_reg_mlx(st, addr, 0, &r_val); +	if (result) +		return result; +	/* enable self test */ +	r_val |= 0x100; +	result = inv_i2c_write_mlx_reg(st, addr, 0, r_val); +	if (result) +		return result; +	msleep(200); +	result = inv_read_mlx_z_axis(st, &meas_coil); +	if (result) +		return result; +	result = inv_write_mlx_cmd(st, 0xD0); +	if (result) +		return result; +	result = inv_write_mlx_reg(st); +	if (result) +		return result; +	diff = abs(meas_ref - meas_coil); +	if (diff < 25 || diff > 300) +		result = 1; + +	/*restore to non-bypass mode */ +	result |= inv_i2c_single_write(st, REG_INT_PIN_CFG, +			st->plat_data.int_config); + +	return result; +} + +/* + *  inv_setup_compass_mlx() - Configure akm series compass. + */ +static int inv_setup_compass_mlx(struct inv_mpu_state *st) +{ +	int result; +	int addr; + +	addr = st->plat_data.secondary_i2c_addr; +	/* set to bypass mode */ +	result = inv_i2c_single_write(st, REG_INT_PIN_CFG, +				st->plat_data.int_config | BIT_BYPASS_EN); +	if (result) +		return result; +	result = inv_write_mlx_reg(st); +	if (result) +		return result; + +	/*restore to non-bypass mode */ +	result = inv_i2c_single_write(st, REG_INT_PIN_CFG, +						st->plat_data.int_config); +	if (result) +		return result; + +	/*setup master mode and master clock and ES bit*/ +	result = inv_i2c_single_write(st, REG_I2C_MST_CTRL, BIT_WAIT_FOR_ES); +	if (result) +		return result; + +	/* slave 0 used to write read measurement command, write mode */ +	result = inv_i2c_single_write(st, REG_I2C_SLV0_ADDR, addr); +	if (result) +		return result; +	/* ignore the register address, send out data only */ +	result = inv_i2c_single_write(st, INV_MPU_REG_I2C_SLV0_DO, +					DATA_MLX_CMD_READ_MEASURE); +	if (result) +		return result; + +	/* slave 1 used to read status bytes and data of read measurement */ +	result = inv_i2c_single_write(st, REG_I2C_SLV1_ADDR, +						INV_MPU_BIT_I2C_READ | addr); +	if (result) +		return result; +	/* slave 2 used to write single measurement command, write mode */ +	result = inv_i2c_single_write(st, REG_I2C_SLV2_ADDR, addr); +	if (result) +		return result; +	/* ignore the register address, send out data only */ +	result = inv_i2c_single_write(st, INV_MPU_REG_I2C_SLV2_DO, +					DATA_MLX_CMD_SINGLE_MEASURE); +	if (result) +		return result; +	/* slave 3 used to read status bytes and data of read measurement */ +	result = inv_i2c_single_write(st, REG_I2C_SLV3_ADDR, +					INV_MPU_BIT_I2C_READ | addr); + +	st->slave_compass->scale = DATA_MLX_SCALE; + +	return result; +} + +static int inv_suspend_mlx(struct inv_mpu_state *st) +{ +	int result; + +	result = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, 0); +	if (result) +		return result; +	result = inv_i2c_single_write(st, REG_I2C_SLV1_CTRL, 0); +	if (result) +		return result; +	result = inv_i2c_single_write(st, REG_I2C_SLV2_CTRL, 0); +	if (result) +		return result; +	result = inv_i2c_single_write(st, REG_I2C_SLV3_CTRL, 0); + +	return result; +} + +static int inv_resume_mlx(struct inv_mpu_state *st) +{ +	int result; + +	/* enable, ignore register, write 1 bytes */ +	result = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, +						INV_MPU_BIT_SLV_EN | +						INV_MPU_BIT_REG_DIS | +						1); +	if (result) +		return result; + +	/* enable, ignore register, read 9 bytes */ +	result = inv_i2c_single_write(st, REG_I2C_SLV1_CTRL, +						INV_MPU_BIT_SLV_EN | +						INV_MPU_BIT_REG_DIS | +						DATA_MLX_READ_DATA_BYTES); +	if (result) +		return result; +	/* enable, ignore register, write 1 bytes */ +	result = inv_i2c_single_write(st, REG_I2C_SLV2_CTRL, +						INV_MPU_BIT_SLV_EN | +						INV_MPU_BIT_REG_DIS | +						1); +	if (result) +		return result; + +	/* enable, ignore register, read 1 bytes */ +	result = inv_i2c_single_write(st, REG_I2C_SLV3_CTRL, +						INV_MPU_BIT_SLV_EN | +						INV_MPU_BIT_REG_DIS | +						1); + +	return result; +} + +static struct inv_mpu_slave slave_akm = { +	.suspend   = inv_suspend_akm, +	.resume    = inv_resume_akm, +	.get_scale = inv_read_akm_scale, +	.set_scale = inv_write_akm_scale, +	.self_test = inv_check_akm_self_test, +	.setup     = inv_setup_compass_akm, +	.read_data = inv_akm_read_data, +	.rate_scale = AKM_RATE_SCALE, +	.min_read_time = DATA_AKM_MIN_READ_TIME, +}; + +static struct inv_mpu_slave slave_mlx90399 = { +	.suspend   = inv_suspend_mlx, +	.resume    = inv_resume_mlx, +	.get_scale = inv_read_mlx_scale, +	.set_scale = inv_write_mlx_scale, +	.self_test = inv_check_mlx_self_test, +	.setup     = inv_setup_compass_mlx, +	.read_data = inv_mlx_read_data, +	.rate_scale = MLX_RATE_SCALE, +	.min_read_time = DATA_MLX_MIN_READ_TIME, +}; + +int inv_mpu_setup_compass_slave(struct inv_mpu_state *st) +{ +	switch (st->plat_data.sec_slave_id) { +	case COMPASS_ID_AK8975: +	case COMPASS_ID_AK8972: +	case COMPASS_ID_AK8963: +	case COMPASS_ID_AK09911: +		st->slave_compass = &slave_akm; +		break; +	case COMPASS_ID_MLX90399: +		st->slave_compass = &slave_mlx90399; +		break; +	default: +		return -EINVAL; +	} + +	return st->slave_compass->setup(st); +} + diff --git a/drivers/staging/iio/imu/inv_mpu/inv_slave_pressure.c b/drivers/staging/iio/imu/inv_mpu/inv_slave_pressure.c new file mode 100644 index 00000000000..7843556c732 --- /dev/null +++ b/drivers/staging/iio/imu/inv_mpu/inv_slave_pressure.c @@ -0,0 +1,510 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> + +#include "inv_mpu_iio.h" + +/* Constants */ +#define SHIFT_RIGHT_4_POSITION				 4 +#define SHIFT_LEFT_2_POSITION                2 +#define SHIFT_LEFT_4_POSITION                4 +#define SHIFT_LEFT_5_POSITION                5 +#define SHIFT_LEFT_8_POSITION                8 +#define SHIFT_LEFT_12_POSITION               12 +#define SHIFT_LEFT_16_POSITION               16 + +/* Sensor Specific constants */ +#define BMP280_SLEEP_MODE                    0x00 +#define BMP280_FORCED_MODE                   0x01 +#define BMP280_NORMAL_MODE                   0x03 +#define BMP280_SOFT_RESET                    0xB6 + +#define BMP280_DELAYTIME_MS_NONE             0 +#define BMP280_DELAYTIME_MS_5                5 +#define BMP280_DELAYTIME_MS_6                6 +#define BMP280_DELAYTIME_MS_8                8 +#define BMP280_DELAYTIME_MS_12               12 +#define BMP280_DELAYTIME_MS_22               22 +#define BMP280_DELAYTIME_MS_38               38 + +#define BMP280_OVERSAMPLING_SKIPPED          0x00 +#define BMP280_OVERSAMPLING_1X               0x01 +#define BMP280_OVERSAMPLING_2X               0x02 +#define BMP280_OVERSAMPLING_4X               0x03 +#define BMP280_OVERSAMPLING_8X               0x04 +#define BMP280_OVERSAMPLING_16X              0x05 + +#define BMP280_ULTRALOWPOWER_MODE            0x00 +#define BMP280_LOWPOWER_MODE	             0x01 +#define BMP280_STANDARDRESOLUTION_MODE       0x02 +#define BMP280_HIGHRESOLUTION_MODE           0x03 +#define BMP280_ULTRAHIGHRESOLUTION_MODE      0x04 + +#define BMP280_ULTRALOWPOWER_OSRS_P          BMP280_OVERSAMPLING_1X +#define BMP280_ULTRALOWPOWER_OSRS_T          BMP280_OVERSAMPLING_1X + +#define BMP280_LOWPOWER_OSRS_P	             BMP280_OVERSAMPLING_2X +#define BMP280_LOWPOWER_OSRS_T	             BMP280_OVERSAMPLING_1X + +#define BMP280_STANDARDRESOLUTION_OSRS_P     BMP280_OVERSAMPLING_4X +#define BMP280_STANDARDRESOLUTION_OSRS_T     BMP280_OVERSAMPLING_1X + +#define BMP280_HIGHRESOLUTION_OSRS_P         BMP280_OVERSAMPLING_8X +#define BMP280_HIGHRESOLUTION_OSRS_T         BMP280_OVERSAMPLING_1X + +#define BMP280_ULTRAHIGHRESOLUTION_OSRS_P    BMP280_OVERSAMPLING_16X +#define BMP280_ULTRAHIGHRESOLUTION_OSRS_T    BMP280_OVERSAMPLING_2X + +#define BMP280_FILTERCOEFF_OFF               0x00 +#define BMP280_FILTERCOEFF_2                 0x01 +#define BMP280_FILTERCOEFF_4                 0x02 +#define BMP280_FILTERCOEFF_8                 0x03 +#define BMP280_FILTERCOEFF_16                0x04 + +/*calibration parameters */ +#define BMP280_DIG_T1_LSB_REG                0x88 +#define BMP280_DIG_T1_MSB_REG                0x89 +#define BMP280_DIG_T2_LSB_REG                0x8A +#define BMP280_DIG_T2_MSB_REG                0x8B +#define BMP280_DIG_T3_LSB_REG                0x8C +#define BMP280_DIG_T3_MSB_REG                0x8D +#define BMP280_DIG_P1_LSB_REG                0x8E +#define BMP280_DIG_P1_MSB_REG                0x8F +#define BMP280_DIG_P2_LSB_REG                0x90 +#define BMP280_DIG_P2_MSB_REG                0x91 +#define BMP280_DIG_P3_LSB_REG                0x92 +#define BMP280_DIG_P3_MSB_REG                0x93 +#define BMP280_DIG_P4_LSB_REG                0x94 +#define BMP280_DIG_P4_MSB_REG                0x95 +#define BMP280_DIG_P5_LSB_REG                0x96 +#define BMP280_DIG_P5_MSB_REG                0x97 +#define BMP280_DIG_P6_LSB_REG                0x98 +#define BMP280_DIG_P6_MSB_REG                0x99 +#define BMP280_DIG_P7_LSB_REG                0x9A +#define BMP280_DIG_P7_MSB_REG                0x9B +#define BMP280_DIG_P8_LSB_REG                0x9C +#define BMP280_DIG_P8_MSB_REG                0x9D +#define BMP280_DIG_P9_LSB_REG                0x9E +#define BMP280_DIG_P9_MSB_REG                0x9F + +#define BMP280_CHIPID_REG                    0xD0  /*Chip ID Register */ +#define BMP280_RESET_REG                     0xE0  /*Softreset Register */ +#define BMP280_STATUS_REG                    0xF3  /*Status Register */ +#define BMP280_CTRLMEAS_REG                  0xF4  /*Ctrl Measure Register */ +#define BMP280_CONFIG_REG                    0xF5  /*Configuration Register */ +#define BMP280_PRESSURE_MSB_REG              0xF7  /*Pressure MSB Register */ +#define BMP280_PRESSURE_LSB_REG              0xF8  /*Pressure LSB Register */ +#define BMP280_PRESSURE_XLSB_REG             0xF9  /*Pressure XLSB Register */ +#define BMP280_TEMPERATURE_MSB_REG           0xFA  /*Temperature MSB Reg */ +#define BMP280_TEMPERATURE_LSB_REG           0xFB  /*Temperature LSB Reg */ +#define BMP280_TEMPERATURE_XLSB_REG          0xFC  /*Temperature XLSB Reg */ + +/* Status Register */ +#define BMP280_STATUS_REG_MEASURING__POS           3 +#define BMP280_STATUS_REG_MEASURING__MSK           0x08 +#define BMP280_STATUS_REG_MEASURING__LEN           1 +#define BMP280_STATUS_REG_MEASURING__REG           BMP280_STATUS_REG + +#define BMP280_STATUS_REG_IMUPDATE__POS            0 +#define BMP280_STATUS_REG_IMUPDATE__MSK            0x01 +#define BMP280_STATUS_REG_IMUPDATE__LEN            1 +#define BMP280_STATUS_REG_IMUPDATE__REG            BMP280_STATUS_REG + +/* Control Measurement Register */ +#define BMP280_CTRLMEAS_REG_OSRST__POS             5 +#define BMP280_CTRLMEAS_REG_OSRST__MSK             0xE0 +#define BMP280_CTRLMEAS_REG_OSRST__LEN             3 +#define BMP280_CTRLMEAS_REG_OSRST__REG             BMP280_CTRLMEAS_REG + +#define BMP280_CTRLMEAS_REG_OSRSP__POS             2 +#define BMP280_CTRLMEAS_REG_OSRSP__MSK             0x1C +#define BMP280_CTRLMEAS_REG_OSRSP__LEN             3 +#define BMP280_CTRLMEAS_REG_OSRSP__REG             BMP280_CTRLMEAS_REG + +#define BMP280_CTRLMEAS_REG_MODE__POS              0 +#define BMP280_CTRLMEAS_REG_MODE__MSK              0x03 +#define BMP280_CTRLMEAS_REG_MODE__LEN              2 +#define BMP280_CTRLMEAS_REG_MODE__REG              BMP280_CTRLMEAS_REG + +/* Configuation Register */ +#define BMP280_CONFIG_REG_TSB__POS                 5 +#define BMP280_CONFIG_REG_TSB__MSK                 0xE0 +#define BMP280_CONFIG_REG_TSB__LEN                 3 +#define BMP280_CONFIG_REG_TSB__REG                 BMP280_CONFIG_REG + +#define BMP280_CONFIG_REG_FILTER__POS              2 +#define BMP280_CONFIG_REG_FILTER__MSK              0x1C +#define BMP280_CONFIG_REG_FILTER__LEN              3 +#define BMP280_CONFIG_REG_FILTER__REG              BMP280_CONFIG_REG + +#define BMP280_CONFIG_REG_SPI3WEN__POS             0 +#define BMP280_CONFIG_REG_SPI3WEN__MSK             0x01 +#define BMP280_CONFIG_REG_SPI3WEN__LEN             1 +#define BMP280_CONFIG_REG_SPI3WEN__REG             BMP280_CONFIG_REG + +/* Data Register */ +#define BMP280_PRESSURE_XLSB_REG_DATA__POS         4 +#define BMP280_PRESSURE_XLSB_REG_DATA__MSK         0xF0 +#define BMP280_PRESSURE_XLSB_REG_DATA__LEN         4 +#define BMP280_PRESSURE_XLSB_REG_DATA__REG         BMP280_PRESSURE_XLSB_REG + +#define BMP280_TEMPERATURE_XLSB_REG_DATA__POS      4 +#define BMP280_TEMPERATURE_XLSB_REG_DATA__MSK      0xF0 +#define BMP280_TEMPERATURE_XLSB_REG_DATA__LEN      4 +#define BMP280_TEMPERATURE_XLSB_REG_DATA__REG      BMP280_TEMPERATURE_XLSB_REG + +#define BMP280_RATE_SCALE  34 +#define DATA_BMP280_MIN_READ_TIME            (32 * NSEC_PER_MSEC) +#define BMP280_DATA_BYTES  6 +#define FAKE_DATA_NUM_BYTES 10 + +/** this structure holds all device specific calibration parameters */ +struct bmp280_calibration_param_t { +	u32 dig_T1; +	s32 dig_T2; +	s32 dig_T3; +	u32 dig_P1; +	s32 dig_P2; +	s32 dig_P3; +	s32 dig_P4; +	s32 dig_P5; +	s32 dig_P6; +	s32 dig_P7; +	s32 dig_P8; +	s32 dig_P9; + +	s32 t_fine; +}; +/** BMP280 image registers data structure */ +struct bmp280_t { +	struct bmp280_calibration_param_t cal_param; + +	u8 chip_id; +	u8 dev_addr; + +	u8 waittime; + +	u8 osrs_t; +	u8 osrs_p; +}; +static struct bmp280_t bmp280; + +static int bmp280_get_calib_param(struct inv_mpu_state *st) +{ +	u8 d[24]; +	int r; + +	r = inv_aux_read(BMP280_DIG_T1_LSB_REG, 24, d); +	if (r) +		return r; + +	bmp280.cal_param.dig_T1 = (u16)((((u16)((u8)d[1])) << +		SHIFT_LEFT_8_POSITION) | d[0]); +	bmp280.cal_param.dig_T2 = (s16)((((s16)((s8)d[3])) << +		SHIFT_LEFT_8_POSITION) | d[2]); +	bmp280.cal_param.dig_T3 = (s16)((((s16)((s8)d[5])) << +		SHIFT_LEFT_8_POSITION) | d[4]); +	bmp280.cal_param.dig_P1 = (u16)((((u16)((u8)d[7])) << +		SHIFT_LEFT_8_POSITION) | d[6]); +	bmp280.cal_param.dig_P2 = (s16)((((s16)((s8)d[9])) << +		SHIFT_LEFT_8_POSITION) | d[8]); +	bmp280.cal_param.dig_P3 = (s16)((((s16)((s8)d[11])) << +		SHIFT_LEFT_8_POSITION) | d[10]); +	bmp280.cal_param.dig_P4 = (s16)((((s16)((s8)d[13])) << +		SHIFT_LEFT_8_POSITION) | d[12]); +	bmp280.cal_param.dig_P5 = (s16)((((s16)((s8)d[15])) << +		SHIFT_LEFT_8_POSITION) | d[14]); +	bmp280.cal_param.dig_P6 = (s16)((((s16)((s8)d[17])) << +		SHIFT_LEFT_8_POSITION) | d[16]); +	bmp280.cal_param.dig_P7 = (s16)((((s16)((s8)d[19])) << +		SHIFT_LEFT_8_POSITION) | d[18]); +	bmp280.cal_param.dig_P8 = (s16)((((s16)((s8)d[21])) << +		SHIFT_LEFT_8_POSITION) | d[20]); +	bmp280.cal_param.dig_P9 = (s16)((((s16)((s8)d[23])) << +		SHIFT_LEFT_8_POSITION) | d[22]); + +	return 0; +} + +static int inv_setup_bmp280(struct inv_mpu_state *st) +{ +	int r; +	u8 d[10]; + +	/* set to bypass mode */ +	r = inv_i2c_single_write(st, REG_INT_PIN_CFG, +				st->plat_data.int_config | BIT_BYPASS_EN); +	if (r) +		return r; +	/* issue soft reset */ +	r = inv_aux_write(BMP280_RESET_REG, BMP280_SOFT_RESET); +	if (r) +		return r; +	msleep(100); +	r = inv_aux_read(BMP280_CHIPID_REG, 1, d); +	if (r) +		return r; +	/* set pressure as ultra high resolution */ +	bmp280.osrs_t = BMP280_ULTRAHIGHRESOLUTION_OSRS_T; +	bmp280.osrs_p = BMP280_ULTRAHIGHRESOLUTION_OSRS_P; + +	/* set IIR filter as 4 */ +	r = inv_aux_write(BMP280_CONFIG_REG_FILTER__REG, +			BMP280_FILTERCOEFF_16 << SHIFT_LEFT_2_POSITION); +	if (r) +		return r; +	r = bmp280_get_calib_param(st); +	if (r) +		return r; + +	/*restore to non-bypass mode */ +	r = inv_i2c_single_write(st, REG_INT_PIN_CFG, +			st->plat_data.int_config); +	if (r) +		return r; + +	/* setup master mode and master clock and ES bit */ +	r = inv_i2c_single_write(st, REG_I2C_MST_CTRL, BIT_WAIT_FOR_ES); +	if (r) +		return r; +	/*slave 3 is used for pressure mode change only*/ +	r = inv_i2c_single_write(st, REG_I2C_SLV3_ADDR, +						st->plat_data.aux_i2c_addr); +	if (r) +		return r; +	/* pressure sensor mode register address */ +	r = inv_i2c_single_write(st, REG_I2C_SLV3_REG, BMP280_CTRLMEAS_REG); +	if (r) +		return r; +	d[0] = (bmp280.osrs_t << SHIFT_LEFT_5_POSITION) + +			(bmp280.osrs_p << SHIFT_LEFT_2_POSITION) + +						BMP280_FORCED_MODE; +	r = inv_i2c_single_write(st, INV_MPU_REG_I2C_SLV3_DO, d[0]); + +	return r; +} + +static int inv_check_bmp280_self_test(struct inv_mpu_state *st) +{ +	return 0; +} +static int inv_write_bmp280_scale(struct inv_mpu_state *st, int data) +{ +	return 0; +} +static int inv_read_bmp280_scale(struct inv_mpu_state *st, int *scale) +{ +	return 0; +} + +static int inv_resume_bmp280(struct inv_mpu_state *st) +{ +	int r; + +	if ((!st->sensor[SENSOR_COMPASS].on) && st->chip_config.dmp_on) { +		/* if compass is disabled, read fake data for DMP */ +		/*read mode */ +		r = inv_i2c_single_write(st, REG_I2C_SLV0_ADDR, +						INV_MPU_BIT_I2C_READ | +						st->plat_data.aux_i2c_addr); +		if (r) +			return r; +		/* read calibration data as the fake data */ +		r = inv_i2c_single_write(st, REG_I2C_SLV0_REG, +						BMP280_DIG_T1_LSB_REG); +		if (r) +			return r; +		/* slave 0 is enabled, read 10 bytes from here */ +		r = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, +						INV_MPU_BIT_SLV_EN | +						FAKE_DATA_NUM_BYTES); +	} + +	/* slave 2 is used to read data from pressure sensor */ +	/*read mode */ +	r = inv_i2c_single_write(st, REG_I2C_SLV2_ADDR, +					INV_MPU_BIT_I2C_READ | +					st->plat_data.aux_i2c_addr); +	if (r) +		return r; +	/* start from pressure sensor  */ +	r = inv_i2c_single_write(st, REG_I2C_SLV2_REG, +					BMP280_PRESSURE_MSB_REG); +	if (r) +		return r; + +	/* slave 2 is enabled, read 6 bytes from here */ +	r = inv_i2c_single_write(st, REG_I2C_SLV2_CTRL, +				INV_MPU_BIT_SLV_EN | BMP280_DATA_BYTES); +	if (r) +		return r; +	/* slave 3 is enabled, write byte length is 1 */ +	r = inv_i2c_single_write(st, REG_I2C_SLV3_CTRL, +						INV_MPU_BIT_SLV_EN | 1); + +	return r; +} + +static int inv_suspend_bmp280(struct inv_mpu_state *st) +{ +	int r; + +	if ((!st->sensor[SENSOR_COMPASS].on) && st->chip_config.dmp_on) { +		/* slave 0 is disabled */ +		r = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, 0); +		if (r) +			return r; +	} + +	/* slave 2 is disabled */ +	r = inv_i2c_single_write(st, REG_I2C_SLV2_CTRL, 0); +	if (r) +		return r; +	/* slave 3 is disabled */ +	r = inv_i2c_single_write(st, REG_I2C_SLV3_CTRL, 0); + +	return r; +} + +static s32 bmp280_compensate_T_int32(s32 adc_t) +{ +	s32 v_x1_u32r = 0; +	s32 v_x2_u32r = 0; +	s32 temperature = 0; + +	v_x1_u32r  = ((((adc_t >> 3) - ((s32) +		bmp280.cal_param.dig_T1 << 1))) * +		((s32)bmp280.cal_param.dig_T2)) >> 11; +	v_x2_u32r  = (((((adc_t >> 4) - +		((s32)bmp280.cal_param.dig_T1)) * ((adc_t >> 4) - +		((s32)bmp280.cal_param.dig_T1))) >> 12) * +		((s32)bmp280.cal_param.dig_T3)) >> 14; +	bmp280.cal_param.t_fine = v_x1_u32r + v_x2_u32r; +	temperature  = (bmp280.cal_param.t_fine * 5 + 128) >> 8; + +	return temperature; +} + + +static u32 bmp280_compensate_P_int32(s32 adc_p) +{ +	s32 v_x1_u32r = 0; +	s32 v_x2_u32r = 0; +	u32 pressure = 0; + +	v_x1_u32r = (((s32)bmp280.cal_param.t_fine) >> 1) - +		(s32)64000; +	v_x2_u32r = (((v_x1_u32r >> 2) * (v_x1_u32r >> 2)) >> 11) * +		((s32)bmp280.cal_param.dig_P6); +	v_x2_u32r = v_x2_u32r + ((v_x1_u32r * +		((s32)bmp280.cal_param.dig_P5)) << 1); +	v_x2_u32r = (v_x2_u32r >> 2) + +		(((s32)bmp280.cal_param.dig_P4) << 16); +	v_x1_u32r = (((bmp280.cal_param.dig_P3 * (((v_x1_u32r >> 2) * +		(v_x1_u32r >> 2)) >> 13)) >> 3) + +		((((s32)bmp280.cal_param.dig_P2) * +		v_x1_u32r) >> 1)) >> 18; +	v_x1_u32r = ((((32768+v_x1_u32r)) * +		((s32)bmp280.cal_param.dig_P1))	>> 15); +	/* Avoid exception caused by division by zero */ +	if (v_x1_u32r == 0) +		return 0; +	pressure = (((u32)(((s32)1048576) - adc_p) - +		(v_x2_u32r >> 12))) * 3125; +	if (pressure < 0x80000000) +		pressure = (pressure << 1) / ((u32)v_x1_u32r); +	else +		pressure = (pressure / (u32)v_x1_u32r) * 2; +	v_x1_u32r = (((s32)bmp280.cal_param.dig_P9) * +		((s32)(((pressure >> 3) * (pressure >> 3)) >> 13))) +		>> 12; +	v_x2_u32r = (((s32)(pressure >> 2)) * +		((s32)bmp280.cal_param.dig_P8)) >> 13; +	pressure = (u32)((s32)pressure + +		((v_x1_u32r + v_x2_u32r + bmp280.cal_param.dig_P7) >> 4)); + +	return pressure; +} + +static int inv_bmp280_read_data(struct inv_mpu_state *st, short *o) +{ +	int r, i; +	u8 d[BMP280_DATA_BYTES], reg_addr; +	s32 upressure, utemperature; + +	if (st->chip_config.dmp_on) { +		for (i = 0; i < 6; i++) +			d[i] = st->fifo_data[i]; +	} else { +		if (st->sensor[SENSOR_COMPASS].on) +			reg_addr = REG_EXT_SENS_DATA_08; +		else +			reg_addr = REG_EXT_SENS_DATA_00; +		r = inv_i2c_read(st, reg_addr, BMP280_DATA_BYTES, d); +		if (r) +			return r; +	} +	/* pressure */ +	upressure = (s32)((((s32)(d[0])) +		<< SHIFT_LEFT_12_POSITION) | (((u32)(d[1])) +		<< SHIFT_LEFT_4_POSITION) | ((u32)d[2] >> +		SHIFT_RIGHT_4_POSITION)); + +	/* Temperature */ +	utemperature = (s32)((( +		(s32) (d[3])) << SHIFT_LEFT_12_POSITION) | +		(((u32)(d[4])) << SHIFT_LEFT_4_POSITION) +		| ((u32)d[5] >> SHIFT_RIGHT_4_POSITION)); + +	bmp280_compensate_T_int32(utemperature); +	r = bmp280_compensate_P_int32(upressure); +	o[0] = 0; +	o[1] = (r >> 16); +	o[2] = (r & 0xffff); + +	return 0; +} + +static struct inv_mpu_slave slave_bmp280 = { +	.suspend   = inv_suspend_bmp280, +	.resume    = inv_resume_bmp280, +	.get_scale = inv_read_bmp280_scale, +	.set_scale = inv_write_bmp280_scale, +	.self_test = inv_check_bmp280_self_test, +	.setup     = inv_setup_bmp280, +	.read_data = inv_bmp280_read_data, +	.rate_scale = BMP280_RATE_SCALE, +	.min_read_time = DATA_BMP280_MIN_READ_TIME, +}; + +int inv_mpu_setup_pressure_slave(struct inv_mpu_state *st) +{ +	switch (st->plat_data.aux_slave_id) { +	case PRESSURE_ID_BMP280: +		st->slave_pressure = &slave_bmp280; +		break; +	default: +		return -EINVAL; +	} + +	return st->slave_pressure->setup(st); +} + diff --git a/drivers/staging/iio/imu/inv_mpu/olio_specials.h b/drivers/staging/iio/imu/inv_mpu/olio_specials.h new file mode 100644 index 00000000000..e882c3eea86 --- /dev/null +++ b/drivers/staging/iio/imu/inv_mpu/olio_specials.h @@ -0,0 +1,17 @@ +/* olio_specials.h - header specific to Olio Devices */ + +/*  + * Description: + * ============ + * This file holds a couple of #defines needed by the Olio H1 to set the + * low power states the way we want them. + */ + +/*  + * Modification History: + * ===================== + * 01a, 20150331, mfj  Created + */ + +#define OLIO_DEF_FREQ  0x03 +#define OLIO_DEF_THRES 0xE0  /* 224 */ diff --git a/drivers/staging/iio/inv_test/Kconfig b/drivers/staging/iio/inv_test/Kconfig new file mode 100644 index 00000000000..86c30bd8a63 --- /dev/null +++ b/drivers/staging/iio/inv_test/Kconfig @@ -0,0 +1,13 @@ +# +# Kconfig for Invensense IIO testing hooks +# + +config INV_TESTING +    boolean "Invensense IIO testing hooks" +    depends on INV_MPU_IIO || INV_AMI306_IIO || INV_YAS530 || INV_HUB_IIO +    default n +    help +      This flag enables display of additional testing information from the +      Invensense IIO drivers. +      It also enables the I2C counters facility to perform IO profiling. +      Some additional sysfs entries will appear when this flag is enabled. diff --git a/drivers/staging/iio/inv_test/Makefile b/drivers/staging/iio/inv_test/Makefile new file mode 100644 index 00000000000..4f0edd3de90 --- /dev/null +++ b/drivers/staging/iio/inv_test/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for Invensense IIO testing hooks. +# + +obj-$(CONFIG_INV_TESTING) += inv_counters.o + diff --git a/drivers/staging/iio/inv_test/inv_counters.c b/drivers/staging/iio/inv_test/inv_counters.c new file mode 100644 index 00000000000..3b26ca97284 --- /dev/null +++ b/drivers/staging/iio/inv_test/inv_counters.c @@ -0,0 +1,154 @@ +/* + * @file inv_counters.c + * @brief Exports i2c read write counts through sysfs + * + * @version 0.1 + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/miscdevice.h> +#include <linux/err.h> +#include <linux/sysfs.h> +#include <linux/kdev_t.h> +#include <linux/string.h> +#include <linux/jiffies.h> +#include <linux/spinlock.h> +#include <linux/kernel_stat.h> + +#include "inv_counters.h" + +static int mpu_irq; +static int accel_irq; +static int compass_irq; + +struct inv_counters { +	uint32_t i2c_tempreads; +	uint32_t i2c_mpureads; +	uint32_t i2c_mpuwrites; +	uint32_t i2c_accelreads; +	uint32_t i2c_accelwrites; +	uint32_t i2c_compassreads; +	uint32_t i2c_compasswrites; +	uint32_t i2c_compassirq; +	uint32_t i2c_accelirq; +}; + +static struct inv_counters Counters; + +static ssize_t i2c_counters_show(struct class *cls, +			struct class_attribute *attr, char *buf) +{ +	return scnprintf(buf, PAGE_SIZE, +		"%ld.%03ld %u %u %u %u %u %u %u %u %u %u\n", +		jiffies / HZ, ((jiffies % HZ) * (1024 / HZ)), +		mpu_irq ? kstat_irqs(mpu_irq) : 0, +		Counters.i2c_tempreads, +		Counters.i2c_mpureads, Counters.i2c_mpuwrites, +		accel_irq ? kstat_irqs(accel_irq) : Counters.i2c_accelirq, +		Counters.i2c_accelreads, Counters.i2c_accelwrites, +		compass_irq ? kstat_irqs(compass_irq) : Counters.i2c_compassirq, +		Counters.i2c_compassreads, Counters.i2c_compasswrites); +} + +void inv_iio_counters_set_i2cirq(enum irqtype type, int irq) +{ +	switch (type) { +	case IRQ_MPU: +		mpu_irq = irq; +		break; +	case IRQ_ACCEL: +		accel_irq = irq; +		break; +	case IRQ_COMPASS: +		compass_irq = irq; +		break; +	} +} +EXPORT_SYMBOL_GPL(inv_iio_counters_set_i2cirq); + +void inv_iio_counters_tempread(int count) +{ +	Counters.i2c_tempreads += count; +} +EXPORT_SYMBOL_GPL(inv_iio_counters_tempread); + +void inv_iio_counters_mpuread(int count) +{ +	Counters.i2c_mpureads += count; +} +EXPORT_SYMBOL_GPL(inv_iio_counters_mpuread); + +void inv_iio_counters_mpuwrite(int count) +{ +	Counters.i2c_mpuwrites += count; +} +EXPORT_SYMBOL_GPL(inv_iio_counters_mpuwrite); + +void inv_iio_counters_accelread(int count) +{ +	Counters.i2c_accelreads += count; +} +EXPORT_SYMBOL_GPL(inv_iio_counters_accelread); + +void inv_iio_counters_accelwrite(int count) +{ +	Counters.i2c_accelwrites += count; +} +EXPORT_SYMBOL_GPL(inv_iio_counters_accelwrite); + +void inv_iio_counters_compassread(int count) +{ +	Counters.i2c_compassreads += count; +} +EXPORT_SYMBOL_GPL(inv_iio_counters_compassread); + +void inv_iio_counters_compasswrite(int count) +{ +	Counters.i2c_compasswrites += count; +} +EXPORT_SYMBOL_GPL(inv_iio_counters_compasswrite); + +void inv_iio_counters_compassirq(void) +{ +	Counters.i2c_compassirq++; +} +EXPORT_SYMBOL_GPL(inv_iio_counters_compassirq); + +void inv_iio_counters_accelirq(void) +{ +	Counters.i2c_accelirq++; +} +EXPORT_SYMBOL_GPL(inv_iio_counters_accelirq); + +static struct class_attribute inv_class_attr[] = { +	__ATTR(i2c_counter, S_IRUGO, i2c_counters_show, NULL), +	__ATTR_NULL +}; + +static struct class inv_counters_class = { +	.name = "inv_counters", +	.owner = THIS_MODULE, +	.class_attrs = (struct class_attribute *) &inv_class_attr +}; + +static int __init inv_counters_init(void) +{ +	memset(&Counters, 0, sizeof(Counters)); + +	return class_register(&inv_counters_class); +} + +static void __exit inv_counters_exit(void) +{ +	class_unregister(&inv_counters_class); +} + +module_init(inv_counters_init); +module_exit(inv_counters_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("GESL"); +MODULE_DESCRIPTION("inv_counters debug support"); + diff --git a/drivers/staging/iio/inv_test/inv_counters.h b/drivers/staging/iio/inv_test/inv_counters.h new file mode 100644 index 00000000000..d60dac9d97b --- /dev/null +++ b/drivers/staging/iio/inv_test/inv_counters.h @@ -0,0 +1,72 @@ +/* + * @file  inv_counters.h + * @brief Debug file to keep track of various counters for the InvenSense + *        sensor drivers. + * + * @version 0.1 + */ + +#ifndef _INV_COUNTERS_H_ +#define _INV_COUNTERS_H_ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/sysfs.h> +#include <linux/string.h> +#include <linux/jiffies.h> +#include <linux/spinlock.h> + +#ifdef CONFIG_INV_TESTING + +enum irqtype { +	IRQ_MPU, +	IRQ_ACCEL, +	IRQ_COMPASS +}; + +#define INV_I2C_INC_MPUREAD(x)		inv_iio_counters_mpuread(x) +#define INV_I2C_INC_MPUWRITE(x)		inv_iio_counters_mpuwrite(x) +#define INV_I2C_INC_ACCELREAD(x)	inv_iio_counters_accelread(x) +#define INV_I2C_INC_ACCELWRITE(x)	inv_iio_counters_accelwrite(x) +#define INV_I2C_INC_COMPASSREAD(x)	inv_iio_counters_compassread(x) +#define INV_I2C_INC_COMPASSWRITE(x)	inv_iio_counters_compasswrite(x) + +#define INV_I2C_INC_TEMPREAD(x)		inv_iio_counters_tempread(x) + +#define INV_I2C_SETIRQ(type, irq)	inv_iio_counters_set_i2cirq(type, irq) +#define INV_I2C_INC_COMPASSIRQ()	inv_iio_counters_compassirq() +#define INV_I2C_INC_ACCELIRQ()		inv_iio_counters_accelirq() + +void inv_iio_counters_mpuread(int count); +void inv_iio_counters_mpuwrite(int count); +void inv_iio_counters_accelread(int count); +void inv_iio_counters_accelwrite(int count); +void inv_iio_counters_compassread(int count); +void inv_iio_counters_compasswrite(int count); + +void inv_iio_counters_tempread(int count); + +void inv_iio_counters_set_i2cirq(enum irqtype type, int irq); +void inv_iio_counters_compassirq(void); +void inv_iio_counters_accelirq(void); + +#else + +#define INV_I2C_INC_MPUREAD(x) +#define INV_I2C_INC_MPUWRITE(x) +#define INV_I2C_INC_ACCELREAD(x) +#define INV_I2C_INC_ACCELWRITE(x) +#define INV_I2C_INC_COMPASSREAD(x) +#define INV_I2C_INC_COMPASSWRITE(x) + +#define INV_I2C_INC_TEMPREAD(x) + +#define INV_I2C_SETIRQ(type, irq) +#define INV_I2C_INC_COMPASSIRQ() +#define INV_I2C_INC_ACCELIRQ() + +#endif /* CONFIG_INV_TESTING */ + +#endif /* _INV_COUNTERS_H_ */ + diff --git a/drivers/usb/phy/phy-ulpi.c b/drivers/usb/phy/phy-ulpi.c index 217339dd7a9..67f48dd8dca 100644 --- a/drivers/usb/phy/phy-ulpi.c +++ b/drivers/usb/phy/phy-ulpi.c @@ -47,6 +47,8 @@ struct ulpi_info {  static struct ulpi_info ulpi_ids[] = {  	ULPI_INFO(ULPI_ID(0x04cc, 0x1504), "NXP ISP1504"),  	ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"), +    ULPI_INFO(ULPI_ID(0x0451, 0x1507), "TI TUSB1210"), +    ULPI_INFO(ULPI_ID(0x0451, 0x1508), "TI TUSB1211"),  };  static int ulpi_set_otg_flags(struct usb_phy *phy) diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig index af8f97ebbe0..92f8d32b63e 100644 --- a/drivers/video/omap2/displays/Kconfig +++ b/drivers/video/omap2/displays/Kconfig @@ -16,7 +16,13 @@ config PANEL_TFP410  	help  	  Driver for TFP410 DPI-to-DVI chip. The driver uses i2c to read EDID  	  information from the monitor. - +	   +config PANEL_ILI_9342 +	tristate "ili9342 display controller" +	depends on OMAP2_DSS_DPI && SPI +	help +	  LCD Display controller used on the Olio H1 +	    config PANEL_LGPHILIPS_LB035Q02  	tristate "LG.Philips LB035Q02 LCD Panel"  	depends on OMAP2_DSS_DPI && SPI diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile index abc0d9dae56..3fb5d3ac750 100644 --- a/drivers/video/omap2/displays/Makefile +++ b/drivers/video/omap2/displays/Makefile @@ -1,5 +1,6 @@  obj-$(CONFIG_PANEL_GENERIC_DPI) += panel-generic-dpi.o  obj-$(CONFIG_PANEL_TFP410) += panel-tfp410.o +obj-$(CONFIG_PANEL_ILI_9342) += panel-ili9342.o  obj-$(CONFIG_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o  obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o  obj-$(CONFIG_PANEL_NEC_NL8048HL11_01B) += panel-nec-nl8048hl11-01b.o diff --git a/drivers/video/omap2/displays/panel-ili9342.c b/drivers/video/omap2/displays/panel-ili9342.c new file mode 100644 index 00000000000..40a9713aa0d --- /dev/null +++ b/drivers/video/omap2/displays/panel-ili9342.c @@ -0,0 +1,598 @@ +#define DEBUG +/* + * Driver for ili9342 display driver + * + * Copyright (C) 2014,2015 Olio Devices Inc. + * Author: Evan Wilson <evan@oliodevices.com> + * Author: Mattis Fjallstrom <mattis@oliodevices.com> + * + * Adapted from panel-generic-dpi.c, panel-nec-nl8048hl11-01b.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/gpio.h> +#include <linux/device.h> +#include <linux/regulator/consumer.h> + +#include <video/omapdss.h> + +#define OLIODEBUG +#ifdef OLIODEBUG +#define oliodebug(...) printk ( __VA_ARGS__ ) +#else  +#define oliodebug(...)  +#endif  + +struct panel_config { +	struct omap_video_timings timings; + +	int power_on_delay; +	int power_off_delay; + +	/* +	 * Used to match device to panel configuration +	 * when use generic panel driver +	 */ +	const char *name; +}; + +/* Panel configurations */ +static struct panel_config ili9342_panels[] = { +	/* Olio H1 panel */ +	{ +		{ +			.x_res		= 320, +			.y_res		= 240, + +			.pixel_clock	= 5333, + +			.hsw		= 10, +			.hfp		= 10, +			.hbp		= 10, + +			.vsw		= 1, +			.vfp		= 1, +			.vbp		= 1, + +			.vsync_level	= OMAPDSS_SIG_ACTIVE_LOW, +			.hsync_level	= OMAPDSS_SIG_ACTIVE_LOW, +			.data_pclk_edge	= OMAPDSS_DRIVE_SIG_RISING_EDGE, +			.de_level	= OMAPDSS_SIG_ACTIVE_HIGH, +			.sync_pclk_edge	= OMAPDSS_DRIVE_SIG_RISING_EDGE, +		}, +		.name			= "olio_h1_panel", +	}, +}; + +struct panel_drv_data { + +	struct omap_dss_device *dssdev; + +	struct panel_config *panel_config; + +	struct mutex lock; +}; + +static int ili9342_panel_power_on(struct omap_dss_device *dssdev) +{ +	int r; +	struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); +	struct panel_config *panel_config = drv_data->panel_config; + +	if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) +		return 0; + +	omapdss_dpi_set_timings(dssdev, &dssdev->panel.timings); +	omapdss_dpi_set_data_lines(dssdev, dssdev->phy.dpi.data_lines); + +	r = omapdss_dpi_display_enable(dssdev); +	if (r) +		goto err0; + +	/* wait couple of vsyncs until enabling the LCD */ +	if (panel_config->power_on_delay) +		msleep(panel_config->power_on_delay); + +	if (dssdev->platform_enable) { +		r = dssdev->platform_enable(dssdev); +		if (r) +			goto err1; +	} + +	return 0; +err1: +	omapdss_dpi_display_disable(dssdev); +err0: +	return r; +} + +static void ili9342_panel_power_off(struct omap_dss_device *dssdev) +{ +	struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); +	struct panel_config *panel_config = drv_data->panel_config; + +	if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) +		return; + +	if (dssdev->platform_disable) +		dssdev->platform_disable(dssdev); + +	/* wait couple of vsyncs after disabling the LCD */ +	if (panel_config->power_off_delay) +		msleep(panel_config->power_off_delay); + +	omapdss_dpi_display_disable(dssdev); +} + +static int ili9342_panel_probe(struct omap_dss_device *dssdev) +{ +	struct panel_config *panel_config = NULL; +	struct panel_drv_data *drv_data = NULL; +	int i; + +	dev_dbg(&dssdev->dev, "probe\n"); + +	if (!dssdev || !dssdev->name) +		return -EINVAL; + +	for (i = 0; i < ARRAY_SIZE(ili9342_panels); i++) { +		if (strcmp(dssdev->name, ili9342_panels[i].name) == 0) { +			panel_config = &ili9342_panels[i]; +			break; +		} +	} + +	if (!panel_config) { +		dev_err(&dssdev->dev, "Could not find %s in ili9342 panel configs\n", dssdev->name); +		return -EINVAL; +	} + +	dssdev->panel.timings = panel_config->timings; + +	drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL); +	if (!drv_data) +		return -ENOMEM; + +	drv_data->dssdev = dssdev; +	drv_data->panel_config = panel_config; + +	mutex_init(&drv_data->lock); + +	dev_set_drvdata(&dssdev->dev, drv_data); + +	return 0; +} + +static void __exit ili9342_panel_remove(struct omap_dss_device *dssdev) +{ +	dev_dbg(&dssdev->dev, "remove\n"); + +	dev_set_drvdata(&dssdev->dev, NULL); +} + +static int ili9342_panel_enable(struct omap_dss_device *dssdev) +{ +	struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); +	int r; + +	mutex_lock(&drv_data->lock); + +	r = ili9342_panel_power_on(dssdev); +	if (r) +		goto err; + +	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; +err: +	mutex_unlock(&drv_data->lock); + +	return r; +} + +static void ili9342_panel_disable(struct omap_dss_device *dssdev) +{ +	struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); + +	mutex_lock(&drv_data->lock); + +	ili9342_panel_power_off(dssdev); + +	dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + +	mutex_unlock(&drv_data->lock); +} + +static void ili9342_panel_set_timings(struct omap_dss_device *dssdev, +		struct omap_video_timings *timings) +{ +	struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); + +	mutex_lock(&drv_data->lock); + +	omapdss_dpi_set_timings(dssdev, timings); + +	dssdev->panel.timings = *timings; + +	mutex_unlock(&drv_data->lock); +} + +static void ili9342_panel_get_timings(struct omap_dss_device *dssdev, +		struct omap_video_timings *timings) +{ +	struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); + +	mutex_lock(&drv_data->lock); + +	*timings = dssdev->panel.timings; + +	mutex_unlock(&drv_data->lock); +} + +static int ili9342_panel_check_timings(struct omap_dss_device *dssdev, +		struct omap_video_timings *timings) +{ +	struct panel_drv_data *drv_data = dev_get_drvdata(&dssdev->dev); +	int r; + +	mutex_lock(&drv_data->lock); + +	r = dpi_check_timings(dssdev, timings); + +	mutex_unlock(&drv_data->lock); + +	return r; +} + +static int ili9342_get_recommended_bpp(struct omap_dss_device *dssdev) { +	return 24; +} + +/*************************************************************************** + * suspend & resume  + *  + * For now, this is all handled by the SPI driver (see below). + * Leaving this functions here should we change our minds.  + */ + +static int ili9342_disp_suspend (struct device * dev) { +  ili9342_panel_disable((struct omap_dss_device *) dev); +  return 0; +} + +static int ili9342_disp_resume (struct device * dev) { +  ili9342_panel_enable((struct omap_dss_device *) dev); +  return 0; +} + +static struct dev_pm_ops ili9342_disp_pm_ops = { +    SET_SYSTEM_SLEEP_PM_OPS(ili9342_disp_suspend, ili9342_disp_resume) +}; + + +static struct omap_dss_driver ili9342_driver = { +	.probe		= ili9342_panel_probe, +	.remove		= __exit_p(ili9342_panel_remove), + +	.enable		= ili9342_panel_enable, +	.disable	= ili9342_panel_disable, + +	.set_timings	= ili9342_panel_set_timings, +	.get_timings	= ili9342_panel_get_timings, +	.check_timings	= ili9342_panel_check_timings, + +	.get_recommended_bpp = ili9342_get_recommended_bpp, + +	.driver         = { +		.name   = "ili9342_panel", +		.owner  = THIS_MODULE, +        .pm     = &ili9342_disp_pm_ops, +	}, +}; + +/* ====================================================================== */ + +/* Here follows the SPI driver - it's required by the panel, but the  + * coupling is rather weak. It registers another driver. + */ + +/* ====================================================================== */ + +static struct regulator *spi_regulator; + +static int ili9342_spi_write(struct spi_device *spi, bool cmd, unsigned char val) { +	unsigned short buf; +	struct spi_message m; +	struct spi_transfer t = { +			.tx_buf = &buf, +			.len = 2, +			.bits_per_word = 9, +	}; +	int r; + +	if(cmd) { +		buf = 0; +	} else { +		buf = 1 << 8; +	} +	buf |= val; + +	dev_dbg(&spi->dev, "SPI sync: %x", buf); +	spi_message_init(&m); +	spi_message_add_tail(&t, &m); +	r = spi_sync(spi, &m); +	if(r < 0) { +		dev_err(&spi->dev, "SPI sync failed."); +		return -EINVAL; +	} +	return 0; +} + +static int ili9342_write_cmd(struct spi_device *spi, unsigned char val) { +	return ili9342_spi_write(spi, 1, val); +} + +static int ili9342_write_data(struct spi_device *spi, unsigned char val) { +	return ili9342_spi_write(spi, 0, val); +} + +static inline void ili9342_init_seq(struct spi_device *spi) { +	ili9342_write_cmd(spi, 0xC8); +	ili9342_write_data(spi, 0xFF); +	ili9342_write_data(spi, 0x93); +	ili9342_write_data(spi, 0x42); + +	ili9342_write_cmd(spi, 0xB0); +	ili9342_write_data(spi, 0xE0); + +//  Timing settings + +//	ili9342_write_cmd(spi, 0xB5); +//	ili9342_write_data(spi, 0x04); +//	ili9342_write_data(spi, 0x01); +//	ili9342_write_data(spi, 0x0a); +//	ili9342_write_data(spi, 0x14); + +	//ili9342_write_cmd(spi, 0xB1); +	//ili9342_write_data(spi, 0x00); +	//ili9342_write_data(spi, 0x10); + +	ili9342_write_cmd(spi, 0x0B); +	ili9342_write_data(spi, 0x20); + +	ili9342_write_cmd(spi, 0xF6); +	ili9342_write_data(spi, 0x01); +	ili9342_write_data(spi, 0x00); +	ili9342_write_data(spi, 0x06); + + +	ili9342_write_cmd(spi, 0xB4); +	ili9342_write_data(spi, 0x00); + +	ili9342_write_cmd(spi, 0xC0); +	ili9342_write_data(spi, 0x16); +	ili9342_write_data(spi, 0x11); + +	ili9342_write_cmd(spi, 0xC1); +	ili9342_write_data(spi, 0x01); + +	ili9342_write_cmd(spi, 0xC5); +	ili9342_write_data(spi, 0xDB); + +// RGB color mode + +	//ili9342_write_cmd(spi, 0x36); +	//ili9342_write_data(spi, 0x08); + +	ili9342_write_cmd(spi, 0xB6); +	ili9342_write_data(spi, 0x0A); +	ili9342_write_data(spi, 0x00); +	ili9342_write_data(spi, 0x1D); +	ili9342_write_data(spi, 0x04); +// GAMMA settings +	//ili9342_write_cmd(spi, 0x26); +	//ili9342_write_data(spi, 0x04); + +	ili9342_write_cmd(spi, 0xE0); +	ili9342_write_data(spi, 0x00);  //63 +	ili9342_write_data(spi, 0x1b);  //62 +	ili9342_write_data(spi, 0x22);  //61 +	ili9342_write_data(spi, 0x05);  //59 +	ili9342_write_data(spi, 0x13);  //57 +	ili9342_write_data(spi, 0x07);  //50 +	ili9342_write_data(spi, 0x4c);  //43 +	ili9342_write_data(spi, 0xa7);  //27 +	ili9342_write_data(spi, 0x5f);  //20 +	ili9342_write_data(spi, 0x05);  //13 +	ili9342_write_data(spi, 0x0b);  //06 +	ili9342_write_data(spi, 0x09);  //04 +	ili9342_write_data(spi, 0x32);  //02 +	ili9342_write_data(spi, 0x36);  //01 +	ili9342_write_data(spi, 0x0F);  //00 + +	ili9342_write_cmd(spi, 0xE1); +	ili9342_write_data(spi, 0x00);  //00 +	ili9342_write_data(spi, 0x0c);  //01 +	ili9342_write_data(spi, 0x0d);  //02 +	ili9342_write_data(spi, 0x05);  //04 +	ili9342_write_data(spi, 0x11);  //06 +	ili9342_write_data(spi, 0x06);  //13 +	ili9342_write_data(spi, 0x30);  //20 +	ili9342_write_data(spi, 0x58);  //27 +	ili9342_write_data(spi, 0x44);  //43 +	ili9342_write_data(spi, 0x08);  //50 +	ili9342_write_data(spi, 0x14);  //57 +	ili9342_write_data(spi, 0x0c);  //59 +	ili9342_write_data(spi, 0x1e);  //61 +	ili9342_write_data(spi, 0x24);  //62 +	ili9342_write_data(spi, 0x0F);  //63 + +	ili9342_write_cmd(spi, 0x35); +	ili9342_write_data(spi, 0x00); + +	ili9342_write_cmd(spi, 0x3A); +	ili9342_write_data(spi, 0x66); + +	ili9342_write_cmd(spi, 0x11); +	msleep(120); +	ili9342_write_cmd(spi, 0x29); +} + + +static inline void init_ili9342_hw (struct spi_device *spi) { +	struct omap_dss_device *panel = spi->dev.platform_data; + +	gpio_set_value(panel->reset_gpio, 1); +	mdelay(1); +	gpio_set_value(panel->reset_gpio, 0); +	mdelay(50); +	gpio_set_value(panel->reset_gpio, 1); +	mdelay(120); + +	ili9342_init_seq(spi); +} + +static inline int init_ili9342_spi(struct spi_device *spi) { +	struct omap_dss_device *panel = spi->dev.platform_data; +	int reset_gpio; + +	if(!panel->reset_gpio) { +		dev_err(&spi->dev, "platform data requires reset\n"); +		return -EINVAL; +	} + +	reset_gpio = panel->reset_gpio; + +	if (!panel) { +		dev_err(&spi->dev, "no platform data\n"); +		return -EINVAL; +	} + +	if(gpio_request_one(reset_gpio, GPIOF_OUT_INIT_LOW, "ili9342-reset")) { +		dev_err(&spi->dev, "Could not request reset gpio %d", panel->reset_gpio); +		return -EINVAL; +	} + +	if(gpio_export(reset_gpio, 0)) { +		dev_err(&spi->dev, "Could not export reset gpio %d", panel->reset_gpio); +		return -EINVAL; +	} + +    init_ili9342_hw (spi); + +	return 0; +} + +static int ili9342_spi_probe(struct spi_device *spi) +{ +    int err; +    int ret; + +    spi_regulator = devm_regulator_get(&spi->dev, "vdd"); + +	if (IS_ERR(spi_regulator)) { +		dev_err(&spi->dev, "regulator get failed\n"); +		err = PTR_ERR(spi_regulator); +		spi_regulator = NULL; +		return err; +	} + +    ret = regulator_enable(spi_regulator); + +	if (ret) { +		dev_err(&spi->dev, "Failed to enable vdd: %d\n", ret); +		return ret; +	} + +	init_ili9342_spi(spi); + +	return omap_dss_register_driver(&ili9342_driver); +} + +static int ili9342_spi_remove(struct spi_device *spi) +{ +    oliodebug ("OLIO %s:%s Removing SPI\n", __FILE__, __FUNCTION__); + +	omap_dss_unregister_driver(&ili9342_driver); +	return 0; +} + +static int ili9342_suspend(struct device *dev) { +    int ret; + +    oliodebug ("OLIO %s:%s Suspending SPI for %s\n", __FILE__, __FUNCTION__, dev_name(dev)); + +    ret = regulator_disable(spi_regulator); +	if (ret) { +		dev_err(dev, "Failed to disable vdd:%d\n", +			ret); +		return ret; +	} +     +    return 0; +} + + +static int ili9342_resume(struct device *dev) { +    int ret; + +    /* HACK WARNING: We need an spi_device here. If, as can be assumed, +     * the device pointer passed in points to a device in an spi_device, +     * it's the first device in the spi_device struct. In other words,  +     * it's address is the same as the spi_device and a cast should be OK. +     */ +     +    /* IF, otoh, that's an incorrect assumption ... then this will lead  +     * to horrible crashes. But oh well, you can't win 'em all. +     */ + +    struct spi_device * spi = (struct spi_device *) dev; + +    oliodebug ("OLIO %s:%s Resuming SPI for %s\n", __FILE__, __FUNCTION__, dev_name(dev)); + +    ret = regulator_enable(spi_regulator); + +	if (ret) { +		dev_err(&spi->dev, "Failed to enable vdd: %d\n", ret); +		return ret; +	} + +	ili9342_init_seq(spi);     /* init_ili9342_hw (spi); */ + +    return 0; +} + + +static struct dev_pm_ops ili9342_pm_ops = { +    SET_SYSTEM_SLEEP_PM_OPS(ili9342_suspend, ili9342_resume) +}; + + +static struct spi_driver ili9342_spi_driver = { +	.probe = ili9342_spi_probe, +	.remove = ili9342_spi_remove, +	.driver = { +		.name = "ili9342-spi", +		.owner = THIS_MODULE, +        .pm = &ili9342_pm_ops, +	}, +}; + +module_spi_driver(ili9342_spi_driver); + +MODULE_AUTHOR("Evan Wilson <evan@oliodevice.com"); +MODULE_DESCRIPTION("ili9342 Display Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c index 7464de4c93f..18a9429d686 100644 --- a/drivers/video/omap2/omapfb/omapfb-main.c +++ b/drivers/video/omap2/omapfb/omapfb-main.c @@ -393,7 +393,7 @@ static int check_fb_res_bounds(struct fb_var_screeninfo *var)  		var->xres_virtual = var->xres;  	if (var->yres_virtual == 0) -		var->yres_virtual = var->yres; +		var->yres_virtual = 2 * var->yres;  	if (var->xres_virtual < xres_min || var->yres_virtual < yres_min)  		return -EINVAL; @@ -1571,7 +1571,7 @@ static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size,  			DBG("adjusting fb mem size for VRFB, %u -> %lu\n",  					w * h * bytespp, size);  		} else { -			size = w * h * bytespp; +			size = 2 * w * h * bytespp;  		}  	} @@ -1882,7 +1882,7 @@ static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi)  		}  		var->xres_virtual = var->xres; -		var->yres_virtual = var->yres; +		var->yres_virtual = 2 * var->yres;  		if (!var->bits_per_pixel) {  			switch (omapfb_get_recommended_bpp(fbdev, display)) { @@ -1903,7 +1903,7 @@ static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi)  		var->xres = 320;  		var->yres = 240;  		var->xres_virtual = var->xres; -		var->yres_virtual = var->yres; +		var->yres_virtual = 2 * var->yres;  		if (!var->bits_per_pixel)  			var->bits_per_pixel = 16;  	} diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index 99e379b7439..bc74c3f4c86 100644 --- a/include/linux/i2c/atmel_mxt_ts.h +++ b/include/linux/i2c/atmel_mxt_ts.h @@ -15,35 +15,16 @@  #include <linux/types.h> -/* For key_map array */ -#define MXT_NUM_GPIO		4 - -/* Orient */ -#define MXT_NORMAL		0x0 -#define MXT_DIAGONAL		0x1 -#define MXT_HORIZONTAL_FLIP	0x2 -#define MXT_ROTATED_90_COUNTER	0x3 -#define MXT_VERTICAL_FLIP	0x4 -#define MXT_ROTATED_90		0x5 -#define MXT_ROTATED_180		0x6 -#define MXT_DIAGONAL_COUNTER	0x7 -  /* The platform data for the Atmel maXTouch touchscreen driver */  struct mxt_platform_data { -	const u8 *config; -	size_t config_length; - -	unsigned int x_line; -	unsigned int y_line; -	unsigned int x_size; -	unsigned int y_size; -	unsigned int blen; -	unsigned int threshold; -	unsigned int voltage; -	unsigned char orient;  	unsigned long irqflags; -	bool is_tp; -	const unsigned int key_map[MXT_NUM_GPIO]; +	u8 t19_num_keys; +	const unsigned int *t19_keymap; +	int t15_num_keys; +	const unsigned int *t15_keymap; +	unsigned long gpio_reset; +	const char *cfg_name; +	const char *input_name;  };  #endif /* __LINUX_ATMEL_MXT_TS_H */ diff --git a/include/linux/mpu.h b/include/linux/mpu.h new file mode 100644 index 00000000000..cda715bcfa4 --- /dev/null +++ b/include/linux/mpu.h @@ -0,0 +1,111 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +*/ + +#ifndef __MPU_H_ +#define __MPU_H_ + +#ifdef __KERNEL__ +#include <linux/types.h> +#include <linux/ioctl.h> +#endif + +enum secondary_slave_type { +	SECONDARY_SLAVE_TYPE_NONE, +	SECONDARY_SLAVE_TYPE_ACCEL, +	SECONDARY_SLAVE_TYPE_COMPASS, +	SECONDARY_SLAVE_TYPE_PRESSURE, + +	SECONDARY_SLAVE_TYPE_TYPES +}; + +enum ext_slave_id { +	ID_INVALID = 0, +	GYRO_ID_MPU3050, +	GYRO_ID_MPU6050A2, +	GYRO_ID_MPU6050B1, +	GYRO_ID_MPU6050B1_NO_ACCEL, +	GYRO_ID_ITG3500, + +	ACCEL_ID_LIS331, +	ACCEL_ID_LSM303DLX, +	ACCEL_ID_LIS3DH, +	ACCEL_ID_KXSD9, +	ACCEL_ID_KXTF9, +	ACCEL_ID_BMA150, +	ACCEL_ID_BMA222, +	ACCEL_ID_BMA250, +	ACCEL_ID_ADXL34X, +	ACCEL_ID_MMA8450, +	ACCEL_ID_MMA845X, +	ACCEL_ID_MPU6050, + +	COMPASS_ID_AK8963, +	COMPASS_ID_AK8975, +	COMPASS_ID_AK8972, +	COMPASS_ID_AMI30X, +	COMPASS_ID_AMI306, +	COMPASS_ID_YAS529, +	COMPASS_ID_YAS530, +	COMPASS_ID_HMC5883, +	COMPASS_ID_LSM303DLH, +	COMPASS_ID_LSM303DLM, +	COMPASS_ID_MMC314X, +	COMPASS_ID_HSCDTD002B, +	COMPASS_ID_HSCDTD004A, +	COMPASS_ID_MLX90399, +	COMPASS_ID_AK09911, + +	PRESSURE_ID_BMP085, +	PRESSURE_ID_BMP280, +}; + +#define INV_PROD_KEY(ver, rev) (ver * 100 + rev) +/** + * struct mpu_platform_data - Platform data for the mpu driver + * @int_config:		Bits [7:3] of the int config register. + * @level_shifter:	0: VLogic, 1: VDD + * @orientation:	Orientation matrix of the gyroscope + * @sec_slave_type:     secondary slave device type, can be compass, accel, etc + * @sec_slave_id:       id of the secondary slave device + * @secondary_i2c_address: secondary device's i2c address + * @secondary_orientation: secondary device's orientation matrix + * @key:                key for MPL library. + * + * Contains platform specific information on how to configure the MPU3050 to + * work on this platform.  The orientation matricies are 3x3 rotation matricies + * that are applied to the data to rotate from the mounting orientation to the + * platform orientation.  The values must be one of 0, 1, or -1 and each row and + * column should have exactly 1 non-zero value. + */ +struct mpu_platform_data { +	__u8 int_config; +	__u8 level_shifter; +	__s8 orientation[9]; +	enum secondary_slave_type sec_slave_type; +	enum ext_slave_id sec_slave_id; +	__u16 secondary_i2c_addr; +	__s8 secondary_orientation[9]; +	__u8 key[16]; +	enum secondary_slave_type aux_slave_type; +	enum ext_slave_id aux_slave_id; +	__u16 aux_i2c_addr; + +#ifdef CONFIG_DTS_INV_MPU_IIO +	int (*power_on)(struct mpu_platform_data *); +	int (*power_off)(struct mpu_platform_data *); +	struct regulator *vdd_ana; +	struct regulator *vdd_i2c; +#endif +}; + +#endif	/* __MPU_H_ */ diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h index 211ff67e8b0..95fc482cef3 100644 --- a/include/linux/mtd/bbm.h +++ b/include/linux/mtd/bbm.h @@ -93,8 +93,6 @@ struct nand_bbt_descr {  #define NAND_BBT_CREATE_EMPTY	0x00000400  /* Search good / bad pattern through all pages of a block */  #define NAND_BBT_SCANALLPAGES	0x00000800 -/* Scan block empty during good / bad block scan */ -#define NAND_BBT_SCANEMPTY	0x00001000  /* Write bbt if neccecary */  #define NAND_BBT_WRITE		0x00002000  /* Read and write back block contents when writing bbt */ diff --git a/include/linux/platform_data/mtd-nand-omap2.h b/include/linux/platform_data/mtd-nand-omap2.h index 6bf9ef43ddb..3e9dd6676b9 100644 --- a/include/linux/platform_data/mtd-nand-omap2.h +++ b/include/linux/platform_data/mtd-nand-omap2.h @@ -1,6 +1,4 @@  /* - * arch/arm/plat-omap/include/mach/nand.h - *   * Copyright (C) 2006 Micron Technology Inc.   *   * This program is free software; you can redistribute it and/or modify @@ -23,13 +21,16 @@ enum nand_io {  };  enum omap_ecc { -		/* 1-bit ecc: stored at end of spare area */ -	OMAP_ECC_HAMMING_CODE_DEFAULT = 0, /* Default, s/w method */ -	OMAP_ECC_HAMMING_CODE_HW, /* gpmc to detect the error */ -		/* 1-bit ecc: stored at beginning of spare area as romcode */ -	OMAP_ECC_HAMMING_CODE_HW_ROMCODE, /* gpmc method & romcode layout */ -	OMAP_ECC_BCH4_CODE_HW, /* 4-bit BCH ecc code */ -	OMAP_ECC_BCH8_CODE_HW, /* 8-bit BCH ecc code */ +	/* 1-bit  ECC calculation by GPMC, Error detection by Software */ +	OMAP_ECC_HAM1_CODE_HW = 0, +	/* 4-bit  ECC calculation by GPMC, Error detection by Software */ +	OMAP_ECC_BCH4_CODE_HW_DETECTION_SW, +	/* 4-bit  ECC calculation by GPMC, Error detection by ELM */ +	OMAP_ECC_BCH4_CODE_HW, +	/* 8-bit  ECC calculation by GPMC, Error detection by Software */ +	OMAP_ECC_BCH8_CODE_HW_DETECTION_SW, +	/* 8-bit  ECC calculation by GPMC, Error detection by ELM */ +	OMAP_ECC_BCH8_CODE_HW,  };  struct gpmc_nand_regs { @@ -63,5 +64,6 @@ struct omap_nand_platform_data {  	/* for passing the partitions */  	struct device_node	*of_node; +	struct device_node	*elm_of_node;  };  #endif diff --git a/include/linux/platform_data/mtd-onenand-omap2.h b/include/linux/platform_data/mtd-onenand-omap2.h index e9a9fb188f9..56ff0e6f5ad 100644 --- a/include/linux/platform_data/mtd-onenand-omap2.h +++ b/include/linux/platform_data/mtd-onenand-omap2.h @@ -1,6 +1,4 @@  /* - * arch/arm/plat-omap/include/mach/onenand.h - *   * Copyright (C) 2006 Nokia Corporation   * Author: Juha Yrjola   * diff --git a/include/linux/tty.h b/include/linux/tty.h index 8780bd2a272..7a175b4b241 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -269,7 +269,10 @@ struct tty_struct {  	void *driver_data;  	struct list_head tty_files; -#define N_TTY_BUF_SIZE 4096 +  /* default was 4096 - at one place this is used in a short, so +   * can't be big. +   */ +#define N_TTY_BUF_SIZE 8192  	unsigned char closing:1;  	unsigned short minimum_to_wake; diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c index 66967ba6f75..12f74fb3de7 100644 --- a/sound/soc/codecs/dmic.c +++ b/sound/soc/codecs/dmic.c @@ -32,11 +32,12 @@ static struct snd_soc_dai_driver dmic_dai = {  	.capture = {  		.stream_name = "Capture",  		.channels_min = 1, -		.channels_max = 8, +		.channels_max = 2,  		.rates = SNDRV_PCM_RATE_CONTINUOUS,  		.formats = SNDRV_PCM_FMTBIT_S32_LE  			| SNDRV_PCM_FMTBIT_S24_LE  			| SNDRV_PCM_FMTBIT_S16_LE, +		.sig_bits = 24  	},  }; diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 77157ed6efd..66d98e26a24 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -26,6 +26,14 @@ config SND_OMAP_SOC_MCPDM  config SND_OMAP_SOC_HDMI  	tristate +config SND_OMAP_SOC_OMAP3_H1 +	tristate "SoC Audio support for OMAP3 H1 Board" +	depends on SND_OMAP_SOC && MACH_OMAP3_H1 +	select SND_OMAP_SOC_MCBSP +	select SND_SOC_DMIC +	help +	  Say Y if you want to add support for SoC audio on the Olio H1 board. +  config SND_OMAP_SOC_N810  	tristate "SoC Audio support for Nokia N810"  	depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index 7272bf4da6d..15d3e0105ef 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile @@ -21,6 +21,7 @@ snd-soc-omap-c55-objs := omap-c55audio.o  snd-soc-omap-abe-twl6040-objs := omap-abe-twl6040.o  snd-soc-omap-twl4030-objs := omap-twl4030.o  snd-soc-omap3pandora-objs := omap3pandora.o +snd-soc-omap3h1-objs := omap3h1.o  snd-soc-omap-hdmi-card-objs := omap-hdmi-card.o  obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o @@ -33,4 +34,5 @@ obj-$(CONFIG_SND_OMAP_SOC_C55) += snd-soc-omap-c55.o  obj-$(CONFIG_SND_OMAP_SOC_OMAP_ABE_TWL6040) += snd-soc-omap-abe-twl6040.o  obj-$(CONFIG_SND_OMAP_SOC_OMAP_TWL4030) += snd-soc-omap-twl4030.o  obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o +obj-$(CONFIG_SND_OMAP_SOC_OMAP3_H1) += snd-soc-omap3h1.o  obj-$(CONFIG_SND_OMAP_SOC_OMAP_HDMI) += snd-soc-omap-hdmi-card.o diff --git a/sound/soc/omap/omap3h1.c b/sound/soc/omap/omap3h1.c new file mode 100644 index 00000000000..179c53d4655 --- /dev/null +++ b/sound/soc/omap/omap3h1.c @@ -0,0 +1,147 @@ +/* + * omap3h1.c  --  SoC audio for OMAP3 H1 + * + * Author: Evan Wilson <evan@oliodevices.com> + * + * Adapted from omap3pandora + * Author: Steve Sakoman <steve@sakoman.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This 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/clk.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> + +#include <asm/mach-types.h> +#include <linux/platform_data/asoc-ti-mcbsp.h> + +#include "omap-mcbsp.h" + +static int omap3h1_hw_params(struct snd_pcm_substream *substream, +	struct snd_pcm_hw_params *params) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_dai *cpu_dai = rtd->cpu_dai; +	//unsigned int fmt; +	int ret = 0; +	unsigned int freq; + +	//freq = 256 * params_rate(params); +    // We are triggering from the 96 MHz FSCK +	pr_info("ASoc OMAP3H1: setting system clock to: %d", freq); +	ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_FCLK, +				     96000000, SND_SOC_CLOCK_OUT); +	if (ret < 0) { +		printk(KERN_ERR "can't set DMIC cpu system clock\n"); +		return ret; +	} + +	// Set the divider so that our clock rate is ~3KHz, this is about 48KHz sampling frequency on the mic +	// (32-bit/channel, 2 channels, 48 Khz sampling) +	ret = snd_soc_dai_set_clkdiv(cpu_dai, OMAP_MCBSP_CLKGDV, 32); +	if (ret < 0) { +		pr_err("ASoc OMAP3H1: can't set SRG clock divider\n"); +		return ret; +	} + +	return 0; +} + +static struct snd_soc_ops omap3h1_ops = { +	.hw_params = omap3h1_hw_params, +}; + +//static const struct snd_soc_dapm_widget dmic_dapm_widgets[] = { +//	SND_SOC_DAPM_MIC("Digital Mic", NULL), +//}; +// +//static const struct snd_soc_dapm_route dmic_audio_map[] = { +//	{"DMic", NULL, "Digital Mic"}, +//}; + +/* Digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link omap3h1_dai = { +		.name = "DMIC", +		.stream_name = "DMIC Capture", +		.cpu_dai_name = "omap-mcbsp.3", +		.codec_dai_name = "dmic-hifi", +		.platform_name = "omap-pcm-audio", +		.codec_name = "dmic-codec", +		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | +				SND_SOC_DAIFMT_CBS_CFS, +		.ops = &omap3h1_ops, +}; + +/* Audio machine driver */ +static struct snd_soc_card snd_soc_omap3h1 = { +	.name = "omap3h1", +	.owner = THIS_MODULE, +	.dai_link = &omap3h1_dai, +	.num_links = 1, + +//	.dapm_widgets = dmic_dapm_widgets, +//	.num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets), +//	.dapm_routes = dmic_audio_map, +//	.num_dapm_routes = ARRAY_SIZE(dmic_audio_map), +}; + +static struct platform_device *omap3h1_snd_device; + +static int __init omap3h1_soc_init(void) +{ +	int ret; + +	if (!(machine_is_omap3_h1())) +		return -ENODEV; +	pr_info("OMAP3 H1 SoC init\n"); + +	omap3h1_snd_device = platform_device_alloc("soc-audio", -1); +	if (!omap3h1_snd_device) { +		printk(KERN_ERR "Platform device allocation failed\n"); +		return -ENOMEM; +	} + +	platform_set_drvdata(omap3h1_snd_device, &snd_soc_omap3h1); + +	ret = platform_device_add(omap3h1_snd_device); +	if (ret) +		goto err1; + +	return 0; + +err1: +	printk(KERN_ERR "Unable to add platform device\n"); +	platform_device_put(omap3h1_snd_device); + +	return ret; +} + +static void __exit omap3h1_soc_exit(void) +{ +	platform_device_unregister(omap3h1_snd_device); +} + +module_init(omap3h1_soc_init); +module_exit(omap3h1_soc_exit); + +MODULE_AUTHOR("Evan Wilson <evan@oliodevices.com"); +MODULE_DESCRIPTION("ALSA SoC OMAP3 H1"); +MODULE_LICENSE("GPL");  |